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