xref: /original-bsd/usr.bin/tn3270/ascii/map3270.c (revision ba762ddc)
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