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