Home | History | Annotate | Line # | Download | only in src
      1 /*	$NetBSD: rcslex.c,v 1.2 2016/01/14 04:22:39 christos Exp $	*/
      2 
      3 /* lexical analysis of RCS files */
      4 
      5 /******************************************************************************
      6  *                     Lexical Analysis.
      7  *                     hashtable, Lexinit, nextlex, getlex, getkey,
      8  *                     getid, getnum, readstring, printstring, savestring,
      9  *                     checkid, fatserror, error, faterror, warn, diagnose
     10  *                     Testprogram: define LEXDB
     11  ******************************************************************************
     12  */
     13 
     14 /* Copyright 1982, 1988, 1989 Walter Tichy
     15    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
     16    Distributed under license by the Free Software Foundation, Inc.
     17 
     18 This file is part of RCS.
     19 
     20 RCS is free software; you can redistribute it and/or modify
     21 it under the terms of the GNU General Public License as published by
     22 the Free Software Foundation; either version 2, or (at your option)
     23 any later version.
     24 
     25 RCS is distributed in the hope that it will be useful,
     26 but WITHOUT ANY WARRANTY; without even the implied warranty of
     27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     28 GNU General Public License for more details.
     29 
     30 You should have received a copy of the GNU General Public License
     31 along with RCS; see the file COPYING.
     32 If not, write to the Free Software Foundation,
     33 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     34 
     35 Report problems and direct all questions to:
     36 
     37     rcs-bugs (at) cs.purdue.edu
     38 
     39 */
     40 
     41 
     42 
     43 /*
     44  * Log: rcslex.c,v
     45  * Revision 5.19  1995/06/16 06:19:24  eggert
     46  * Update FSF address.
     47  *
     48  * Revision 5.18  1995/06/01 16:23:43  eggert
     49  * (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate):
     50  * New functions.
     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.
     60  *
     61  * Revision 5.17  1994/03/20 04:52:58  eggert
     62  * Don't worry if madvise fails.  Add Orewind.  Remove lint.
     63  *
     64  * Revision 5.16  1993/11/09 17:55:29  eggert
     65  * Fix `label: }' typo.
     66  *
     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.
     70  *
     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 _.
     74  *
     75  * Revision 5.13  1992/02/17  23:02:27  eggert
     76  * Work around NFS mmap SIGBUS problem.
     77  *
     78  * Revision 5.12  1992/01/06  02:42:34  eggert
     79  * Use OPEN_O_BINARY if mode contains 'b'.
     80  *
     81  * Revision 5.11  1991/11/03  03:30:44  eggert
     82  * Fix porting bug to ancient hosts lacking vfprintf.
     83  *
     84  * Revision 5.10  1991/10/07  17:32:46  eggert
     85  * Support piece tables even if !has_mmap.
     86  *
     87  * Revision 5.9  1991/09/24  00:28:42  eggert
     88  * Don't export errsay().
     89  *
     90  * Revision 5.8  1991/08/19  03:13:55  eggert
     91  * Add eoflex(), mmap support.  Tune.
     92  *
     93  * Revision 5.7  1991/04/21  11:58:26  eggert
     94  * Add MS-DOS support.
     95  *
     96  * Revision 5.6  1991/02/25  07:12:42  eggert
     97  * Work around fputs bug.  strsave -> str_save (DG/UX name clash)
     98  *
     99  * Revision 5.5  1990/12/04  05:18:47  eggert
    100  * Use -I for prompts and -q for diagnostics.
    101  *
    102  * Revision 5.4  1990/11/19  20:05:28  hammer
    103  * no longer gives warning about unknown keywords if -q is specified
    104  *
    105  * Revision 5.3  1990/11/01  05:03:48  eggert
    106  * When ignoring unknown phrases, copy them to the output RCS file.
    107  *
    108  * Revision 5.2  1990/09/04  08:02:27  eggert
    109  * Count RCS lines better.
    110  *
    111  * Revision 5.1  1990/08/29  07:14:03  eggert
    112  * Work around buggy compilers with defective argument promotion.
    113  *
    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.
    119  *
    120  * Revision 4.6  89/05/01  15:13:07  narten
    121  * changed copyright header to reflect current distribution rules
    122  *
    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.
    128  *
    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
    132  *
    133  * Revision 4.3  87/10/18  10:37:18  narten
    134  * Updating version numbers. Changes relative to 1.1 actually relative
    135  * to version 4.1
    136  *
    137  * Revision 1.3  87/09/24  14:00:17  narten
    138  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
    139  * warnings)
    140  *
    141  * Revision 1.2  87/03/27  14:22:33  jenkins
    142  * Port to suns
    143  *
    144  * Revision 4.1  83/03/25  18:12:51  wft
    145  * Only changed Header to Id.
    146  *
    147  * Revision 3.3  82/12/10  16:22:37  wft
    148  * Improved error messages, changed exit status on error to 1.
    149  *
    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).
    154  *
    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.
    158  */
    159 
    160 
    161 /*
    162 #define LEXDB
    163 */
    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
    167  * as identifiers.
    168  */
    169 
    170 
    171 
    172 #include "rcsbase.h"
    173 
    174 libId(lexId, "Id: rcslex.c,v 5.19 1995/06/16 06:19:24 eggert Exp ")
    175 
    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*));
    182 
    183 static struct hshentry *nexthsh;  /*pointer to next hash entry, set by lookup*/
    184 
    185 enum tokens     nexttok;    /*next token, set by nextlex                    */
    186 
    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  */
    190 
    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			    */
    195 
    196 FILE *          frewrite;   /*file descriptor for echoing input             */
    197 
    198 FILE *		foutptr;    /* copy of frewrite, but 0 to suppress echo  */
    199 
    200 static struct buf tokbuf;   /* token buffer				    */
    201 
    202 char const *    NextString; /* next token				    */
    203 
    204 /*
    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.
    209  */
    210 #ifndef hshsize
    211 #	define hshsize 511
    212 #endif
    213 
    214 static struct hshentry *hshtab[hshsize]; /*hashtable			    */
    215 
    216 static int ignored_phrases; /* have we ignored phrases in this RCS file? */
    217 
    218     void
    219 warnignore()
    220 {
    221     if (!ignored_phrases) {
    222 	ignored_phrases = true;
    223 	rcswarn("Unknown phrases like `%s ...;' are present.", NextString);
    224     }
    225 }
    226 
    227 
    228 
    229 	static void
    230 lookup(str)
    231 	char const *str;
    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
    235  * into nexthsh.
    236  */
    237 {
    238 	register unsigned ihash;  /* index into hashtable */
    239 	register char const *sp;
    240 	register struct hshentry *n, **p;
    241 
    242         /* calculate hash code */
    243 	sp = str;
    244         ihash = 0;
    245 	while (*sp)
    246 		ihash  =  (ihash<<2) + *sp++;
    247 	ihash %= hshsize;
    248 
    249 	for (p = &hshtab[ihash];  ;  p = &n->nexthsh)
    250 		if (!(n = *p)) {
    251 			/* empty slot found */
    252 			*p = n = ftalloc(struct hshentry);
    253 			n->num = fstr_save(str);
    254 			n->nexthsh = 0;
    255 #			ifdef LEXDB
    256 				VOID printf("\nEntered: %s at %u ", str, ihash);
    257 #			endif
    258 			break;
    259 		} else if (strcmp(str, n->num) == 0)
    260 			/* match found */
    261 			break;
    262 	nexthsh = n;
    263 	NextString = n->num;
    264 }
    265 
    266 
    267 
    268 
    269 
    270 
    271 	void
    272 Lexinit()
    273 /* Function: Initialization of lexical analyzer:
    274  * initializes the hashtable,
    275  * initializes nextc, nexttok if finptr != 0
    276  */
    277 {       register int            c;
    278 
    279 	for (c = hshsize;  0 <= --c;  ) {
    280 		hshtab[c] = 0;
    281         }
    282 
    283 	nerror = 0;
    284 	if (finptr) {
    285 		foutptr = 0;
    286 		hshenter = true;
    287 		ignored_phrases = false;
    288 		rcsline = 1;
    289 		bufrealloc(&tokbuf, 2);
    290 		Iget_(finptr, nextc)
    291                 nextlex();            /*initial token*/
    292         }
    293 }
    294 
    295 
    296 
    297 
    298 
    299 
    300 
    301 	void
    302 nextlex()
    303 
    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.
    310  */
    311 {       int c;
    312 	declarecache;
    313 	register FILE *frew;
    314         register char * sp;
    315 	char const *limit;
    316         register enum tokens d;
    317 	register RILE *fin;
    318 
    319 	fin=finptr; frew=foutptr;
    320 	setupcache(fin); cache(fin);
    321 	c = nextc;
    322 
    323 	for (;;) { switch ((d = ctab[c])) {
    324 
    325 	default:
    326 		fatserror("unknown character `%c'", c);
    327 		/*NOTREACHED*/
    328 
    329         case NEWLN:
    330 		++rcsline;
    331 #               ifdef LEXDB
    332 		afputc('\n',stdout);
    333 #               endif
    334                 /* Note: falls into next case */
    335 
    336         case SPACE:
    337 		GETC_(frew, c)
    338 		continue;
    339 
    340 	case IDCHAR:
    341 	case LETTER:
    342 	case Letter:
    343 		d = ID;
    344 		/* fall into */
    345 	case DIGIT:
    346 	case PERIOD:
    347 		sp = tokbuf.string;
    348 		limit = sp + tokbuf.size;
    349 		*sp++ = c;
    350 		for (;;) {
    351 			GETC_(frew, c)
    352 			switch (ctab[c]) {
    353 			    case IDCHAR:
    354 			    case LETTER:
    355 			    case Letter:
    356 				d = ID;
    357 				/* fall into */
    358 			    case DIGIT:
    359 			    case PERIOD:
    360 				*sp++ = c;
    361 				if (limit <= sp)
    362 					sp = bufenlarge(&tokbuf, &limit);
    363 				continue;
    364 
    365 			    default:
    366 				break;
    367 			}
    368 			break;
    369                 }
    370 		*sp = 0;
    371 		if (d == DIGIT  ||  d == PERIOD) {
    372 			d = NUM;
    373 			if (hshenter) {
    374 				lookup(tokbuf.string);
    375 				break;
    376 			}
    377 		}
    378 		NextString = fstr_save(tokbuf.string);
    379 		break;
    380 
    381         case SBEGIN: /* long string */
    382 		d = STRING;
    383                 /* note: only the initial SBEGIN has been read*/
    384                 /* read the string, and reset nextc afterwards*/
    385 		break;
    386 
    387 	case COLON:
    388 	case SEMI:
    389 		GETC_(frew, c)
    390 		break;
    391 	} break; }
    392 	nextc = c;
    393 	nexttok = d;
    394 	uncache(fin);
    395 }
    396 
    397 	int
    398 eoflex()
    399 /*
    400  * Yield true if we look ahead to the end of the input, false otherwise.
    401  * nextc becomes undefined at end of file.
    402  */
    403 {
    404 	register int c;
    405 	declarecache;
    406 	register FILE *fout;
    407 	register RILE *fin;
    408 
    409 	c = nextc;
    410 	fin = finptr;
    411 	fout = foutptr;
    412 	setupcache(fin); cache(fin);
    413 
    414 	for (;;) {
    415 		switch (ctab[c]) {
    416 			default:
    417 				nextc = c;
    418 				uncache(fin);
    419 				return false;
    420 
    421 			case NEWLN:
    422 				++rcsline;
    423 				/* fall into */
    424 			case SPACE:
    425 				cachegeteof_(c, {uncache(fin);return true;})
    426 				break;
    427 		}
    428 		if (fout)
    429 			aputc_(c, fout)
    430 	}
    431 }
    432 
    433 
    434 int getlex(token)
    435 enum tokens token;
    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.
    440  */
    441 {
    442         if (nexttok==token) {
    443                 nextlex();
    444                 return(true);
    445         } else  return(false);
    446 }
    447 
    448 	int
    449 getkeyopt(key)
    450 	char const *key;
    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.
    454  */
    455 {
    456 	if (nexttok==ID  &&  strcmp(key,NextString) == 0) {
    457 		 /* match found */
    458 		 ffree1(NextString);
    459 		 nextlex();
    460 		 return(true);
    461         }
    462         return(false);
    463 }
    464 
    465 	void
    466 getkey(key)
    467 	char const *key;
    468 /* Check that the current input token is a keyword identical to key,
    469  * and advance the input by calling nextlex.
    470  */
    471 {
    472 	if (!getkeyopt(key))
    473 		fatserror("missing '%s' keyword", key);
    474 }
    475 
    476 	void
    477 getkeystring(key)
    478 	char const *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.
    481  */
    482 {
    483 	getkey(key);
    484 	if (nexttok != STRING)
    485 		fatserror("missing string after '%s' keyword", key);
    486 }
    487 
    488 
    489 	char const *
    490 getid()
    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.
    495  */
    496 {
    497 	register char const *name;
    498         if (nexttok==ID) {
    499                 name = NextString;
    500                 nextlex();
    501                 return name;
    502 	} else
    503 		return 0;
    504 }
    505 
    506 
    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.
    512  */
    513 {
    514         register struct hshentry * num;
    515         if (nexttok==NUM) {
    516                 num=nexthsh;
    517                 nextlex();
    518                 return num;
    519 	} else
    520 		return 0;
    521 }
    522 
    523 	struct cbuf
    524 getphrases(key)
    525 	char const *key;
    526 /*
    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.
    531 */
    532 {
    533     declarecache;
    534     register int c;
    535     register char const *kn;
    536     struct cbuf r;
    537     register RILE *fin;
    538     register FILE *frew;
    539 #   if large_memory
    540 #	define savech_(c) ;
    541 #   else
    542 	register char *p;
    543 	char const *limit;
    544 	struct buf b;
    545 #	define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);}
    546 #   endif
    547 
    548     if (nexttok!=ID  ||  strcmp(NextString,key) == 0)
    549 	clear_buf(&r);
    550     else {
    551 	warnignore();
    552 	fin = finptr;
    553 	frew = foutptr;
    554 	setupcache(fin); cache(fin);
    555 #	if large_memory
    556 	    r.string = (char const*)cacheptr() - strlen(NextString) - 1;
    557 #	else
    558 	    bufautobegin(&b);
    559 	    bufscpy(&b, NextString);
    560 	    p = b.string + strlen(b.string);
    561 	    limit = b.string + b.size;
    562 #	endif
    563 	ffree1(NextString);
    564 	c = nextc;
    565 	for (;;) {
    566 	    for (;;) {
    567 		savech_(c)
    568 		switch (ctab[c]) {
    569 		    default:
    570 			fatserror("unknown character `%c'", c);
    571 			/*NOTREACHED*/
    572 		    case NEWLN:
    573 			++rcsline;
    574 			/* fall into */
    575 		    case COLON: case DIGIT: case LETTER: case Letter:
    576 		    case PERIOD: case SPACE:
    577 			GETC_(frew, c)
    578 			continue;
    579 		    case SBEGIN: /* long string */
    580 			for (;;) {
    581 			    for (;;) {
    582 				GETC_(frew, c)
    583 				savech_(c)
    584 				switch (c) {
    585 				    case '\n':
    586 					++rcsline;
    587 					/* fall into */
    588 				    default:
    589 					continue;
    590 
    591 				    case SDELIM:
    592 					break;
    593 				}
    594 				break;
    595 			    }
    596 			    GETC_(frew, c)
    597 			    if (c != SDELIM)
    598 				break;
    599 			    savech_(c)
    600 			}
    601 			continue;
    602 		    case SEMI:
    603 			cacheget_(c)
    604 			if (ctab[c] == NEWLN) {
    605 			    if (frew)
    606 				aputc_(c, frew)
    607 			    ++rcsline;
    608 			    savech_(c)
    609 			    cacheget_(c)
    610 			}
    611 #			if large_memory
    612 			    r.size = (char const*)cacheptr() - 1 - r.string;
    613 #			endif
    614 			for (;;) {
    615 			    switch (ctab[c]) {
    616 				case NEWLN:
    617 					++rcsline;
    618 					/* fall into */
    619 				case SPACE:
    620 					cacheget_(c)
    621 					continue;
    622 
    623 				default: break;
    624 			    }
    625 			    break;
    626 			}
    627 			if (frew)
    628 			    aputc_(c, frew)
    629 			break;
    630 		}
    631 		break;
    632 	    }
    633 	    if (ctab[c] == Letter) {
    634 		    for (kn = key;  c && *kn==c;  kn++)
    635 			GETC_(frew, c)
    636 		    if (!*kn)
    637 			switch (ctab[c]) {
    638 			    case DIGIT: case LETTER: case Letter:
    639 			    case IDCHAR: case PERIOD:
    640 				break;
    641 			    default:
    642 				nextc = c;
    643 				NextString = fstr_save(key);
    644 				nexttok = ID;
    645 				uncache(fin);
    646 				goto returnit;
    647 			}
    648 #		    if !large_memory
    649 			{
    650 			    register char const *ki;
    651 			    for (ki=key; ki<kn; )
    652 				savech_(*ki++)
    653 			}
    654 #		    endif
    655 	    } else {
    656 		    nextc = c;
    657 		    uncache(fin);
    658 		    nextlex();
    659 		    break;
    660 	    }
    661 	}
    662     returnit:;
    663 #	if !large_memory
    664 	    return bufremember(&b, (size_t)(p - b.string));
    665 #	endif
    666     }
    667     return r;
    668 }
    669 
    670 
    671 	void
    672 readstring()
    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.                        */
    676 {       int c;
    677 	declarecache;
    678 	register FILE *frew;
    679 	register RILE *fin;
    680 	fin=finptr; frew=foutptr;
    681 	setupcache(fin); cache(fin);
    682 	for (;;) {
    683 		GETC_(frew, c)
    684 		switch (c) {
    685 		    case '\n':
    686 			++rcsline;
    687 			break;
    688 
    689 		    case SDELIM:
    690 			GETC_(frew, c)
    691 			if (c != SDELIM) {
    692 				/* end of string */
    693 				nextc = c;
    694 				uncache(fin);
    695 				return;
    696 			}
    697 			break;
    698 		}
    699 	}
    700 }
    701 
    702 
    703 	void
    704 printstring()
    705 /* Function: copy a string to stdout, until terminated with a single SDELIM.
    706  * Does not advance nextlex at the end.
    707  */
    708 {
    709         int c;
    710 	declarecache;
    711 	register FILE *fout;
    712 	register RILE *fin;
    713 	fin=finptr;
    714 	fout = stdout;
    715 	setupcache(fin); cache(fin);
    716 	for (;;) {
    717 		cacheget_(c)
    718 		switch (c) {
    719 		    case '\n':
    720 			++rcsline;
    721 			break;
    722 		    case SDELIM:
    723 			cacheget_(c)
    724 			if (c != SDELIM) {
    725                                 nextc=c;
    726 				uncache(fin);
    727                                 return;
    728                         }
    729 			break;
    730                 }
    731 		aputc_(c,fout)
    732         }
    733 }
    734 
    735 
    736 
    737 	struct cbuf
    738 savestring(target)
    739 	struct buf *target;
    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.
    745  */
    746 {
    747         int c;
    748 	declarecache;
    749 	register FILE *frew;
    750 	register char *tp;
    751 	register RILE *fin;
    752 	char const *limit;
    753 	struct cbuf r;
    754 
    755 	fin=finptr; frew=foutptr;
    756 	setupcache(fin); cache(fin);
    757 	tp = target->string;  limit = tp + target->size;
    758 	for (;;) {
    759 		GETC_(frew, c)
    760 		switch (c) {
    761 		    case '\n':
    762 			++rcsline;
    763 			break;
    764 		    case SDELIM:
    765 			GETC_(frew, c)
    766 			if (c != SDELIM) {
    767                                 /* end of string */
    768                                 nextc=c;
    769 				r.string = target->string;
    770 				r.size = tp - r.string;
    771 				uncache(fin);
    772 				return r;
    773                         }
    774 			break;
    775                 }
    776 		if (tp == limit)
    777 			tp = bufenlarge(target, &limit);
    778 		*tp++ = c;
    779         }
    780 }
    781 
    782 
    783 	static char *
    784 checkidentifier(id, delimiter, dotok)
    785 	register char *id;
    786 	int delimiter;
    787 	register int 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.   */
    795 {
    796         register char    *temp;
    797 	register char c;
    798 	register char delim = delimiter;
    799 	int isid = false;
    800 
    801 	temp = id;
    802 	for (;;  id++) {
    803 		switch (ctab[(unsigned char)(c = *id)]) {
    804 			case IDCHAR:
    805 			case LETTER:
    806 			case Letter:
    807 				isid = true;
    808 				continue;
    809 
    810 			case DIGIT:
    811 				continue;
    812 
    813 			case PERIOD:
    814 				if (dotok)
    815 					continue;
    816 				break;
    817 
    818 			default:
    819 				break;
    820 		}
    821 		break;
    822 	}
    823 	if (	 ! isid
    824 	    ||	 (c  &&  (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n')))
    825 	) {
    826                 /* append \0 to end of id before error message */
    827 		while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim)
    828 		    id++;
    829                 *id = '\0';
    830 		faterror("invalid %s `%s'",
    831 			dotok ? "identifier" : "symbol", temp
    832 		);
    833 	}
    834 	return id;
    835 }
    836 
    837 	char *
    838 checkid(id, delimiter)
    839 	char *id;
    840 	int delimiter;
    841 {
    842 	return checkidentifier(id, delimiter, true);
    843 }
    844 
    845 	char *
    846 checksym(sym, delimiter)
    847 	char *sym;
    848 	int delimiter;
    849 {
    850 	return checkidentifier(sym, delimiter, false);
    851 }
    852 
    853 	void
    854 checksid(id)
    855 	char *id;
    856 /* Check whether the string ID is an identifier.  */
    857 {
    858 	VOID checkid(id, 0);
    859 }
    860 
    861 	void
    862 checkssym(sym)
    863 	char *sym;
    864 {
    865 	VOID checksym(sym, 0);
    866 }
    867 
    868 
    869 #if !large_memory
    870 #   define Iclose(f) fclose(f)
    871 #else
    872 # if !maps_memory
    873     static int Iclose P((RILE *));
    874 	static int
    875     Iclose(f)
    876 	register RILE *f;
    877     {
    878 	tfree(f->base);
    879 	f->base = 0;
    880 	return fclose(f->stream);
    881     }
    882 # else
    883     static int Iclose P((RILE *));
    884 	static int
    885     Iclose(f)
    886 	register RILE *f;
    887     {
    888 	(* f->deallocate) (f);
    889 	f->base = 0;
    890 	return close(f->fd);
    891     }
    892 
    893 #   if has_map_fd
    894 	static void map_fd_deallocate P((RILE *));
    895 	    static void
    896 	map_fd_deallocate(f)
    897 	    register RILE *f;
    898 	{
    899 	    if (vm_deallocate(
    900 		task_self(),
    901 		(vm_address_t) f->base,
    902 		(vm_size_t) (f->lim - f->base)
    903 	    ) != KERN_SUCCESS)
    904 		efaterror("vm_deallocate");
    905 	}
    906 #   endif
    907 #   if has_mmap
    908 	static void mmap_deallocate P((RILE *));
    909 	    static void
    910 	mmap_deallocate(f)
    911 	    register RILE *f;
    912 	{
    913 	    if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0)
    914 		efaterror("munmap");
    915 	}
    916 #   endif
    917     static void read_deallocate P((RILE *));
    918 	static void
    919     read_deallocate(f)
    920 	RILE *f;
    921     {
    922 	tfree(f->base);
    923     }
    924 
    925     static void nothing_to_deallocate P((RILE *));
    926 	static void
    927     nothing_to_deallocate(f)
    928 	RILE *f;
    929     {
    930     }
    931 # endif
    932 #endif
    933 
    934 
    935 #if large_memory && maps_memory
    936 	static RILE *fd2_RILE P((int,char const*,struct stat*));
    937 	static RILE *
    938 fd2_RILE(fd, name, status)
    939 #else
    940 	static RILE *fd2RILE P((int,char const*,char const*,struct stat*));
    941 	static RILE *
    942 fd2RILE(fd, name, type, status)
    943 	char const *type;
    944 #endif
    945 	int fd;
    946 	char const *name;
    947 	register struct stat *status;
    948 {
    949 	struct stat st;
    950 
    951 	if (!status)
    952 		status = &st;
    953 	if (fstat(fd, status) != 0)
    954 		efaterror(name);
    955 	if (!S_ISREG(status->st_mode)) {
    956 		error("`%s' is not a regular file", name);
    957 		VOID close(fd);
    958 		errno = EINVAL;
    959 		return 0;
    960 	} else {
    961 
    962 #	    if !(large_memory && maps_memory)
    963 		FILE *stream;
    964 		if (!(stream = fdopen(fd, type)))
    965 			efaterror(name);
    966 #	    endif
    967 
    968 #	    if !large_memory
    969 		return stream;
    970 #	    else
    971 #		define RILES 3
    972 	      {
    973 		static RILE rilebuf[RILES];
    974 
    975 		register RILE *f;
    976 		size_t s = status->st_size;
    977 
    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");
    983 #		if maps_memory
    984 			f->deallocate = nothing_to_deallocate;
    985 #		endif
    986 		if (!s) {
    987 		    static unsigned char nothing;
    988 		    f->base = &nothing; /* Any nonzero address will do.  */
    989 		} else {
    990 		    f->base = 0;
    991 #		    if has_map_fd
    992 			map_fd(
    993 				fd, (vm_offset_t)0, (vm_address_t*) &f->base,
    994 				TRUE, (vm_size_t)s
    995 			);
    996 			f->deallocate = map_fd_deallocate;
    997 #		    endif
    998 #		    if has_mmap
    999 			if (!f->base) {
   1000 			    catchmmapints();
   1001 			    f->base = (unsigned char *) mmap(
   1002 				(char *)0, s, PROT_READ, MAP_FILE|MAP_SHARED,
   1003 				fd, (off_t)0
   1004 			    );
   1005 #			    ifndef MAP_FAILED
   1006 #			    define MAP_FAILED (-1)
   1007 #			    endif
   1008 			    if (f->base == (unsigned char *) MAP_FAILED)
   1009 				f->base = 0;
   1010 			    else {
   1011 #				if has_NFS && mmap_signal
   1012 				    /*
   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.
   1017 				    */
   1018 				    readAccessFilenameBuffer(name, f->base);
   1019 #				endif
   1020 			    }
   1021 			    f->deallocate = mmap_deallocate;
   1022 			}
   1023 #		    endif
   1024 		    if (!f->base) {
   1025 			f->base = tnalloc(unsigned char, s);
   1026 #			if maps_memory
   1027 			{
   1028 			    /*
   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.
   1032 			    */
   1033 			    char *bufptr = (char *) f->base;
   1034 			    size_t bufsiz = s;
   1035 			    do {
   1036 				ssize_t r = read(fd, bufptr, bufsiz);
   1037 				switch (r) {
   1038 				    case -1:
   1039 					efaterror(name);
   1040 
   1041 				    case 0:
   1042 					/* The file must have shrunk!  */
   1043 					status->st_size = s -= bufsiz;
   1044 					bufsiz = 0;
   1045 					break;
   1046 
   1047 				    default:
   1048 					bufptr += r;
   1049 					bufsiz -= r;
   1050 					break;
   1051 				}
   1052 			    } while (bufsiz);
   1053 			    if (lseek(fd, (off_t)0, SEEK_SET) == -1)
   1054 				efaterror(name);
   1055 			    f->deallocate = read_deallocate;
   1056 			}
   1057 #			endif
   1058 		    }
   1059 		}
   1060 		f->ptr = f->base;
   1061 		f->lim = f->base + s;
   1062 		f->fd = fd;
   1063 #		if !maps_memory
   1064 		    f->readlim = f->base;
   1065 		    f->stream = stream;
   1066 #		endif
   1067 		if_advise_access(s, f, MADV_SEQUENTIAL);
   1068 		return f;
   1069 	      }
   1070 #	    endif
   1071 	}
   1072 }
   1073 
   1074 #if !maps_memory && large_memory
   1075 	int
   1076 Igetmore(f)
   1077 	register RILE *f;
   1078 {
   1079 	register fread_type r;
   1080 	register size_t s = f->lim - f->readlim;
   1081 
   1082 	if (BUFSIZ < s)
   1083 		s = BUFSIZ;
   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!  */
   1087 		return 0;
   1088 	}
   1089 	f->readlim += r;
   1090 	return 1;
   1091 }
   1092 #endif
   1093 
   1094 #if has_madvise && has_mmap && large_memory
   1095 	void
   1096 advise_access(f, advice)
   1097 	register RILE *f;
   1098 	int advice;
   1099 {
   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.  */
   1103 }
   1104 #endif
   1105 
   1106 	RILE *
   1107 #if large_memory && maps_memory
   1108 I_open(name, status)
   1109 #else
   1110 Iopen(name, type, status)
   1111 	char const *type;
   1112 #endif
   1113 	char const *name;
   1114 	struct stat *status;
   1115 /* Open NAME for reading, yield its descriptor, and set *STATUS.  */
   1116 {
   1117 	int fd = fdSafer(open(name, O_RDONLY
   1118 #		if OPEN_O_BINARY
   1119 			|  (strchr(type,'b') ? OPEN_O_BINARY : 0)
   1120 #		endif
   1121 	));
   1122 
   1123 	if (fd < 0)
   1124 		return 0;
   1125 #	if large_memory && maps_memory
   1126 		return fd2_RILE(fd, name, status);
   1127 #	else
   1128 		return fd2RILE(fd, name, type, status);
   1129 #	endif
   1130 }
   1131 
   1132 
   1133 static int Oerrloop;
   1134 
   1135 	void
   1136 Oerror()
   1137 {
   1138 	if (Oerrloop)
   1139 		exiterr();
   1140 	Oerrloop = true;
   1141 	efaterror("output error");
   1142 }
   1143 
   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(); }
   1148 
   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; }
   1153 
   1154 #if !large_memory
   1155 	void
   1156 testIeof(f)
   1157 	FILE *f;
   1158 {
   1159 	testIerror(f);
   1160 	if (feof(f))
   1161 		Ieof();
   1162 }
   1163 void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
   1164 #endif
   1165 
   1166 void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); }
   1167 
   1168 void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); }
   1169 void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); }
   1170 void oflush()
   1171 {
   1172 	if (fflush(workstdout ? workstdout : stdout) != 0  &&  !Oerrloop)
   1173 		Oerror();
   1174 }
   1175 
   1176 	void
   1177 fatcleanup(already_newline)
   1178 	int already_newline;
   1179 {
   1180 	VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
   1181 	exiterr();
   1182 }
   1183 
   1184 	static void
   1185 startsay(s, t)
   1186 	const char *s, *t;
   1187 {
   1188 	oflush();
   1189 	if (s)
   1190 	    aprintf(stderr, "%s: %s: %s", cmdid, s, t);
   1191 	else
   1192 	    aprintf(stderr, "%s: %s", cmdid, t);
   1193 }
   1194 
   1195 	static void
   1196 fatsay(s)
   1197 	char const *s;
   1198 {
   1199 	startsay(s, "");
   1200 }
   1201 
   1202 	static void
   1203 errsay(s)
   1204 	char const *s;
   1205 {
   1206 	fatsay(s);
   1207 	nerror++;
   1208 }
   1209 
   1210 	static void
   1211 warnsay(s)
   1212 	char const *s;
   1213 {
   1214 	startsay(s, "warning: ");
   1215 }
   1216 
   1217 void eerror(s) char const *s; { enerror(errno,s); }
   1218 
   1219 	void
   1220 enerror(e,s)
   1221 	int e;
   1222 	char const *s;
   1223 {
   1224 	errsay((char const*)0);
   1225 	errno = e;
   1226 	perror(s);
   1227 	eflush();
   1228 }
   1229 
   1230 void efaterror(s) char const *s; { enfaterror(errno,s); }
   1231 
   1232 	void
   1233 enfaterror(e,s)
   1234 	int e;
   1235 	char const *s;
   1236 {
   1237 	fatsay((char const*)0);
   1238 	errno = e;
   1239 	perror(s);
   1240 	fatcleanup(true);
   1241 }
   1242 
   1243 #if has_prototypes
   1244 	void
   1245 error(char const *format,...)
   1246 #else
   1247 	/*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
   1248 #endif
   1249 /* non-fatal error */
   1250 {
   1251 	va_list args;
   1252 	errsay((char const*)0);
   1253 	vararg_start(args, format);
   1254 	fvfprintf(stderr, format, args);
   1255 	va_end(args);
   1256 	afputc('\n',stderr);
   1257 	eflush();
   1258 }
   1259 
   1260 #if has_prototypes
   1261 	void
   1262 rcserror(char const *format,...)
   1263 #else
   1264 	/*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl
   1265 #endif
   1266 /* non-fatal RCS file error */
   1267 {
   1268 	va_list args;
   1269 	errsay(RCSname);
   1270 	vararg_start(args, format);
   1271 	fvfprintf(stderr, format, args);
   1272 	va_end(args);
   1273 	afputc('\n',stderr);
   1274 	eflush();
   1275 }
   1276 
   1277 #if has_prototypes
   1278 	void
   1279 workerror(char const *format,...)
   1280 #else
   1281 	/*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl
   1282 #endif
   1283 /* non-fatal working file error */
   1284 {
   1285 	va_list args;
   1286 	errsay(workname);
   1287 	vararg_start(args, format);
   1288 	fvfprintf(stderr, format, args);
   1289 	va_end(args);
   1290 	afputc('\n',stderr);
   1291 	eflush();
   1292 }
   1293 
   1294 #if has_prototypes
   1295 	void
   1296 fatserror(char const *format,...)
   1297 #else
   1298 	/*VARARGS1*/ void
   1299 	fatserror(format, va_alist) char const *format; va_dcl
   1300 #endif
   1301 /* fatal RCS file syntax error */
   1302 {
   1303 	va_list args;
   1304 	oflush();
   1305 	VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline);
   1306 	vararg_start(args, format);
   1307 	fvfprintf(stderr, format, args);
   1308 	va_end(args);
   1309 	fatcleanup(false);
   1310 }
   1311 
   1312 #if has_prototypes
   1313 	void
   1314 faterror(char const *format,...)
   1315 #else
   1316 	/*VARARGS1*/ void faterror(format, va_alist)
   1317 	char const *format; va_dcl
   1318 #endif
   1319 /* fatal error, terminates program after cleanup */
   1320 {
   1321 	va_list args;
   1322 	fatsay((char const*)0);
   1323 	vararg_start(args, format);
   1324 	fvfprintf(stderr, format, args);
   1325 	va_end(args);
   1326 	fatcleanup(false);
   1327 }
   1328 
   1329 #if has_prototypes
   1330 	void
   1331 rcsfaterror(char const *format,...)
   1332 #else
   1333 	/*VARARGS1*/ void rcsfaterror(format, va_alist)
   1334 	char const *format; va_dcl
   1335 #endif
   1336 /* fatal RCS file error, terminates program after cleanup */
   1337 {
   1338 	va_list args;
   1339 	fatsay(RCSname);
   1340 	vararg_start(args, format);
   1341 	fvfprintf(stderr, format, args);
   1342 	va_end(args);
   1343 	fatcleanup(false);
   1344 }
   1345 
   1346 #if has_prototypes
   1347 	void
   1348 warn(char const *format,...)
   1349 #else
   1350 	/*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
   1351 #endif
   1352 /* warning */
   1353 {
   1354 	va_list args;
   1355 	if (!quietflag) {
   1356 		warnsay((char *)0);
   1357 		vararg_start(args, format);
   1358 		fvfprintf(stderr, format, args);
   1359 		va_end(args);
   1360 		afputc('\n', stderr);
   1361 		eflush();
   1362 	}
   1363 }
   1364 
   1365 #if has_prototypes
   1366 	void
   1367 rcswarn(char const *format,...)
   1368 #else
   1369 	/*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl
   1370 #endif
   1371 /* RCS file warning */
   1372 {
   1373 	va_list args;
   1374 	if (!quietflag) {
   1375 		warnsay(RCSname);
   1376 		vararg_start(args, format);
   1377 		fvfprintf(stderr, format, args);
   1378 		va_end(args);
   1379 		afputc('\n', stderr);
   1380 		eflush();
   1381 	}
   1382 }
   1383 
   1384 #if has_prototypes
   1385 	void
   1386 workwarn(char const *format,...)
   1387 #else
   1388 	/*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl
   1389 #endif
   1390 /* working file warning */
   1391 {
   1392 	va_list args;
   1393 	if (!quietflag) {
   1394 		warnsay(workname);
   1395 		vararg_start(args, format);
   1396 		fvfprintf(stderr, format, args);
   1397 		va_end(args);
   1398 		afputc('\n', stderr);
   1399 		eflush();
   1400 	}
   1401 }
   1402 
   1403 	void
   1404 redefined(c)
   1405 	int c;
   1406 {
   1407 	warn("redefinition of -%c option", c);
   1408 }
   1409 
   1410 #if has_prototypes
   1411 	void
   1412 diagnose(char const *format,...)
   1413 #else
   1414 	/*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
   1415 #endif
   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. */
   1420 {
   1421 	va_list args;
   1422         if (!quietflag) {
   1423 		oflush();
   1424 		vararg_start(args, format);
   1425 		fvfprintf(stderr, format, args);
   1426 		va_end(args);
   1427 		eflush();
   1428         }
   1429 }
   1430 
   1431 
   1432 
   1433 	void
   1434 afputc(c, f)
   1435 /* afputc(c,f); acts like aputc_(c,f) but is smaller and slower.  */
   1436 	int c;
   1437 	register FILE *f;
   1438 {
   1439 	aputc_(c,f)
   1440 }
   1441 
   1442 
   1443 	void
   1444 aputs(s, iop)
   1445 	char const *s;
   1446 	FILE *iop;
   1447 /* Function: Put string s on file iop, abort on error.
   1448  */
   1449 {
   1450 #if has_fputs
   1451 	if (fputs(s, iop) < 0)
   1452 		Oerror();
   1453 #else
   1454 	awrite(s, strlen(s), iop);
   1455 #endif
   1456 }
   1457 
   1458 
   1459 
   1460 	void
   1461 #if has_prototypes
   1462 fvfprintf(FILE *stream, char const *format, va_list args)
   1463 #else
   1464 	fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
   1465 #endif
   1466 /* like vfprintf, except abort program on error */
   1467 {
   1468 #if has_vfprintf
   1469 	if (vfprintf(stream, format, args) < 0)
   1470 		Oerror();
   1471 #else
   1472 #	if has__doprintf
   1473 		_doprintf(stream, format, args);
   1474 #	else
   1475 #	if has__doprnt
   1476 		_doprnt(format, args, stream);
   1477 #	else
   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]
   1482 		);
   1483 #	endif
   1484 #	endif
   1485 	if (ferror(stream))
   1486 		Oerror();
   1487 #endif
   1488 }
   1489 
   1490 #if has_prototypes
   1491 	void
   1492 aprintf(FILE *iop, char const *fmt, ...)
   1493 #else
   1494 	/*VARARGS2*/ void
   1495 aprintf(iop, fmt, va_alist)
   1496 FILE *iop;
   1497 char const *fmt;
   1498 va_dcl
   1499 #endif
   1500 /* Function: formatted output. Same as fprintf in stdio,
   1501  * but aborts program on error
   1502  */
   1503 {
   1504 	va_list ap;
   1505 	vararg_start(ap, fmt);
   1506 	fvfprintf(iop, fmt, ap);
   1507 	va_end(ap);
   1508 }
   1509 
   1510 
   1511 
   1512 #ifdef LEXDB
   1513 /* test program reading a stream of lexemes and printing the tokens.
   1514  */
   1515 
   1516 
   1517 
   1518 	int
   1519 main(argc,argv)
   1520 int argc; char * argv[];
   1521 {
   1522         cmdid="lextest";
   1523         if (argc<2) {
   1524 		aputs("No input file\n",stderr);
   1525 		exitmain(EXIT_FAILURE);
   1526         }
   1527 	if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
   1528 		faterror("can't open input file %s",argv[1]);
   1529         }
   1530         Lexinit();
   1531 	while (!eoflex()) {
   1532         switch (nexttok) {
   1533 
   1534         case ID:
   1535                 VOID printf("ID: %s",NextString);
   1536                 break;
   1537 
   1538         case NUM:
   1539 		if (hshenter)
   1540                    VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
   1541                 else
   1542                    VOID printf("NUM, unentered: %s",NextString);
   1543                 hshenter = !hshenter; /*alternate between dates and numbers*/
   1544                 break;
   1545 
   1546         case COLON:
   1547                 VOID printf("COLON"); break;
   1548 
   1549         case SEMI:
   1550                 VOID printf("SEMI"); break;
   1551 
   1552         case STRING:
   1553                 readstring();
   1554                 VOID printf("STRING"); break;
   1555 
   1556         case UNKN:
   1557                 VOID printf("UNKN"); break;
   1558 
   1559         default:
   1560                 VOID printf("DEFAULT"); break;
   1561         }
   1562         VOID printf(" | ");
   1563         nextlex();
   1564         }
   1565 	exitmain(EXIT_SUCCESS);
   1566 }
   1567 
   1568 void exiterr() { _exit(EXIT_FAILURE); }
   1569 
   1570 
   1571 #endif
   1572