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