home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / rcs57pc3.zip / rcs / src / rcslex.c < prev    next >
C/C++ Source or Header  |  1996-10-29  |  36KB  |  1,570 lines

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