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