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 #include "yasr.h"
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 
24 #ifndef TRUE
25 #define TRUE 1
26 #endif
27 #ifndef FALSE
28 #define FALSE 1
29 #endif
30 
31 char ttsbuf[80];
32 
33 static Tts_synth synth[] = {
34   {"", "%s\r", "\030", "\005c3", "\005c0", TRUE, "", ""},	/* speakout */
35   {"", "%s[:syn]", "\003", "[:sa le]", "[:sa c]", FALSE, "[]{}\\|_@#^*<>\"`~", ""},	/* DECtalk */
36   {"s\r", "q {%s}\rd\r", "s\r", "l {%c}\r", NULL, FALSE, "[]{}\\|_@#^*<>\"`~^", "exit"},	/* emacspeak server */
37   {"", "%s", "\030", "\001c", "\001t", TRUE, "", ""},	/* doubletalk */
38   {"", "%s\r\r", "\030\030\r\r", NULL, NULL, TRUE, "", ""},	/* BNS */
39   {"", "%s\r\n", "\030", "@P1", "@P0", TRUE, "@", ""},	/* Apollo */
40   {"(audio_mode 'async)\n",
41    "(SayText \"%s\")\n",
42    "(audio_mode 'shutup)\n",
43    "(SayText '%c)\n",
44    NULL,
45    TRUE,
46    "",
47    "\003\")\n"}, /* festival */
48   {"", "%s\r\n", "\033", "%c\r", NULL, TRUE, "@", ""},	/* Ciber232 */
49 /* speech dispatcher (note: various things handled with custom code) */
50   {NULL, NULL, "CANCEL SELF\r\n", NULL, NULL, FALSE, "", "quit\r\n"},
51 };
52 
53 static char *dict[256];
54 static int tts_flushed = 0;
55 
56 
dict_read(char * buf)57 int dict_read(char *buf)
58 {
59   int c;
60   char *p;
61 
62   if (isdigit((int) buf[0]))
63   {
64     c = strtol(buf, &p, 0);
65     if (c > 255)
66     {
67       return (1);
68     }
69     p++;
70   } else
71   {
72     c = buf[0];
73     p = buf + 2;
74   }
75   dict[c] = strdup(p);
76 
77   return (0);
78 }
79 
80 
dict_write(FILE * fp)81 void dict_write(FILE * fp)
82 {
83   int i;
84 
85   for (i = 0; i < 256; i++)
86   {
87     if (dict[i])
88     {
89       if (i > 31 && i < 127 && i != 35 && i != 91)
90       {
91 	(void) fprintf(fp, "%c=%s\n", i, dict[i]);
92       } else
93       {
94 	(void) fprintf(fp, "0x%.2x=%s\n", i, dict[i]);
95       }
96     }
97   }
98 }
99 
100 
101 /* Search for a program on the path */
102 
tts_flush()103 void tts_flush()
104 {
105   if (tts.outlen)
106   {
107     speak(tts.buf, tts.outlen);
108   }
109 
110   tts.outlen = 0;
111 
112 }
113 
114 #if 0
115 static void tts_wait(int usecs)
116 {
117   char buf[100];
118 
119   if (usecs != -1 && !readable(tts.fd, usecs)) return;
120   while (readable(tts.fd, 0))
121   {
122     read(tts.fd, buf, sizeof(buf));
123   }
124 }
125 #endif
126 
tts_silence()127 void tts_silence()
128 {
129   char tmp[1] = { 0 };
130 
131   if (tts_flushed)
132   {
133     return;
134   }
135   (void) tcflush(tts.fd, TCOFLUSH);
136   tts.obufhead = tts.obuftail = tts.flood = 0;
137   opt_queue_empty(1);
138   tts_send(synth[tts.synth].flush, strlen(synth[tts.synth].flush));
139   tts.oflag = 0;
140   tts_flushed = 1;
141 
142   if (tts.synth == TTS_DECTALK)
143   {
144     do
145     {
146       if (!readable(tts.fd, 50000)) break;
147       if (read(tts.fd, tmp, 1) == -1)
148       {
149 	perror("tts_silence");
150 	break;
151       }
152     } while (tmp[0] != 1 && tmp[0] != 0);
153     /* Following is a hack; without it, [:sa c] could have been sent and
154        lost by the DEC-talk if a silence happens immediately after it */
155     tts_send("[:sa c]", 7);
156     tts.oflag = 0;	/* pretend we didn't just do that */
157   }
158 }
159 
160 
tts_obufpack()161 static void tts_obufpack()
162 {
163   (void) memmove(tts.obuf, tts.obuf + tts.obufhead,
164 		 tts.obuftail - tts.obufhead);
165   tts.obuftail -= tts.obufhead;
166   tts.obufhead = 0;
167 }
168 
169 
170 #ifdef TTSLOG
171 static int ofd;
172 #endif
173 
174 
tts_obufout(int x)175  /*ARGSUSED*/ static void tts_obufout(int x)
176 {
177   int len, len2;
178   int oldoflag;
179 
180   if (!tts.flood)
181   {
182     opt_queue_empty(2);
183   }
184   oldoflag = tts.oflag;
185   while (tts.obufhead < tts.obuftail)
186   {
187     len = strlen(tts.obuf + tts.obufhead);
188     if (len > 1024)
189     {
190       len = 1024;
191     }
192     len2 = write(tts.fd, tts.obuf + tts.obufhead, len);
193 #ifdef TTSLOG
194     (void) write(ofd, tts.obuf + tts.obufhead, len2);
195 #endif
196     tts.obufhead += len2;
197     if (len2 < len)
198     {
199       tts.oflag = oldoflag;
200       (void) signal(SIGALRM, &tts_obufout);
201       alarm(1);
202       return;
203     }
204     while (tts.obufhead < tts.obuftail && !tts.obuf[tts.obufhead])
205     {
206       tts.obufhead++;
207     }
208   }
209   tts.flood = 0;
210   tts.oflag = oldoflag;
211   (void) signal(SIGALRM, &tts_obufout);
212 }
213 
214 
tts_addbuf(char * buf,int len,int len2)215 static void tts_addbuf(char *buf, int len, int len2)
216 {
217   char *ptr;
218 
219   tts.flood = 1;
220   if (len2 == -1)
221   {
222     ptr = buf;
223   } else
224   {
225     ptr = buf + len2;
226     len -= len2;
227   }
228   if (tts.obuftail + len > tts.obuflen)
229   {
230     if ((tts.obuftail - tts.obufhead) + len > tts.obuflen)
231     {
232       tts.obuflen += 1024 + len - (len % 1024);
233       tts.obuf = realloc(tts.obuf, tts.obuflen);
234     } else
235     {
236       tts_obufpack();
237     }
238   }
239   (void) memcpy(tts.obuf + tts.obuftail, ptr, len);
240   tts.obuftail += len;
241   tts.obuf[tts.obuftail++] = 0;
242   (void) alarm(1);
243 }
244 
245 
tts_send(char * buf,int len)246 void tts_send(char *buf, int len)
247 {
248   int len2;
249 
250   if (!len)
251   {
252     return;
253   }
254   if (tts_flushed)
255   {
256     tts_flushed = 0;
257   }
258 #ifndef SILENT
259   if (!tts.flood)
260   {
261     len2 = write(tts.fd, buf, len);
262 
263 #ifdef TTSLOG
264     (void) write(ofd, buf, len2);
265 #endif
266 
267     if (len2 < len)
268     {
269       tts_addbuf(buf, len, len2);
270     }
271   } else
272     tts_addbuf(buf, len, 0);
273 #endif
274 
275   tts.oflag = 1;
276 }
277 
278 
unspeakable(unsigned char ch)279 static int unspeakable(unsigned char ch)
280 {
281   char *p = synth[tts.synth].unspeakable;
282 
283   if (ch < 32) return 1;
284   while (*p)
285   {
286     if (*p++ == ch) return 1;
287   }
288   return (0);
289 }
290 
tts_end()291 void tts_end()
292 {
293   tts_send(synth[tts.synth].end, strlen(synth[tts.synth].end));
294 }
295 
tts_out(unsigned char * buf,int len)296 void tts_out(unsigned char *buf, int len)
297 {
298   char obuf[1024];
299   char *p;
300   int obo = 0;	/* current offset into obuf */
301   int i;
302   int xml = 0;
303 
304   if (!len) return;
305   opt_queue_empty(0);
306   if (tts.synth == TTS_SPEECHD)
307   {
308     char *q;
309     tts_send("SPEAK\r\n", 7);
310     if (buf[0] == '.' && buf[1] == '\r') tts_send(".", 1);
311     for (p = (char *)buf; (q = strstr(p + 1, "\r.\r")) && q < (char *)buf+len; p = q)
312     {
313       tts_send(p, q - p);
314     }
315     tts_send(p, (long)buf + len - (long)p);
316     tts_send("\r\n.\r\n", 5);
317     return;
318   }
319   p = synth[tts.synth].say;
320   opt_queue_empty(0);
321   while (*p)
322   {
323     if (*p == '%')
324     {
325       p++;
326       for (i = 0; i < len; i++)
327       {
328 	if (unspeakable((unsigned char)buf[i]))
329 	{
330 	  if (obo > 0 && obuf[obo - 1] != ' ')
331 	  {
332 	    obuf[obo++] = ' ';
333 	    tts_send(obuf, obo);
334 	    obo = 0;
335 	  }
336 	  if (dict[buf[i]])
337 	  {
338 	    (void) strcpy(obuf + obo, dict[buf[i]]);
339 	    obo = strlen(obuf);
340 	  }
341 	  obuf[obo++] = 32;
342 	}
343 	else if (xml)
344 	{
345 	  switch (buf[i])
346 	  {
347 	  /* For some reason, &lt; does not work for me with Voxeo */
348 	  case '<': strcpy(obuf + obo, " less than "); obo += 11; break;
349 	  case '>': strcpy(obuf + obo, "&gt;"); obo += 4; break;
350 	  case '&': strcpy(obuf + obo, "&amp;"); obo += 5; break;
351 	  default: obuf[obo++] = buf[i]; break;
352 	  }
353 	}
354 	else
355 	{
356 	  if (ui.split_caps && i > 0 && islower(buf[i-1]) && isupper(buf[i]))
357 	  {
358 	    obuf[obo++] = ' ';
359 	  }
360 	  obuf[obo++] = buf[i];
361 	}
362 	if (obo > (sizeof(obuf) / sizeof(obuf[0])) - 6)
363 	{
364 	  tts_send(obuf, obo);
365 	  obo = 0;
366 	}
367       }
368     }
369     else
370     {
371       obuf[obo++] = *p;
372     }
373     p++;
374   }
375   tts_send(obuf, obo);
376 }
377 
tts_say(char * buf)378 void tts_say(char *buf)
379 {
380   tts_out((unsigned char *) buf, strlen(buf));
381 }
382 
383 
tts_say_printf(char * fmt,...)384 void tts_say_printf(char *fmt, ...)
385 {
386   va_list arg;
387   char buf[200];
388 
389   va_start(arg, fmt);
390   (void) vsnprintf(buf, sizeof(buf), fmt, arg);
391   tts_say(buf);
392   va_end(arg);
393 }
394 
395 
tts_saychar(unsigned char ch)396 void tts_saychar(unsigned char ch)
397 {
398   int i, j = 0;
399   int stack[10];
400 
401   if (!ch)
402   {
403     ch = 32;
404   }
405   if (tts.synth == TTS_SPEECHD)
406   {
407     if (ch == 32) tts_send("CHAR space\r\n", 12);
408     else tts_printf_ll("CHAR %c\r\n", ch);
409     return;
410   }
411   if (unspeakable(ch) && dict[ch])
412   {
413     tts_say(dict[ch]);
414     return;
415   }
416   if (!synth[tts.synth].charoff)
417   {				/* assume on string does everything */
418     (void) sprintf(ttsbuf, synth[tts.synth].charon, ch);
419     tts_send(ttsbuf, strlen(ttsbuf));
420     return;
421   }
422 
423   for (i = 0; i < NUMOPTS; i++)
424   {
425     if ((opt[i].type & 0x3f) != OT_TREE && !opt[i].ptr && opt[i].synth == tts.synth)
426     {
427       stack[j++] = i;
428       stack[j++] = opt_getval(i, 0);
429       opt_set(i, &opt[i].v.enum_max);
430     }
431   }
432   ttsbuf[1] = 0;
433   opt_queue_empty(3);
434   ttsbuf[0] = ch;
435   tts_send(ttsbuf, 1);
436   if (synth[tts.synth].saychar_needs_flush)
437   {
438     tts_send(synth[tts.synth].say + 2, strlen(synth[tts.synth].say) - 2);
439   }
440   while (j)
441   {
442     j -= 2;
443     opt_set(stack[j], stack + j + 1);
444   }
445 }
446 
447 
448 static char *ph_alph[] = {
449   "alpha", "bravo", "charlie", "delta", "echo",
450   "foxtrot", "golf", "hotel", "india", "juliet",
451   "kilo", "lima", "mike", "november", "oscar",
452   "papa", "quebec", "romeo", "sierra", "tango",
453   "uniform", "victor", "whisky", "x ray",
454   "yankee", "zulu"
455 };
456 
457 
tts_sayphonetic(unsigned char ch)458 void tts_sayphonetic(unsigned char ch)
459 {
460   if (!isalpha(ch))
461   {
462     tts_saychar(ch);
463     return;
464   }
465   if (isupper(ch))
466   {
467     tts_say(_("cap"));
468   } else
469   {
470     ch = toupper(ch);
471   }
472   tts_say(ph_alph[--ch & 0x3f]);
473 }
474 
open_tcp(char * port)475 static int open_tcp(char *port)
476 {
477   char host[1024];
478   int portnum;
479   int fd;
480   struct sockaddr_in servaddr;
481   char *p;
482   int len;
483 
484   p = strstr(port, ":");
485   portnum = atoi(p + 1);
486   len = p - port;
487   if (len > 1023)
488   {
489     fprintf(stderr, "synthesizer port too long\n");
490     exit(1);
491   }
492   memcpy(host, port, len);
493   host[len] = '\0';
494   if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
495   {
496     perror("socket");
497     exit(1);
498   }
499   memset(&servaddr, 0, sizeof(servaddr));
500   servaddr.sin_family = AF_INET;
501   servaddr.sin_port = htons(portnum);
502 
503 #ifdef HAVE_INET_PTON
504   if (inet_pton(AF_INET, host, &servaddr.sin_addr) <= 0)
505   {
506     perror("inet_pton");
507     exit(1);
508   }
509 #else
510   servaddr.sin_addr.s_addr = inet_addr(host);
511 #endif
512 
513   if (connect(fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
514   {
515     perror("connect");
516     exit(1);
517   }
518   return fd;
519 }
520 
tts_init(int first_call)521 int tts_init( int first_call)
522 {
523   struct termios t;
524   char *arg[8];
525   int i;
526   char *portname;
527   int mode = O_RDWR | O_NONBLOCK;
528   char buf[100];
529 
530   portname = getfn(tts.port);
531   tts.pid = 0;
532   tts.reinit = !first_call;
533 
534   (void) signal(SIGALRM, &tts_obufout);
535 
536 #ifdef TTSLOG
537   ofd = open("tts.log", O_WRONLY | O_CREAT);
538   if (ofd == -1)
539   {
540     perror("open");
541   }
542 #endif
543   if (first_call && tts.port[0] != '|' && strstr(tts.port, ":"))
544   {
545     tts.fd = open_tcp(tts.port);
546   }
547   else if (tts.port[0] != '|')
548   {
549     if (tts.synth == TTS_DECTALK)
550     {
551       mode = O_NOCTTY | O_RDWR;
552     }
553     else if (tts.synth == TTS_EMACSPEAK_SERVER)
554     {
555       mode = O_WRONLY;
556     }
557     if (first_call)
558     {
559       tts.fd = open(portname, mode);
560     }
561     if (tts.fd == -1)
562     {
563       perror("tts");
564       exit(1);
565     }
566     (void) tcgetattr(tts.fd, &t);
567     if (tts.synth == TTS_DECTALK)
568     {
569       /* When sending a ^C, make sure we send a ^C and not a break */
570       cfmakeraw(&t);
571       /* following two lines match the Emacspeak server */
572       t.c_iflag |= IXON|IXOFF;
573       t.c_lflag |= IEXTEN;
574       /* When the DEC-talk powers on, it sends several ^A's.  Discard them */
575       tcflush(tts.fd, TCIFLUSH);
576     }
577     t.c_cflag |= CRTSCTS | CLOCAL;
578     t.c_cc[VMIN] = 0;
579     t.c_cc[VTIME] = 10;
580     cfsetospeed(&t, B9600);
581     (void) tcsetattr(tts.fd, 0, &t);
582   }
583   else
584   {				/* a pipe */
585     (void) strcpy(buf, tts.port + 1);
586     arg[i = 0] = strtok(buf, " ");
587     while (i < 7)
588     {
589       if (!(arg[++i] = strtok(NULL, " ")))
590       {
591 	break;
592       }
593     }
594 
595     if (first_call)
596     {
597       if (openpty(&tts.fd, &tts.fd_slave, NULL, NULL, NULL) == -1)
598       {
599 	perror("openpty");
600 	exit(1);
601       }
602     }
603 
604     if (!(tts.pid = fork()))
605     {
606       /* child -- set up tty */
607       (void) dup2(tts.fd_slave, 0);
608       (void) dup2(tts.fd_slave, 1);
609       (void) dup2(tts.fd_slave, 2);
610       /* tts.fd_slave is not closed */
611     }
612     if (!tts.pid)
613     {
614       (void) execvp(arg[0], arg);
615       perror("execpv");
616     }
617     (void) usleep(10000);
618     if (tts.pid == -1)
619     {
620       perror("forkpty");
621     }
622   }
623   if (tts.synth == TTS_SPEECHD)
624   {
625     char buf[200];
626     char *logname = getenv("LOGNAME");
627     if (logname == NULL) logname = getlogin();
628     tts_printf_ll("SET self CLIENT_NAME %s:yasr:tts\r\n", logname);
629   }
630   else tts_send(synth[tts.synth].init, strlen(synth[tts.synth].init));
631 
632   /* init is now finished */
633   tts.reinit = 0;
634 
635   return (0);
636 }
637 
tts_reinit2()638 int tts_reinit2()
639 {
640   tts_init(0);
641   tts_initsynth(NULL);
642   return (0);
643 }
644 
tts_addchr(char ch)645 void tts_addchr(char ch)
646 {
647   tts.buf[tts.outlen++] = ch;
648   if (tts.outlen > 250)
649   {
650     tts_flush();
651   }
652 }
653 
654 
tts_initsynth(int * argp)655  /*ARGSUSED*/ void tts_initsynth(int *argp)
656 {
657   int i, v;
658 
659   for (i = 0; i < NUMOPTS; i++)
660   {
661     if ((opt[i].type & 0x40) &&
662 	opt[i].synth == tts.synth && (opt[i].type & 0x3f) != 0x03)
663     {
664       v = opt_getval(i, 0);
665       opt_set(i, &v);
666     }
667   }
668   if (!ui.silent)
669   {
670     tts_say(_("Synthesizer reinitialized."));
671   }
672 }
673 
674 
675 
tts_reinit(int * argp)676  /*ARGSUSED*/ void tts_reinit(int *argp)
677 {
678   int pid=tts.pid;
679 
680   if (pid==0)
681   {
682       return;
683   }
684 
685   tts.reinit=1; /* Start reinit */
686 
687   tts_silence();
688   usleep(500000);
689 
690   if (kill (pid, SIGTERM)!=0)
691   {
692     if (errno==ESRCH)
693     {
694       tts_reinit2();
695     }
696     else
697     {
698       kill (pid, SIGKILL);
699     }
700   }
701 
702   /* wait init completion (tts.fd must be available) */
703   while(tts.reinit)
704   {
705       usleep(100000);
706   }
707 }
708 
709 
tts_charon()710 void tts_charon()
711 {
712   ttssend(synth[tts.synth].charon);
713 }
714 
715 
tts_charoff()716 void tts_charoff()
717 {
718   ttssend(synth[tts.synth].charoff);
719 }
720 
721 
tts_printf_ll(const char * str,...)722 void tts_printf_ll(const char *str, ...)
723 {
724   char buf[200];
725   va_list args;
726 
727   va_start(args, str);
728   vsprintf(buf, str, args);
729   tts_send(buf, strlen(buf));
730 }
731