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 * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcslex.c,v 1.8 1999/08/27 23:36:47 peter Exp $
43 * $DragonFly: src/gnu/usr.bin/rcs/lib/rcslex.c,v 1.2 2003/06/17 04:25:47 dillon Exp $
45 * Revision 5.19 1995/06/16 06:19:24 eggert
48 * Revision 5.18 1995/06/01 16:23:43 eggert
49 * (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate):
51 * (Iclose): If large_memory and maps_memory, use them to deallocate mapping.
52 * (fd2RILE): Use map_fd if available.
53 * If one mapping method fails, try the next instead of giving up;
54 * if they all fail, fall back on ordinary read.
55 * Work around bug: root mmap over NFS succeeds, but accessing dumps core.
56 * Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t.
57 * (advise_access): Use madvise only if this instance used mmap.
58 * (Iopen): Use fdSafer to get safer file descriptor.
59 * (aflush): Moved here from rcsedit.c.
61 * Revision 5.17 1994/03/20 04:52:58 eggert
62 * Don't worry if madvise fails. Add Orewind. Remove lint.
64 * Revision 5.16 1993/11/09 17:55:29 eggert
65 * Fix `label: }' typo.
67 * Revision 5.15 1993/11/03 17:42:27 eggert
68 * Improve quality of diagnostics by putting file names in them more often.
69 * Don't discard ignored phrases.
71 * Revision 5.14 1992/07/28 16:12:44 eggert
72 * Identifiers may now start with a digit and (unless they are symbolic names)
73 * may contain `.'. Avoid `unsigned'. Statement macro names now end in _.
75 * Revision 5.13 1992/02/17 23:02:27 eggert
76 * Work around NFS mmap SIGBUS problem.
78 * Revision 5.12 1992/01/06 02:42:34 eggert
79 * Use OPEN_O_BINARY if mode contains 'b'.
81 * Revision 5.11 1991/11/03 03:30:44 eggert
82 * Fix porting bug to ancient hosts lacking vfprintf.
84 * Revision 5.10 1991/10/07 17:32:46 eggert
85 * Support piece tables even if !has_mmap.
87 * Revision 5.9 1991/09/24 00:28:42 eggert
88 * Don't export errsay().
90 * Revision 5.8 1991/08/19 03:13:55 eggert
91 * Add eoflex(), mmap support. Tune.
93 * Revision 5.7 1991/04/21 11:58:26 eggert
96 * Revision 5.6 1991/02/25 07:12:42 eggert
97 * Work around fputs bug. strsave -> str_save (DG/UX name clash)
99 * Revision 5.5 1990/12/04 05:18:47 eggert
100 * Use -I for prompts and -q for diagnostics.
102 * Revision 5.4 1990/11/19 20:05:28 hammer
103 * no longer gives warning about unknown keywords if -q is specified
105 * Revision 5.3 1990/11/01 05:03:48 eggert
106 * When ignoring unknown phrases, copy them to the output RCS file.
108 * Revision 5.2 1990/09/04 08:02:27 eggert
109 * Count RCS lines better.
111 * Revision 5.1 1990/08/29 07:14:03 eggert
112 * Work around buggy compilers with defective argument promotion.
114 * Revision 5.0 1990/08/22 08:12:55 eggert
115 * Remove compile-time limits; use malloc instead.
116 * Report errno-related errors with perror().
117 * Ansify and Posixate. Add support for ISO 8859.
118 * Use better hash function.
120 * Revision 4.6 89/05/01 15:13:07 narten
121 * changed copyright header to reflect current distribution rules
123 * Revision 4.5 88/08/28 15:01:12 eggert
124 * Don't loop when writing error messages to a full filesystem.
125 * Flush stderr/stdout when mixing output.
126 * Yield exit status compatible with diff(1).
127 * Shrink stdio code size; allow cc -R; remove lint.
129 * Revision 4.4 87/12/18 11:44:47 narten
130 * fixed to use "varargs" in "fprintf"; this is required if it is to
131 * work on a SPARC machine such as a Sun-4
133 * Revision 4.3 87/10/18 10:37:18 narten
134 * Updating version numbers. Changes relative to 1.1 actually relative
137 * Revision 1.3 87/09/24 14:00:17 narten
138 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
141 * Revision 1.2 87/03/27 14:22:33 jenkins
144 * Revision 4.1 83/03/25 18:12:51 wft
145 * Only changed $Header to $Id.
147 * Revision 3.3 82/12/10 16:22:37 wft
148 * Improved error messages, changed exit status on error to 1.
150 * Revision 3.2 82/11/28 21:27:10 wft
151 * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
152 * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
153 * properly in case there is an IO-error (e.g., file system full).
155 * Revision 3.1 82/10/11 19:43:56 wft
156 * removed unused label out:;
157 * made sure all calls to getc() return into an integer, not a char.
164 /* version LEXDB is for testing the lexical analyzer. The testprogram
165 * reads a stream of lexemes, enters the revision numbers into the
166 * hashtable, and prints the recognized tokens. Keywords are recognized
174 libId(lexId, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcslex.c,v 1.2 2003/06/17 04:25:47 dillon Exp $")
176 static char *checkidentifier P((char*,int,int));
177 static void errsay P((char const*));
178 static void fatsay P((char const*));
179 static void lookup P((char const*));
180 static void startsay P((const char*,const char*));
181 static void warnsay P((char const*));
183 static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/
185 enum tokens nexttok; /*next token, set by nextlex */
187 int hshenter; /*if true, next suitable lexeme will be entered */
188 /*into the symbol table. Handle with care. */
189 int nextc; /*next input character, initialized by Lexinit */
191 long rcsline; /*current line-number of input */
192 int nerror; /*counter for errors */
193 int quietflag; /*indicates quiet mode */
194 RILE * finptr; /*input file descriptor */
196 FILE * frewrite; /*file descriptor for echoing input */
198 FILE * foutptr; /* copy of frewrite, but 0 to suppress echo */
200 static struct buf tokbuf; /* token buffer */
202 char const * NextString; /* next token */
205 * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
206 * so hshsize should be odd.
207 * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
208 * Software--practice & experience 20, 2 (Feb 1990), 209-224.
214 static struct hshentry *hshtab[hshsize]; /*hashtable */
216 static int ignored_phrases; /* have we ignored phrases in this RCS file? */
221 if (!ignored_phrases) {
222 ignored_phrases = true;
223 rcswarn("Unknown phrases like `%s ...;' are present.", NextString);
232 /* Function: Looks up the character string pointed to by str in the
233 * hashtable. If the string is not present, a new entry for it is created.
234 * In any case, the address of the corresponding hashtable entry is placed
238 register unsigned ihash; /* index into hashtable */
239 register char const *sp;
240 register struct hshentry *n, **p;
242 /* calculate hash code */
246 ihash = (ihash<<2) + *sp++;
249 for (p = &hshtab[ihash]; ; p = &n->nexthsh)
251 /* empty slot found */
252 *p = n = ftalloc(struct hshentry);
253 n->num = fstr_save(str);
256 VOID printf("\nEntered: %s at %u ", str, ihash);
259 } else if (strcmp(str, n->num) == 0)
273 /* Function: Initialization of lexical analyzer:
274 * initializes the hashtable,
275 * initializes nextc, nexttok if finptr != 0
279 for (c = hshsize; 0 <= --c; ) {
287 ignored_phrases = false;
289 bufrealloc(&tokbuf, 2);
291 nextlex(); /*initial token*/
304 /* Function: Reads the next token and sets nexttok to the next token code.
305 * Only if hshenter is set, a revision number is entered into the
306 * hashtable and a pointer to it is placed into nexthsh.
307 * This is useful for avoiding that dates are placed into the hashtable.
308 * For ID's and NUM's, NextString is set to the character string.
309 * Assumption: nextc contains the next character.
316 register enum tokens d;
319 fin=finptr; frew=foutptr;
320 setupcache(fin); cache(fin);
323 for (;;) { switch ((d = ctab[c])) {
326 fatserror("unknown character `%c'", c);
334 /* Note: falls into next case */
348 limit = sp + tokbuf.size;
362 sp = bufenlarge(&tokbuf, &limit);
371 if (d == DIGIT || d == PERIOD) {
374 lookup(tokbuf.string);
378 NextString = fstr_save(tokbuf.string);
381 case SBEGIN: /* long string */
383 /* note: only the initial SBEGIN has been read*/
384 /* read the string, and reset nextc afterwards*/
400 * Yield true if we look ahead to the end of the input, false otherwise.
401 * nextc becomes undefined at end of file.
412 setupcache(fin); cache(fin);
425 cachegeteof_(c, {uncache(fin);return true;})
436 /* Function: Checks if nexttok is the same as token. If so,
437 * advances the input by calling nextlex and returns true.
438 * otherwise returns false.
439 * Doesn't work for strings and keywords; loses the character string for ids.
442 if (nexttok==token) {
445 } else return(false);
451 /* Function: If the current token is a keyword identical to key,
452 * advances the input by calling nextlex and returns true;
453 * otherwise returns false.
456 if (nexttok==ID && strcmp(key,NextString) == 0) {
468 /* Check that the current input token is a keyword identical to key,
469 * and advance the input by calling nextlex.
473 fatserror("missing '%s' keyword", key);
479 /* Check that the current input token is a keyword identical to key,
480 * and advance the input by calling nextlex; then look ahead for a string.
484 if (nexttok != STRING)
485 fatserror("missing string after '%s' keyword", key);
491 /* Function: Checks if nexttok is an identifier. If so,
492 * advances the input by calling nextlex and returns a pointer
493 * to the identifier; otherwise returns 0.
494 * Treats keywords as identifiers.
497 register char const *name;
507 struct hshentry * getnum()
508 /* Function: Checks if nexttok is a number. If so,
509 * advances the input by calling nextlex and returns a pointer
510 * to the hashtable entry. Otherwise returns 0.
511 * Doesn't work if hshenter is false.
514 register struct hshentry * num;
527 * Get a series of phrases that do not start with KEY. Yield resulting buffer.
528 * Stop when the next phrase starts with a token that is not an identifier,
529 * or is KEY. Copy input to foutptr if it is set. Unlike ignorephrases(),
530 * this routine assumes nextlex() has already been invoked before we start.
535 register char const *kn;
540 # define savech_(c) ;
545 # define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);}
548 if (nexttok!=ID || strcmp(NextString,key) == 0)
554 setupcache(fin); cache(fin);
556 r.string = (char const*)cacheptr() - strlen(NextString) - 1;
559 bufscpy(&b, NextString);
560 p = b.string + strlen(b.string);
561 limit = b.string + b.size;
570 fatserror("unknown character `%c'", c);
575 case COLON: case DIGIT: case LETTER: case Letter:
576 case PERIOD: case SPACE:
579 case SBEGIN: /* long string */
604 if (ctab[c] == NEWLN) {
612 r.size = (char const*)cacheptr() - 1 - r.string;
633 if (ctab[c] == Letter) {
634 for (kn = key; c && *kn==c; kn++)
638 case DIGIT: case LETTER: case Letter:
639 case IDCHAR: case PERIOD:
643 NextString = fstr_save(key);
650 register char const *ki;
651 for (ki=key; ki<kn; )
664 return bufremember(&b, (size_t)(p - b.string));
673 /* skip over characters until terminating single SDELIM */
674 /* If foutptr is set, copy every character read to foutptr. */
675 /* Does not advance nextlex at the end. */
680 fin=finptr; frew=foutptr;
681 setupcache(fin); cache(fin);
705 /* Function: copy a string to stdout, until terminated with a single SDELIM.
706 * Does not advance nextlex at the end.
715 setupcache(fin); cache(fin);
740 /* Copies a string terminated with SDELIM from file finptr to buffer target.
741 * Double SDELIM is replaced with SDELIM.
742 * If foutptr is set, the string is also copied unchanged to foutptr.
743 * Does not advance nextlex at the end.
744 * Yield a copy of *TARGET, except with exact length.
755 fin=finptr; frew=foutptr;
756 setupcache(fin); cache(fin);
757 tp = target->string; limit = tp + target->size;
769 r.string = target->string;
770 r.size = tp - r.string;
777 tp = bufenlarge(target, &limit);
784 checkidentifier(id, delimiter, dotok)
788 /* Function: check whether the string starting at id is an */
789 /* identifier and return a pointer to the delimiter*/
790 /* after the identifier. White space, delim and 0 */
791 /* are legal delimiters. Aborts the program if not*/
792 /* a legal identifier. Useful for checking commands*/
793 /* If !delim, the only delimiter is 0. */
794 /* Allow '.' in identifier only if DOTOK is set. */
798 register char delim = delimiter;
803 switch (ctab[(unsigned char)(c = *id)]) {
824 || (c && (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n')))
826 /* append \0 to end of id before error message */
827 while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim)
830 faterror("invalid %s `%s'",
831 dotok ? "identifier" : "symbol", temp
838 checkid(id, delimiter)
842 return checkidentifier(id, delimiter, true);
846 checksym(sym, delimiter)
850 return checkidentifier(sym, delimiter, false);
856 /* Check whether the string ID is an identifier. */
865 VOID checksym(sym, 0);
870 # define Iclose(f) fclose(f)
873 static int Iclose P((RILE *));
880 return fclose(f->stream);
883 static int Iclose P((RILE *));
888 (* f->deallocate) (f);
894 static void map_fd_deallocate P((RILE *));
901 (vm_address_t) f->base,
902 (vm_size_t) (f->lim - f->base)
904 efaterror("vm_deallocate");
908 static void mmap_deallocate P((RILE *));
913 if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0)
917 static void read_deallocate P((RILE *));
925 static void nothing_to_deallocate P((RILE *));
927 nothing_to_deallocate(f)
935 #if large_memory && maps_memory
936 static RILE *fd2_RILE P((int,char const*,struct stat*));
938 fd2_RILE(fd, name, status)
940 static RILE *fd2RILE P((int,char const*,char const*,struct stat*));
942 fd2RILE(fd, name, type, status)
947 register struct stat *status;
953 if (fstat(fd, status) != 0)
955 if (!S_ISREG(status->st_mode)) {
956 error("`%s' is not a regular file", name);
962 # if !(large_memory && maps_memory)
964 if (!(stream = fdopen(fd, type)))
973 static RILE rilebuf[RILES];
976 size_t s = status->st_size;
978 if (s != status->st_size)
979 faterror("%s: too large", name);
980 for (f = rilebuf; f->base; f++)
981 if (f == rilebuf+RILES)
982 faterror("too many RILEs");
984 f->deallocate = nothing_to_deallocate;
987 static unsigned char nothing;
988 f->base = ¬hing; /* Any nonzero address will do. */
993 fd, (vm_offset_t)0, (vm_address_t*) &f->base,
996 f->deallocate = map_fd_deallocate;
1001 f->base = (unsigned char *) mmap(
1002 (char *)0, s, PROT_READ, MAP_SHARED,
1006 # define MAP_FAILED (-1)
1008 if (f->base == (unsigned char *) MAP_FAILED)
1011 # if has_NFS && mmap_signal
1013 * On many hosts, the superuser
1014 * can mmap an NFS file it can't read.
1015 * So access the first page now, and print
1016 * a nice message if a bus error occurs.
1018 readAccessFilenameBuffer(name, f->base);
1021 f->deallocate = mmap_deallocate;
1025 f->base = tnalloc(unsigned char, s);
1029 * We can't map the file into memory for some reason.
1030 * Read it into main memory all at once; this is
1031 * the simplest substitute for memory mapping.
1033 char *bufptr = (char *) f->base;
1036 ssize_t r = read(fd, bufptr, bufsiz);
1042 /* The file must have shrunk! */
1043 status->st_size = s -= bufsiz;
1053 if (lseek(fd, (off_t)0, SEEK_SET) == -1)
1055 f->deallocate = read_deallocate;
1061 f->lim = f->base + s;
1064 f->readlim = f->base;
1067 if_advise_access(s, f, MADV_SEQUENTIAL);
1074 #if !maps_memory && large_memory
1079 register fread_type r;
1080 register size_t s = f->lim - f->readlim;
1084 if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
1085 testIerror(f->stream);
1086 f->lim = f->readlim; /* The file might have shrunk! */
1094 #if has_madvise && has_mmap && large_memory
1096 advise_access(f, advice)
1100 if (f->deallocate == mmap_deallocate)
1101 VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice);
1102 /* Don't worry if madvise fails; it's only advisory. */
1107 #if large_memory && maps_memory
1108 I_open(name, status)
1110 Iopen(name, type, status)
1114 struct stat *status;
1115 /* Open NAME for reading, yield its descriptor, and set *STATUS. */
1117 int fd = fdSafer(open(name, O_RDONLY
1119 | (strchr(type,'b') ? OPEN_O_BINARY : 0)
1125 # if large_memory && maps_memory
1126 return fd2_RILE(fd, name, status);
1128 return fd2RILE(fd, name, type, status);
1133 static int Oerrloop;
1141 efaterror("output error");
1144 void Ieof() { fatserror("unexpected end of file"); }
1145 void Ierror() { efaterror("input error"); }
1146 void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
1147 void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
1149 void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
1150 void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
1151 void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
1152 void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
1163 void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
1166 void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); }
1168 void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); }
1169 void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); }
1172 if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop)
1177 fatcleanup(already_newline)
1178 int already_newline;
1180 VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
1190 aprintf(stderr, "%s: %s: %s", cmdid, s, t);
1192 aprintf(stderr, "%s: %s", cmdid, t);
1214 startsay(s, "warning: ");
1217 void eerror(s) char const *s; { enerror(errno,s); }
1224 errsay((char const*)0);
1230 void efaterror(s) char const *s; { enfaterror(errno,s); }
1237 fatsay((char const*)0);
1245 error(char const *format,...)
1247 /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
1249 /* non-fatal error */
1252 errsay((char const*)0);
1253 vararg_start(args, format);
1254 fvfprintf(stderr, format, args);
1256 afputc('\n',stderr);
1262 rcserror(char const *format,...)
1264 /*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl
1266 /* non-fatal RCS file error */
1270 vararg_start(args, format);
1271 fvfprintf(stderr, format, args);
1273 afputc('\n',stderr);
1279 workerror(char const *format,...)
1281 /*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl
1283 /* non-fatal working file error */
1287 vararg_start(args, format);
1288 fvfprintf(stderr, format, args);
1290 afputc('\n',stderr);
1296 fatserror(char const *format,...)
1299 fatserror(format, va_alist) char const *format; va_dcl
1301 /* fatal RCS file syntax error */
1305 VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline);
1306 vararg_start(args, format);
1307 fvfprintf(stderr, format, args);
1314 faterror(char const *format,...)
1316 /*VARARGS1*/ void faterror(format, va_alist)
1317 char const *format; va_dcl
1319 /* fatal error, terminates program after cleanup */
1322 fatsay((char const*)0);
1323 vararg_start(args, format);
1324 fvfprintf(stderr, format, args);
1331 rcsfaterror(char const *format,...)
1333 /*VARARGS1*/ void rcsfaterror(format, va_alist)
1334 char const *format; va_dcl
1336 /* fatal RCS file error, terminates program after cleanup */
1340 vararg_start(args, format);
1341 fvfprintf(stderr, format, args);
1348 warn(char const *format,...)
1350 /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
1357 vararg_start(args, format);
1358 fvfprintf(stderr, format, args);
1360 afputc('\n', stderr);
1367 rcswarn(char const *format,...)
1369 /*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl
1371 /* RCS file warning */
1376 vararg_start(args, format);
1377 fvfprintf(stderr, format, args);
1379 afputc('\n', stderr);
1386 workwarn(char const *format,...)
1388 /*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl
1390 /* working file warning */
1395 vararg_start(args, format);
1396 fvfprintf(stderr, format, args);
1398 afputc('\n', stderr);
1407 warn("redefinition of -%c option", c);
1412 diagnose(char const *format,...)
1414 /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
1416 /* prints a diagnostic message */
1417 /* Unlike the other routines, it does not append a newline. */
1418 /* This lets some callers suppress the newline, and is faster */
1419 /* in implementations that flush stderr just at the end of each printf. */
1424 vararg_start(args, format);
1425 fvfprintf(stderr, format, args);
1435 /* afputc(c,f); acts like aputc_(c,f) but is smaller and slower. */
1447 /* Function: Put string s on file iop, abort on error.
1451 if (fputs(s, iop) < 0)
1454 awrite(s, strlen(s), iop);
1462 fvfprintf(FILE *stream, char const *format, va_list args)
1464 fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
1466 /* like vfprintf, except abort program on error */
1469 if (vfprintf(stream, format, args) < 0)
1473 _doprintf(stream, format, args);
1476 _doprnt(format, args, stream);
1478 int *a = (int *)args;
1479 VOID fprintf(stream, format,
1480 a[0], a[1], a[2], a[3], a[4],
1481 a[5], a[6], a[7], a[8], a[9]
1492 aprintf(FILE *iop, char const *fmt, ...)
1495 aprintf(iop, fmt, va_alist)
1500 /* Function: formatted output. Same as fprintf in stdio,
1501 * but aborts program on error
1505 vararg_start(ap, fmt);
1506 fvfprintf(iop, fmt, ap);
1513 /* test program reading a stream of lexemes and printing the tokens.
1520 int argc; char * argv[];
1524 aputs("No input file\n",stderr);
1525 exitmain(EXIT_FAILURE);
1527 if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
1528 faterror("can't open input file %s",argv[1]);
1535 VOID printf("ID: %s",NextString);
1540 VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
1542 VOID printf("NUM, unentered: %s",NextString);
1543 hshenter = !hshenter; /*alternate between dates and numbers*/
1547 VOID printf("COLON"); break;
1550 VOID printf("SEMI"); break;
1554 VOID printf("STRING"); break;
1557 VOID printf("UNKN"); break;
1560 VOID printf("DEFAULT"); break;
1565 exitmain(EXIT_SUCCESS);
1568 void exiterr() { _exit(EXIT_FAILURE); }