iiomenu.c - iomenu - interactive terminal-based selection menu Err bitreich.org 70 hgit clone git://bitreich.org/iomenu git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/iomenu URL:git://bitreich.org/iomenu git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/iomenu bitreich.org 70 1Log /scm/iomenu/log.gph bitreich.org 70 1Files /scm/iomenu/files.gph bitreich.org 70 1Refs /scm/iomenu/refs.gph bitreich.org 70 1Tags /scm/iomenu/tag bitreich.org 70 1README /scm/iomenu/file/README.gph bitreich.org 70 1LICENSE /scm/iomenu/file/LICENSE.gph bitreich.org 70 i--- Err bitreich.org 70 iiomenu.c (8228B) Err bitreich.org 70 i--- Err bitreich.org 70 i 1 #include Err bitreich.org 70 i 2 #include Err bitreich.org 70 i 3 #include Err bitreich.org 70 i 4 #include Err bitreich.org 70 i 5 #include Err bitreich.org 70 i 6 #include Err bitreich.org 70 i 7 #include Err bitreich.org 70 i 8 #include Err bitreich.org 70 i 9 #include Err bitreich.org 70 i 10 #include Err bitreich.org 70 i 11 #include Err bitreich.org 70 i 12 #include Err bitreich.org 70 i 13 #include Err bitreich.org 70 i 14 #include "compat.h" Err bitreich.org 70 i 15 #include "term.h" Err bitreich.org 70 i 16 #include "utf8.h" Err bitreich.org 70 i 17 Err bitreich.org 70 i 18 struct { Err bitreich.org 70 i 19 char input[256]; Err bitreich.org 70 i 20 size_t cur; Err bitreich.org 70 i 21 Err bitreich.org 70 i 22 char **lines_buf; Err bitreich.org 70 i 23 size_t lines_count; Err bitreich.org 70 i 24 Err bitreich.org 70 i 25 char **match_buf; Err bitreich.org 70 i 26 size_t match_count; Err bitreich.org 70 i 27 } ctx; Err bitreich.org 70 i 28 Err bitreich.org 70 i 29 int flag_comment; Err bitreich.org 70 i 30 Err bitreich.org 70 i 31 /* Err bitreich.org 70 i 32 * Keep the line if it match every token (in no particular order, Err bitreich.org 70 i 33 * and allowed to be overlapping). Err bitreich.org 70 i 34 */ Err bitreich.org 70 i 35 static int Err bitreich.org 70 i 36 match_line(char *line, char **tokv) Err bitreich.org 70 i 37 { Err bitreich.org 70 i 38 if (flag_comment && line[0] == '#') Err bitreich.org 70 i 39 return 2; Err bitreich.org 70 i 40 for (; *tokv != NULL; tokv++) Err bitreich.org 70 i 41 if (strcasestr(line, *tokv) == NULL) Err bitreich.org 70 i 42 return 0; Err bitreich.org 70 i 43 return 1; Err bitreich.org 70 i 44 } Err bitreich.org 70 i 45 Err bitreich.org 70 i 46 /* Err bitreich.org 70 i 47 * Free the structures, reset the terminal state and exit with an Err bitreich.org 70 i 48 * error message. Err bitreich.org 70 i 49 */ Err bitreich.org 70 i 50 static void Err bitreich.org 70 i 51 die(const char *msg) Err bitreich.org 70 i 52 { Err bitreich.org 70 i 53 int e = errno; Err bitreich.org 70 i 54 Err bitreich.org 70 i 55 term_raw_off(2); Err bitreich.org 70 i 56 Err bitreich.org 70 i 57 fprintf(stderr, "iomenu: "); Err bitreich.org 70 i 58 errno = e; Err bitreich.org 70 i 59 perror(msg); Err bitreich.org 70 i 60 Err bitreich.org 70 i 61 exit(1); Err bitreich.org 70 i 62 } Err bitreich.org 70 i 63 Err bitreich.org 70 i 64 void * Err bitreich.org 70 i 65 xrealloc(void *ptr, size_t sz) Err bitreich.org 70 i 66 { Err bitreich.org 70 i 67 ptr = realloc(ptr, sz); Err bitreich.org 70 i 68 if (ptr == NULL) Err bitreich.org 70 i 69 die("realloc"); Err bitreich.org 70 i 70 return ptr; Err bitreich.org 70 i 71 } Err bitreich.org 70 i 72 Err bitreich.org 70 i 73 void * Err bitreich.org 70 i 74 xmalloc(size_t sz) Err bitreich.org 70 i 75 { Err bitreich.org 70 i 76 void *ptr; Err bitreich.org 70 i 77 Err bitreich.org 70 i 78 ptr = malloc(sz); Err bitreich.org 70 i 79 if (ptr == NULL) Err bitreich.org 70 i 80 die("malloc"); Err bitreich.org 70 i 81 return ptr; Err bitreich.org 70 i 82 } Err bitreich.org 70 i 83 Err bitreich.org 70 i 84 static void Err bitreich.org 70 i 85 do_move(int sign) Err bitreich.org 70 i 86 { Err bitreich.org 70 i 87 /* integer overflow will do what we need */ Err bitreich.org 70 i 88 for (size_t i = ctx.cur + sign; i < ctx.match_count; i += sign) { Err bitreich.org 70 i 89 if (flag_comment == 0 || ctx.match_buf[i][0] != '#') { Err bitreich.org 70 i 90 ctx.cur = i; Err bitreich.org 70 i 91 break; Err bitreich.org 70 i 92 } Err bitreich.org 70 i 93 } Err bitreich.org 70 i 94 } Err bitreich.org 70 i 95 Err bitreich.org 70 i 96 /* Err bitreich.org 70 i 97 * First split input into token, then match every token independently against Err bitreich.org 70 i 98 * every line. The matching lines fills matches. Matches are searched inside Err bitreich.org 70 i 99 * of `searchv' of size `searchc' Err bitreich.org 70 i 100 */ Err bitreich.org 70 i 101 static void Err bitreich.org 70 i 102 do_filter(char **search_buf, size_t search_count) Err bitreich.org 70 i 103 { Err bitreich.org 70 i 104 char **t, *tokv[(sizeof ctx.input + 1) * sizeof(char *)]; Err bitreich.org 70 i 105 char *b, buf[sizeof ctx.input]; Err bitreich.org 70 i 106 Err bitreich.org 70 i 107 strlcpy(buf, ctx.input, sizeof buf); Err bitreich.org 70 i 108 Err bitreich.org 70 i 109 for (b = buf, t = tokv; (*t = strsep(&b, " \t")) != NULL; t++) Err bitreich.org 70 i 110 continue; Err bitreich.org 70 i 111 *t = NULL; Err bitreich.org 70 i 112 Err bitreich.org 70 i 113 ctx.cur = ctx.match_count = 0; Err bitreich.org 70 i 114 for (size_t n = 0; n < search_count; n++) Err bitreich.org 70 i 115 if (match_line(search_buf[n], tokv)) Err bitreich.org 70 i 116 ctx.match_buf[ctx.match_count++] = search_buf[n]; Err bitreich.org 70 i 117 if (flag_comment && ctx.match_buf[ctx.cur][0] == '#') Err bitreich.org 70 i 118 do_move(+1); Err bitreich.org 70 i 119 } Err bitreich.org 70 i 120 Err bitreich.org 70 i 121 static void Err bitreich.org 70 i 122 do_move_page(signed int sign) Err bitreich.org 70 i 123 { Err bitreich.org 70 i 124 int rows = term.winsize.ws_row - 1; Err bitreich.org 70 i 125 size_t i = ctx.cur - ctx.cur % rows + rows * sign; Err bitreich.org 70 i 126 Err bitreich.org 70 i 127 if (i >= ctx.match_count) Err bitreich.org 70 i 128 return; Err bitreich.org 70 i 129 ctx.cur = i - 1; Err bitreich.org 70 i 130 Err bitreich.org 70 i 131 do_move(+1); Err bitreich.org 70 i 132 } Err bitreich.org 70 i 133 Err bitreich.org 70 i 134 static void Err bitreich.org 70 i 135 do_move_header(signed int sign) Err bitreich.org 70 i 136 { Err bitreich.org 70 i 137 do_move(sign); Err bitreich.org 70 i 138 Err bitreich.org 70 i 139 if (flag_comment == 0) Err bitreich.org 70 i 140 return; Err bitreich.org 70 i 141 for (ctx.cur += sign;; ctx.cur += sign) { Err bitreich.org 70 i 142 char *cur = ctx.match_buf[ctx.cur]; Err bitreich.org 70 i 143 Err bitreich.org 70 i 144 if (ctx.cur >= ctx.match_count) { Err bitreich.org 70 i 145 ctx.cur--; Err bitreich.org 70 i 146 break; Err bitreich.org 70 i 147 } Err bitreich.org 70 i 148 if (cur[0] == '#') Err bitreich.org 70 i 149 break; Err bitreich.org 70 i 150 } Err bitreich.org 70 i 151 Err bitreich.org 70 i 152 do_move(+1); Err bitreich.org 70 i 153 } Err bitreich.org 70 i 154 Err bitreich.org 70 i 155 static void Err bitreich.org 70 i 156 do_remove_word(void) Err bitreich.org 70 i 157 { Err bitreich.org 70 i 158 int len, i; Err bitreich.org 70 i 159 Err bitreich.org 70 i 160 len = strlen(ctx.input) - 1; Err bitreich.org 70 i 161 for (i = len; i >= 0 && isspace(ctx.input[i]); i--) Err bitreich.org 70 i 162 ctx.input[i] = '\0'; Err bitreich.org 70 i 163 len = strlen(ctx.input) - 1; Err bitreich.org 70 i 164 for (i = len; i >= 0 && !isspace(ctx.input[i]); i--) Err bitreich.org 70 i 165 ctx.input[i] = '\0'; Err bitreich.org 70 i 166 do_filter(ctx.lines_buf, ctx.lines_count); Err bitreich.org 70 i 167 } Err bitreich.org 70 i 168 Err bitreich.org 70 i 169 static void Err bitreich.org 70 i 170 do_add_char(char c) Err bitreich.org 70 i 171 { Err bitreich.org 70 i 172 int len; Err bitreich.org 70 i 173 Err bitreich.org 70 i 174 len = strlen(ctx.input); Err bitreich.org 70 i 175 if (len + 1 == sizeof ctx.input) Err bitreich.org 70 i 176 return; Err bitreich.org 70 i 177 if (isprint(c)) { Err bitreich.org 70 i 178 ctx.input[len] = c; Err bitreich.org 70 i 179 ctx.input[len + 1] = '\0'; Err bitreich.org 70 i 180 } Err bitreich.org 70 i 181 do_filter(ctx.match_buf, ctx.match_count); Err bitreich.org 70 i 182 } Err bitreich.org 70 i 183 Err bitreich.org 70 i 184 static void Err bitreich.org 70 i 185 do_print_selection(void) Err bitreich.org 70 i 186 { Err bitreich.org 70 i 187 if (flag_comment) { Err bitreich.org 70 i 188 char **match = ctx.match_buf + ctx.cur; Err bitreich.org 70 i 189 Err bitreich.org 70 i 190 while (--match >= ctx.match_buf) { Err bitreich.org 70 i 191 if ((*match)[0] == '#') { Err bitreich.org 70 i 192 fprintf(stdout, "%s", *match + 1); Err bitreich.org 70 i 193 break; Err bitreich.org 70 i 194 } Err bitreich.org 70 i 195 } Err bitreich.org 70 i 196 fprintf(stdout, "%c", '\t'); Err bitreich.org 70 i 197 } Err bitreich.org 70 i 198 term_raw_off(2); Err bitreich.org 70 i 199 if (ctx.match_count == 0 Err bitreich.org 70 i 200 || (flag_comment && ctx.match_buf[ctx.cur][0] == '#')) Err bitreich.org 70 i 201 fprintf(stdout, "%s\n", ctx.input); Err bitreich.org 70 i 202 else Err bitreich.org 70 i 203 fprintf(stdout, "%s\n", ctx.match_buf[ctx.cur]); Err bitreich.org 70 i 204 term_raw_on(2); Err bitreich.org 70 i 205 } Err bitreich.org 70 i 206 Err bitreich.org 70 i 207 /* Err bitreich.org 70 i 208 * Big case table, that calls itself back for with TERM_KEY_ALT (aka Esc), TERM_KEY_CSI Err bitreich.org 70 i 209 * (aka Esc + [). These last two have values above the range of ASCII. Err bitreich.org 70 i 210 */ Err bitreich.org 70 i 211 static int Err bitreich.org 70 i 212 key_action(void) Err bitreich.org 70 i 213 { Err bitreich.org 70 i 214 int key; Err bitreich.org 70 i 215 Err bitreich.org 70 i 216 key = term_get_key(stderr); Err bitreich.org 70 i 217 switch (key) { Err bitreich.org 70 i 218 case TERM_KEY_CTRL('Z'): Err bitreich.org 70 i 219 term_raw_off(2); Err bitreich.org 70 i 220 kill(getpid(), SIGSTOP); Err bitreich.org 70 i 221 term_raw_on(2); Err bitreich.org 70 i 222 break; Err bitreich.org 70 i 223 case TERM_KEY_CTRL('C'): Err bitreich.org 70 i 224 case TERM_KEY_CTRL('D'): Err bitreich.org 70 i 225 return -1; Err bitreich.org 70 i 226 case TERM_KEY_CTRL('U'): Err bitreich.org 70 i 227 ctx.input[0] = '\0'; Err bitreich.org 70 i 228 do_filter(ctx.lines_buf, ctx.lines_count); Err bitreich.org 70 i 229 break; Err bitreich.org 70 i 230 case TERM_KEY_CTRL('W'): Err bitreich.org 70 i 231 do_remove_word(); Err bitreich.org 70 i 232 break; Err bitreich.org 70 i 233 case TERM_KEY_DELETE: Err bitreich.org 70 i 234 case TERM_KEY_BACKSPACE: Err bitreich.org 70 i 235 ctx.input[strlen(ctx.input) - 1] = '\0'; Err bitreich.org 70 i 236 do_filter(ctx.lines_buf, ctx.lines_count); Err bitreich.org 70 i 237 break; Err bitreich.org 70 i 238 case TERM_KEY_ARROW_UP: Err bitreich.org 70 i 239 case TERM_KEY_CTRL('P'): Err bitreich.org 70 i 240 do_move(-1); Err bitreich.org 70 i 241 break; Err bitreich.org 70 i 242 case TERM_KEY_ALT('p'): Err bitreich.org 70 i 243 do_move_header(-1); Err bitreich.org 70 i 244 break; Err bitreich.org 70 i 245 case TERM_KEY_ARROW_DOWN: Err bitreich.org 70 i 246 case TERM_KEY_CTRL('N'): Err bitreich.org 70 i 247 do_move(+1); Err bitreich.org 70 i 248 break; Err bitreich.org 70 i 249 case TERM_KEY_ALT('n'): Err bitreich.org 70 i 250 do_move_header(+1); Err bitreich.org 70 i 251 break; Err bitreich.org 70 i 252 case TERM_KEY_PAGE_UP: Err bitreich.org 70 i 253 case TERM_KEY_ALT('v'): Err bitreich.org 70 i 254 do_move_page(-1); Err bitreich.org 70 i 255 break; Err bitreich.org 70 i 256 case TERM_KEY_PAGE_DOWN: Err bitreich.org 70 i 257 case TERM_KEY_CTRL('V'): Err bitreich.org 70 i 258 do_move_page(+1); Err bitreich.org 70 i 259 break; Err bitreich.org 70 i 260 case TERM_KEY_TAB: Err bitreich.org 70 i 261 if (ctx.match_count == 0) Err bitreich.org 70 i 262 break; Err bitreich.org 70 i 263 strlcpy(ctx.input, ctx.match_buf[ctx.cur], sizeof(ctx.input)); Err bitreich.org 70 i 264 do_filter(ctx.match_buf, ctx.match_count); Err bitreich.org 70 i 265 break; Err bitreich.org 70 i 266 case TERM_KEY_ENTER: Err bitreich.org 70 i 267 case TERM_KEY_CTRL('M'): Err bitreich.org 70 i 268 do_print_selection(); Err bitreich.org 70 i 269 return 0; Err bitreich.org 70 i 270 default: Err bitreich.org 70 i 271 do_add_char(key); Err bitreich.org 70 i 272 } Err bitreich.org 70 i 273 Err bitreich.org 70 i 274 return 1; Err bitreich.org 70 i 275 } Err bitreich.org 70 i 276 Err bitreich.org 70 i 277 static void Err bitreich.org 70 i 278 print_line(char *line, int highlight) Err bitreich.org 70 i 279 { Err bitreich.org 70 i 280 if (flag_comment && line[0] == '#') { Err bitreich.org 70 i 281 fprintf(stderr, "\n\x1b[1m\r%.*s\x1b[m", Err bitreich.org 70 i 282 term_at_width(line + 1, term.winsize.ws_col, 0), line + 1); Err bitreich.org 70 i 283 } else if (highlight) { Err bitreich.org 70 i 284 fprintf(stderr, "\n\x1b[47;30m\x1b[K\r%.*s\x1b[m", Err bitreich.org 70 i 285 term_at_width(line, term.winsize.ws_col, 0), line); Err bitreich.org 70 i 286 } else { Err bitreich.org 70 i 287 fprintf(stderr, "\n%.*s", Err bitreich.org 70 i 288 term_at_width(line, term.winsize.ws_col, 0), line); Err bitreich.org 70 i 289 } Err bitreich.org 70 i 290 } Err bitreich.org 70 i 291 Err bitreich.org 70 i 292 static void Err bitreich.org 70 i 293 do_print_screen(void) Err bitreich.org 70 i 294 { Err bitreich.org 70 i 295 char **m; Err bitreich.org 70 i 296 int p, c, cols, rows; Err bitreich.org 70 i 297 size_t i; Err bitreich.org 70 i 298 Err bitreich.org 70 i 299 cols = term.winsize.ws_col; Err bitreich.org 70 i 300 rows = term.winsize.ws_row - 1; /* -1 to keep one line for user input */ Err bitreich.org 70 i 301 p = c = 0; Err bitreich.org 70 i 302 i = ctx.cur - ctx.cur % rows; Err bitreich.org 70 i 303 m = ctx.match_buf + i; Err bitreich.org 70 i 304 fprintf(stderr, "\x1b[2J"); Err bitreich.org 70 i 305 while (p < rows && i < ctx.match_count) { Err bitreich.org 70 i 306 print_line(*m, i == ctx.cur); Err bitreich.org 70 i 307 p++, i++, m++; Err bitreich.org 70 i 308 } Err bitreich.org 70 i 309 fprintf(stderr, "\x1b[H%.*s", Err bitreich.org 70 i 310 term_at_width(ctx.input, cols, c), ctx.input); Err bitreich.org 70 i 311 fflush(stderr); Err bitreich.org 70 i 312 } Err bitreich.org 70 i 313 Err bitreich.org 70 i 314 static void Err bitreich.org 70 i 315 sig_winch(int sig) Err bitreich.org 70 i 316 { Err bitreich.org 70 i 317 if (ioctl(STDERR_FILENO, TIOCGWINSZ, &term.winsize) == -1) Err bitreich.org 70 i 318 die("ioctl"); Err bitreich.org 70 i 319 do_print_screen(); Err bitreich.org 70 i 320 signal(sig, sig_winch); Err bitreich.org 70 i 321 } Err bitreich.org 70 i 322 Err bitreich.org 70 i 323 static void Err bitreich.org 70 i 324 usage(char const *arg0) Err bitreich.org 70 i 325 { Err bitreich.org 70 i 326 fprintf(stderr, "usage: %s [-#] 0;) { Err bitreich.org 70 i 388 switch (opt) { Err bitreich.org 70 i 389 case 'v': Err bitreich.org 70 i 390 fprintf(stdout, "%s\n", VERSION); Err bitreich.org 70 i 391 exit(0); Err bitreich.org 70 i 392 case '#': Err bitreich.org 70 i 393 flag_comment = 1; Err bitreich.org 70 i 394 break; Err bitreich.org 70 i 395 default: Err bitreich.org 70 i 396 usage(arg0); Err bitreich.org 70 i 397 } Err bitreich.org 70 i 398 } Err bitreich.org 70 i 399 argc -= optind; Err bitreich.org 70 i 400 argv += optind; Err bitreich.org 70 i 401 Err bitreich.org 70 i 402 read_stdin(&buf); Err bitreich.org 70 i 403 split_lines(buf); Err bitreich.org 70 i 404 Err bitreich.org 70 i 405 do_filter(ctx.lines_buf, ctx.lines_count); Err bitreich.org 70 i 406 Err bitreich.org 70 i 407 if (!isatty(2)) Err bitreich.org 70 i 408 die("file descriptor 2 (stderr)"); Err bitreich.org 70 i 409 Err bitreich.org 70 i 410 freopen("/dev/tty", "w+", stderr); Err bitreich.org 70 i 411 if (stderr == NULL) Err bitreich.org 70 i 412 die("re-opening standard error read/write"); Err bitreich.org 70 i 413 Err bitreich.org 70 i 414 term_raw_on(2); Err bitreich.org 70 i 415 sig_winch(SIGWINCH); Err bitreich.org 70 i 416 Err bitreich.org 70 i 417 #ifdef __OpenBSD__ Err bitreich.org 70 i 418 pledge("stdio tty", NULL); Err bitreich.org 70 i 419 #endif Err bitreich.org 70 i 420 Err bitreich.org 70 i 421 while (key_action() > 0) Err bitreich.org 70 i 422 do_print_screen(); Err bitreich.org 70 i 423 Err bitreich.org 70 i 424 term_raw_off(2); Err bitreich.org 70 i 425 Err bitreich.org 70 i 426 return 0; Err bitreich.org 70 i 427 } Err bitreich.org 70 .