1 /*- 2 * Copyright (c) 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)map3270.c 8.1 (Berkeley) 06/06/93"; 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 static 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, *lp; 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 lp = &lex; 307 if ((lp->type == LEX_CHAR) && !IsPrint(lp->value)) { 308 UnGet(lex); 309 return(0); /* illegal character in quoted string */ 310 } 311 if (pointer >= output.array+sizeof output.array) { 312 return(0); /* too long */ 313 } 314 *pointer++ = lex.value; 315 } 316 output.length = pointer-output.array; 317 return(&output); 318 } 319 320 #ifdef NOTUSED 321 static stringWithLength * 322 GetCharString() 323 { 324 lexicon lex; 325 static stringWithLength output; 326 char *pointer = output.array; 327 328 lex = Get(); 329 330 while ((lex.type == LEX_CHAR) && 331 !isspace(lex.value) && (lex.value != '=')) { 332 *pointer++ = lex.value; 333 lex = Get(); 334 if (pointer >= output.array + sizeof output.array) { 335 return(0); /* too long */ 336 } 337 } 338 UnGet(lex); 339 output.length = pointer-output.array; 340 return(&output); 341 } 342 #endif /* NOTUSED */ 343 344 static 345 GetCharacter(character) 346 int character; /* desired character */ 347 { 348 lexicon lex; 349 350 lex = Get(); 351 352 if ((lex.type != LEX_CHAR) || (lex.value != character)) { 353 UnGet(lex); 354 return(0); 355 } 356 return(1); 357 } 358 359 #ifdef NOTUSED 360 static 361 GetString(string) 362 char *string; /* string to get */ 363 { 364 lexicon lex; 365 366 while (*string) { 367 lex = Get(); 368 if ((lex.type != LEX_CHAR) || (lex.value != *string&0xff)) { 369 UnGet(lex); 370 return(0); /* XXX restore to state on entry */ 371 } 372 string++; 373 } 374 return(1); 375 } 376 #endif /* NOTUSED */ 377 378 379 static stringWithLength * 380 GetAlphaMericString() 381 { 382 lexicon lex, *lp; 383 static stringWithLength output = { 0 }; 384 char *pointer = output.array; 385 # define IsAlnum(c) (isalnum(c) || (c == '_') \ 386 || (c == '-') || (c == '.')) 387 388 lex = Get(); 389 lp = &lex; 390 391 if ((lp->type != LEX_CHAR) || !IsAlnum(lp->value)) { 392 UnGet(lex); 393 return(0); 394 } 395 396 while ((lp->type == LEX_CHAR) && IsAlnum(lp->value)) { 397 *pointer++ = lex.value; 398 lex = Get(); 399 } 400 UnGet(lex); 401 *pointer = 0; 402 output.length = pointer-output.array; 403 return(&output); 404 } 405 406 407 /* eat up characters until a new line, or end of file. returns terminating 408 character. 409 */ 410 411 static lexicon 412 EatToNL() 413 { 414 lexicon lex; 415 416 lex = Get(); 417 418 while (!((lex.type != LEX_ESCAPED) && (lex.type != LEX_CARETED) && 419 (lex.value == '\n')) && (!(lex.type == LEX_END_OF_FILE))) { 420 lex = Get(); 421 } 422 if (lex.type != LEX_END_OF_FILE) { 423 return(Get()); 424 } else { 425 return(lex); 426 } 427 } 428 429 430 static void 431 GetWS() 432 { 433 lexicon lex, *lp; 434 435 lex = Get(); 436 lp = &lex; 437 438 while ((lp->type == LEX_CHAR) && 439 (isspace(lp->value) || (lp->value == '#'))) { 440 if (lex.value == '#') { 441 lex = EatToNL(); 442 } else { 443 lex = Get(); 444 } 445 } 446 UnGet(lex); 447 } 448 449 static void 450 FreeState(pState) 451 state *pState; 452 { 453 extern int free(); 454 455 free((char *)pState); 456 } 457 458 459 static state * 460 GetState() 461 { 462 state *pState; 463 extern char *malloc(); 464 465 pState = (state *) malloc(sizeof (state)); 466 467 pState->result = STATE_NULL; 468 pState->next = 0; 469 470 return(pState); 471 } 472 473 474 static state * 475 FindMatchAtThisLevel(pState, character) 476 state *pState; 477 int character; 478 { 479 while (pState) { 480 if (pState->match == character) { 481 return(pState); 482 } 483 pState = pState->next; 484 } 485 return(0); 486 } 487 488 489 static state * 490 PasteEntry(head, string, count, identifier) 491 state *head; /* points to who should point here... */ 492 char *string; /* which characters to paste */ 493 int count; /* number of character to do */ 494 char *identifier; /* for error messages */ 495 { 496 state *pState, *other; 497 498 if (!doPaste) { /* flag to not have any side effects */ 499 return((state *)1); 500 } 501 if (!count) { 502 return(head); /* return pointer to the parent */ 503 } 504 if ((head->result != STATE_NULL) && (head->result != STATE_GOTO)) { 505 /* this means that a previously defined sequence is an initial 506 * part of this one. 507 */ 508 fprintf(stderr, "Conflicting entries found when scanning %s\n", 509 identifier); 510 return(0); 511 } 512 # ifdef DEBUG 513 if (debug) { 514 fprintf(stderr, "%s", uncontrol(*string)); 515 } 516 # endif /* DEBUG */ 517 pState = GetState(); 518 pState->match = *string; 519 if (head->result == STATE_NULL) { 520 head->result = STATE_GOTO; 521 head->address = pState; 522 other = pState; 523 } else { /* search for same character */ 524 if ((other = FindMatchAtThisLevel(head->address, *string)) != 0) { 525 FreeState(pState); 526 } else { 527 pState->next = head->address; 528 head->address = pState; 529 other = pState; 530 } 531 } 532 return(PasteEntry(other, string+1, count-1, identifier)); 533 } 534 535 static 536 GetInput(tc, identifier) 537 int tc; 538 char *identifier; /* entry being parsed (for error messages) */ 539 { 540 stringWithLength *outputString; 541 state *head; 542 state fakeQueue; 543 544 if (doPaste) { 545 head = headOfQueue; /* always points to level above this one */ 546 } else { 547 head = &fakeQueue; /* don't have any side effects... */ 548 } 549 550 if ((outputString = GetQuotedString()) == 0) { 551 return(0); 552 } else if (IsPrint(outputString->array[0])) { 553 fprintf(stderr, 554 "first character of sequence for %s is not a control type character\n", 555 identifier); 556 return(0); 557 } else { 558 if ((head = PasteEntry(head, outputString->array, 559 outputString->length, identifier)) == 0) { 560 return(0); 561 } 562 GetWS(); 563 while ((outputString = GetQuotedString()) != 0) { 564 if ((head = PasteEntry(head, outputString->array, 565 outputString->length, identifier)) == 0) { 566 return(0); 567 } 568 GetWS(); 569 } 570 } 571 if (!doPaste) { 572 return(1); 573 } 574 if ((head->result != STATE_NULL) && (head->result != tc)) { 575 /* this means that this sequence is an initial part 576 * of a previously defined one. 577 */ 578 fprintf(stderr, "Conflicting entries found when scanning %s\n", 579 identifier); 580 return(0); 581 } else { 582 head->result = tc; 583 return(1); /* done */ 584 } 585 } 586 587 static 588 GetDefinition() 589 { 590 stringWithLength *string; 591 int Tc; 592 593 GetWS(); 594 if ((string = GetAlphaMericString()) == 0) { 595 return(0); 596 } 597 string->array[string->length] = 0; 598 if (doPaste) { 599 if ((Tc = (*GetTc)(string->array)) == -1) { 600 if (picky) { 601 fprintf(stderr, "%s: unknown 3270 key identifier\n", 602 string->array); 603 } 604 Tc = STATE_NULL; 605 } 606 } else { 607 Tc = STATE_NULL; /* XXX ? */ 608 } 609 GetWS(); 610 if (!GetCharacter('=')) { 611 fprintf(stderr, 612 "Required equal sign after 3270 key identifier %s missing\n", 613 string->array); 614 return(0); 615 } 616 GetWS(); 617 if (!GetInput(Tc, string->array)) { 618 fprintf(stderr, "Missing definition part for 3270 key %s\n", 619 string->array); 620 return(0); 621 } else { 622 GetWS(); 623 while (GetCharacter('|')) { 624 # ifdef DEBUG 625 if (debug) { 626 fprintf(stderr, " or "); 627 } 628 # endif /* DEBUG */ 629 GetWS(); 630 if (!GetInput(Tc, string->array)) { 631 fprintf(stderr, "Missing definition part for 3270 key %s\n", 632 string->array); 633 return(0); 634 } 635 GetWS(); 636 } 637 } 638 GetWS(); 639 if (!GetCharacter(';')) { 640 fprintf(stderr, "Missing semi-colon for 3270 key %s\n", string->array); 641 return(0); 642 } 643 # ifdef DEBUG 644 if (debug) { 645 fprintf(stderr, ";\n"); 646 } 647 # endif /* DEBUG */ 648 return(1); 649 } 650 651 652 static 653 GetDefinitions() 654 { 655 if (!GetDefinition()) { 656 return(0); 657 } else { 658 while (GetDefinition()) { 659 ; 660 } 661 } 662 return(1); 663 } 664 665 static 666 GetBegin() 667 { 668 GetWS(); 669 if (!GetCharacter('{')) { 670 return(0); 671 } 672 return(1); 673 } 674 675 static 676 GetEnd() 677 { 678 GetWS(); 679 if (!GetCharacter('}')) { 680 return(0); 681 } 682 return(1); 683 } 684 685 static 686 GetName() 687 { 688 if (!GetAlphaMericString()) { 689 return(0); 690 } 691 GetWS(); 692 while (GetAlphaMericString()) { 693 GetWS(); 694 } 695 return(1); 696 } 697 698 static 699 GetNames() 700 { 701 GetWS(); 702 if (!GetName()) { 703 return(0); 704 } else { 705 GetWS(); 706 while (GetCharacter('|')) { 707 GetWS(); 708 if (!GetName()) { 709 return(0); 710 } 711 } 712 } 713 return(1); 714 } 715 716 static 717 GetEntry0() 718 { 719 if (!GetBegin()) { 720 fprintf(stderr, "no '{'\n"); 721 return(0); 722 } else if (!GetDefinitions()) { 723 fprintf(stderr, "unable to parse the definitions\n"); 724 return(0); 725 } else if (!GetEnd()) { 726 fprintf(stderr, "No '}' or scanning stopped early due to error.\n"); 727 return(0); 728 } else { 729 /* done */ 730 return(1); 731 } 732 } 733 734 735 static 736 GetEntry() 737 { 738 if (!GetNames()) { 739 fprintf(stderr, "Invalid name field in entry.\n"); 740 return(0); 741 } else { 742 return(GetEntry0()); 743 } 744 } 745 746 /* position ourselves within a given filename to the entry for the current 747 * KEYBD (or TERM) variable 748 */ 749 750 Position(filename, keybdPointer) 751 char *filename; 752 char *keybdPointer; 753 { 754 lexicon lex; 755 stringWithLength *name = 0; 756 stringWithLength *oldName; 757 # define Return(x) {doPaste = 1; return(x);} 758 759 doPaste = 0; 760 761 if ((ourFile = fopen(filename, "r")) == NULL) { 762 # if !defined(MSDOS) 763 fprintf(stderr, "Unable to open file %s\n", filename); 764 # endif /* !defined(MSDOS) */ 765 Return(0); 766 } 767 lex = Get(); 768 while (lex.type != LEX_END_OF_FILE) { 769 UnGet(lex); 770 /* now, find an entry that is our type. */ 771 GetWS(); 772 oldName = name; 773 if ((name = GetAlphaMericString()) != 0) { 774 if (!ustrcmp(name->array, keybdPointer)) { 775 /* need to make sure there is a name here... */ 776 lex.type = LEX_CHAR; 777 lex.value = 'a'; 778 UnGet(lex); 779 Return(1); 780 } 781 } else if (GetCharacter('|')) { 782 ; /* more names coming */ 783 } else { 784 lex = Get(); 785 UnGet(lex); 786 if (lex.type != LEX_END_OF_FILE) { 787 if (!GetEntry0()) { /* start of an entry */ 788 fprintf(stderr, 789 "error was in entry for %s in file %s\n", 790 (oldName)? oldName->array:"(unknown)", filename); 791 Return(0); 792 } 793 } 794 } 795 lex = Get(); 796 } 797 #if !defined(MSDOS) 798 fprintf(stderr, "Unable to find entry for %s in file %s\n", keybdPointer, 799 filename); 800 #endif /* !defined(MSDOS) */ 801 Return(0); 802 } 803 804 char * 805 strsave(string) 806 char *string; 807 { 808 char *p; 809 extern char *malloc(); 810 811 p = malloc((unsigned int)strlen(string)+1); 812 if (p != 0) { 813 strcpy(p, string); 814 } 815 return(p); 816 } 817 818 819 /* 820 * InitControl - our interface to the outside. What we should 821 * do is figure out keyboard (or terminal) type, set up file pointer 822 * (or string pointer), etc. 823 */ 824 825 state * 826 InitControl(keybdPointer, pickyarg, translator) 827 char *keybdPointer; 828 int pickyarg; /* Should we be picky? */ 829 int (*translator)(); /* Translates ascii string to integer */ 830 { 831 extern char *getenv(); 832 int GotIt; 833 834 picky = pickyarg; 835 GetTc = translator; 836 837 if (keybdPointer == 0) { 838 keybdPointer = getenv("KEYBD"); 839 } 840 if (keybdPointer == 0) { 841 keybdPointer = getenv("TERM"); 842 } 843 844 /* 845 * Some environments have getenv() return 846 * out of a static area. So, save the keyboard name. 847 */ 848 if (keybdPointer) { 849 keybdPointer = strsave(keybdPointer); 850 } 851 environPointer = getenv("MAP3270"); 852 if (environPointer 853 && (environPointer[0] != '/') 854 #if defined(MSDOS) 855 && (environPointer[0] != '\\') 856 #endif /* defined(MSDOS) */ 857 && (strncmp(keybdPointer, environPointer, 858 strlen(keybdPointer) != 0) 859 || (environPointer[strlen(keybdPointer)] != '{'))) /* } */ 860 { 861 environPointer = 0; 862 } 863 864 if ((!environPointer) 865 #if defined(MSDOS) 866 || (*environPointer == '\\') 867 #endif /* defined(MSDOS) */ 868 || (*environPointer == '/')) { 869 usePointer = 0; 870 GotIt = 0; 871 if (!keybdPointer) { 872 #if !defined(MSDOS) 873 fprintf(stderr, "%s%s%s%s", 874 "Neither the KEYBD environment variable nor the TERM ", 875 "environment variable\n(one of which is needed to determine ", 876 "the type of keyboard you are using)\n", 877 "is set. To set it, say 'setenv KEYBD <type>'\n"); 878 #endif /* !defined(MSDOS) */ 879 } else { 880 if (environPointer) { 881 GotIt = Position(environPointer, keybdPointer); 882 } 883 if (!GotIt) { 884 GotIt = Position("/etc/map3270", keybdPointer); 885 } 886 } 887 if (!GotIt) { 888 if (environPointer) { 889 GotIt = Position(environPointer, "unknown"); 890 } 891 if (!GotIt) { 892 GotIt = Position("/etc/map3270", keybdPointer); 893 } 894 } 895 if (!GotIt) { 896 #if !defined(MSDOS) 897 fprintf(stderr, "Using default key mappings.\n"); 898 #endif /* !defined(MSDOS) */ 899 usePointer = 1; /* flag use of non-file */ 900 whichkey = keysgeneric; 901 environPointer = *whichkey; /* use default table */ 902 } 903 } else { 904 usePointer = 1; 905 } 906 (void) GetEntry(); 907 return(firstentry.address); 908 } 909