1 /* lexical analysis of RCS files */
3 /******************************************************************************
5 * hashtable, Lexinit, nextlex, getlex, getkey,
6 * getid, getnum, readstring, printstring, savestring,
7 * checkid, fatserror, error, faterror, warn, diagnose
8 * Testprogram: define LEXDB
9 ******************************************************************************
12 /* Copyright 1982, 1988, 1989 Walter Tichy
13 Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
14 Distributed under license by the Free Software Foundation, Inc.
16 This file is part of RCS.
18 RCS is free software; you can redistribute it and/or modify
19 it under the terms of the GNU General Public License as published by
20 the Free Software Foundation; either version 2, or (at your option)
23 RCS is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with RCS; see the file COPYING.
30 If not, write to the Free Software Foundation,
31 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 Report problems and direct all questions to:
35 rcs-bugs@cs.purdue.edu
42 * Revision 5.19 1995/06/16 06:19:24 eggert
45 * Revision 5.18 1995/06/01 16:23:43 eggert
46 * (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate):
48 * (Iclose): If large_memory and maps_memory, use them to deallocate mapping.
49 * (fd2RILE): Use map_fd if available.
50 * If one mapping method fails, try the next instead of giving up;
51 * if they all fail, fall back on ordinary read.
52 * Work around bug: root mmap over NFS succeeds, but accessing dumps core.
53 * Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t.
54 * (advise_access): Use madvise only if this instance used mmap.
55 * (Iopen): Use fdSafer to get safer file descriptor.
56 * (aflush): Moved here from rcsedit.c.
58 * Revision 5.17 1994/03/20 04:52:58 eggert
59 * Don't worry if madvise fails. Add Orewind. Remove lint.
61 * Revision 5.16 1993/11/09 17:55:29 eggert
62 * Fix `label: }' typo.
64 * Revision 5.15 1993/11/03 17:42:27 eggert
65 * Improve quality of diagnostics by putting file names in them more often.
66 * Don't discard ignored phrases.
68 * Revision 5.14 1992/07/28 16:12:44 eggert
69 * Identifiers may now start with a digit and (unless they are symbolic names)
70 * may contain `.'. Avoid `unsigned'. Statement macro names now end in _.
72 * Revision 5.13 1992/02/17 23:02:27 eggert
73 * Work around NFS mmap SIGBUS problem.
75 * Revision 5.12 1992/01/06 02:42:34 eggert
76 * Use OPEN_O_BINARY if mode contains 'b'.
78 * Revision 5.11 1991/11/03 03:30:44 eggert
79 * Fix porting bug to ancient hosts lacking vfprintf.
81 * Revision 5.10 1991/10/07 17:32:46 eggert
82 * Support piece tables even if !has_mmap.
84 * Revision 5.9 1991/09/24 00:28:42 eggert
85 * Don't export errsay().
87 * Revision 5.8 1991/08/19 03:13:55 eggert
88 * Add eoflex(), mmap support. Tune.
90 * Revision 5.7 1991/04/21 11:58:26 eggert
93 * Revision 5.6 1991/02/25 07:12:42 eggert
94 * Work around fputs bug. strsave -> str_save (DG/UX name clash)
96 * Revision 5.5 1990/12/04 05:18:47 eggert
97 * Use -I for prompts and -q for diagnostics.
99 * Revision 5.4 1990/11/19 20:05:28 hammer
100 * no longer gives warning about unknown keywords if -q is specified
102 * Revision 5.3 1990/11/01 05:03:48 eggert
103 * When ignoring unknown phrases, copy them to the output RCS file.
105 * Revision 5.2 1990/09/04 08:02:27 eggert
106 * Count RCS lines better.
108 * Revision 5.1 1990/08/29 07:14:03 eggert
109 * Work around buggy compilers with defective argument promotion.
111 * Revision 5.0 1990/08/22 08:12:55 eggert
112 * Remove compile-time limits; use malloc instead.
113 * Report errno-related errors with perror().
114 * Ansify and Posixate. Add support for ISO 8859.
115 * Use better hash function.
117 * Revision 4.6 89/05/01 15:13:07 narten
118 * changed copyright header to reflect current distribution rules
120 * Revision 4.5 88/08/28 15:01:12 eggert
121 * Don't loop when writing error messages to a full filesystem.
122 * Flush stderr/stdout when mixing output.
123 * Yield exit status compatible with diff(1).
124 * Shrink stdio code size; allow cc -R; remove lint.
126 * Revision 4.4 87/12/18 11:44:47 narten
127 * fixed to use "varargs" in "fprintf"; this is required if it is to
128 * work on a SPARC machine such as a Sun-4
130 * Revision 4.3 87/10/18 10:37:18 narten
131 * Updating version numbers. Changes relative to 1.1 actually relative
134 * Revision 1.3 87/09/24 14:00:17 narten
135 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
138 * Revision 1.2 87/03/27 14:22:33 jenkins
141 * Revision 4.1 83/03/25 18:12:51 wft
142 * Only changed $Header to $Id.
144 * Revision 3.3 82/12/10 16:22:37 wft
145 * Improved error messages, changed exit status on error to 1.
147 * Revision 3.2 82/11/28 21:27:10 wft
148 * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
149 * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
150 * properly in case there is an IO-error (e.g., file system full).
152 * Revision 3.1 82/10/11 19:43:56 wft
153 * removed unused label out:;
154 * made sure all calls to getc() return into an integer, not a char.
161 /* version LEXDB is for testing the lexical analyzer. The testprogram
162 * reads a stream of lexemes, enters the revision numbers into the
163 * hashtable, and prints the recognized tokens. Keywords are recognized
171 libId(lexId, "$FreeBSD$")
173 static char *checkidentifier P((char*,int,int));
174 static void errsay P((char const*));
175 static void fatsay P((char const*));
176 static void lookup P((char const*));
177 static void startsay P((const char*,const char*));
178 static void warnsay P((char const*));
180 static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/
182 enum tokens nexttok; /*next token, set by nextlex */
184 int hshenter; /*if true, next suitable lexeme will be entered */
185 /*into the symbol table. Handle with care. */
186 int nextc; /*next input character, initialized by Lexinit */
188 long rcsline; /*current line-number of input */
189 int nerror; /*counter for errors */
190 int quietflag; /*indicates quiet mode */
191 RILE * finptr; /*input file descriptor */
193 FILE * frewrite; /*file descriptor for echoing input */
195 FILE * foutptr; /* copy of frewrite, but 0 to suppress echo */
197 static struct buf tokbuf; /* token buffer */
199 char const * NextString; /* next token */
202 * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
203 * so hshsize should be odd.
204 * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
205 * Software--practice & experience 20, 2 (Feb 1990), 209-224.
211 static struct hshentry *hshtab[hshsize]; /*hashtable */
213 static int ignored_phrases; /* have we ignored phrases in this RCS file? */
218 if (!ignored_phrases) {
219 ignored_phrases = true;
220 rcswarn("Unknown phrases like `%s ...;' are present.", NextString);
229 /* Function: Looks up the character string pointed to by str in the
230 * hashtable. If the string is not present, a new entry for it is created.
231 * In any case, the address of the corresponding hashtable entry is placed
235 register unsigned ihash; /* index into hashtable */
236 register char const *sp;
237 register struct hshentry *n, **p;
239 /* calculate hash code */
243 ihash = (ihash<<2) + *sp++;
246 for (p = &hshtab[ihash]; ; p = &n->nexthsh)
248 /* empty slot found */
249 *p = n = ftalloc(struct hshentry);
250 n->num = fstr_save(str);
253 VOID printf("\nEntered: %s at %u ", str, ihash);
256 } else if (strcmp(str, n->num) == 0)
270 /* Function: Initialization of lexical analyzer:
271 * initializes the hashtable,
272 * initializes nextc, nexttok if finptr != 0
276 for (c = hshsize; 0 <= --c; ) {
284 ignored_phrases = false;
286 bufrealloc(&tokbuf, 2);
288 nextlex(); /*initial token*/
301 /* Function: Reads the next token and sets nexttok to the next token code.
302 * Only if hshenter is set, a revision number is entered into the
303 * hashtable and a pointer to it is placed into nexthsh.
304 * This is useful for avoiding that dates are placed into the hashtable.
305 * For ID's and NUM's, NextString is set to the character string.
306 * Assumption: nextc contains the next character.
313 register enum tokens d;
316 fin=finptr; frew=foutptr;
317 setupcache(fin); cache(fin);
320 for (;;) { switch ((d = ctab[c])) {
323 fatserror("unknown character `%c'", c);
331 /* Note: falls into next case */
345 limit = sp + tokbuf.size;
359 sp = bufenlarge(&tokbuf, &limit);
368 if (d == DIGIT || d == PERIOD) {
371 lookup(tokbuf.string);
375 NextString = fstr_save(tokbuf.string);
378 case SBEGIN: /* long string */
380 /* note: only the initial SBEGIN has been read*/
381 /* read the string, and reset nextc afterwards*/
397 * Yield true if we look ahead to the end of the input, false otherwise.
398 * nextc becomes undefined at end of file.
409 setupcache(fin); cache(fin);
422 cachegeteof_(c, {uncache(fin);return true;})
433 /* Function: Checks if nexttok is the same as token. If so,
434 * advances the input by calling nextlex and returns true.
435 * otherwise returns false.
436 * Doesn't work for strings and keywords; loses the character string for ids.
439 if (nexttok==token) {
442 } else return(false);
448 /* Function: If the current token is a keyword identical to key,
449 * advances the input by calling nextlex and returns true;
450 * otherwise returns false.
453 if (nexttok==ID && strcmp(key,NextString) == 0) {
465 /* Check that the current input token is a keyword identical to key,
466 * and advance the input by calling nextlex.
470 fatserror("missing '%s' keyword", key);
476 /* Check that the current input token is a keyword identical to key,
477 * and advance the input by calling nextlex; then look ahead for a string.
481 if (nexttok != STRING)
482 fatserror("missing string after '%s' keyword", key);
488 /* Function: Checks if nexttok is an identifier. If so,
489 * advances the input by calling nextlex and returns a pointer
490 * to the identifier; otherwise returns 0.
491 * Treats keywords as identifiers.
494 register char const *name;
504 struct hshentry * getnum()
505 /* Function: Checks if nexttok is a number. If so,
506 * advances the input by calling nextlex and returns a pointer
507 * to the hashtable entry. Otherwise returns 0.
508 * Doesn't work if hshenter is false.
511 register struct hshentry * num;
524 * Get a series of phrases that do not start with KEY. Yield resulting buffer.
525 * Stop when the next phrase starts with a token that is not an identifier,
526 * or is KEY. Copy input to foutptr if it is set. Unlike ignorephrases(),
527 * this routine assumes nextlex() has already been invoked before we start.
532 register char const *kn;
537 # define savech_(c) ;
542 # define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);}
545 if (nexttok!=ID || strcmp(NextString,key) == 0)
551 setupcache(fin); cache(fin);
553 r.string = (char const*)cacheptr() - strlen(NextString) - 1;
556 bufscpy(&b, NextString);
557 p = b.string + strlen(b.string);
558 limit = b.string + b.size;
567 fatserror("unknown character `%c'", c);
572 case COLON: case DIGIT: case LETTER: case Letter:
573 case PERIOD: case SPACE:
576 case SBEGIN: /* long string */
601 if (ctab[c] == NEWLN) {
609 r.size = (char const*)cacheptr() - 1 - r.string;
630 if (ctab[c] == Letter) {
631 for (kn = key; c && *kn==c; kn++)
635 case DIGIT: case LETTER: case Letter:
636 case IDCHAR: case PERIOD:
640 NextString = fstr_save(key);
647 register char const *ki;
648 for (ki=key; ki<kn; )
661 return bufremember(&b, (size_t)(p - b.string));
670 /* skip over characters until terminating single SDELIM */
671 /* If foutptr is set, copy every character read to foutptr. */
672 /* Does not advance nextlex at the end. */
677 fin=finptr; frew=foutptr;
678 setupcache(fin); cache(fin);
702 /* Function: copy a string to stdout, until terminated with a single SDELIM.
703 * Does not advance nextlex at the end.
712 setupcache(fin); cache(fin);
737 /* Copies a string terminated with SDELIM from file finptr to buffer target.
738 * Double SDELIM is replaced with SDELIM.
739 * If foutptr is set, the string is also copied unchanged to foutptr.
740 * Does not advance nextlex at the end.
741 * Yield a copy of *TARGET, except with exact length.
752 fin=finptr; frew=foutptr;
753 setupcache(fin); cache(fin);
754 tp = target->string; limit = tp + target->size;
766 r.string = target->string;
767 r.size = tp - r.string;
774 tp = bufenlarge(target, &limit);
781 checkidentifier(id, delimiter, dotok)
785 /* Function: check whether the string starting at id is an */
786 /* identifier and return a pointer to the delimiter*/
787 /* after the identifier. White space, delim and 0 */
788 /* are legal delimiters. Aborts the program if not*/
789 /* a legal identifier. Useful for checking commands*/
790 /* If !delim, the only delimiter is 0. */
791 /* Allow '.' in identifier only if DOTOK is set. */
795 register char delim = delimiter;
800 switch (ctab[(unsigned char)(c = *id)]) {
821 || (c && (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n')))
823 /* append \0 to end of id before error message */
824 while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim)
827 faterror("invalid %s `%s'",
828 dotok ? "identifier" : "symbol", temp
835 checkid(id, delimiter)
839 return checkidentifier(id, delimiter, true);
843 checksym(sym, delimiter)
847 return checkidentifier(sym, delimiter, false);
853 /* Check whether the string ID is an identifier. */
862 VOID checksym(sym, 0);
867 # define Iclose(f) fclose(f)
870 static int Iclose P((RILE *));
877 return fclose(f->stream);
880 static int Iclose P((RILE *));
885 (* f->deallocate) (f);
891 static void map_fd_deallocate P((RILE *));
898 (vm_address_t) f->base,
899 (vm_size_t) (f->lim - f->base)
901 efaterror("vm_deallocate");
905 static void mmap_deallocate P((RILE *));
910 if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0)
914 static void read_deallocate P((RILE *));
922 static void nothing_to_deallocate P((RILE *));
924 nothing_to_deallocate(f)
932 #if large_memory && maps_memory
933 static RILE *fd2_RILE P((int,char const*,struct stat*));
935 fd2_RILE(fd, name, status)
937 static RILE *fd2RILE P((int,char const*,char const*,struct stat*));
939 fd2RILE(fd, name, type, status)
944 register struct stat *status;
950 if (fstat(fd, status) != 0)
952 if (!S_ISREG(status->st_mode)) {
953 error("`%s' is not a regular file", name);
959 # if !(large_memory && maps_memory)
961 if (!(stream = fdopen(fd, type)))
970 static RILE rilebuf[RILES];
973 size_t s = status->st_size;
975 if (s != status->st_size)
976 faterror("%s: too large", name);
977 for (f = rilebuf; f->base; f++)
978 if (f == rilebuf+RILES)
979 faterror("too many RILEs");
981 f->deallocate = nothing_to_deallocate;
984 static unsigned char nothing;
985 f->base = ¬hing; /* Any nonzero address will do. */
990 fd, (vm_offset_t)0, (vm_address_t*) &f->base,
993 f->deallocate = map_fd_deallocate;
998 f->base = (unsigned char *) mmap(
999 (char *)0, s, PROT_READ, MAP_SHARED,
1003 # define MAP_FAILED (-1)
1005 if (f->base == (unsigned char *) MAP_FAILED)
1008 # if has_NFS && mmap_signal
1010 * On many hosts, the superuser
1011 * can mmap an NFS file it can't read.
1012 * So access the first page now, and print
1013 * a nice message if a bus error occurs.
1015 readAccessFilenameBuffer(name, f->base);
1018 f->deallocate = mmap_deallocate;
1022 f->base = tnalloc(unsigned char, s);
1026 * We can't map the file into memory for some reason.
1027 * Read it into main memory all at once; this is
1028 * the simplest substitute for memory mapping.
1030 char *bufptr = (char *) f->base;
1033 ssize_t r = read(fd, bufptr, bufsiz);
1039 /* The file must have shrunk! */
1040 status->st_size = s -= bufsiz;
1050 if (lseek(fd, (off_t)0, SEEK_SET) == -1)
1052 f->deallocate = read_deallocate;
1058 f->lim = f->base + s;
1061 f->readlim = f->base;
1064 if_advise_access(s, f, MADV_SEQUENTIAL);
1071 #if !maps_memory && large_memory
1076 register fread_type r;
1077 register size_t s = f->lim - f->readlim;
1081 if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
1082 testIerror(f->stream);
1083 f->lim = f->readlim; /* The file might have shrunk! */
1091 #if has_madvise && has_mmap && large_memory
1093 advise_access(f, advice)
1097 if (f->deallocate == mmap_deallocate)
1098 VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice);
1099 /* Don't worry if madvise fails; it's only advisory. */
1104 #if large_memory && maps_memory
1105 I_open(name, status)
1107 Iopen(name, type, status)
1111 struct stat *status;
1112 /* Open NAME for reading, yield its descriptor, and set *STATUS. */
1114 int fd = fdSafer(open(name, O_RDONLY
1116 | (strchr(type,'b') ? OPEN_O_BINARY : 0)
1122 # if large_memory && maps_memory
1123 return fd2_RILE(fd, name, status);
1125 return fd2RILE(fd, name, type, status);
1130 static int Oerrloop;
1138 efaterror("output error");
1141 void Ieof() { fatserror("unexpected end of file"); }
1142 void Ierror() { efaterror("input error"); }
1143 void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
1144 void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
1146 void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
1147 void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
1148 void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
1149 void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
1160 void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
1163 void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); }
1165 void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); }
1166 void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); }
1169 if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop)
1174 fatcleanup(already_newline)
1175 int already_newline;
1177 VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
1187 aprintf(stderr, "%s: %s: %s", cmdid, s, t);
1189 aprintf(stderr, "%s: %s", cmdid, t);
1211 startsay(s, "warning: ");
1214 void eerror(s) char const *s; { enerror(errno,s); }
1221 errsay((char const*)0);
1227 void efaterror(s) char const *s; { enfaterror(errno,s); }
1234 fatsay((char const*)0);
1242 error(char const *format,...)
1244 /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
1246 /* non-fatal error */
1249 errsay((char const*)0);
1250 vararg_start(args, format);
1251 fvfprintf(stderr, format, args);
1253 afputc('\n',stderr);
1259 rcserror(char const *format,...)
1261 /*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl
1263 /* non-fatal RCS file error */
1267 vararg_start(args, format);
1268 fvfprintf(stderr, format, args);
1270 afputc('\n',stderr);
1276 workerror(char const *format,...)
1278 /*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl
1280 /* non-fatal working file error */
1284 vararg_start(args, format);
1285 fvfprintf(stderr, format, args);
1287 afputc('\n',stderr);
1293 fatserror(char const *format,...)
1296 fatserror(format, va_alist) char const *format; va_dcl
1298 /* fatal RCS file syntax error */
1302 VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline);
1303 vararg_start(args, format);
1304 fvfprintf(stderr, format, args);
1311 faterror(char const *format,...)
1313 /*VARARGS1*/ void faterror(format, va_alist)
1314 char const *format; va_dcl
1316 /* fatal error, terminates program after cleanup */
1319 fatsay((char const*)0);
1320 vararg_start(args, format);
1321 fvfprintf(stderr, format, args);
1328 rcsfaterror(char const *format,...)
1330 /*VARARGS1*/ void rcsfaterror(format, va_alist)
1331 char const *format; va_dcl
1333 /* fatal RCS file error, terminates program after cleanup */
1337 vararg_start(args, format);
1338 fvfprintf(stderr, format, args);
1345 warn(char const *format,...)
1347 /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
1354 vararg_start(args, format);
1355 fvfprintf(stderr, format, args);
1357 afputc('\n', stderr);
1364 rcswarn(char const *format,...)
1366 /*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl
1368 /* RCS file warning */
1373 vararg_start(args, format);
1374 fvfprintf(stderr, format, args);
1376 afputc('\n', stderr);
1383 workwarn(char const *format,...)
1385 /*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl
1387 /* working file warning */
1392 vararg_start(args, format);
1393 fvfprintf(stderr, format, args);
1395 afputc('\n', stderr);
1404 warn("redefinition of -%c option", c);
1409 diagnose(char const *format,...)
1411 /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
1413 /* prints a diagnostic message */
1414 /* Unlike the other routines, it does not append a newline. */
1415 /* This lets some callers suppress the newline, and is faster */
1416 /* in implementations that flush stderr just at the end of each printf. */
1421 vararg_start(args, format);
1422 fvfprintf(stderr, format, args);
1432 /* afputc(c,f); acts like aputc_(c,f) but is smaller and slower. */
1444 /* Function: Put string s on file iop, abort on error.
1448 if (fputs(s, iop) < 0)
1451 awrite(s, strlen(s), iop);
1459 fvfprintf(FILE *stream, char const *format, va_list args)
1461 fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
1463 /* like vfprintf, except abort program on error */
1466 if (vfprintf(stream, format, args) < 0)
1470 _doprintf(stream, format, args);
1473 _doprnt(format, args, stream);
1475 int *a = (int *)args;
1476 VOID fprintf(stream, format,
1477 a[0], a[1], a[2], a[3], a[4],
1478 a[5], a[6], a[7], a[8], a[9]
1489 aprintf(FILE *iop, char const *fmt, ...)
1492 aprintf(iop, fmt, va_alist)
1497 /* Function: formatted output. Same as fprintf in stdio,
1498 * but aborts program on error
1502 vararg_start(ap, fmt);
1503 fvfprintf(iop, fmt, ap);
1510 /* test program reading a stream of lexemes and printing the tokens.
1517 int argc; char * argv[];
1521 aputs("No input file\n",stderr);
1522 exitmain(EXIT_FAILURE);
1524 if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
1525 faterror("can't open input file %s",argv[1]);
1532 VOID printf("ID: %s",NextString);
1537 VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
1539 VOID printf("NUM, unentered: %s",NextString);
1540 hshenter = !hshenter; /*alternate between dates and numbers*/
1544 VOID printf("COLON"); break;
1547 VOID printf("SEMI"); break;
1551 VOID printf("STRING"); break;
1554 VOID printf("UNKN"); break;
1557 VOID printf("DEFAULT"); break;
1562 exitmain(EXIT_SUCCESS);
1565 void exiterr() { _exit(EXIT_FAILURE); }