sob.c (18973B)
1#include <ctype.h> 2#include <errno.h> 3#include <fcntl.h> 4#include <limits.h> 5#include <locale.h> 6#include <signal.h> 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10#include <sys/ioctl.h> 11#include <sys/select.h> 12#include <unistd.h> 13#include <termios.h> 14#include <time.h> 15#include <wchar.h> 16 17#include "arg.h" 18char *argv0; 19 20#define LEN(x) (sizeof (x) / sizeof *(x)) 21#define ISUTF8(c) (((c) & 0xc0) != 0x80) 22 23struct line { 24 char line[16384]; /* static line buffer */ 25 size_t bytesiz; /* length in bytes */ 26 size_t utflen; /* length in characters */ 27 size_t bytepos; /* index position (in bytes) */ 28 size_t utfpos; /* pos in characters */ 29 size_t colpos; /* cursor position (in columns) */ 30 size_t collen; /* total length (in columns) */ 31}; 32 33static void cb_handleinput(const char *, size_t, size_t); 34static void cb_pipe_insert(const char *, size_t, size_t); 35static void cb_pipe_replaceword(const char *, size_t, size_t); 36 37static void line_clear(void); 38static void line_copywordcursor(char *, size_t); 39static void line_cursor_begin(void); 40static void line_cursor_end(void); 41static void line_cursor_move(size_t); 42static void line_cursor_next(void); 43static void line_cursor_prev(void); 44static void line_cursor_wordprev(void); 45static void line_cursor_wordnext(void); 46static void line_delcharprev(void); 47static void line_delcharnext(void); 48static void line_deltoend(void); 49static void line_delwordprev(void); 50static void line_delwordcursor(void); 51static void line_draw(void); 52static void line_exit(void); 53static void line_getwordpos(size_t, size_t, size_t *, size_t *, size_t *, 54 size_t *); 55static void line_getwordposprev(size_t, size_t, size_t *, size_t *); 56static void line_getwordposnext(size_t, size_t, size_t *, size_t *); 57static void line_inserttext(const char *); 58static void line_newline(void); 59static void line_out(void); 60static void line_prompt(void); 61static int line_promptlen(void); 62static int line_pipeto(char **, void (*)(const char *, size_t, size_t)); 63static int line_wordpipeto(char **, 64 void (*)(const char *, size_t, size_t)); 65 66static int pipe_rw(int, int, char *, 67 void (*)(const char *, size_t, size_t)); 68static int pipe_cmd(char *[], char *, 69 void (*)(const char *, size_t, size_t)); 70 71static void clear(void); 72static void gettermsize(void); 73static void handleinput(const unsigned char *, size_t); 74static void initialinput(void); 75static void resize(void); 76static void run(void); 77static void setup(void); 78static void sighandler(int); 79static void usage(void); 80 81static size_t colw(const char *, size_t); 82static int nonspace(int c); 83static size_t utf8len(const char *); 84static size_t utfprevn(const char *, size_t, size_t); 85static size_t utfnextn(const char *, size_t, size_t); 86static void utfuntilchar(size_t *, size_t *, int (*)(int), int); 87 88static struct termios ttystate, ttysave; 89 90static struct line line; 91static size_t dirtylen; /* dirty length (in columns) */ 92static int cols = 79, rows = 24; 93static int termattrset = 0; 94static int isrunning = 1; 95static FILE * outfp = NULL; 96static FILE * lineoutfp = NULL; 97 98#include "config.h" 99 100static int 101nonspace(int c) 102{ 103 return !isspace(c); 104} 105 106static size_t 107colw(const char *s, size_t max) 108{ 109 size_t len = 0, i; 110 wchar_t w; 111 int r; 112 113 for (i = 0; *s && i < max; s++, i++) { 114 if (ISUTF8(*s)) { 115 if ((r = mbtowc(&w, s, i + 4 > max ? max - i : 4)) == -1) 116 break; 117 if ((r = wcwidth(w)) == -1) 118 r = 1; 119 len += r; 120 } 121 } 122 return len; 123} 124 125static size_t 126utf8len(const char *s) 127{ 128 size_t i; 129 130 for (i = 0; *s; s++) { 131 if (ISUTF8(*s)) 132 i++; 133 } 134 return i; 135} 136 137/* returns amount of bytes needed to go to previous utf char 138 * p is index in bytes. */ 139static size_t 140utfprevn(const char *s, size_t p, size_t n) 141{ 142 size_t i; 143 144 for (i = 1; p > 0; p--, i++) { 145 if (ISUTF8(s[p - 1]) && !--n) 146 return i; 147 } 148 return 0; 149} 150 151/* returns amount of bytes needed to go to next utf char 152 * p is index in bytes. */ 153static size_t 154utfnextn(const char *s, size_t p, size_t n) 155{ 156 size_t i; 157 158 for (i = 1; s[p]; p++, i++) { 159 if (ISUTF8(s[p + 1]) && !--n) 160 return i; 161 } 162 return 0; 163} 164 165/* b is byte start pos, u is utf pos, f is filter function, 166 * dir is -1 or +1 for prev or next */ 167static void 168utfuntilchar(size_t *b, size_t *u, int (*f)(int), int dir) 169{ 170 size_t n; 171 172 if (dir > 0) { 173 while (*u < line.utflen && *b < line.bytesiz) { 174 if (f(line.line[*b])) 175 break; 176 if ((n = utfnextn(line.line, *b, 1)) == 0) 177 break; 178 *b += n; 179 (*u)++; 180 } 181 182 } else { 183 while (*u > 0 && *b > 0) { 184 if (f(line.line[*b - 1])) 185 break; 186 if ((n = utfprevn(line.line, *b, 1)) == 0) 187 break; 188 *b -= n; 189 (*u)--; 190 } 191 } 192} 193 194static void 195line_inserttext(const char *s) 196{ 197 size_t siz, ulen, clen; 198 199 siz = strlen(s); 200 if (line.bytepos + siz + 1 > sizeof(line.line)) 201 return; 202 clen = colw(s, siz); 203 ulen = utf8len(s); 204 /* append */ 205 if (line.bytepos == line.bytesiz) { 206 memmove(&line.line[line.bytepos], s, siz); 207 } else { 208 /* insert */ 209 memmove(&line.line[line.bytepos + siz], &line.line[line.bytepos], 210 line.bytesiz - line.bytepos); 211 memcpy(&line.line[line.bytepos], s, siz); 212 } 213 line.bytepos += siz; 214 line.bytesiz += siz; 215 line.line[line.bytesiz + 1] = '\0'; 216 line.utflen = utf8len(line.line); 217 line.utfpos += ulen; 218 line.colpos += clen; 219 line.collen = colw(line.line, line.bytesiz); 220} 221 222/* like mksh, toggle counting of escape codes in prompt with "\x01" */ 223static int 224line_promptlen(void) 225{ 226 size_t i; 227 int t = 0, n = 0; 228 229 for (i = 0; prompt[i]; i++) { 230 if (prompt[i] == 1) 231 t = !t; 232 else if (!t && ISUTF8(prompt[i])) 233 n++; 234 } 235 return n; 236} 237 238static void 239line_prompt(void) 240{ 241 size_t i; 242 243 for (i = 0; prompt[i]; i++) { 244 if (prompt[i] != 1) 245 fputc(prompt[i], outfp); 246 } 247} 248 249static void 250clear(void) 251{ 252 /* clear screen, move cursor to (0, 0) */ 253 fprintf(outfp, "\x1b[2J\x1b[H"); 254 fflush(outfp); 255} 256 257static void 258line_draw(void) 259{ 260 size_t i; 261 262 fprintf(outfp, "\x1b[?25l"); /* hide cursor */ 263 fprintf(outfp, "\x1b[H"); /* move cursor to (0, 0) */ 264 265 line_prompt(); 266 fwrite(line.line, 1, line.bytesiz, outfp); 267 268 /* replace dirty chars with ' ' */ 269 if (dirtylen > line.collen) { 270 for (i = line.collen; i < dirtylen; i++) 271 fputc(' ', outfp); 272 } 273 line_cursor_move(line.colpos); 274 fprintf(outfp, "\x1b[?25h"); /* show cursor */ 275 fflush(outfp); 276} 277 278static void 279line_out(void) 280{ 281 fprintf(lineoutfp, "%s\n", line.line); 282 fflush(lineoutfp); 283} 284 285static void 286line_cursor_move(size_t newpos) 287{ 288 size_t x, y = 0; 289 290 x = newpos + line_promptlen(); 291 292 /* linewrap */ 293 if (cols > 0 && x > (size_t)cols - 1) { 294 y = (x - (x % cols)) / cols; 295 x %= cols; 296 } 297 fprintf(outfp, "\x1b[%lu;%luH", (unsigned long)y + 1, (unsigned long)x + 1); 298 fflush(outfp); 299} 300 301static void 302line_cursor_wordprev(void) 303{ 304 line_getwordposprev(line.bytepos, line.utfpos, &line.bytepos, 305 &line.utfpos); 306 line.colpos = colw(line.line, line.bytepos); 307 line_cursor_move(line.colpos); 308} 309 310static void 311line_cursor_wordnext(void) 312{ 313 line_getwordposnext(line.bytepos, line.utfpos, &line.bytepos, 314 &line.utfpos); 315 line.colpos = colw(line.line, line.bytepos); 316 line_cursor_move(line.colpos); 317} 318 319static void 320line_cursor_begin(void) 321{ 322 line.bytepos = 0; 323 line.utfpos = 0; 324 line.colpos = 0; 325 line_cursor_move(line.colpos); 326} 327 328static void 329line_cursor_prev(void) 330{ 331 size_t n; 332 333 if (line.utfpos <= 0) 334 return; 335 if ((n = utfprevn(line.line, line.bytepos, 1)) == 0) 336 return; 337 338 line.bytepos -= n; 339 line.utfpos--; 340 line.colpos -= colw(&line.line[line.bytepos], n); 341 line_cursor_move(line.colpos); 342} 343 344static void 345line_cursor_next(void) 346{ 347 size_t n; 348 349 if (line.utfpos >= line.utflen) 350 return; 351 352 if ((n = utfnextn(line.line, line.bytepos, 1)) == 0) 353 return; 354 line.colpos += colw(&line.line[line.bytepos], n); 355 line.bytepos += n; 356 line.utfpos++; 357 line_cursor_move(line.colpos); 358} 359 360static void 361line_cursor_end(void) 362{ 363 line.bytepos = line.bytesiz; 364 line.utfpos = line.utflen; 365 line.colpos = line.collen; 366 line_cursor_move(line.colpos); 367} 368 369static void 370line_clear(void) 371{ 372 memset(&line, 0, sizeof(line)); 373 line_draw(); 374} 375 376static void 377line_delcharnext(void) 378{ 379 size_t siz; 380 381 if (line.utfpos == line.utflen || line.utflen <= 0) 382 return; 383 384 if ((siz = utfnextn(line.line, line.bytepos, 1)) == 0) 385 return; 386 387 line.collen -= colw(&line.line[line.bytepos], siz); 388 389 memmove(&line.line[line.bytepos], &line.line[line.bytepos + siz], 390 line.bytesiz - line.bytepos - siz); 391 392 line.bytesiz -= siz; 393 line.line[line.bytesiz] = '\0'; 394 line.utflen--; 395 line_draw(); 396} 397 398static void 399line_delcharprev(void) 400{ 401 size_t siz, col; 402 403 if (line.utfpos <= 0 || line.utflen <= 0) 404 return; 405 if ((siz = utfprevn(line.line, line.bytepos, 1)) == 0) 406 return; 407 408 col = colw(&line.line[line.bytepos - siz], siz); 409 410 memmove(&line.line[line.bytepos - siz], &line.line[line.bytepos], 411 line.bytesiz - line.bytepos); 412 413 line.bytepos -= siz; 414 line.bytesiz -= siz; 415 line.line[line.bytesiz] = '\0'; 416 line.utflen--; 417 line.utfpos--; 418 line.colpos -= col; 419 line.collen -= col; 420 line_draw(); 421} 422 423static void 424line_deltoend(void) 425{ 426 line.line[line.bytepos] = '\0'; 427 line.bytesiz = line.bytepos; 428 line.utflen = utf8len(line.line); 429 line.utfpos = line.utflen; 430 line.collen = colw(line.line, line.bytesiz); 431 line.colpos = line.collen; 432 line_draw(); 433} 434 435static void 436line_delwordcursor(void) 437{ 438 size_t bs, be, us, ue, siz, len; 439 440 line_getwordpos(line.bytepos, line.utfpos, &bs, &be, &us, &ue); 441 442 siz = be - bs; 443 len = ue - us; 444 445 memmove(&line.line[bs], &line.line[be], line.bytesiz - be); 446 447 line.bytesiz -= siz; 448 line.bytepos = bs; 449 line.line[line.bytesiz] = '\0'; 450 line.utfpos = us; 451 line.utflen -= len; 452 line.collen = colw(line.line, line.bytesiz); 453 line.colpos = colw(line.line, bs); 454 line_draw(); 455} 456 457static void 458line_delwordprev(void) 459{ 460 size_t bs, us, siz, len; 461 462 if (line.utfpos <= 0 || line.utflen <= 0) 463 return; 464 465 line_getwordposprev(line.bytepos, line.utfpos, &bs, &us); 466 467 siz = line.bytepos - bs; 468 len = line.utfpos - us; 469 line.colpos -= colw(&line.line[bs], siz); 470 471 memmove(&line.line[bs], &line.line[line.bytepos], 472 line.bytesiz - line.bytepos); 473 474 line.bytesiz -= siz; 475 line.bytepos = bs; 476 line.line[line.bytesiz] = '\0'; 477 line.utfpos = us; 478 line.utflen -= len; 479 line.collen = colw(line.line, line.bytesiz); 480 line_draw(); 481} 482 483static void 484line_newline(void) 485{ 486 line_out(); 487 memset(&line, 0, sizeof(line)); 488 line_draw(); 489} 490 491static void 492line_exit(void) 493{ 494 fprintf(outfp, "\n"); 495 fflush(outfp); 496 isrunning = 0; 497} 498 499static void 500line_getwordpos(size_t b, size_t u, size_t *bs, size_t *be, 501 size_t *us, size_t *ue) 502{ 503 size_t tb = b, tu = u; 504 505 utfuntilchar(&b, &u, isspace, -1); 506 if (bs) 507 *bs = b; 508 if (us) 509 *us = u; 510 511 /* seek from original specified position */ 512 utfuntilchar(&tb, &tu, isspace, +1); 513 if (be) 514 *be = tb; 515 if (ue) 516 *ue = tu; 517} 518 519static void 520line_getwordposprev(size_t sb, size_t su, size_t *b, size_t *u) 521{ 522 utfuntilchar(&sb, &su, nonspace, -1); 523 utfuntilchar(&sb, &su, isspace, -1); 524 if (b) 525 *b = sb; 526 if (u) 527 *u = su; 528} 529 530static void 531line_getwordposnext(size_t sb, size_t su, size_t *b, size_t *u) 532{ 533 utfuntilchar(&sb, &su, nonspace, +1); 534 utfuntilchar(&sb, &su, isspace, +1); 535 if (b) 536 *b = sb; 537 if (u) 538 *u = su; 539} 540 541static void 542line_copywordcursor(char *buf, size_t bufsiz) 543{ 544 size_t bs, be, len; 545 546 line_getwordpos(line.bytepos, line.utfpos, &bs, &be, NULL, NULL); 547 len = be - bs; 548 549 /* truncate */ 550 if (len + 1 > bufsiz) 551 len = bufsiz - 1; 552 memcpy(buf, &line.line[bs], len); 553 buf[len] = '\0'; 554} 555 556/* It will be tried first to write to the pipe and then read. 557 * if fd_in == -1 don't read, if fd_out == -1 or writestr == NULL don't write. 558 * f is the read callback() on the data (buf, read_size, total_read). 559 * if f is NULL no data is read from the pipe. */ 560static int 561pipe_rw(int fd_in, int fd_out, char *writestr, 562 void (*f)(const char *, size_t, size_t)) 563{ 564 char buf[PIPE_BUF]; 565 struct timeval tv; 566 fd_set fdr, fdw; 567 size_t total = 0; 568 ssize_t r; 569 int maxfd, status = -1, haswritten = 0; 570 571 if (fd_out == -1 || writestr == NULL) 572 haswritten = 1; 573 574 while (isrunning) { 575 FD_ZERO(&fdr); 576 FD_ZERO(&fdw); 577 if (haswritten) { 578 if (!f || fd_in == -1) 579 break; 580 FD_SET(fd_in, &fdr); 581 maxfd = fd_in; 582 } else { 583 FD_SET(fd_out, &fdw); 584 maxfd = fd_out; 585 } 586 memset(&tv, 0, sizeof(tv)); 587 tv.tv_usec = 50000; /* 50 ms */ 588 589 if ((r = select(maxfd + 1, haswritten ? &fdr : NULL, 590 haswritten ? NULL : &fdw, NULL, &tv)) == -1) { 591 if (errno != EINTR) 592 goto fini; 593 } else if (!r) { /* timeout */ 594 continue; 595 } 596 if (fd_out != -1 && FD_ISSET(fd_out, &fdw)) { 597 if (write(fd_out, writestr, strlen(writestr)) == -1) { 598 if (errno == EWOULDBLOCK || errno == EAGAIN || 599 errno == EINTR) 600 continue; 601 else if (errno == EPIPE) 602 goto fini; 603 goto fini; 604 } 605 if (fd_out > 2) 606 close(fd_out); /* sends EOF */ 607 fd_out = -1; 608 haswritten = 1; 609 } 610 if (haswritten && fd_in != -1 && FD_ISSET(fd_in, &fdr)) { 611 r = read(fd_in, buf, sizeof(buf)); 612 if (r == -1) { 613 if (errno == EWOULDBLOCK || errno == EAGAIN) 614 continue; 615 goto fini; 616 } 617 if (r > 0) { 618 buf[r] = '\0'; 619 total += (size_t)r; 620 if (f) 621 f(buf, r, total); 622 } else if (!r) { 623 status = 0; 624 goto fini; 625 } 626 } 627 } 628fini: 629 if (fd_in != -1 && fd_in > 2) 630 close(fd_in); 631 if (fd_out != -1 && fd_out > 2) 632 close(fd_out); 633 return status; 634} 635 636static int 637pipe_cmd(char *cmd[], char *writestr, void (*f)(const char *, size_t, size_t)) 638{ 639 struct sigaction sa; 640 pid_t pid; 641 int pc[2], cp[2]; 642 643 if (pipe(pc) == -1 || pipe(cp) == -1) { 644 perror("pipe"); 645 return -1; 646 } 647 pid = fork(); 648 if (pid == -1) { 649 perror("fork"); 650 return -1; 651 } else if (pid == 0) { 652 /* child */ 653 setenv("SOBLINE", line.line, 1); 654 setenv("SOBWRITE", writestr, 1); 655 close(cp[0]); 656 close(pc[1]); 657 658 if (dup2(pc[0], 0) == -1 || dup2(cp[1], 1) == -1) { 659 perror("dup2"); 660 return -1; 661 } 662 663 if (execv(cmd[0], (char**)cmd) == -1) { 664 perror("execv"); 665 _exit(1); /* NOTE: must be _exit */ 666 } 667 _exit(0); 668 } else { 669 /* parent */ 670 close(pc[0]); 671 close(cp[1]); 672 673 /* ignore SIGPIPE, we handle this for write(). */ 674 memset(&sa, 0, sizeof(sa)); 675 sa.sa_flags = SA_RESTART; 676 sa.sa_handler = SIG_IGN; 677 sigaction(SIGPIPE, &sa, NULL); 678 679 if (pipe_rw(cp[0], pc[1], writestr, f) == -1) 680 return -1; 681 } 682 return 0; 683} 684 685static void 686cb_handleinput(const char *buf, size_t len, size_t total) 687{ 688 if (!len || !total) 689 return; 690 handleinput((unsigned char *)buf, len); 691} 692 693static void 694cb_pipe_insert(const char *buf, size_t len, size_t total) 695{ 696 if (!len || !total) 697 return; 698 memset(&line, 0, sizeof(line)); 699 handleinput((unsigned char *)buf, len); 700} 701 702static void 703cb_pipe_replaceword(const char *buf, size_t len, size_t total) 704{ 705 if (!len) 706 return; 707 /* first read: delete word under cursor. */ 708 if (len == total) 709 line_delwordcursor(); 710 handleinput((unsigned char *)buf, len); 711} 712 713static int 714line_pipeto(char **cmd, void (*f)(const char *, size_t, size_t)) 715{ 716 return pipe_cmd(cmd, line.line, f); 717} 718 719/* pipe word under cursor and replace it */ 720static int 721line_wordpipeto(char **cmd, void (*f)(const char *, size_t, size_t)) 722{ 723 char wordbuf[BUFSIZ]; 724 725 wordbuf[0] = '\0'; 726 line_copywordcursor(wordbuf, sizeof(wordbuf)); 727 728 return pipe_cmd((char**)cmd, wordbuf, f); 729} 730 731static void 732sighandler(int signum) 733{ 734 switch(signum) { 735 case SIGINT: /* fallthrough */ 736 case SIGTERM: 737 line_exit(); 738 break; 739 case SIGWINCH: 740 clear(); 741 gettermsize(); 742 resize(); 743 line_draw(); 744 break; 745 } 746} 747 748static void 749gettermsize(void) 750{ 751 struct winsize w; 752 753 if (ioctl(0, TIOCGWINSZ, &w) == -1) 754 return; 755 cols = w.ws_col; 756 rows = w.ws_row; 757} 758 759static void 760resize(void) 761{ 762 pipe_cmd((char **)resizecmd, line.line, NULL); 763} 764 765static void 766handleinput(const unsigned char *input, size_t len) 767{ 768 size_t p = 0, keylen, i; 769 int ismatch = 0; 770 char buf[BUFSIZ]; 771 772 dirtylen = line.collen; 773 while (p < len && input[p] != '\0') { 774 if (input[p] == 0x1b || iscntrl(input[p])) { 775 ismatch = 0; 776 for (i = 0; i < LEN(keybinds); i++) { 777 keylen = strlen((char*)keybinds[i].key); 778 if (len - p >= keylen && 779 memcmp(&input[p], keybinds[i].key, keylen) == 0) { 780 keybinds[i].func(); 781 p += keylen; 782 ismatch = 1; 783 break; 784 } 785 } 786 if (!ismatch) { 787 if (input[p] == 0x1b) 788 return; 789 p++; 790 } 791 } else { 792 for (i = p; input[i] && input[i] > 0x1b; i++) 793 ; 794 if (i - p < sizeof(buf)) { 795 memcpy(buf, &input[p], i - p); 796 buf[i - p] = '\0'; 797 p = i; 798 line_inserttext((char*)buf); 799 line_draw(); 800 } else { 801 p++; 802 } 803 } 804 } 805} 806 807static void 808setup(void) 809{ 810 struct sigaction sa; 811 812 lineoutfp = stdout; 813 outfp = stderr; 814 setlocale(LC_ALL, ""); 815 816 /* signal handling */ 817 memset(&sa, 0, sizeof(sa)); 818 sa.sa_flags = SA_RESTART; 819 sa.sa_handler = sighandler; 820 sigaction(SIGTERM, &sa, NULL); 821 sigaction(SIGINT, &sa, NULL); 822 sigaction(SIGWINCH, &sa, NULL); 823 /* reap zombie childs >=) */ 824 sa.sa_handler = SIG_IGN; 825 sigaction(SIGCHLD, &sa, NULL); 826 /* ignore SIGPIPE, we handle this for write(). */ 827 sa.sa_handler = SIG_IGN; 828 sigaction(SIGPIPE, &sa, NULL); 829 830 if (tcgetattr(0, &ttystate) == 0) { 831 termattrset = 1; 832 ttysave = ttystate; 833 /* turn off canonical mode and echo */ 834 ttystate.c_lflag &= ~(ICANON | ECHO); 835 ttystate.c_cc[VMIN] = 1; 836 /* set the terminal attributes */ 837 tcsetattr(0, TCSANOW, &ttystate); 838 } else { 839 /* not a tty */ 840 initialinput(); 841 /* setup tty again because we (re)open "/dev/tty" */ 842 termattrset = 0; 843 if (tcgetattr(0, &ttystate) == 0) { 844 termattrset = 1; 845 ttysave = ttystate; 846 /* turn off canonical mode and echo */ 847 ttystate.c_lflag &= ~(ICANON | ECHO); 848 ttystate.c_cc[VMIN] = 1; 849 /* set the terminal attributes */ 850 tcsetattr(0, TCSANOW, &ttystate); 851 } 852 } 853 /* get terminal window size */ 854 gettermsize(); 855} 856 857static void 858run(void) 859{ 860 if (!isrunning) 861 return; 862 863 /* clear screen */ 864 clear(); 865 866 fcntl(0, F_SETFL, O_NONBLOCK); 867 line_draw(); 868 869 pipe_rw(0, -1, NULL, cb_handleinput); 870 871 /* cleanup: restore terminal attributes */ 872 if (termattrset) { 873 ttystate.c_lflag = ttysave.c_lflag; 874 tcsetattr(0, TCSANOW, &ttystate); 875 } 876} 877 878static void 879initialinput(void) 880{ 881 int fd; 882 883 /* read initial input from stdin */ 884 fcntl(0, F_SETFL, O_NONBLOCK); 885 pipe_rw(0, -1, NULL, cb_handleinput); 886 /* restore terminal attributes */ 887 if (termattrset) { 888 ttystate.c_lflag = ttysave.c_lflag; 889 tcsetattr(0, TCSANOW, &ttystate); 890 } 891 if (!isrunning) 892 return; 893 /* close and re-attach to stdin */ 894 close(0); 895 if ((fd = open("/dev/tty", O_RDONLY | O_NONBLOCK)) == -1) { 896 fprintf(stderr, "open: /dev/tty: %s\n", strerror(errno)); 897 exit(1); 898 } 899 if (dup2(fd, 0) == -1) { 900 fprintf(stderr, "dup2: /dev/tty: %s\n", strerror(errno)); 901 exit(1); 902 } 903} 904 905static void 906usage(void) 907{ 908 fprintf(stderr, "usage: %s [-p prompt]\n", argv0); 909 exit(1); 910} 911 912int 913main(int argc, char **argv) 914{ 915 ARGBEGIN { 916 case 'p': 917 prompt = EARGF(usage()); 918 break; 919 default: 920 usage(); 921 } ARGEND; 922 923 setup(); 924 run(); 925 926 return 0; 927}