diff options
author | Krow Savcik <krow@savcik.xyz> | 2025-08-31 15:11:01 +0200 |
---|---|---|
committer | Krow Savcik <krow@savcik.xyz> | 2025-08-31 15:11:01 +0200 |
commit | 0fa7537ce5006a9c99bc7e295a4ae4ff0b920fda (patch) | |
tree | 057a91b30ab61417970c5defe825ce16a094ac39 /renu.c |
Diffstat (limited to 'renu.c')
-rw-r--r-- | renu.c | 387 |
1 files changed, 387 insertions, 0 deletions
@@ -0,0 +1,387 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "util.h" +#include "debug.h" +#include "config.h" + +#define MAXLINE 128 + +static char menu[MAXLINE]; + +void +usage() +{ + printf("remu [FILE]\n"); + exit(0); +} + +int +launch_menu(int *pfd, char *prompt) +{ + int pipefd[4]; + pid_t pid; + if (pipe(pipefd)) + return 1; + if (pipe(&pipefd[2])) { + close(pipefd[0]); + close(pipefd[1]); + return 1; + } + + pid = fork(); + + switch (pid) { + case -1: + close(pipefd[0]); + close(pipefd[1]); + close(pipefd[2]); + close(pipefd[3]); + return 1; + case 0: + break; + default: + pfd[0] = pipefd[0]; + pfd[1] = pipefd[3]; + close(pipefd[1]); + close(pipefd[2]); + return 0; + } + + /* Child process */ + close(pipefd[0]); + close(pipefd[3]); + + if (dup2(pipefd[1], STDOUT_FILENO) == -1) + exit(1); + if (dup2(pipefd[2], STDIN_FILENO) == -1) + exit(1); + + execvp(menu_cmd[0], (char **)menu_cmd); + close(pipefd[1]); + close(pipefd[2]); + exit(0); +} + +char * +nextline(char *c) +{ + while (*c != '\n') + c++; + + return ++c; +} + +char * +setline(char *p) +{ + char *end; + end = nextline(p); + end[-1] = 0; + return end; +} + +void +resetline(char *end) +{ + end[-1] = '\n'; +} + +char * +nextarg(char *c) +{ + while (*c == '\0') + c++; + + return c; +} + +char * +process_quoted(char *pnt) +{ + size_t off = 0; + char *ret = NULL; + pnt[0] = 0; + pnt++; + while (pnt[off] != '"') { + if (pnt[off] == '\0' || pnt[off] == '\n') + return NULL; + + if (pnt[off] == '\\') { + if (pnt[off+1] == '\0' || pnt[off+1] == '\n') + return NULL; + off++; + } + pnt[0] = pnt[off]; + pnt++; + } + + for (ret = &(pnt[off+1]); pnt != ret; pnt++) + pnt[0] = 0; + + return ret; +} + +char * +process_unquoted(char *pnt) +{ + size_t off = 0; + char *ret = NULL; + while (pnt[off] && pnt[off] != '\n') { + if (pnt[off] == ' ') { + ret = &pnt[off+1]; + break; + } + + if (pnt[off] == '"') + return NULL; + + if (pnt[off] == '\\') { + if (pnt[off+1] == '\0' || pnt[off+1] == '\n') + return NULL; + off++; + } + pnt[0] = pnt[off]; + pnt++; + } + + ret = ret ? ret : &pnt[off]; + + for (; pnt != ret; pnt++) + pnt[0] = 0; + + return ret; +} + +char * +process_line(char *pnt) +{ + while (*pnt && *pnt != '\n') { + if (*pnt == '"') + pnt = process_quoted(pnt); + else + pnt = process_unquoted(pnt); + + if (pnt == NULL) + return NULL; + } + + return pnt; +} + +unsigned int +preprocess(char *buf) +{ + char *pnt = buf; + unsigned int line = 0; + while (*pnt) { + line++; + if (*pnt == '#' || *pnt == '\n') { + pnt = nextline(pnt); + continue; + } + + if (pnt[1] != ' ') + return line; + + pnt[1] = 0; + pnt = process_line(pnt+2); + if (pnt == NULL) + return line; + pnt = nextline(pnt); + } + + return 0; +} + +int +submenu(char *line) +{ + char *e, *arg, *ea; + arg = nextarg(line+2); + ea = &arg[strlen(arg) - 1]; + if (*ea == '\n') + return 1; + + arg = nextarg(ea+1); + e = setline(line); + strncpy(menu, arg, MAXLINE); + menu[MAXLINE-1] = 0; + resetline(e); + return 0; +} + +int +print(char *line) +{ + char *e, *arg, *ea; + arg = nextarg(line+2); + + ea = &arg[strlen(arg) - 1]; + if (*ea != '\n' && *nextarg(ea+1) != '\n') + arg = nextarg(ea+1); + + e = setline(line); + printf("%s\n", arg); + resetline(e); + return 0; +} + +int +run(char *line) +{ + char *e, *arg, *ea; + arg = nextarg(line+2); + + ea = &arg[strlen(arg) - 1]; + if (*ea == '\n') + return 1; + + arg = nextarg(ea+1); + e = setline(line); + if (execl("/bin/sh", "sh", "-c", arg, NULL)) + error("Error executing process: %s\n", strerror(errno)); + resetline(e); + return 0; +} + +int +main(int argc, char *argv[]) +{ + char *buf, *stop, *pnt, *end, *mpnt, *line; + unsigned int i; + int pfd[2]; + FILE *f[2]; + char option[MAXLINE]; + + strcpy(menu, default_menu); + + if (argc != 2) + usage(); + + buf = f_read(argv[1]); + if (buf == NULL) { + fprintf(stderr, "Couldn't read file %s\n", argv[1]); + return 1; + } + + stop = &buf[strlen(buf)]; + if (stop == buf) { + fprintf(stderr, "Wrong format in %s: Empty file\n", argv[1]); + return 1; + } else if (stop[-1] != '\n') { + fprintf(stderr, "Wrong format in %s: Missing EOL at the end of the file\n", argv[1]); + return 1; + } + + if (i = preprocess(buf)) { + fprintf(stderr, "Wrong format in %s:%u\n", argv[1], i); + return 1; + } + + + while (1) { + for (pnt = buf; pnt != stop; pnt = nextline(pnt)) { + if (*pnt != 'm') + continue; + pnt = nextarg(pnt+1); + end = setline(pnt); + + if (strcmp(menu, pnt) == 0) + break; + resetline(end); + } + + if (pnt == stop) { + fprintf(stderr, "Couldn't find menu %s in file %s\n", menu, argv[1]); + return 1; + } + + resetline(end); + + /* Query option */ + if (launch_menu(pfd, NULL)) { + fprintf(stderr, "Couldn't open the menu\n"); + return 1; + } + + f[0] = fdopen(pfd[0], "r"); + f[1] = fdopen(pfd[1], "w"); + + if (f[0] == NULL || f[1] == NULL) { + fprintf(stderr, "Couldn't open the menu\n"); + return 1; + } + + mpnt = nextline(pnt); + for (pnt = mpnt; pnt != stop; pnt = nextline(pnt)) { + if (*pnt == 'm') + break; + + if (*pnt == '\n' || *pnt == '#') + continue; + + pnt = nextarg(pnt+1); + end = setline(pnt); + fprintf(f[1], "%s\n", pnt); + resetline(end); + } + + fclose(f[1]); + close(pfd[1]); + + if (fgets(option, MAXLINE, f[0]) == NULL) { + fprintf(stderr, "Couldn't open the menu\n"); + return 1; + } + option[strlen(option)-1] = 0; + + for (line = mpnt; line != stop; line = nextline(line)) { + if (*line == 'm') + break; + + if (*line == '\n' || *line == '#') + continue; + + pnt = nextarg(line+1); + + if (*pnt == '\n') { + fprintf(stderr, "Wrong format in %s: Not enough arguments\n", argv[1]); + return 1; + } + + end = setline(pnt); + if (strcmp(pnt, option) == 0) + break; + resetline(end); + } + + /* No option selected */ + if (*line == 'm' || line == stop) + break; + + resetline(end); + switch (line[0]) { + case 's': + if (submenu(line)) { + fprintf(stderr, "Wrong format in %s: Not enough arguments\n", argv[1]); + return 1; + } + break; + case 'p': + print(line); + return 0; + case 'r': + if (run(line)) { + fprintf(stderr, "Wrong format in %s: Not enough arguments\n", argv[1]); + return 1; + } + return 0; + default: + return 0; + } + } +} |