xref: /original-bsd/usr.bin/tn3270/ascii/map3270.c (revision 5f69ff4a)
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	3.4 (Berkeley) 08/28/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