1 /*
2 * YASR ("Yet Another Screen Reader") is an attempt at a lightweight,
3 * portable screen reader.
4 *
5 * Copyright (C) 2001-2002 by Michael P. Gorse. All rights reserved.
6 *
7 * YASR comes with ABSOLUTELY NO WARRANTY.
8 *
9 * This is free software, placed under the terms of the
10 * GNU Lesser General Public License, as published by the Free Software
11 * Foundation. Please see the file COPYING for details.
12 *
13 * Web Page: http://yasr.sf.net
14 *
15 * This software is maintained by:
16 * Michael P. Gorse <mgorse@users.sourceforge.net>
17 */
18
19 /* main.c -- contains main() and the i/o-handling functions along with the
20 * code to maintain our virtual window
21 *
22 * See COPYING for copying information.
23 */
24
25 #include "yasr.h"
26 #include "term.h"
27 #include <utmpx.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31
32 static int cpid;
33 static int size;
34 static int master, slave;
35 char *conffile = NULL;
36 unsigned char buf[256];
37 char usershell[OPT_STR_SIZE];
38 static struct termios t;
39 Win *win;
40 static Win *winsave;
41 static int win_scrollmin, win_scrollmax; /* tbd -- move these back into Win */
42 Tts tts;
43 Ui ui;
44 Uirev rev;
45 static int speaking = 1;
46 int kbuf[100];
47 int kbuflen = 0;
48 static unsigned char okbuf[100];
49 static int okbuflen = 0;
50 static int oldcr = 0, oldcc = 0, oldch = 0;
51 char voices[TTS_SYNTH_COUNT][64];
52 static int shell = 0;
53 int special = 0;
54 int cl_synth = 0;
55 int cl_synthport = 0;
56
57 static char **subprog = NULL; /* if non-NULL, then exec it instead of shell */
58
59 static Win *wininit(int, int);
60 static void win_end(Win *);
61
62 extern char **environ;
63
64
65 #define PARM1 (parm[0] ? parm[0] : 1)
66 #define PARM2 (parm[1] ? parm[1] : 1)
67
yasr_ttyname_r(int fd,char * p,int size)68 static void yasr_ttyname_r(int fd, char *p, int size)
69 {
70 char *t;
71
72 if ((t = ttyname(fd)) == NULL) strcpy(p, "stdin");
73 else (void) strncpy(p, t, size);
74 }
75
child()76 static void child()
77 {
78 char arg[20];
79 char *cp;
80 char envstr[40];
81
82 (void) login_tty(slave);
83 if (!geteuid())
84 { /* if we're setuid root */
85 yasr_ttyname_r(0, (char *) buf, 32);
86 (void) chown((char *) buf, getuid(), -1);
87 (void) setuid(getuid());
88 }
89 cp = usershell + strlen(usershell) - 1;
90 while (*cp != '/')
91 {
92 cp--;
93 }
94 cp++;
95 arg[0] = shell ? '-' : '\0';
96 *(arg + 1) = '\0';
97 (void) strcat(arg, cp);
98 (void) sprintf(envstr, "SHELL=%s", usershell);
99 (void) putenv(envstr);
100 if (subprog)
101 {
102 char *devname = ttyname(0);
103
104 (void) setsid();
105 (void) close(0);
106 if (open(devname, O_RDWR) < 0)
107 {
108 perror(devname);
109 }
110
111 (void) execve(subprog[0], subprog, environ);
112 }
113 else
114 {
115 (void) execl(usershell, arg, (void *) 0);
116 }
117 perror("execl");
118 exit(1);
119 }
120
121 /* get the appropriate name for a tty from the filename */
122
rnget(char * s,char * d)123 static void rnget(char *s, char *d)
124 {
125 (void) strcpy(d, s + 5);
126 }
127
utmpconv(char * s,char * d,int pid)128 static void utmpconv(char *s, char *d, int pid)
129 {
130 #ifdef UTMP_HACK
131 #ifdef sun
132 struct utmpx *up;
133 char *rs = (char *) buf, *rd = (char *) buf + 64;
134 char *rstail = NULL, *rdtail = NULL;
135
136 rnget(s, rs);
137 rnget(d, rd);
138
139 if (rs)
140 {
141 if (strstr(rs, "/dev/") != NULL)
142 {
143 rstail = strdup(rs + sizeof("/dev/") - 1);
144 }
145 else
146 {
147 rstail = strdup(rs);
148 }
149 }
150
151 if (rd)
152 {
153 if (strstr(rd, "/dev/") != NULL)
154 {
155 rdtail = strdup(rd + sizeof("/dev/") - 1);
156 }
157 else
158 {
159 rdtail = strdup(rd);
160 }
161 }
162
163 setutxent();
164 while ((up = getutxent()) != NULL)
165 {
166 if (!strcmp(up->ut_line, rstail))
167 {
168 (void) strcpy(up->ut_line, rdtail);
169 (void) time(&up->ut_tv.tv_sec);
170 up->ut_pid = pid;
171
172 (void) pututxline(up);
173 updwtmpx("wtmpx", up);
174 break;
175 }
176 }
177
178 endutxent();
179 #else
180 FILE *fp;
181 fpos_t fpos;
182 struct utmp u;
183 char *rs = (char *) buf, *rd = (char *) buf + 64;
184
185 rnget(s, rs);
186 rnget(d, rd);
187 fp = fopen("/var/run/utmp", "r+");
188 if (!fp) return;
189 for (;;)
190 {
191 (void) fgetpos(fp, &fpos);
192 (void) fread(&u, sizeof(struct utmp), 1, fp);
193 if (feof(fp))
194 {
195 break;
196 }
197 if (!strcmp(u.ut_line, rs))
198 {
199 (void) strcpy(u.ut_line, rd);
200 (void) fsetpos(fp, &fpos);
201 (void) fwrite(&u, sizeof(struct utmp), 1, fp);
202 break;
203 }
204 }
205 (void) fclose(fp);
206 #endif /*sun */
207 #endif
208 }
209
finish(int sig)210 /*ARGSUSED*/ static void finish(int sig)
211 {
212 tts_end();
213 (void) tcsetattr(0, TCSAFLUSH, &t);
214 yasr_ttyname_r(slave, (char *) buf + 128, 32);
215 yasr_ttyname_r(0, (char *) buf + 192, 32);
216 utmpconv((char *) buf + 128, (char *) buf + 192, getpid());
217 if (tts.pid)
218 {
219 (void) kill(tts.pid, 9);
220 }
221 if (cpid)
222 {
223 (void) kill(cpid, 9);
224 }
225 exit(0);
226 }
227
228 static void getoutput();
229
child_finish(int sig)230 /*ARGSUSED*/ static void child_finish(int sig)
231 {
232 int pid = 0;
233
234 while (readable(master, 0)) getoutput();
235 (void) signal(SIGCHLD, &child_finish);
236 pid = waitpid(-1, NULL, WNOHANG);
237 /* It is possible for the child to die and for this handler to be called
238 before fork() ever returned, in which case cpid will still hold a value
239 of 0, so need to check for that. */
240 if ((pid == tts.pid) && tts.reinit)
241 {
242 tts_reinit2();
243 }
244 else if (cpid == 0 || pid == cpid)
245 {
246 finish(0);
247 }
248 }
249
is_char(int ch)250 static int is_char(int ch)
251 {
252 #ifdef __linux__
253 /* return true as long as ch isn't prefixed by <esc> */
254 while (ch != (ch % 0xff)) ch >>= 8;
255 return (ch != 27);
256 #else
257 /* assume high ascii means meta */
258 return (ch >= 32 && ch <= 126);
259 #endif
260 }
261
is_separator(int ch)262 static int is_separator(int ch)
263 {
264 int result=0;
265 if ((ch != ch % 0xFF) || isspace(ch))
266 {
267 result=1;
268 }
269 else if (((ch >= 0x21) && (ch <= 0x2F))
270 || ((ch >= 0x3A) && (ch <= 0x40))
271 || ((ch >= 0x5B) && (ch <= 0x60))
272 || ((ch >= 0x7B) && (ch <= 0x7E))
273 )
274 {
275 result=1;
276 }
277 return result;
278 }
279
280
281
282
getinput()283 static void getinput()
284 {
285 int key;
286
287 size = read(0, buf, 255);
288 if (size <= 0)
289 {
290 finish(0);
291 }
292 key = (int) buf[0];
293 if (size > 1)
294 {
295 key = (key << 8) + buf[1];
296 }
297 if (size > 2)
298 {
299 key = (key << 8) + buf[2];
300 }
301 if (size > 3)
302 {
303 key = (key << 8) + buf[3];
304 }
305 if (key >> 8 == 0x1b4f)
306 {
307 key += 0x000c00;
308 }
309
310 /* Convert high-bit meta keys to escape form */
311 #ifndef __linux__
312 if (key >= 0x80 && key <= 0xff) key += 0x1a80;
313 #endif
314 if (key == ui.disable)
315 {
316 if (ui.disabled)
317 {
318 tts_initsynth(NULL);
319 ui.disabled = ui.silent = 0;
320 tts_say(_("yasr enabled."));
321 }
322 else
323 {
324 tts_silence();
325 tts_say(_("yasr disabled."));
326 ui.silent = ui.disabled = 1;
327 }
328 return;
329 }
330 else if (ui.disabled)
331 {
332 (void) write(master, buf, size);
333 return;
334 }
335
336 tts_silence();
337 if (ui.silent == -1)
338 {
339 ui.silent = 0;
340 }
341 ui.silent = -ui.silent;
342 if (ui.meta)
343 {
344 (void) write(master, buf, size);
345 ui.meta = 0;
346 return;
347 }
348 if (ui.kbsay == 2 && is_separator(key))
349 {
350 tts_out(okbuf, okbuflen);
351 okbuflen = tts.oflag = 0;
352 }
353 if (!ui_keypress(key))
354 {
355 (void) write(master, buf, size);
356 }
357 }
358
wincpy(Win ** d,Win * s)359 static void wincpy(Win ** d, Win * s)
360 {
361 int i;
362
363 if (*d) win_end(*d);
364 *d = wininit(s->rows, s->cols);
365 (*d)->cr = s->cr;
366 (*d)->cc = s->cc;
367 (*d)->mode = s->mode;
368 for (i = 0; i < s->rows; i++)
369 {
370 (void) memcpy((*d)->row[i], s->row[i], s->cols * CHARSIZE);
371 }
372 (void) memcpy(&(*d)->savecp, &s->savecp, sizeof(Curpos));
373 }
374
win_end(Win * win)375 static void win_end(Win * win)
376 {
377 int i;
378
379 for (i = 0; i < win->rows; i++)
380 {
381 free(win->row[i]);
382 }
383 free(win->row);
384 free(win->tab);
385 }
386
win_scrollup()387 static void win_scrollup()
388 {
389 int i;
390 chartype *tmpc;
391
392 if (rev.cr) rev.cr--;
393 if (oldcr) oldcr--;
394 tmpc = win->row[win_scrollmin];
395 (void) memset(tmpc, 0, win->cols * CHARSIZE);
396 for (i = win_scrollmin; i < win_scrollmax; i++)
397 {
398 win->row[i] = win->row[i + 1];
399 }
400 win->row[i] = tmpc;
401 win->cr--;
402 }
403
win_lf()404 static void win_lf()
405 {
406 int i;
407 chartype *tmpc;
408
409 if (win->cr == win_scrollmax)
410 {
411 if (rev.cr) rev.cr--;
412 if (oldcr) oldcr--;
413 tmpc = win->row[win_scrollmin];
414 (void) memset(tmpc, 0, win->cols * CHARSIZE);
415 for (i = win_scrollmin; i < win_scrollmax; i++)
416 {
417 win->row[i] = win->row[i + 1];
418 }
419 win->row[i] = tmpc;
420 win->cr--;
421 }
422 win->cr++;
423 }
424
win_scrolldown()425 static void win_scrolldown()
426 {
427 int i;
428 chartype *tmpc;
429
430 tmpc = win->row[win_scrollmax];
431 (void) memset(tmpc, 0, CHARSIZE * win->cols);
432 for (i = win_scrollmax - 1; i >= win->cr; i--)
433 {
434 win->row[i + 1] = win->row[i];
435 }
436 win->row[win->cr] = tmpc;
437 }
438
win_rlf()439 static void win_rlf()
440 {
441 if (win->cr == win_scrollmin) win_scrolldown(); else win->cr--;
442 }
443
readable(int fd,int wait)444 int readable(int fd, int wait)
445 {
446 fd_set fds;
447 struct timeval tv;
448
449 tv.tv_usec = wait % 1000000;
450 tv.tv_sec = wait / 1000000;
451 FD_ZERO(&fds);
452 FD_SET(fd, &fds);
453 (void) select(fd + 1, &fds, NULL, NULL, &tv);
454 return (FD_ISSET(fd, &fds));
455 }
456
gulp(unsigned char * buf,int * size,unsigned char * cp,unsigned char ** ep)457 static char *gulp(unsigned char *buf, int *size, unsigned char *cp, unsigned char **ep)
458 {
459 int os = *size;
460 int n;
461
462 if (!readable(master, 1000000)) return NULL;
463 if (cp - buf >= 200)
464 {
465 if (ep)
466 {
467 n = buf + *size - *ep;
468 (void) memmove(buf, *ep, 256 - n);
469 *size = n + read(master, buf + n, 255 - n);
470 buf[*size] = '\0';
471 (void) write(1, buf + n, *size - n);
472 *ep = buf;
473 return ((char *) buf + n);
474 }
475 *size = read(master, buf, 255);
476 buf[*size] = '\0';
477 (void) write(1, buf, *size);
478 return ((char *) buf);
479 }
480 *size += read(master, buf + *size, 255 - *size);
481 buf[*size] = '\0';
482 return ((char *) (buf + os));
483 }
484
kbsay()485 static void kbsay()
486 {
487 if (!ui.kbsay) return;
488 if (buf[0] == 8 || kbuf[0] == 127)
489 {
490 if ((ui.kbsay == 2)
491 && (okbuflen!=0))
492 {
493 okbuf[--okbuflen] = 0;
494 }
495
496 /*tts_say(_("back")); */
497 return;
498 }
499 if (ui.kbsay == 1)
500 {
501 tts_saychar(kbuf[0]);
502 return;
503 }
504
505 /* ui.kbsay == 2 -- handle word echo */
506 if (okbuflen < sizeof(kbuf) - 1 && is_char(kbuf[0]))
507 {
508 okbuf[okbuflen++] = kbuf[0];
509 }
510 }
511
512 #define MIN(a, b) ((a)>(b)? (b): (a))
513
win_csi(char * buf,unsigned char ** pp,int * size)514 static void win_csi(char *buf, unsigned char **pp, int *size)
515 {
516 int parm[16], numparms = 0;
517 int ignore = 0;
518 char *p;
519 int i;
520 int x;
521
522 p = (char *) *pp;
523 if (*p == '[')
524 p++;
525 if (*p == '?')
526 p++;
527 while (!*p || isdigit((int) *p) || *p == ';')
528 {
529 if (!*p)
530 {
531 if (!(p = gulp((unsigned char *) buf, size, (unsigned char *) p, pp)))
532 {
533 return;
534 }
535 }
536 else
537 {
538 p++;
539 }
540 }
541 p = (char *) *pp;
542 if (*p == '[')
543 {
544 ignore = 1;
545 p++;
546 }
547 if (*p == '?')
548 {
549 p++;
550 }
551 (void) memset(&parm, 0, sizeof(int) * 16);
552 while (numparms < 16 && (*p == ';' || isdigit((int) *p)))
553 {
554 parm[numparms++] = atoi(p);
555 while (isdigit((int) *p))
556 {
557 p++;
558 }
559 if (*p == ';') p++; else break; /* tbd -- is this redundant? */
560 }
561
562 *pp = (unsigned char *) p + 1;
563 if (ignore)
564 {
565 return;
566 }
567 switch (*p)
568 {
569 case '@': /* insert characters */
570 (void) memmove(win->row[win->cr] + win->cc + PARM1, win->row[win->cr] + win->cc, (win->cols - win->cc - PARM1) * CHARSIZE);
571 (void) memset(win->row[win->cr] + win->cc, 0, parm[0] * CHARSIZE);
572 break;
573
574 case 'A': /* move up */
575 win->cr -= PARM1;
576 if (win->cr < 0)
577 {
578 win->cr = 0;
579 }
580 break;
581
582 case 'B': /* move down */
583 win->cr += PARM1;
584 break;
585
586 case 'C':
587 case 'a': /* move right */
588 win->cc += PARM1;
589 break;
590
591 case 'D': /* move left */
592 win->cc -= PARM1;
593 break;
594
595 case 'E':
596 case 'e': /* move down indicated number of rows */
597 win->cr += PARM1;
598 win->cc = 0;
599 break;
600
601 case 'F': /* move up indicated number of rows */
602 win->cr -= PARM1;
603 win->cc = 0;
604 break;
605
606 case 'G':
607 case '`':
608 win->cc = parm[0] - 1;
609 break;
610
611 case 'H':
612 case 'f': /* move to specified row/col or top left if none given */
613 if ((kbuf[0] == 8 || kbuf[0] == 127) &&
614 PARM1 == win->cr + 1 && PARM2 == win->cc && !tts.oflag)
615 {
616 kbsay();
617 ui_saychar(win->cr, win->cc - 1);
618 }
619 win->cr = PARM1 - 1;
620 win->cc = PARM2 - 1;
621 break;
622
623 case 'J':
624 switch (parm[0])
625 {
626 case 0: /* erase from cursor to end of win->creen */
627 (void) memset(win->row[win->cr] + win->cc, 0, CHARSIZE * (win->cols - win->cc));
628 for (i = win->cr + 1; i < win->rows; i++)
629 {
630 (void) memset(win->row[i], 0, win->cols * CHARSIZE);
631 }
632 break;
633
634 case 1: /* erase from start to cursor */
635 (void) memset(win->row[win->cr], 0, win->cc);
636 for (i = 0; i < win->cr; i++)
637 {
638 (void) memset(win->row[i], 0, win->cols * CHARSIZE);
639 }
640 break;
641
642 case 2: /* erase whole screen */
643 for (i = 0; i < win->rows; i++)
644 {
645 (void) memset(win->row[i], 0, win->cols * CHARSIZE);
646 }
647 break;
648 }
649 break;
650
651 case 'K':
652 switch (parm[0])
653 {
654 case 0:
655 (void) memset(win->cr[win->row] + win->cc, 0, (win->cols - win->cc) * CHARSIZE);
656 break;
657
658 case 1:
659 (void) memset(win->row[win->cr], 0, (win->cc + 1) * CHARSIZE);
660 break;
661
662 case 2:
663 (void) memset(win->row[win->cr], 0, win->cols * CHARSIZE);
664 break;
665 }
666 break;
667
668 case 'L': /* insert rows */
669 x = MIN(PARM1, win_scrollmax - win->cr);
670 for (i = win_scrollmax; i >= win->cr + x; i--)
671 {
672 (void) memcpy(win->row[i], win->row[i - x], win->cols * CHARSIZE);
673 }
674 for (i = win->cr; i < win->cr + x; i++)
675 {
676 (void) memset(win->row[i], 0, win->cols * CHARSIZE);
677 }
678 break;
679
680 case 'M':
681 x = MIN(PARM1, win_scrollmax - win->cr);
682 if (x + win->cr > win_scrollmax)
683 {
684 x = win_scrollmax - win->cr;
685 }
686 for (i = win->cr; i <= win_scrollmax - x; i++)
687 {
688 (void) memcpy(win->row[i], win->row[i + x], win->cols * CHARSIZE);
689 }
690 for (i = win_scrollmax - x + 1; i <= win_scrollmax; i++)
691 {
692 (void) memset(win->row[i], 0, win->cols * CHARSIZE);
693 }
694 break;
695
696 case 'P': /* delete characters */
697 x = MIN(PARM1, win->cols - win->cc);
698 (void) memmove(win->row[win->cr] + win->cc, win->row[win->cr] + win->cc + x, (win->cols - win->cc - x) * CHARSIZE);
699 (void) memset(win->row[win->cr] + win->cols - x, 0, x * CHARSIZE);
700 break;
701
702 case 'S': /* Scroll up */
703 for (i = 0; i < PARM1; i++)
704 {
705 win_scrollup();
706 }
707 break;
708
709 case 'T': /* Scroll down */
710 for (i = 0; i < PARM1; i++)
711 {
712 win_scrolldown();;
713 }
714 break;
715
716 case 'X': /* Erase characters */
717 x = MIN(PARM1, win->cols - win->cc);
718 (void) memset(win->row[win->cr] + win->cc, 0, x * CHARSIZE);
719 break;
720
721 case 'd': /* move to indicated row */
722 win->cr = PARM1 - 1;
723 break;
724
725 case 'g':
726 switch (parm[0])
727 {
728 case 3:
729 for (i = 0; i < win->cols; i++)
730 {
731 win->tab[i] = 0;
732 }
733 break;
734
735 case 0:
736 win->tab[win->cc] = 0;
737 break;
738 }
739 break;
740
741 case 'h':
742 win->mode |= 1 << (PARM1 - 1);
743 break;
744
745 case 'l':
746 win->mode &= ~(1 << (PARM1 - 1));
747 break;
748
749 case 'm':
750 for (i = 0; i < numparms; i++)
751 {
752 switch (parm[i])
753 {
754 case 0:
755 win->attr = 0;
756 break;
757 case 1:
758 win->attr |= ATTR_BOLD;
759 break;
760 case 2:
761 win->attr |= ATTR_HALFBRIGHT;
762 break;
763 case 4:
764 win->attr |= ATTR_UNDERSCORE;
765 break;
766 case 5:
767 win->attr |= ATTR_BLINK;
768 break;
769 case 7:
770 win->attr |= ATTR_RV;
771 break;
772 case 24:
773 win->attr &= ~ATTR_UNDERSCORE;
774 break;
775 case 25:
776 win->attr &= ~ATTR_BLINK;
777 break;
778 case 27:
779 win->attr &= ~ATTR_RV;
780 break;
781 }
782 }
783 break;
784
785 case 'r':
786 win_scrollmin = (parm[0] ? parm[0] - 1 : 0);
787 win_scrollmax = parm[1] ? MIN(parm[1], win->rows) - 1 : win->rows - 1;
788 break;
789
790 case 's':
791 win->savecp.cr = win->cr;
792 win->savecp.cc = win->cc;
793 break;
794
795 case 'u':
796 win->cr = win->savecp.cr;
797 win->cc = win->savecp.cc;
798 break;
799 }
800
801 if (win->cr >= win->rows)
802 {
803 win->cr = win->rows - 1;
804 }
805 else if (win->cr < 0)
806 {
807 win->cr = 0;
808 }
809 if (win->cc >= win->cols - 1) win->cc = win->cols - 1;
810 else if (win->cc < 0) win->cc = 0;
811 }
812
win_addchr(char ch,int tflag)813 static void win_addchr(char ch, int tflag)
814 {
815 if (win->cc == win->cols)
816 {
817 win->cc = 0;
818 win_lf();
819 win->carry++;
820 }
821 if (win->mode & 0x08)
822 {
823 (void) memmove(win->row[win->cr] + win->cc + 1, win->row[win->cr] + win->cc, (win->cols - win->cc - 1) * CHARSIZE);
824 }
825 win->row[win->cr][win->cc++] = (win->attr << 8) + ch;
826 if (tflag)
827 {
828 if (ui.silent != 1)
829 {
830 tts_addchr(ch);
831 }
832 }
833 }
834
realchar(chartype ch)835 char realchar(chartype ch)
836 {
837 char r;
838
839 r = ch & 0xff;
840 if (!r)
841 {
842 r = 32;
843 }
844
845 return (r);
846 }
847
848 /* bol -- beginning of line? */
bol(int cr,int cc)849 static int bol(int cr, int cc)
850 {
851 int i;
852 chartype *rptr;
853
854 rptr = win->row[cr];
855 for (i = 0; i < cc; i++)
856 {
857 if (y_isblank(rptr[i]))
858 {
859 return (0);
860 }
861 }
862
863 return (1);
864 }
865
866 /* eol -- end of line? */
867
eol(int cr,int cc)868 static int eol(int cr, int cc)
869 {
870 int i;
871 chartype *rptr;
872
873 rptr = win->row[cr];
874 for (i = cc + 1; i < win->cols; i++)
875 {
876 if (y_isblank(rptr[i]))
877 {
878 return (0);
879 }
880 }
881
882 return (1);
883 }
884
885 /* firstword -- are we in the first word of the line? */
886
firstword(int cr,int cc)887 static int firstword(int cr, int cc)
888 {
889 chartype *rptr;
890 int i;
891
892 rptr = win->row[cr];
893 i = cc;
894 while (i && !y_isblank(rptr[i]))
895 {
896 i--;
897 }
898 for (; i; i--)
899 {
900 if (!y_isblank(rptr[i]))
901 {
902 return (0);
903 }
904 }
905
906 return (1);
907 }
908
909 /* lastword -- are we in the last word of the line? */
910
lastword(int cr,int cc)911 /*ARGSUSED*/ static int lastword(int cr, int cc)
912 {
913 chartype *rptr;
914 int i = 0;
915
916 rptr = win->row[cr];
917 if (y_isblank(rptr[i]))
918 {
919 i++;
920 }
921 while (i < win->cols && !y_isblank(rptr[i]))
922 {
923 i++;
924 }
925 while (i < win->cols)
926 {
927 if (!y_isblank(rptr[i++]))
928 {
929 return (0);
930 }
931 }
932
933 return (1);
934 }
935
getoutput()936 static void getoutput()
937 {
938 char ch = 0;
939 unsigned char *p;
940 int i;
941 int chr = 0;
942 static int stathit = 0, oldoflag = 0;
943
944 size = read(master, buf, 255);
945 if (size < 0)
946 {
947 perror("read");
948 exit(1);
949 }
950 buf[size] = 0;
951
952 #ifdef TERMTEST
953 (void) printf("size=%d buf=%s\n", size, buf);
954 #endif
955
956 if (!size)
957 {
958 finish(0);
959 }
960 (void) write(1, buf, size);
961 p = buf;
962
963 while (p - buf < size)
964 {
965 switch (ch = *p++)
966 {
967 case 0:
968 case 7:
969 break;
970
971 case 8:
972 if (win->cc) win->cc--;
973 else if (win->carry && win->cr)
974 {
975 win->cr--;
976 win->cc = win->cols - 1;
977 win->carry--;
978 }
979 if ((kbuf[0] == 8 || kbuf[0] == 127) && !tts.oflag && !ui.silent)
980 {
981 kbsay();
982 ui_saychar(win->cr, win->cc);
983 }
984 if (tts.outlen)
985 {
986 tts.outlen--;
987 }
988 break;
989
990 case 9:
991 for (i = win->cc + 1; i < win->cols; i++)
992 {
993 if (win->tab[i])
994 {
995 win->cc = i;
996 break;
997 }
998 }
999 if (i == win->cols)
1000 {
1001 win->cc = i - 1;
1002 }
1003 break;
1004
1005 case 10:
1006 case 11:
1007 case 12:
1008 win_lf();
1009 break;
1010
1011 case 13:
1012 win->cc = win->carry = 0;
1013 break;
1014
1015 case 14:
1016 case 15:
1017 break; /* may need to change in the future */
1018
1019 case 27:
1020 if (!*p && !(p = (unsigned char *) gulp(buf, &size, p, NULL)))
1021 {
1022 return;
1023 }
1024 switch (*p++)
1025 {
1026 case 'D':
1027 win_lf();
1028 break;
1029 case 'E':
1030 break; /* FIXME -- new line */
1031 case 'H':
1032 win->tab[win->cc] = 1;
1033 break;
1034 case 'M':
1035 win_rlf();
1036 break;
1037 case '7':
1038 wincpy(&winsave, win);
1039 oldcr = oldcc = 255;
1040 break;
1041 case '8':
1042 wincpy(&win, winsave);
1043 break;
1044 case '[':
1045 win_csi((char *) buf, &p, &size);
1046 break;
1047 }
1048 break;
1049
1050 /* case 155: */
1051 /* win_csi(buf, &p, &size); */
1052 /* break; *//* supported by Linux console, at least */
1053
1054 default:
1055 #if 0
1056 if (special)
1057 {
1058 if (ch == '<')
1059 {
1060 speaking = 0; /* hack for medievia.com */
1061 }
1062 else if (ch == '>')
1063 {
1064 speaking = 1;
1065 }
1066 }
1067 #endif
1068 if (ch == (char)kbuf[0] && win->cr == oldcr && win->cc == oldcc && kbuflen)
1069 {
1070 /* this character was (probably) echoed as a result of a keystroke */
1071 kbsay();
1072 win_addchr(ch, 0);
1073 (void) memmove(kbuf, kbuf + 1, (--kbuflen) * sizeof(int));
1074 }
1075 else
1076 {
1077 win_addchr(ch, speaking && (!special || !win->cr));
1078 }
1079 chr = 1;
1080 }
1081 if (!chr && ch != 8 && (stathit == 0 || ch < '0' || ch > '9'))
1082 {
1083 tts_flush();
1084 }
1085 else
1086 {
1087 chr = 0;
1088 }
1089 if ((!win->cc && win->cr > win->rows - 3) || win->cr == win->rows - 1)
1090 {
1091 stathit = win->cr;
1092 oldoflag = tts.oflag;
1093 }
1094 if (stathit && stathit - win->cr > 1)
1095 {
1096 stathit = -1;
1097 tts.oflag = oldoflag;
1098 }
1099 }
1100 if (ch == 13 || ch == 10 || ch == 32)
1101 {
1102 tts_flush();
1103 }
1104 if (size > 1)
1105 {
1106 if (!readable(master, 0)) tts_flush(); else return;
1107 }
1108 else if (ch == 32 || ch == 13)
1109 {
1110 tts_flush();
1111 }
1112 if (tts.oflag || kbuf[0] == 13 || kbuf[0] == 3 || ui.silent)
1113 {
1114 tts.oflag = stathit = 0;
1115 oldcr = win->cr;
1116 oldcc = win->cc;
1117 oldch = win->row[win->cr][win->cc];
1118 return;
1119 }
1120 stathit = tts.outlen = 0;
1121
1122 /* Nothing was spoken. See if the program moved the cursor.
1123 * If that's the case, read something based on how the cursor moved.
1124 */
1125 if (win->cr == oldcr)
1126 {
1127 switch (win->cc - oldcc)
1128 {
1129 case 1: /* cursor moved right one character */
1130 if ((realchar(win->row[win->cr][win->cc - 1]) == kbuf[0] &&
1131 realchar(oldch) != kbuf[0]) ||
1132 ((y_isblank(oldch) && kbuf[0] == 32)))
1133 {
1134 break;
1135 }
1136 if (kbuf[0] == 0x1b5b43 ||
1137 ((ui.curtrack == 2)
1138 && (ui.kbsay != 2 || is_separator(kbuf[0]))))
1139 {
1140 ui_saychar(win->cr, win->cc);
1141 }
1142 break;
1143
1144 case 0:
1145 break;
1146
1147 case -1:
1148 if (kbuf[0] == 0x1b5b44 || ui.curtrack == 2)
1149 {
1150 ui_saychar(win->cr, win->cc);
1151 }
1152 break;
1153
1154 default:
1155 if (ui.curtrack == 2)
1156 {
1157 if (eol(win->cr, win->cc)) ui_saychar(win->cr, win->cc);
1158 else
1159 {
1160 ui_sayword(win->cr, cblank(win->cr, win->cc) ?
1161 win->cc + 1 : win->cc);
1162 }
1163 }
1164 }
1165 }
1166 else if ((kbuf[0] == 0x1b5b43 && bol(win->cr, win->cc)) ||
1167 (kbuf[0] == 0x1b5b44 && eol(win->cr, win->cc)))
1168 {
1169 ui_saychar(win->cr, win->cc);
1170 }
1171 else
1172 {
1173 switch (win->cr - oldcr)
1174 {
1175 case 1: /* cursor moved down a line */
1176 if (kbuf[0] == 0x1b5b42)
1177 {
1178 ui_sayline(win->cr, 1);
1179 break;
1180 }
1181 if (win->cc == 0 && (oldcr == win->cols - 1 || kbuf[0] == 0x1b5b43))
1182 {
1183 ui_saychar(win->cr, win->cc);
1184 break;
1185 }
1186 if (ui.curtrack < 2)
1187 {
1188 break;
1189 }
1190 if (win->cc && bol(win->cr, win->cc) && lastword(oldcr, oldcc) && oldcc)
1191 {
1192 ui_sayword(win->cr, win->cc);
1193 }
1194 else
1195 {
1196 ui_sayline(win->cr, 1);
1197 }
1198 break;
1199 case -1: /* cursor moved up a line */
1200 if (kbuf[0] == 0x1b5b41)
1201 {
1202 ui_sayline(win->cr, 1);
1203 break;
1204 }
1205 if (ui.curtrack < 2) break;
1206 if (win->cc == win->cols - 1 && (oldcr == 0 || kbuf[0] == 0x1b5b44))
1207 {
1208 ui_saychar(win->cr, win->cc);
1209 }
1210 else if (lastword(win->cr, win->cc) &&
1211 !firstword(win->cr, win->cc) &&
1212 (!win->cc || cblank(win->cr, win->cc - 1)) &&
1213 firstword(oldcr, oldcc))
1214 {
1215 ui_sayword(win->cr, win->cc);
1216 }
1217 else ui_sayline(win->cr, 1);
1218 break;
1219 }
1220 }
1221 oldcr = win->cr;
1222 oldcc = win->cc;
1223 oldch = win->row[win->cr][win->cc];
1224 }
1225
get_tts_input()1226 static void get_tts_input()
1227 {
1228 if (!readable(tts.fd, 1)) return;
1229 (void) read(tts.fd, buf, 100);
1230 }
1231
parent()1232 static void parent()
1233 {
1234 fd_set rf;
1235 struct termios rt;
1236 int maxfd;
1237
1238 (void) memcpy(&rt, &t, sizeof(struct termios));
1239 cfmakeraw(&rt);
1240 rt.c_cc[VMIN] = 1;
1241 rt.c_cc[VTIME] = 0;
1242 (void) tcsetattr(0, TCSAFLUSH, &rt);
1243 yasr_ttyname_r(0, (char *) (buf + 128), 32);
1244 yasr_ttyname_r(slave, (char *) (buf + 192), 32);
1245 utmpconv((char *) (buf + 128), (char *) (buf + 192), cpid);
1246 maxfd = (master > tts.fd ? master : tts.fd) + 1;
1247
1248 for (;;)
1249 {
1250 int result;
1251 FD_ZERO(&rf);
1252 FD_SET(master, &rf);
1253 FD_SET(0, &rf);
1254 FD_SET(tts.fd, &rf);
1255 result = select(maxfd, &rf, NULL, NULL, NULL);
1256 if (result == -1)
1257 {
1258 if (errno == EINTR) continue;
1259 else
1260 {
1261 perror("select");
1262 break;
1263 }
1264 }
1265 if (FD_ISSET(0, &rf))
1266 {
1267 getinput();
1268 }
1269 if (FD_ISSET(master, &rf))
1270 {
1271 getoutput();
1272 kbuflen = 0;
1273 }
1274 if (FD_ISSET(tts.fd, &rf))
1275 {
1276 get_tts_input();
1277 }
1278 }
1279 }
1280
1281 #if 0
1282 int isctty()
1283 {
1284 ttyname_r(0, buf, 64);
1285
1286 return (!strcmp(buf, "/dev/console") ||
1287 (!strncmp(buf, "/dev/tty", 8) && isdigit(buf[8])));
1288 }
1289 #endif
1290
main(int argc,char * argv[])1291 int main(int argc, char *argv[])
1292 {
1293 struct winsize winsz = { 0, 0 };
1294 int flag = 1;
1295
1296 /* initialize gettext */
1297 #ifdef ENABLE_NLS
1298 setlocale(LC_ALL, "");
1299 bindtextdomain(PACKAGE, LOCALEDIR);
1300 textdomain(PACKAGE);
1301 #endif
1302
1303 if (argv[0][0] == '-') shell = 1;
1304 if (isatty(0))
1305 {
1306 (void) ioctl(0, TIOCGWINSZ, &winsz);
1307 }
1308 if (!winsz.ws_row)
1309 {
1310 winsz.ws_row = 25;
1311 winsz.ws_col = 80;
1312 }
1313 win = wininit(winsz.ws_row, winsz.ws_col);
1314 win_scrollmin = 0;
1315 win_scrollmax = winsz.ws_row - 1;
1316 winsave = NULL;
1317 uinit();
1318 (void) memset(&tts, 0, sizeof(Tts));
1319 opt_init();
1320 (void) memset(voices, 0, sizeof(voices));
1321 while (flag)
1322 {
1323 switch (getopt(argc, argv, "C:c:s:p:"))
1324 {
1325 case 'C':
1326 conffile = strdup(optarg);
1327 break;
1328 case 'c':
1329 argv[0] = "/bin/sh";
1330 (void) execv(argv[0], argv);
1331 break;
1332 case 's':
1333 (void) sprintf((char *) buf, "synthesizer=%s", optarg);
1334 opt_read((char *) buf, 0);
1335 cl_synth = 1;
1336 break;
1337 case 'p':
1338 (void) sprintf((char *) buf, "synthesizer port=%s", optarg);
1339 opt_read((char *) buf, 0);
1340 cl_synthport = 1;
1341 break;
1342
1343 default:
1344 flag = 0;
1345 }
1346 }
1347 if (argv[optind])
1348 {
1349 subprog = argv + optind;
1350 }
1351 readconf();
1352
1353 #if 0 /* this doesn't work */
1354 if (!isctty())
1355 {
1356 cp = usershell + strlen(usershell) - 1;
1357 while (*cp && *cp != '/')
1358 {
1359 cp--;
1360 }
1361 cp++;
1362 argv[0] = cp;
1363 (void) execv(usershell, argv);
1364 }
1365 #endif
1366
1367 (void) openpty(&master, &slave, NULL, NULL, &winsz);
1368 (void) signal(SIGCHLD, &child_finish);
1369 (void) tcgetattr(0, &t);
1370 cpid = fork();
1371 if (cpid > 0) parent();
1372 else if (cpid == 0) child();
1373 perror("fork");
1374 return -1;
1375 }
1376
wininit(int nr,int nc)1377 static Win *wininit(int nr, int nc)
1378 {
1379 int i;
1380 Win *win;
1381
1382 win = (Win *) calloc(sizeof(Win), 1);
1383 if (!win)
1384 {
1385 fprintf(stderr, "wininit: cannot allocate %lu bytes\n",
1386 (unsigned long) sizeof(Win));
1387 exit(1);
1388 }
1389 win->rows = nr;
1390 win->cols = nc;
1391 win->row = (chartype **) malloc(win->rows * sizeof(chartype *));
1392 win->cr = win->cc = 0;
1393 for (i = 0; i < win->rows; i++)
1394 {
1395 win->row[i] = (chartype *) calloc(win->cols, CHARSIZE);
1396 if (!win->row[i])
1397 {
1398 fprintf(stderr, "wininit: cannot allocate row %d\n", i);
1399 exit(1);
1400 }
1401 }
1402 win->savecp.cr = win->savecp.cc = 0;
1403 win->tab = (char *) calloc(win->cols, 1);
1404 if (!win->tab)
1405 {
1406 fprintf(stderr, "wininit: cannot allocate win->tab\n");
1407 exit(1);
1408 }
1409 for (i = 0; i < win->cols; i += 8) win->tab[i] = 1;
1410 return (win);
1411 }
1412
speak(char * ibuf,int len)1413 void speak(char *ibuf, int len)
1414 {
1415 char obuf[1024];
1416 int lc = 0, nc = 0;
1417 int len1, olen = 0;
1418 int i;
1419
1420 if (!len) len = strlen(ibuf);
1421 len1 = len - 1;
1422
1423 for (i = 0; i < len; i++)
1424 {
1425 if (ibuf[i] == lc && ++nc == ui.minrc - 1)
1426 {
1427 olen -= nc;
1428 if (olen)
1429 {
1430 tts_out((unsigned char *) obuf, olen);
1431 }
1432 olen = 0;
1433 while (i < len1 && ibuf[i + 1] == lc)
1434 {
1435 i++;
1436 nc++;
1437 }
1438 tts_saychar(lc);
1439 tts_out((unsigned char *) obuf, sprintf(obuf, "%s %d %s\r",_("repeats"), nc + 1,_("times")));
1440 olen = nc = 0;
1441 }
1442 else
1443 {
1444 obuf[olen++] = ibuf[i];
1445 if (ibuf[i] != lc)
1446 {
1447 nc = 0;
1448 }
1449 if (!isalpha((int) ibuf[i]) && !isdigit((int) ibuf[i]) &&
1450 ibuf[i] != 32 && ibuf[i] != '=' && ibuf[i] >= 0)
1451 {
1452 lc = ibuf[i];
1453 }
1454 else lc = 0;
1455 if (olen > 250 && !nc)
1456 {
1457 tts_out((unsigned char *) obuf, olen);
1458 olen = 0;
1459 }
1460 }
1461 }
1462 if (olen)
1463 {
1464 tts_out((unsigned char *) obuf, olen);
1465 }
1466 }
1467