1: /* 2: * Copyright 1984, 1985 by the Regents of the University of 3: * California and by Gregory Glenn Minshall. 4: * 5: * Permission to use, copy, modify, and distribute these 6: * programs and their documentation for any purpose and 7: * without fee is hereby granted, provided that this 8: * copyright and permission appear on all copies and 9: * supporting documentation, the name of the Regents of 10: * the University of California not be used in advertising 11: * or publicity pertaining to distribution of the programs 12: * without specific prior permission, and notice be given in 13: * supporting documentation that copying and distribution is 14: * by permission of the Regents of the University of California 15: * and by Gregory Glenn Minshall. Neither the Regents of the 16: * University of California nor Gregory Glenn Minshall make 17: * representations about the suitability of this software 18: * for any purpose. It is provided "as is" without 19: * express or implied warranty. 20: */ 21: 22: 23: #ifndef LINT 24: static char sccsid[] = "@(#)map3270.c 2.5"; 25: #endif /* LINT */ 26: 27: /* This program reads a description file, somewhat like /etc/termcap, 28: that describes the mapping between the current terminals keyboard and 29: a 3270 keyboard. 30: */ 31: #ifdef DOCUMENTATION_ONLY 32: /* here is a sample (very small) entry... 33: 34: # this table is sensitive to position on a line. In particular, 35: # a terminal definition for a terminal is terminated whenever a 36: # (non-comment) line beginning in column one is found. 37: # 38: # this is an entry to map tvi924 to 3270 keys... 39: v8|tvi924|924|televideo model 924 { 40: pfk1 = '\E1'; 41: pfk2 = '\E2'; 42: clear = '^z'; # clear the screen 43: } 44: */ 45: #endif /* DOCUMENTATION_ONLY */ 46: 47: #include <stdio.h> 48: #include <ctype.h> 49: #include <curses.h> 50: 51: #define IsPrint(c) (isprint(c) || ((c) == ' ')) 52: 53: #define LETS_SEE_ASCII 54: #include "m4.out" 55: 56: #include "state.h" 57: 58: /* this is the list of types returned by the lex processor */ 59: #define LEX_CHAR TC_HIGHEST /* plain unadorned character */ 60: #define LEX_ESCAPED LEX_CHAR+1 /* escaped with \ */ 61: #define LEX_CARETED LEX_ESCAPED+1 /* escaped with ^ */ 62: #define LEX_END_OF_FILE LEX_CARETED+1 /* end of file encountered */ 63: #define LEX_ILLEGAL LEX_END_OF_FILE+1 /* trailing escape character */ 64: 65: /* the following is part of our character set dependancy... */ 66: #define ESCAPE 0x1b 67: #define TAB 0x09 68: #define NEWLINE 0x0a 69: #define CARRIAGE_RETURN 0x0d 70: 71: typedef struct { 72: int type; /* LEX_* - type of character */ 73: int value; /* character this was */ 74: } lexicon; 75: 76: typedef struct { 77: int length; /* length of character string */ 78: char array[500]; /* character string */ 79: } stringWithLength; 80: 81: #define panic(s) { fprintf(stderr, s); exit(1); } 82: 83: static state firstentry = { 0, TC_NULL, 0, 0 }; 84: static state *headOfQueue = &firstentry; 85: 86: /* the following is a primitive adm3a table, to be used when nothing 87: * else seems to be avaliable. 88: */ 89: 90: #ifdef DEBUG 91: static int debug = 0; /* debug flag (for debuggin tables) */ 92: #endif /* DEBUG */ 93: 94: static int doPaste = 1; /* should we have side effects */ 95: static char usePointer; /* use pointer, or file */ 96: static FILE *ourFile; 97: static char *environPointer = 0; /* if non-zero, point to input 98: * string in core. 99: */ 100: static char keys3a[] = 101: #include "default.map3270" /* Define the default default */ 102: ; 103: 104: static int Empty = 1, /* is the unget lifo empty? */ 105: Full = 0; /* is the unget lifo full? */ 106: static lexicon lifo[200]; /* character stack for parser */ 107: static int rp = 0, /* read pointer into lifo */ 108: wp = 0; /* write pointer into lifo */ 109: 110: static int 111: GetC() 112: { 113: int character; 114: 115: if (usePointer) { 116: if (*environPointer) { 117: character = 0xff&*environPointer++; 118: } else { 119: character = EOF; 120: } 121: } else { 122: character = getc(ourFile); 123: } 124: return(character); 125: } 126: 127: static lexicon 128: Get() 129: { 130: lexicon c; 131: register lexicon *pC = &c; 132: register int character; 133: 134: if (!Empty) { 135: *pC = lifo[rp]; 136: rp++; 137: if (rp == sizeof lifo/sizeof (lexicon)) { 138: rp = 0; 139: } 140: if (rp == wp) { 141: Empty = 1; 142: } 143: Full = 0; 144: } else { 145: character = GetC(); 146: switch (character) { 147: case EOF: 148: pC->type = LEX_END_OF_FILE; 149: break; 150: case '^': 151: character = GetC(); 152: if (!IsPrint(character)) { 153: pC->type = LEX_ILLEGAL; 154: } else { 155: pC->type = LEX_CARETED; 156: if (character == '?') { 157: character |= 0x40; /* rubout */ 158: } else { 159: character &= 0x1f; 160: } 161: } 162: break; 163: case '\\': 164: character = GetC(); 165: if (!IsPrint(character)) { 166: pC->type = LEX_ILLEGAL; 167: } else { 168: pC->type = LEX_ESCAPED; 169: switch (character) { 170: case 'E': case 'e': 171: character = ESCAPE; 172: break; 173: case 't': 174: character = TAB; 175: break; 176: case 'n': 177: character = NEWLINE; 178: break; 179: case 'r': 180: character = CARRIAGE_RETURN; 181: break; 182: default: 183: pC->type = LEX_ILLEGAL; 184: break; 185: } 186: } 187: break; 188: default: 189: if ((IsPrint(character)) || isspace(character)) { 190: pC->type = LEX_CHAR; 191: } else { 192: pC->type = LEX_ILLEGAL; 193: } 194: break; 195: } 196: pC->value = character; 197: } 198: return(*pC); 199: } 200: 201: static 202: UnGet(c) 203: lexicon c; /* character to unget */ 204: { 205: if (Full) { 206: fprintf(stderr, "attempt to put too many characters in lifo\n"); 207: panic("map3270"); 208: /* NOTREACHED */ 209: } else { 210: lifo[wp] = c; 211: wp++; 212: if (wp == sizeof lifo/sizeof (lexicon)) { 213: wp = 0; 214: } 215: if (wp == rp) { 216: Full = 1; 217: } 218: Empty = 0; 219: } 220: } 221: 222: /* compare two strings, ignoring case */ 223: 224: ustrcmp(string1, string2) 225: register char *string1; 226: register char *string2; 227: { 228: register int c1, c2; 229: 230: while (c1 = (unsigned char) *string1++) { 231: if (isupper(c1)) { 232: c1 = tolower(c1); 233: } 234: if (isupper(c2 = (unsigned char) *string2++)) { 235: c2 = tolower(c2); 236: } 237: if (c1 < c2) { 238: return(-1); 239: } else if (c1 > c2) { 240: return(1); 241: } 242: } 243: if (*string2) { 244: return(-1); 245: } else { 246: return(0); 247: } 248: } 249: 250: 251: static stringWithLength * 252: GetQuotedString() 253: { 254: lexicon lex; 255: static stringWithLength output; /* where return value is held */ 256: char *pointer = output.array; 257: 258: lex = Get(); 259: if ((lex.type != LEX_CHAR) || (lex.value != '\'')) { 260: UnGet(lex); 261: return(0); 262: } 263: while (1) { 264: lex = Get(); 265: if ((lex.type == LEX_CHAR) && (lex.value == '\'')) { 266: break; 267: } 268: if ((lex.type == LEX_CHAR) && !IsPrint(lex.value)) { 269: UnGet(lex); 270: return(0); /* illegal character in quoted string */ 271: } 272: if (pointer >= output.array+sizeof output.array) { 273: return(0); /* too long */ 274: } 275: *pointer++ = lex.value; 276: } 277: output.length = pointer-output.array; 278: return(&output); 279: } 280: 281: #ifdef NOTUSED 282: static stringWithLength * 283: GetCharString() 284: { 285: lexicon lex; 286: static stringWithLength output; 287: char *pointer = output.array; 288: 289: lex = Get(); 290: 291: while ((lex.type == LEX_CHAR) && 292: !isspace(lex.value) && (lex.value != '=')) { 293: *pointer++ = lex.value; 294: lex = Get(); 295: if (pointer >= output.array + sizeof output.array) { 296: return(0); /* too long */ 297: } 298: } 299: UnGet(lex); 300: output.length = pointer-output.array; 301: return(&output); 302: } 303: #endif /* NOTUSED */ 304: 305: static 306: GetCharacter(character) 307: int character; /* desired character */ 308: { 309: lexicon lex; 310: 311: lex = Get(); 312: 313: if ((lex.type != LEX_CHAR) || (lex.value != character)) { 314: UnGet(lex); 315: return(0); 316: } 317: return(1); 318: } 319: 320: #ifdef NOTUSED 321: static 322: GetString(string) 323: char *string; /* string to get */ 324: { 325: lexicon lex; 326: 327: while (*string) { 328: lex = Get(); 329: if ((lex.type != LEX_CHAR) || (lex.value != *string&0xff)) { 330: UnGet(lex); 331: return(0); /* XXX restore to state on entry */ 332: } 333: string++; 334: } 335: return(1); 336: } 337: #endif /* NOTUSED */ 338: 339: 340: static stringWithLength * 341: GetAlphaMericString() 342: { 343: lexicon lex; 344: static stringWithLength output; 345: char *pointer = output.array; 346: # define IsAlnum(c) (isalnum(c) || (c == '_')|| (c == '-')) 347: 348: lex = Get(); 349: 350: if ((lex.type != LEX_CHAR) || !IsAlnum(lex.value)) { 351: UnGet(lex); 352: return(0); 353: } 354: 355: while ((lex.type == LEX_CHAR) && IsAlnum(lex.value)) { 356: *pointer++ = lex.value; 357: lex = Get(); 358: } 359: UnGet(lex); 360: *pointer = 0; 361: output.length = pointer-output.array; 362: return(&output); 363: } 364: 365: 366: /* eat up characters until a new line, or end of file. returns terminating 367: character. 368: */ 369: 370: static lexicon 371: EatToNL() 372: { 373: lexicon lex; 374: 375: lex = Get(); 376: 377: while (!((lex.type != LEX_ESCAPED) && (lex.value == '\n')) && 378: (!(lex.type == LEX_END_OF_FILE))) { 379: lex = Get(); 380: } 381: if (lex.type != LEX_END_OF_FILE) { 382: return(Get()); 383: } else { 384: return(lex); 385: } 386: } 387: 388: 389: static void 390: GetWS() 391: { 392: lexicon lex; 393: 394: lex = Get(); 395: 396: while ((lex.type == LEX_CHAR) && 397: (isspace(lex.value) || (lex.value == '#'))) { 398: if (lex.value == '#') { 399: lex = EatToNL(); 400: } else { 401: lex = Get(); 402: } 403: } 404: UnGet(lex); 405: } 406: 407: static void 408: FreeState(pState) 409: state *pState; 410: { 411: free((char *)pState); 412: } 413: 414: 415: static state * 416: GetState() 417: { 418: state *pState; 419: char *malloc(); 420: 421: pState = (state *) malloc(sizeof *pState); 422: 423: pState->result = TC_NULL; 424: pState->next = 0; 425: 426: return(pState); 427: } 428: 429: 430: static state * 431: FindMatchAtThisLevel(pState, character) 432: state *pState; 433: int character; 434: { 435: while (pState) { 436: if (pState->match == character) { 437: return(pState); 438: } 439: pState = pState->next; 440: } 441: return(0); 442: } 443: 444: 445: static state * 446: PasteEntry(head, string, count, identifier) 447: state *head; /* points to who should point here... */ 448: char *string; /* which characters to paste */ 449: int count; /* number of character to do */ 450: char *identifier; /* for error messages */ 451: { 452: state *pState, *other; 453: 454: if (!doPaste) { /* flag to not have any side effects */ 455: return((state *)1); 456: } 457: if (!count) { 458: return(head); /* return pointer to the parent */ 459: } 460: if ((head->result != TC_NULL) && (head->result != TC_GOTO)) { 461: /* this means that a previously defined sequence is an initial 462: * part of this one. 463: */ 464: fprintf(stderr, "Conflicting entries found when scanning %s\n", 465: identifier); 466: return(0); 467: } 468: # ifdef DEBUG 469: if (debug) { 470: fprintf(stderr, "%s", unctrl(*string)); 471: } 472: # endif /* DEBUG */ 473: pState = GetState(); 474: pState->match = *string; 475: if (head->result == TC_NULL) { 476: head->result = TC_GOTO; 477: head->address = pState; 478: other = pState; 479: } else { /* search for same character */ 480: if (other = FindMatchAtThisLevel(head->address, *string)) { 481: FreeState(pState); 482: } else { 483: pState->next = head->address; 484: head->address = pState; 485: other = pState; 486: } 487: } 488: return(PasteEntry(other, string+1, count-1, identifier)); 489: } 490: 491: static 492: GetInput(tc, identifier) 493: int tc; 494: char *identifier; /* entry being parsed (for error messages) */ 495: { 496: stringWithLength *outputString; 497: state *head; 498: state fakeQueue; 499: 500: if (doPaste) { 501: head = headOfQueue; /* always points to level above this one */ 502: } else { 503: head = &fakeQueue; /* don't have any side effects... */ 504: } 505: 506: if (!(outputString = GetQuotedString())) { 507: return(0); 508: } else if (IsPrint(outputString->array[0])) { 509: fprintf(stderr, 510: "first character of sequence for %s is not a control type character\n", 511: identifier); 512: return(0); 513: } else { 514: if (!(head = PasteEntry(head, outputString->array, 515: outputString->length, identifier))) { 516: return(0); 517: } 518: GetWS(); 519: while (outputString = GetQuotedString()) { 520: if (!(head = PasteEntry(head, outputString->array, outputString->length, identifier))) { 521: return(0); 522: } 523: GetWS(); 524: } 525: } 526: if (!doPaste) { 527: return(1); 528: } 529: if ((head->result != TC_NULL) && (head->result != tc)) { 530: /* this means that this sequence is an initial part 531: * of a previously defined one. 532: */ 533: fprintf(stderr, "Conflicting entries found when scanning %s\n", 534: identifier); 535: return(0); 536: } else { 537: head->result = tc; 538: return(1); /* done */ 539: } 540: } 541: 542: static 543: GetTc(string) 544: char *string; 545: { 546: register TC_Ascii_t *Tc; 547: 548: for (Tc = TC_Ascii; 549: Tc < TC_Ascii+sizeof TC_Ascii/sizeof (TC_Ascii_t); Tc++) { 550: if (!ustrcmp(string, Tc->tc_name)) { 551: # ifdef DEBUG 552: if (debug) { 553: fprintf(stderr, "%s = ", Tc->tc_name); 554: } 555: # endif /* DEBUG */ 556: return(Tc->tc_value&0xff); 557: } 558: } 559: return(0); 560: } 561: static 562: GetDefinition() 563: { 564: stringWithLength *string; 565: int Tc; 566: 567: GetWS(); 568: if (!(string = GetAlphaMericString())) { 569: return(0); 570: } 571: string->array[string->length] = 0; 572: if (doPaste) { 573: if (!(Tc = GetTc(string->array))) { 574: fprintf(stderr, "%s: unknown 3270 key identifier\n", string->array); 575: return(0); 576: } 577: if (Tc < TC_LOWEST_USER) { 578: fprintf(stderr, "%s is not allowed to be specified by a user.\n", 579: string->array); 580: return(0); 581: } 582: } else { 583: Tc = TC_LOWEST_USER; 584: } 585: GetWS(); 586: if (!GetCharacter('=')) { 587: fprintf(stderr, 588: "Required equal sign after 3270 key identifier %s missing\n", 589: string->array); 590: return(0); 591: } 592: GetWS(); 593: if (!GetInput(Tc, string->array)) { 594: fprintf(stderr, "Missing definition part for 3270 key %s\n", 595: string->array); 596: return(0); 597: } else { 598: GetWS(); 599: while (GetCharacter('|')) { 600: # ifdef DEBUG 601: if (debug) { 602: fprintf(stderr, " or "); 603: } 604: # endif /* DEBUG */ 605: GetWS(); 606: if (!GetInput(Tc, string->array)) { 607: fprintf(stderr, "Missing definition part for 3270 key %s\n", 608: string->array); 609: return(0); 610: } 611: GetWS(); 612: } 613: } 614: GetWS(); 615: if (!GetCharacter(';')) { 616: fprintf(stderr, "Missing semi-colon for 3270 key %s\n", string->array); 617: return(0); 618: } 619: # ifdef DEBUG 620: if (debug) { 621: fprintf(stderr, ";\n"); 622: } 623: # endif /* DEBUG */ 624: return(1); 625: } 626: 627: 628: static 629: GetDefinitions() 630: { 631: if (!GetDefinition()) { 632: return(0); 633: } else { 634: while (GetDefinition()) { 635: ; 636: } 637: } 638: return(1); 639: } 640: 641: static 642: GetBegin() 643: { 644: GetWS(); 645: if (!GetCharacter('{')) { 646: return(0); 647: } 648: return(1); 649: } 650: 651: static 652: GetEnd() 653: { 654: GetWS(); 655: if (!GetCharacter('}')) { 656: return(0); 657: } 658: return(1); 659: } 660: 661: static 662: GetName() 663: { 664: if (!GetAlphaMericString()) { 665: return(0); 666: } 667: GetWS(); 668: while (GetAlphaMericString()) { 669: GetWS(); 670: } 671: return(1); 672: } 673: 674: static 675: GetNames() 676: { 677: GetWS(); 678: if (!GetName()) { 679: return(0); 680: } else { 681: GetWS(); 682: while (GetCharacter('|')) { 683: GetWS(); 684: if (!GetName()) { 685: return(0); 686: } 687: } 688: } 689: return(1); 690: } 691: 692: static 693: GetEntry0() 694: { 695: if (!GetBegin()) { 696: fprintf(stderr, "no '{'\n"); 697: return(0); 698: } else if (!GetDefinitions()) { 699: fprintf(stderr, "unable to parse the definitions\n"); 700: return(0); 701: } else if (!GetEnd()) { 702: fprintf(stderr, "no '}'\n"); 703: return(0); 704: } else { 705: /* done */ 706: return(1); 707: } 708: } 709: 710: 711: static 712: GetEntry() 713: { 714: if (!GetNames()) { 715: fprintf(stderr, "illegal name field in entry\n"); 716: return(0); 717: } else { 718: return(GetEntry0()); 719: } 720: } 721: 722: /* position ourselves within a given filename to the entry for the current 723: * TERM variable 724: */ 725: 726: Position(filename, termPointer) 727: char *filename; 728: char *termPointer; 729: { 730: lexicon lex; 731: stringWithLength *name = 0; 732: stringWithLength *oldName; 733: # define Return(x) {doPaste = 1; return(x);} 734: 735: doPaste = 0; 736: 737: if ((ourFile = fopen(filename, "r")) == NULL) { 738: fprintf(stderr, "Unable to open file %s\n", filename); 739: Return(0); 740: } 741: lex = Get(); 742: while (lex.type != LEX_END_OF_FILE) { 743: UnGet(lex); 744: /* now, find an entry that is our type. */ 745: GetWS(); 746: oldName = name; 747: if (name = GetAlphaMericString()) { 748: if (!ustrcmp(name->array, termPointer)) { 749: /* need to make sure there is a name here... */ 750: lex.type = LEX_CHAR; 751: lex.value = 'a'; 752: UnGet(lex); 753: Return(1); 754: } 755: } else if (GetCharacter('|')) { 756: ; /* more names coming */ 757: } else { 758: lex = Get(); 759: UnGet(lex); 760: if (lex.type != LEX_END_OF_FILE) { 761: if (!GetEntry0()) { /* start of an entry */ 762: fprintf(stderr, "error was in entry for %s in file %s\n", 763: (oldName)? oldName->array:"(unknown)", filename); 764: Return(0); 765: } 766: } 767: } 768: lex = Get(); 769: } 770: fprintf(stderr, "Unable to find entry for %s in file %s\n", termPointer, 771: filename); 772: Return(0); 773: } 774: /* InitControl - our interface to the outside. What we should 775: do is figure out terminal type, set up file pointer (or string 776: pointer), etc. 777: */ 778: 779: state * 780: InitControl() 781: { 782: char *getenv(); 783: int GotIt; 784: char *termPointer; 785: 786: environPointer = getenv("MAP3270"); 787: 788: if ((!environPointer) || (*environPointer == '/')) { 789: usePointer = 0; 790: GotIt = 0; 791: 792: termPointer = getenv("TERM"); 793: if (!termPointer) { 794: fprintf(stderr, 795: "TERM environment variable (that defines the kind of terminal you are using)\n"); 796: fprintf(stderr, 797: "is not set. To set it, say 'setenv TERM <type>'\n"); 798: } else { 799: if (environPointer) { 800: GotIt = Position(environPointer, termPointer); 801: } 802: if (!GotIt) { 803: GotIt = Position("/etc/map3270", termPointer); 804: } 805: } 806: if (!GotIt) { 807: if (environPointer) { 808: GotIt = Position(environPointer, "unknown"); 809: } 810: if (!GotIt) { 811: GotIt = Position("/etc/map3270", "unknown"); 812: } 813: } 814: if (!GotIt) { 815: fprintf(stderr, "Using default key mappings.\n"); 816: environPointer = keys3a; /* use incore table */ 817: usePointer = 1; /* flag use of non-file */ 818: } 819: } else { 820: usePointer = 1; 821: } 822: (void) GetEntry(); 823: return(firstentry.address); 824: }