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