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 }