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