From 8bd8422031eba0f98a904ef20a57507df4df21d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Andr=C3=A9=20Tanner?= Date: Mon, 23 May 2016 17:14:32 +0200 Subject: vis-menu: more cleanup --- vis-menu.c | 414 +++++++++++++++++++++++++++++-------------------------------- 1 file changed, 195 insertions(+), 219 deletions(-) diff --git a/vis-menu.c b/vis-menu.c index 65aeea1..5883a0d 100644 --- a/vis-menu.c +++ b/vis-menu.c @@ -46,11 +46,10 @@ #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) -enum Color { +typedef enum { C_Normal, C_Reverse -}; -typedef enum Color Color; +} Color; typedef struct Item Item; struct Item { @@ -58,24 +57,6 @@ struct Item { Item *left, *right; }; -static void appenditem(Item*, Item**, Item**); -static void calcoffsets(void); -static void cleanup(void); -static void die(const char*); -static void drawtext(const char*, size_t, Color); -static void drawmenu(void); -static char *fstrstr(const char*, const char*); -static void insert(const char*, ssize_t); -static void match(); -static size_t nextrune(int); -static void readstdin(void); -static int run(void); -static void resetline(void); -static void setup(void); -static size_t textw(const char*); -static size_t textwn(const char*, int); -static void xread(int fildes, void *buf, size_t nbyte); - static char text[BUFSIZ] = ""; static int barpos = 0; static int mw, mh; @@ -89,9 +70,9 @@ static Item *prev, *curr, *next, *sel; static struct termios tio_old, tio_new; static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; -void +static void appenditem(Item *item, Item **list, Item **last) { - if(!*last) + if (!*last) *list = item; else (*last)->right = item; @@ -100,47 +81,60 @@ appenditem(Item *item, Item **list, Item **last) { *last = item; } -void +static size_t +textwn(const char *s, int l) { + int b, c; /* bytes and UTF-8 characters */ + + for(b=c=0; s && s[b] && (l<0 || b0) + if (lines > 0) n = lines; else n = mw - (promptw + inputw + textw("<") + textw(">")); - for(i = 0, next = curr; next; next = next->right) - if((i += (lines>0 ? 1 : MIN(textw(next->text), n))) > n) + for (i = 0, next = curr; next; next = next->right) + if ((i += (lines>0 ? 1 : MIN(textw(next->text), n))) > n) break; - for(i = 0, prev = curr; prev && prev->left; prev = prev->left) - if((i += (lines>0 ? 1 : MIN(textw(prev->left->text), n))) > n) + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) + if ((i += (lines>0 ? 1 : MIN(textw(prev->left->text), n))) > n) break; } -void +static void cleanup() { - if(barpos==0) fprintf(stderr, "\n"); + if (barpos == 0) fprintf(stderr, "\n"); else fprintf(stderr, "\033[G\033[K"); tcsetattr(0, TCSANOW, &tio_old); } -void +static void die(const char *s) { tcsetattr(0, TCSANOW, &tio_old); fprintf(stderr, "%s\n", s); exit(1); } -void +static void drawtext(const char *t, size_t w, Color col) { const char *prestr, *poststr; int i, tw; char *buf; - if(w<5) return; /* This is the minimum size needed to write a label: 1 char + 4 padding spaces */ - tw=w-4; /* This is the text width, without the padding */ - if((buf=calloc(1, (tw+1))) == NULL) die("Can't calloc."); - switch(col) { + if (w<5) return; /* This is the minimum size needed to write a label: 1 char + 4 padding spaces */ + tw = w-4; /* This is the text width, without the padding */ + if (!(buf = calloc(1, tw+1))) die("Can't calloc."); + switch (col) { case C_Reverse: prestr="\033[7m"; poststr="\033[0m"; @@ -151,16 +145,22 @@ drawtext(const char *t, size_t w, Color col) { } memset(buf, ' ', tw); - buf[tw]='\0'; + buf[tw] = '\0'; memcpy(buf, t, MIN(strlen(t), tw)); - if(textw(t)>w) /* Remember textw returns the width WITH padding */ - for(i=MAX((tw-4), 0); i w) /* Remember textw returns the width WITH padding */ + for (i = MAX((tw-4), 0); i < tw; i++) buf[i] = '.'; fprintf(stderr, "%s %s %s", prestr, buf, poststr); free(buf); } -void +static void +resetline(void) { + if (barpos != 0) fprintf(stderr, "\033[%iH", barpos > 0 ? 0 : (mh-lines)); + else fprintf(stderr, "\033[%iF", lines); +} + +static void drawmenu(void) { Item *item; int rw; @@ -172,29 +172,29 @@ drawmenu(void) { fprintf(stderr, "\033[0G"); fprintf(stderr, "\033[K"); - if(prompt) + if (prompt) drawtext(prompt, promptw, C_Reverse); - drawtext(text, ((lines==0 && matches)?inputw:mw-promptw), C_Normal); + drawtext(text, (lines==0 && matches) ? inputw : mw-promptw, C_Normal); - if(lines>0) { - if(barpos!=0) resetline(); - for(rw=0, item = curr; item != next; rw++, item = item->right) { + if (lines > 0) { + if (barpos != 0) resetline(); + for (rw = 0, item = curr; item != next; rw++, item = item->right) { fprintf(stderr, "\n"); drawtext(item->text, mw, (item == sel) ? C_Reverse : C_Normal); } - for(; rwleft) + } else if (matches) { + rw = mw-(4+promptw+inputw); + if (curr->left) drawtext("<", 5 /*textw("<")*/, C_Normal); - for(item = curr; item != next; item = item->right) { + for (item = curr; item != next; item = item->right) { drawtext(item->text, MIN(textw(item->text), rw), (item == sel) ? C_Reverse : C_Normal); - if((rw-= textw(item->text)) <= 0) break; + if ((rw -= textw(item->text)) <= 0) break; } - if(next) { + if (next) { fprintf(stderr, "\033[%iG", mw-5); drawtext(">", 5 /*textw(">")*/, C_Normal); } @@ -203,27 +203,14 @@ drawmenu(void) { fprintf(stderr, "\033[%iG", (int)(promptw+textwn(text, cursor)-1)); } -char* +static char* fstrstr(const char *s, const char *sub) { - size_t len; - - for(len = strlen(sub); *s; s++) - if(!fstrncmp(s, sub, len)) - return (char *)s; + for (size_t len = strlen(sub); *s; s++) + if (!fstrncmp(s, sub, len)) + return (char*)s; return NULL; } -void -insert(const char *str, ssize_t n) { - if(strlen(text) + n > sizeof text - 1) - return; - memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); - if(n > 0) - memcpy(&text[cursor], str, n); - cursor += n; - match(); -} - static void match(void) { @@ -278,7 +265,18 @@ match(void) calcoffsets(); } -size_t +static void +insert(const char *str, ssize_t n) { + if (strlen(text) + n > sizeof text - 1) + return; + memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); + if (n > 0) + memcpy(&text[cursor], str, n); + cursor += n; + match(); +} + +static size_t nextrune(int inc) { ssize_t n; @@ -286,42 +284,85 @@ nextrune(int inc) { return n; } -void +static void readstdin() { char buf[sizeof text], *p, *maxstr = NULL; size_t i, max = 0, size = 0; for(i = 0; fgets(buf, sizeof buf, stdin); i++) { - if(i+1 >= size / sizeof *items) - if(!(items = realloc(items, (size += BUFSIZ)))) + if (i+1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) die("Can't realloc."); - if((p = strchr(buf, '\n'))) + if ((p = strchr(buf, '\n'))) *p = '\0'; - if(!(items[i].text = strdup(buf))) + if (!(items[i].text = strdup(buf))) die("Can't strdup."); - if(strlen(items[i].text) > max) + if (strlen(items[i].text) > max) max = textw(maxstr = items[i].text); } - if(items) + if (items) items[i].text = NULL; inputw = textw(maxstr); } -void -resetline(void) { - if(barpos!=0) fprintf(stderr, "\033[%iH", (barpos>0)?0:(mh-lines)); - else fprintf(stderr, "\033[%iF", lines); +static void +xread(int fd, void *buf, size_t nbyte) { + if (read(fd, buf, nbyte) < 0) + die("Can not read."); } -int +static void +setup(void) { + int fd, result = -1; + struct winsize ws; + + /* re-open stdin to read keyboard */ + if (!freopen("/dev/tty", "r", stdin)) die("Can't reopen tty."); + + /* ioctl() the tty to get size */ + fd = open("/dev/tty", O_RDWR); + if (fd == -1) { + mh = 24; + mw = 80; + } else { + result = ioctl(fd, TIOCGWINSZ, &ws); + close(fd); + if (result < 0) { + mw = 80; + mh = 24; + } else { + mw = ws.ws_col; + mh = ws.ws_row; + } + } + + /* change terminal attributes, save old */ + tcgetattr(0, &tio_old); + tio_new = tio_old; + tio_new.c_iflag &= ~(BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + tio_new.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + tio_new.c_cflag &= ~(CSIZE|PARENB); + tio_new.c_cflag |= CS8; + tio_new.c_cc[VMIN] = 1; + tcsetattr(0, TCSANOW, &tio_new); + + lines = MIN(MAX(lines, 0), mh); + promptw = prompt ? textw(prompt) : 0; + inputw = MIN(inputw, mw/3); + match(); + if (barpos != 0) resetline(); + drawmenu(); +} + +static int run(void) { char buf[32]; char c; - while(1) { + for (;;) { xread(0, &c, 1); memset(buf, '\0', sizeof buf); - buf[0]=c; + buf[0] = c; switch_top: switch(c) { case CONTROL('['): @@ -329,7 +370,7 @@ run(void) { esc_switch_top: switch(c) { case CONTROL('['): /* ESC, need to press twice due to console limitations */ - c=CONTROL('C'); + c = CONTROL('C'); goto switch_top; case '[': xread(0, &c, 1); @@ -337,68 +378,72 @@ run(void) { case '1': /* Home */ case '7': case 'H': - if(c!='H') xread(0, &c, 1); /* Remove trailing '~' from stdin */ - c=CONTROL('A'); + if (c != 'H') xread(0, &c, 1); /* Remove trailing '~' from stdin */ + c = CONTROL('A'); goto switch_top; case '3': /* Delete */ xread(0, &c, 1); /* Remove trailing '~' from stdin */ - c=CONTROL('D'); + c = CONTROL('D'); goto switch_top; case '4': /* End */ case '8': case 'F': - if(c!='F') xread(0, &c, 1); /* Remove trailing '~' from stdin */ - c=CONTROL('E'); + if (c != 'F') xread(0, &c, 1); /* Remove trailing '~' from stdin */ + c = CONTROL('E'); goto switch_top; case '5': /* PageUp */ xread(0, &c, 1); /* Remove trailing '~' from stdin */ - c=CONTROL('V'); + c = CONTROL('V'); goto switch_top; case '6': /* PageDown */ xread(0, &c, 1); /* Remove trailing '~' from stdin */ - c='v'; + c = 'v'; goto esc_switch_top; case 'A': /* Up arrow */ - c=CONTROL('P'); + c = CONTROL('P'); goto switch_top; case 'B': /* Down arrow */ - c=CONTROL('N'); + c = CONTROL('N'); goto switch_top; case 'C': /* Right arrow */ - c=CONTROL('F'); + c = CONTROL('F'); goto switch_top; case 'D': /* Left arrow */ - c=CONTROL('B'); + c = CONTROL('B'); goto switch_top; } break; case 'b': - while(cursor > 0 && text[nextrune(-1)] == ' ') + while (cursor > 0 && text[nextrune(-1)] == ' ') cursor = nextrune(-1); - while(cursor > 0 && text[nextrune(-1)] != ' ') + while (cursor > 0 && text[nextrune(-1)] != ' ') cursor = nextrune(-1); break; case 'f': - while(text[cursor] != '\0' && text[nextrune(+1)] == ' ') - cursor = nextrune(+1); - if(text[cursor] != '\0') do + while (text[cursor] != '\0' && text[nextrune(+1)] == ' ') cursor = nextrune(+1); - while(text[cursor] != '\0' && text[cursor] != ' '); + if (text[cursor] != '\0') { + do { + cursor = nextrune(+1); + } while (text[cursor] != '\0' && text[cursor] != ' '); + } break; case 'd': - while(text[cursor] != '\0' && text[nextrune(+1)] == ' ') { + while (text[cursor] != '\0' && text[nextrune(+1)] == ' ') { cursor = nextrune(+1); insert(NULL, nextrune(-1) - cursor); } - if(text[cursor] != '\0') do { - cursor = nextrune(+1); - insert(NULL, nextrune(-1) - cursor); - } while(text[cursor] != '\0' && text[cursor] != ' '); + if (text[cursor] != '\0') { + do { + cursor = nextrune(+1); + insert(NULL, nextrune(-1) - cursor); + } while (text[cursor] != '\0' && text[cursor] != ' '); + } break; case 'v': - if(!next) + if (!next) break; - sel=curr=next; + sel = curr = next; calcoffsets(); break; default: @@ -409,7 +454,7 @@ run(void) { return EXIT_FAILURE; case CONTROL('M'): /* Return */ case CONTROL('J'): - if(sel) strncpy(text, sel->text, sizeof text); /* Complete the input first, when hitting return */ + if (sel) strncpy(text, sel->text, sizeof text); /* Complete the input first, when hitting return */ cursor = strlen(text); match(); drawmenu(); @@ -419,19 +464,19 @@ run(void) { puts(text); return EXIT_SUCCESS; case CONTROL('A'): - if(sel == matches) { - cursor=0; + if (sel == matches) { + cursor = 0; break; } - sel=curr=matches; + sel = curr = matches; calcoffsets(); break; case CONTROL('E'): - if(text[cursor] != '\0') { + if (text[cursor] != '\0') { cursor = strlen(text); break; } - if(next) { + if (next) { curr = matchend; calcoffsets(); curr = prev; @@ -442,42 +487,42 @@ run(void) { sel = matchend; break; case CONTROL('B'): - if(cursor > 0 && (!sel || !sel->left || lines > 0)) { + if (cursor > 0 && (!sel || !sel->left || lines > 0)) { cursor = nextrune(-1); break; } /* fallthrough */ case CONTROL('P'): - if(sel && sel->left && (sel = sel->left)->right == curr) { + if (sel && sel->left && (sel = sel->left)->right == curr) { curr = prev; calcoffsets(); } break; case CONTROL('F'): - if(text[cursor] != '\0') { + if (text[cursor] != '\0') { cursor = nextrune(+1); break; } /* fallthrough */ case CONTROL('N'): - if(sel && sel->right && (sel = sel->right) == next) { + if (sel && sel->right && (sel = sel->right) == next) { curr = next; calcoffsets(); } break; case CONTROL('D'): - if(text[cursor] == '\0') + if (text[cursor] == '\0') break; cursor = nextrune(+1); /* fallthrough */ case CONTROL('H'): case CONTROL('?'): /* Backspace */ - if(cursor == 0) + if (cursor == 0) break; insert(NULL, nextrune(-1) - cursor); break; case CONTROL('I'): /* TAB */ - if(!sel) + if (!sel) break; strncpy(text, sel->text, sizeof text); cursor = strlen(text); @@ -491,19 +536,19 @@ run(void) { insert(NULL, 0 - cursor); break; case CONTROL('W'): - while(cursor > 0 && text[nextrune(-1)] == ' ') + while (cursor > 0 && text[nextrune(-1)] == ' ') insert(NULL, nextrune(-1) - cursor); - while(cursor > 0 && text[nextrune(-1)] != ' ') + while (cursor > 0 && text[nextrune(-1)] != ' ') insert(NULL, nextrune(-1) - cursor); break; case CONTROL('V'): - if(!prev) + if (!prev) break; sel = curr = prev; calcoffsets(); break; default: - if(!iscntrl(*buf)) + if (!iscntrl(*buf)) insert(buf, strlen(buf)); break; } @@ -511,110 +556,41 @@ run(void) { } } -void -setup(void) { - int fd, result=-1; - struct winsize ws; - - /* re-open stdin to read keyboard */ - if(freopen("/dev/tty", "r", stdin) == NULL) die("Can't reopen tty."); - - /* ioctl() the tty to get size */ - fd = open("/dev/tty", O_RDWR); - if(fd == -1) { - mh=24; - mw=80; - } else { - result = ioctl(fd, TIOCGWINSZ, &ws); - close(fd); - if(result<0) { - mw=80; - mh=24; - } else { - mw=ws.ws_col; - mh=ws.ws_row; - } - } - - /* change terminal attributes, save old */ - tcgetattr(0, &tio_old); - memcpy ((char *)&tio_new, (char *)&tio_old, sizeof(struct termios)); - tio_new.c_iflag &= ~(BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); - tio_new.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); - tio_new.c_cflag &= ~(CSIZE|PARENB); - tio_new.c_cflag |= CS8; - tio_new.c_cc[VMIN]=1; - tcsetattr(0, TCSANOW, &tio_new); - - lines=MIN(MAX(lines, 0), mh); - promptw=(prompt?textw(prompt):0); - inputw=MIN(inputw, mw/3); - match(); - if(barpos!=0) resetline(); - drawmenu(); -} - -size_t -textw(const char *s) { - return textwn(s, -1); -} - -size_t -textwn(const char *s, int l) { - int b, c; /* bytes and UTF-8 characters */ - - for(b=c=0; s && s[b] && (l<0 || b i+1) - prompt=argv[++i]; - else { - fprintf(stderr, "Must provide a prompt\n"); - exit(EXIT_FAILURE); - } - } - else if(!strcmp(argv[i], "-l")) { - if(argc > i+1) - lines = atoi(argv[++i]); - else { - fprintf(stderr, "Must provide a line count\n"); - exit(EXIT_FAILURE); - } - } - else { + } else if (!strcmp(argv[i], "-t")) { + barpos = +1; + } else if (!strcmp(argv[i], "-b")) { + barpos = -1; + } else if (argv[i][0] != '-') { strncpy(text, argv[i], sizeof(text)-1); cursor = strlen(text); + } else if (i + 1 == argc) { + usage(); + } else if (!strcmp(argv[i], "-p")) { + prompt = argv[++i]; + } else if (!strcmp(argv[i], "-l")) { + lines = atoi(argv[++i]); + } else { + usage(); } + } readstdin(); setup(); - i = run(); + int status = run(); cleanup(); - return i; + return status; } - -- cgit v1.2.3