URI:
       sob.c - sob - simple output bar
  HTML git clone git://git.codemadness.org/sob
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       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"
           18 char *argv0;
           19 
           20 #define LEN(x)    (sizeof (x) / sizeof *(x))
           21 #define ISUTF8(c) (((c) & 0xc0) != 0x80)
           22 
           23 struct 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 
           33 static void    cb_handleinput(const char *, size_t, size_t);
           34 static void    cb_pipe_insert(const char *, size_t, size_t);
           35 static void    cb_pipe_replaceword(const char *, size_t, size_t);
           36 
           37 static void    line_clear(void);
           38 static void    line_copywordcursor(char *, size_t);
           39 static void    line_cursor_begin(void);
           40 static void    line_cursor_end(void);
           41 static void    line_cursor_move(size_t);
           42 static void    line_cursor_next(void);
           43 static void    line_cursor_prev(void);
           44 static void    line_cursor_wordprev(void);
           45 static void    line_cursor_wordnext(void);
           46 static void    line_delcharprev(void);
           47 static void    line_delcharnext(void);
           48 static void    line_deltoend(void);
           49 static void    line_delwordprev(void);
           50 static void    line_delwordcursor(void);
           51 static void    line_draw(void);
           52 static void    line_exit(void);
           53 static void    line_getwordpos(size_t, size_t, size_t *, size_t *, size_t *,
           54                                size_t *);
           55 static void    line_getwordposprev(size_t, size_t, size_t *, size_t *);
           56 static void    line_getwordposnext(size_t, size_t, size_t *, size_t *);
           57 static void    line_inserttext(const char *);
           58 static void    line_newline(void);
           59 static void    line_out(void);
           60 static void    line_prompt(void);
           61 static int     line_promptlen(void);
           62 static int     line_pipeto(char **, void (*)(const char *, size_t, size_t));
           63 static int     line_wordpipeto(char **,
           64                                void (*)(const char *, size_t, size_t));
           65 
           66 static int     pipe_rw(int, int, char *,
           67                          void (*)(const char *, size_t, size_t));
           68 static int     pipe_cmd(char *[], char *,
           69                         void (*)(const char *, size_t, size_t));
           70 
           71 static void    clear(void);
           72 static void    gettermsize(void);
           73 static void    handleinput(const unsigned char *, size_t);
           74 static void    initialinput(void);
           75 static void    resize(void);
           76 static void    run(void);
           77 static void    setup(void);
           78 static void    sighandler(int);
           79 static void    usage(void);
           80 
           81 static size_t  colw(const char *, size_t);
           82 static int     nonspace(int c);
           83 static size_t  utf8len(const char *);
           84 static size_t  utfprevn(const char *, size_t, size_t);
           85 static size_t  utfnextn(const char *, size_t, size_t);
           86 static void    utfuntilchar(size_t *, size_t *, int (*)(int), int);
           87 
           88 static struct  termios ttystate, ttysave;
           89 
           90 static struct  line line;
           91 static size_t  dirtylen; /* dirty length (in columns) */
           92 static int     cols = 79, rows = 24;
           93 static int     termattrset = 0;
           94 static int     isrunning = 1;
           95 static FILE *  outfp = NULL;
           96 static FILE *  lineoutfp = NULL;
           97 
           98 #include "config.h"
           99 
          100 static int
          101 nonspace(int c)
          102 {
          103         return !isspace(c);
          104 }
          105 
          106 static size_t
          107 colw(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 
          125 static size_t
          126 utf8len(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. */
          139 static size_t
          140 utfprevn(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. */
          153 static size_t
          154 utfnextn(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 */
          167 static void
          168 utfuntilchar(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 
          194 static void
          195 line_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" */
          223 static int
          224 line_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 
          238 static void
          239 line_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 
          249 static void
          250 clear(void)
          251 {
          252         /* clear screen, move cursor to (0, 0) */
          253         fprintf(outfp, "\x1b[2J\x1b[H");
          254         fflush(outfp);
          255 }
          256 
          257 static void
          258 line_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 
          278 static void
          279 line_out(void)
          280 {
          281         fprintf(lineoutfp, "%s\n", line.line);
          282         fflush(lineoutfp);
          283 }
          284 
          285 static void
          286 line_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 
          301 static void
          302 line_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 
          310 static void
          311 line_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 
          319 static void
          320 line_cursor_begin(void)
          321 {
          322         line.bytepos = 0;
          323         line.utfpos = 0;
          324         line.colpos = 0;
          325         line_cursor_move(line.colpos);
          326 }
          327 
          328 static void
          329 line_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 
          344 static void
          345 line_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 
          360 static void
          361 line_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 
          369 static void
          370 line_clear(void)
          371 {
          372         memset(&line, 0, sizeof(line));
          373         line_draw();
          374 }
          375 
          376 static void
          377 line_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 
          398 static void
          399 line_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 
          423 static void
          424 line_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 
          435 static void
          436 line_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 
          457 static void
          458 line_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 
          483 static void
          484 line_newline(void)
          485 {
          486         line_out();
          487         memset(&line, 0, sizeof(line));
          488         line_draw();
          489 }
          490 
          491 static void
          492 line_exit(void)
          493 {
          494         fprintf(outfp, "\n");
          495         fflush(outfp);
          496         isrunning = 0;
          497 }
          498 
          499 static void
          500 line_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 
          519 static void
          520 line_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 
          530 static void
          531 line_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 
          541 static void
          542 line_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. */
          560 static int
          561 pipe_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         }
          628 fini:
          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 
          636 static int
          637 pipe_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 
          685 static void
          686 cb_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 
          693 static void
          694 cb_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 
          702 static void
          703 cb_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 
          713 static int
          714 line_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 */
          720 static int
          721 line_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 
          731 static void
          732 sighandler(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 
          748 static void
          749 gettermsize(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 
          759 static void
          760 resize(void)
          761 {
          762         pipe_cmd((char **)resizecmd, line.line, NULL);
          763 }
          764 
          765 static void
          766 handleinput(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 
          807 static void
          808 setup(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 
          857 static void
          858 run(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 
          878 static void
          879 initialinput(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 
          905 static void
          906 usage(void)
          907 {
          908         fprintf(stderr, "usage: %s [-p prompt]\n", argv0);
          909         exit(1);
          910 }
          911 
          912 int
          913 main(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 }