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