|
|
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.