|
|
1.1 root 1: /*-----------------------------------------------------------------------------
2: Talking BIOS device driver for the AT&T PC6300.
3: Copyright (C) Karl Dahlke 1987
4: This software may be freely used and distributed
5: for any non-profit purpose.
6: *-----------------------------------------------------------------------------
7: */
8:
9: /* words.c: look up words in replacement table */
10:
11: #include "speech.h"
12:
13: #define isvowel(c) vowels[(c) - 'a']
14:
15: /* several English suffixes are recognized.
16: * If present, the suffix may be removed, revealing the root word.
17: * We then look up the root word in the replacement table,
18: * and reapply the suffix.
19: * Thus you only need correct the pronunciation of the singular or present
20: * tense form of the word, and all regular conjugates are also corrected.
21: * The first array defines 10 suffixes.
22: * Additional arrays determine whether the final e or y is dropped,
23: * whether the final consonent is doubled, the length of the suffix, etc. */
24: static char sufshorten[] = {0,1,2,2,3,4,2,1,3,2,2};
25: static char suftab[] = "s es ies ing ing ing d ed ed ied ";
26: static char sufdrop[] = " y e y";
27: static char sufadd[] = {1,2,3,3,4,3,1,3,2,3};
28: static char sufdouble[] = {0,0,0,0,1,0,0,1,0,0};
29:
30: static char vowels[26] = {1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0};
31: static short wdlen; /* length of word being spoken */
32:
33: /* word replacement table */
34: static char *wdreptbl;
35: short sdwdrepreq = 5000; /* patchable */
36: static short wdreplen, wdrepmax;
37:
38: /* macro definition table */
39: static char *mactbl;
40: short sdmacreq = 5000; /* patchable */
41: static short maclen, macmax;
42:
43:
44: /* allocate word replacement table and macro definition table */
45: sdtblload()
46: {
47: static char defreptbl[8];
48: static char defmactbl[8];
49:
50: if(wdreptbl = kalloc(sdwdrepreq)) {
51: wdrepmax = sdwdrepreq; /* able to allocate requested amount */
52: } else {
53: wdreptbl = defreptbl;
54: wdrepmax = 8;
55: }
56: wdreplen = 0;
57: *wdreptbl = 0;
58:
59: if(mactbl = kalloc(sdmacreq)) {
60: macmax = sdmacreq; /* able to allocate requested amount */
61: } else {
62: mactbl = defmactbl;
63: macmax = 8;
64: }
65: maclen = 0;
66: *mactbl = 0;
67: } /* sdtblload */
68:
69: /* speak the current word in the active control structure */
70: sdtextw()
71: {
72: if(sdc->rdflag && !sdc->onesymb)
73: drainset(sdc);
74: sdtext(sdw);
75: } /* sdtextw */
76:
77: /* speak current character */
78: curchar(sayit, asword)
79: {
80: register char c;
81: register i;
82: char *t;
83: static char ctrlstr[] = "controal x";
84: /* phonetic alphabet, words for letters to avoid ambiguity.
85: * important when you need to know exactly which letter (e.g. m or n)
86: * such as variables for equations or programs
87: * words taken from the NATO standard, established in the 1960's
88: * 10 bytes per entry. */
89: static char wfl[] = "\
90: al fa~~~~~brohvo~~~~charlie~~~\
91: delta~~~~~eko~~~~~~~foxtrot~~~\
92: gawlf~~~~~hoatel~~~~india~~~~~\
93: juleyet~~~killo~~~~~liema~~~~~\
94: mike~~~~~~noavember~oscar~~~~~\
95: popa~~~~~~kebeck~~~~roamio~~~~\
96: seeara~~~~tango~~~~~uniform~~~\
97: victor~~~~wiskey~~~~x ray~~~~~\
98: yangkey~~~zoolu~~~~~\
99: ";
100:
101: sdw[0] = 0;
102: cursor_set();
103: c = getc();
104:
105: if(c == '\07') {
106: if(!sdsession && sdnoises) { sdsound(3); return; }
107: t = "bell";
108: goto copywd;
109: }
110: if(c == '\r') {
111: if(!sdsession && sdnoises) { sdcrsnd(); return; }
112: if(sdc->rdflag) return;
113: t = "line";
114: goto copywd;
115: }
116: if(c == '\12') {
117: t = "feed";
118: goto copywd;
119: }
120:
121: if(c && c < 27) {
122: ctrlstr[9] = c|0x40;
123: t = ctrlstr;
124:
125: copywd:
126: i = strlen(t);
127: memcpy(sdw, t, ++i);
128: if(sayit) sdtextw();
129: return;
130: }
131:
132: i = c;
133: if(c) i -= 26;
134: if(c >= '0') {
135: if(c <= '9') goto onelet;
136: i -= 10;
137: }
138: if(c >= 'A') {
139: if(c <= 'Z') goto onelet;
140: i -= 26;
141: }
142: if(c >= 'a') {
143: if(c <= 'z') goto onelet;
144: i -= 26;
145: }
146:
147: t = sdc->punctab + 10*i;
148: goto copywd10;
149:
150: onelet:
151: sdw[0] = c | 0x20;
152: sdw[1] = 0;
153: if(asword && c > '9') {
154: t = wfl + 10*(sdw[0] - 'a');
155: copywd10:
156: for(i=0; i<10; ++i)
157: if((sdw[i] = t[i]) == '~') break;
158: sdw[i] = 0;
159: }
160: if(sayit) sdtextw();
161: } /* curchar */
162:
163: /* read text until EOF or newline or end of symbol */
164: reading()
165: {
166: short i;
167: char c, past_eof, waspunct;
168:
169: cursor_copy();
170: past_eof = waspunct = 0;
171:
172: /* skip whitespace */
173: while(getc() == ' ')
174: if(incptr()) { stopread(); return; }
175:
176: c = getc();
177: if(isdigit(c)) {
178: for(i=0; i<WORDLEN; ++i) {
179: sdw[i] = c;
180: if(incptr()) { past_eof = 1; break; }
181: c = getc();
182: if(!isdigit(c)) break;
183: }
184: if(i < WORDLEN) ++i;
185: sdw[i] = 0;
186: goto spkbuf;
187: } /* end number */
188:
189: c |= 0x20;
190: if(isalnum(c)) {
191: for(i=0; i<WORDLEN; ++i) {
192: sdw[i] = c;
193: if(incptr()) { past_eof = 1; break; }
194: c = getc() | 0x20;
195: if(!islower(c)) break;
196: }
197: if(i < WORDLEN) ++i;
198: sdw[i] = 0;
199:
200: wdlen = i;
201: wdexpand();
202:
203: spkbuf:
204: sdtextw();
205: endword:
206: if(past_eof ||
207: sdc->onesymb && (waspunct || !isalnum(getc())) ||
208: sdc->oneline && waspunct == 2) {
209: stopread();
210: decptr();
211: }
212: cursor_set();
213: return;
214: } /* end word */
215:
216: /* it's a symbol */
217: waspunct = 1;
218: curchar(0, 0);
219: if(getc() == '\r') waspunct = 2;
220:
221: if(sdw[0]) { /* string to be spoken */
222: /* check for an entire line of the symbol */
223: c = getc();
224: i = 1;
225: while(!(past_eof = incptr()) && getc() == c) ++i;
226: if(i > 4) { /* too many, just give count */
227: static char repeat[] = "length xxxxx";
228: int n = i;
229: for(i=4; i>=0; --i) { repeat[i+7] = n%10 + '0'; n /= 10; }
230: for(i=0; i<4; ++i) if(repeat[i+7] != '0') break;
231: memcpy(repeat+7, repeat+7+i, 5);
232:
233: /* curchar() was called in setup mode,
234: * just tack this repeat modifyier on the end of sdw[] */
235: n = strlen(sdw);
236: sdw[n++] = ' ';
237: memcpy(sdw+n, repeat, 14);
238: goto spkbuf;
239: }
240:
241: cursor_copy();
242: sdtextw();
243: }
244:
245: past_eof = incptr();
246: goto endword;
247: } /* reading */
248:
249: /* find word in lookup table, or check for acronym */
250: static wdexpand()
251: {
252: short foundit;
253: short root;
254:
255: if(lookup()) return; /* word replaced */
256:
257: if(root = mkroot()) {
258: wdlen -= sufshorten[root];
259: foundit = lookup();
260: reconst(root);
261: if(foundit) return;
262: }
263:
264: acron();
265: } /* wdexpand */
266:
267: /* extract the root word */
268: static mkroot()
269: {
270: char l0, l1, l2, l3, l4; /* letters */
271: short l;
272:
273: l = wdlen - 5;
274: if(l < 0) return 0; /* word too short to safely "rootinize" */
275: l4 = sdw[l+4];
276: l3 = sdw[l+3];
277: l2 = sdw[l+2];
278: l1 = sdw[l+1];
279: l0 = sdw[l+0];
280: if(l4 == 's') { /* possible plural */
281: if(l3 == 's' || l3 == 'i' || l3 == 'a' || l3 == 'u') return 0;
282: if(l3 == 'e') {
283: if(l2 == 'i') {
284: sdw[l+2] = 'y';
285: sdw[l+3] = 0;
286: return 3;
287: }
288: if(l2 == 's' || l2 == 'h' || l2 == 'z') {
289: sdw[l+3] = 0;
290: return 2;
291: }
292: }
293: /* normal plural */
294: sdw[l+4] = 0;
295: return 1;
296: } /* end final s */
297:
298: if(l == 0) return 0; /* too short */
299:
300: if(l4 == 'g') { /* possible present progressive */
301: if(l3 != 'n' || l2 != 'i') return 0;
302: if(!isvowel(l1)) {
303: if(l1 == l0) { sdw[l+1] = 0; return 5; }
304: if(isvowel(l0) && l0 < 'w' && !isvowel(sdw[l-1])) {
305: sdw[l+2] = 'e';
306: sdw[l+3] = 0;
307: return 6;
308: }
309: }
310: sdw[l+2] = 0;
311: return 4;
312: } /* end ing */
313:
314: if(l4 == 'd') { /* possible past tense */
315: if(l3 != 'e') return 0;
316: if(l2 == 'i') {
317: sdw[l+2] = 'y';
318: sdw[l+3] = 0;
319: return 10;
320: }
321: if(!isvowel(l2)) {
322: if(l2 == l1) { sdw[l+2] = 0; return 8; }
323: if(isvowel(l1) && l1 < 'w' && !isvowel(l0)) {
324: sdw[l+4] = 0;
325: return 7;
326: }
327: }
328: sdw[l+3] = 0;
329: return 9;
330: } /* end final ed */
331:
332: return 0;
333: } /* mkroot */
334:
335: /* reconstruct word based on root and removed suffixes */
336: static reconst(root)
337: {
338: register char *t;
339: register i;
340: char c;
341:
342: if(root--) {
343: t = sdw + wdlen-1;
344: wdlen += sufadd[root];
345: if(sufdouble[root]) c = *t, *++t = c;
346: if(sufdrop[root] == *t) --t, --wdlen;
347: for(i=4*root; i<4*root+4; ++i)
348: *++t = suftab[i];
349: sdw[wdlen] = 0;
350: } /* a real root */
351: } /* reconst */
352:
353: static char *sublookup(w, length)
354: char *w;
355: short length;
356: {
357: register char *t;
358: register short n;
359: short i;
360:
361: /* loop over words in the table */
362: for(t = wdreptbl; n = *t; t += n+2 + t[n+1]) {
363: /* check length and first letter */
364: if(n != length) continue;
365: if(t[1] != *w) continue;
366: /* run strncmp */
367: for(i=1; i<n; ++i)
368: if(t[i+1] != sdw[i]) break;
369: if(i < n) continue;
370: return t; /* match */
371: }
372: return 0; /* no match */
373: } /* sublookup */
374:
375: /* lookup in macro table, similar to sublookup */
376: char *submlookup(mset, key)
377: char mset, key; /* active macro set and key */
378: {
379: register char *t;
380: register short n;
381:
382: /* loop over defined macros */
383: for(t=mactbl; n = *t; t += n) {
384: if(t[1] != mset) continue;
385: if(t[2] != key) continue;
386: return t;
387: } /* end loop */
388:
389: return 0; /* macro not defined */
390: } /* submlookup */
391:
392: /* look up word in pronounciation table */
393: static lookup()
394: {
395: char *t;
396:
397: if(!(t = sublookup(sdw, wdlen))) return 0;
398:
399: t += wdlen+1;
400: wdlen = *t++;
401: memcpy(sdw, t, wdlen);
402: sdw[wdlen] = 0;
403: return 1;
404: } /* lookup */
405:
406: /* if it is an acronym, insert blanks to pronounce letters */
407: static acron()
408: {
409: register i, j;
410: /* legal english three letter initial consonent clusters */
411: static char iclu[] = "chrchlphrphlsclschscrshlshrshwsphsplsprstrthrthw~";
412:
413: /* any vowels in the first four letters? */
414: for(j=0,i=0; i<4; ++i) {
415: if(sdw[i] == 0) break;
416: j += isvowel(sdw[i]);
417: }
418: if(j == 0 || j == 4 || j == wdlen)
419: goto insert;
420:
421: if(j != 1 || wdlen < 4 || !isvowel(sdw[3])) return;
422:
423: for(i=0; iclu[i] != '~'; i+=3) {
424: if(iclu[i] != sdw[0]) continue;
425: if(iclu[i+1] != sdw[1]) continue;
426: if(iclu[i+2] != sdw[2]) continue;
427: return;
428: }
429:
430: insert:
431: /* doesn't look very english, insert blanks */
432: i = wdlen-1;
433: wdlen = j = i + i + 1;
434: while(i >= 0) {
435: sdw[j--] = ' ';
436: sdw[j--] = sdw[i--];
437: }
438: sdw[wdlen] = 0;
439: } /* acron */
440:
441: stopread()
442: {
443: sdc->rdflag = sdc->onesymb = 0;
444: } /* stopread */
445:
446: /* add a word to the pronunciation table */
447: addword(s)
448: char *s;
449: {
450: char c;
451: char *t;
452: short i;
453:
454: i = stringcheck(s, 0);
455: c = s[0];
456: if(isdigit(c) || i == 3) return 3;
457: if(!c) return 0; /* blank line */
458:
459: if(islower(c)) {
460: /* word replacement */
461:
462: /* first remove the old definition */
463: for(i=0; s[i] && s[i] != ' '; ++i) ;
464: c = i;
465: if(t = sublookup(s, c)) {
466: i = c + t[c+1] + 2;
467: wdreplen -= i;
468: memcpy(t, t+i, wdreplen - (t-wdreptbl) + 1);
469: }
470:
471: if(s[c]) {
472: i = strlen(s);
473: if(wdreplen + i >= wdrepmax-1)
474: return 4; /* not enough room */
475: t = wdreptbl + wdreplen;
476: memcpy(t+1, s, i);
477: t[++i] = 0;
478: wdreplen += i;
479: *t = c;
480: t[c+1] = i-c-2;
481: }
482: return 0;
483: } /* end word replacement */
484:
485: /* punctuation replacement */
486: if(c > 'a') c -= 26;
487: if(c > 'A') c -= 26;
488: if(c > '0') c -= 10;
489: c -= ' ';
490: c += 6;
491:
492: for(++s; *s; ++s)
493: if(*s != ' ') break;
494: if(!*s) return 3;
495:
496: t = sdc->punctab + 10*c;
497: for(i=0; i<10; ++i) {
498: if(!s[i]) break;
499: t[i] = s[i];
500: }
501: if(s[i]) return 4;
502:
503: while(i < 10)
504: t[i++] = '~';
505:
506: return 0;
507: } /* addword */
508:
509: static stringcheck(s, macro)
510: char *s;
511: {
512: short i, j;
513: char c, wasblank, macless;
514:
515: wasblank = 1;
516: macless = 0;
517: for(i=j=0; c = s[i]; ++i) {
518: if(!macless) {
519: if(isupper(c)) c |= 0x20;
520: if(c == '\t') c = ' ';
521: }
522: s[j++] = c;
523: if(macless) continue;
524: if(macro && c == '<') { macless = 1; continue; }
525: if(c == ' ') {
526: if(wasblank) --j;
527: wasblank = 1;
528: continue;
529: }
530: wasblank = 0;
531: if(c & 0x80 || c < ' ') return 3;
532: if(c == '.' && j == 2 && s[0] == '#') continue; /* ok */
533: if(j != 1 && !isalnum(c)) return 3;
534: }
535: if(wasblank && j && !macro) --j;
536: s[j] = 0;
537:
538: return (macro && !macless) ? 3 : 0;
539: } /* stringcheck */
540:
541: keybind(s, macro)
542: char *s;
543: {
544: char c;
545: char *t;
546: struct SDCMD *cmdp;
547: char altkey, ctrlkey, shiftkey;
548: short i, keynum;
549: static alpha_alt[] = {
550: 0,30,48,46,32,18,33,34,35,23,36,37,38,50,49,24,25,16,19,31,20,22,47,17,45,21,44};
551: static char pad_cmd[] = {
552: 83,0,82,79,80,81,75,0,77,71,72,73};
553:
554: i = stringcheck(s, macro);
555: if(i == 3) return 3;
556: c = *s++;
557: if(!c) return 0; /* blank line */
558:
559: keynum = 0;
560: altkey = 0;
561: ctrlkey = 0;
562: shiftkey = 0;
563:
564: switch(c) {
565: case '@': /* alt key */
566: altkey = 1;
567: if(*s == 'f' && isdigit(s[1])) { ++s; goto fnkey; }
568: goto letter;
569:
570: case '+':
571: shiftkey = 1;
572: if(*s++ != 'f') return 3;
573: /* fall through to function key */
574:
575: case 'f': /* function key */
576: fnkey:
577: c = *s;
578: while(isdigit(c)) {
579: keynum = 10*keynum + c -'0';
580: c = *++s;
581: }
582: if(keynum <= 0 || keynum > 10) return 3;
583: keynum += 0x3a;
584: if(shiftkey) keynum += 0x19;
585: if(ctrlkey) keynum += 0x23;
586: if(altkey) keynum += 0x2d;
587: break;
588:
589: case '#': /* number pad */
590: c = *s++;
591: if(c < '.' || c > '9') return 3;
592: keynum = pad_cmd[c-'.'];
593: if(!keynum) return 3;
594: break;
595:
596: case '^': /* control key */
597: ctrlkey = 1;
598: if(*s == 'f' && isdigit(s[1])) { ++s; goto fnkey; }
599: letter:
600: c = *s++;
601: if(!islower(c)) return 3;
602: c -= '`';
603: if(altkey)
604: keynum = alpha_alt[c];
605: else keynum = c + 119;
606: break;
607:
608: default: return 3;
609: } /* end switch on first char */
610:
611: if(*s == ' ') ++s;
612:
613: if(*s == '<') { /* macro */
614: ++s;
615: c = 10*sdsession + sdc->macsel;
616: if(t = submlookup(c, keynum)) {
617: /* drop old definition */
618: i = *t;
619: maclen -= i;
620: memcpy(t, t+i, maclen - (t-mactbl) + 1);
621: }
622:
623: if(*s) { /* add new definition */
624: i = strlen(s);
625: if(maclen+i+4 >= macmax) return 4;
626: t = mactbl + maclen;
627: memcpy(t+3, s, i+1);
628: t[1] = c;
629: t[2] = keynum;
630: i += 3;
631: *t = i;
632: maclen += i;
633: }
634: return 0;
635: } /* end macro */
636:
637: if(!*s) { /* unbind key */
638: sdc->keymap[keynum] = 0;
639: return 0;
640: }
641:
642: /* look for this command designator */
643: cmdp = &sdcmds[1];
644: while(*(t = cmdp->brief)) {
645: for(i=0; s[i]; ++i)
646: if(s[i] != t[i]) break;
647: if(!(s[i] | t[i])) {
648: sdc->keymap[keynum] = cmdp - sdcmds;
649: return 0; /* key binding successful */
650: }
651: ++cmdp;
652: } /* end loop through speech functions */
653:
654: return 3; /* no such function */
655: } /* keybind */
656:
657: /* expand macro or simply place the key on the input queue */
658: mexpand(session, key, xcmd)
659: short key, xcmd;
660: {
661: struct SDCONTROL *o = sdcontrol[session];
662: char *t;
663: short n;
664:
665: if(xcmd) {
666: n = 10*session + o->macsel;
667: if(t = submlookup(n, xcmd)) {
668: n = *t;
669: n -= 3, t += 3;
670: while(n--) {
671: /* warning!! MSDOS input expects shorts, not chars.
672: * The high byte is suppose to contain status bits, indicating
673: * shift/control/whatever. I hope command.com, or whatever program
674: * is reading this input, does not really need these bits when
675: * the characters are simple ASCII. */
676: #ifdef MSDOS
677: if((*o->dev_in)(*t++)) {
678: if(!session) sdsound(3);
679: return;
680: }
681: #else
682: (*o->dev_in)(*t++);
683: #endif
684: } /* end pushing macro onto input queue */
685: return;
686: } /* is macro defined */
687: }
688:
689: #ifdef MSDOS
690: if((*o->dev_in)(key)) {
691: if(!session) sdsound(3);
692: }
693: #else
694: (*o->dev_in)(key);
695: #endif
696: } /* mexpand */
697:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.