#include #include #include #include #include #include #include #include #include #include static struct menuitem { char *cmd; //char *desc; } *menu = NULL; static int entries = 0; static int selection = 0; void bye(void) { if (!isendwin()) endwin(); } void init_menu(void) { initscr(); cbreak(); noecho(); keypad(stdscr, 1); box(stdscr, 0, 0); atexit(bye); } void menu_add(char *line) { size_t n = strlen(line); // sigh if (line[n-1] == '\n') line[--n] = '\0'; char *cmd = strchr(line, '\t'); if (cmd) *cmd++ = '\0'; else cmd = line; menu = realloc(menu, sizeof(*menu) * (entries + 1)); menu[entries].cmd = strdup(cmd); //menu[entries].desc = strdup(line); mvaddstr(++entries, 7, line); } void menu_up(void) { if (selection <= 0) return; int i; for (i = selection - 1; i >= 0; --i) if (strlen(menu[i].cmd)) { mvaddstr(selection + 1, 3, " "); selection = i; mvaddstr(selection + 1, 3, "-->"); move(selection + 1, 1); refresh(); return; } } void menu_down(void) { if (selection >= entries - 1) return; int i; for (i = selection + 1; i < entries; ++i) if (strlen(menu[i].cmd)) { mvaddstr(selection + 1, 3, " "); selection = i; mvaddstr(selection + 1, 3, "-->"); move(selection + 1, 1); refresh(); return; } } void menu_select(void) { bye(); execl("/bin/sh", "/bin/sh", "-c", menu[selection].cmd, (char *) NULL); perror("exec failed"); exit(1); } void menu_update(void) { if (!selection) menu_down(); // TODO: something else else refresh(); } int main(int argc, char *argv[]) { if (argc != 2) { fputs("usage: dynmenu socket-file\n", stderr); return 1; } int fd; // O_RDWR on FIFO; see url: // http://fixunix.com/unix/350803-use-select-fifo-after-has-closed.html if ((fd = open((const char *) argv[1], O_RDWR|O_NONBLOCK)) < 0) { perror("error opening socket-file"); return 1; } FILE *fifo; if ((fifo = fdopen(fd, "r")) == NULL) { perror("error opening socket-file: fdopen"); return 1; } init_menu(); fd_set fds; int res; FD_ZERO(&fds); FD_SET(fd, &fds); FD_SET(0, &fds); while ((res = select(fd+1, &fds, NULL, NULL, NULL)) >= 0) { if (FD_ISSET(0, &fds)) { int c = getch(); if (c == ERR) { perror("error reading stdin"); return 1; } switch (c) { case KEY_UP: case 'k': menu_up(); break; case KEY_DOWN: case 'j': menu_down(); break; case KEY_RIGHT: case KEY_ENTER: menu_select(); break; } } if (FD_ISSET(fd, &fds)) { char line[1024]; while (fgets(line, sizeof(line), fifo) != NULL) menu_add(line); // doesn't redraw right away if (ferror(fifo) && errno != EAGAIN) { perror("error reading fifo"); return 1; } menu_update(); // ok, no more lines; now redraw } FD_ZERO(&fds); if (!feof(fifo)) // will never eof if it is a fifo FD_SET(fd, &fds); // could use inotify here... FD_SET(0, &fds); } fputs("unpossible!", stderr); exit(1); }