home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / programming / motasm / motasmdoc / cpp / c / cpp6 < prev   
Encoding:
Text File  |  1995-09-06  |  37.5 KB  |  983 lines

  1. /*
  2.  *                          C P P 6 . C
  3.  *              S u p p o r t   R o u t i n e s
  4.  *
  5.  * Edit History
  6.  * 25-May-84 MM         Added 8-bit support to type table.
  7.  * 30-May-84 ARF        sharp() should output filename in quotes
  8.  * 02-Aug-84 MM         Newline and #line hacking.  sharp() now in cpp1.c
  9.  * 31-Aug-84 MM         USENET net.sources release
  10.  * 11-Sep-84 ado/MM     Keepcomments, also line number pathological
  11.  * 12-Sep-84 ado/MM     bug if comment changes to space and we unget later.
  12.  * 03-Oct-84 gkr/MM     Fixed scannumber bug for '.e' (as in struct.element).
  13.  * 04-Oct-84 MM         Added ungetstring() for token concatenation
  14.  * 08-Oct-84 MM         Yet another attack on number scanning
  15.  * 31-Oct-84 ado        Parameterized $ in identifiers
  16.  *  2-Nov-84 MM         Token concatenation is messier than I thought
  17.  *  6-Dec-84 MM         \<nl> is everywhere invisible.
  18.  */
  19.  
  20. #include        <stdio.h>
  21. #include        <ctype.h>
  22. #include        "cppdef.h"
  23. #include        "cpp.h"
  24.  
  25. /*
  26.  * skipnl()     skips over input text to the end of the line.
  27.  * skipws()     skips over "whitespace" (spaces or tabs), but
  28.  *              not skip over the end of the line.  It skips over
  29.  *              TOK_SEP, however (though that shouldn't happen).
  30.  * scanid()     reads the next token (C identifier) into token[].
  31.  *              The caller has already read the first character of
  32.  *              the identifier.  Unlike macroid(), the token is
  33.  *              never expanded.
  34.  * macroid()    reads the next token (C identifier) into token[].
  35.  *              If it is a #defined macro, it is expanded, and
  36.  *              macroid() returns TRUE, otherwise, FALSE.
  37.  * catenate()   Does the dirty work of token concatenation, TRUE if it did.
  38.  * scanstring() Reads a string from the input stream, calling
  39.  *              a user-supplied function for each character.
  40.  *              This function may be output() to write the
  41.  *              string to the output file, or save() to save
  42.  *              the string in the work buffer.
  43.  * scannumber() Reads a C numeric constant from the input stream,
  44.  *              calling the user-supplied function for each
  45.  *              character.  (output() or save() as noted above.)
  46.  * save()       Save one character in the work[] buffer.
  47.  * savestring() Saves a string in malloc() memory.
  48.  * getfile()    Initialize a new FILEINFO structure, called when
  49.  *              #include opens a new file, or a macro is to be
  50.  *              expanded.
  51.  * getmem()     Get a specified number of bytes from malloc memory.
  52.  * output()     Write one character to stdout (calling putchar) --
  53.  *              implemented as a function so its address may be
  54.  *              passed to scanstring() and scannumber().
  55.  * lookid()     Scans the next token (identifier) from the input
  56.  *              stream.  Looks for it in the #defined symbol table.
  57.  *              Returns a pointer to the definition, if found, or NULL
  58.  *              if not present.  The identifier is stored in token[].
  59.  * defnedel()   Define enter/delete subroutine.  Updates the
  60.  *              symbol table.
  61.  * get()        Read the next byte from the current input stream,
  62.  *              handling end of (macro/file) input and embedded
  63.  *              comments appropriately.  Note that the global
  64.  *              instring is -- essentially -- a parameter to get().
  65.  * cget()       Like get(), but skip over TOK_SEP.
  66.  * unget()      Push last gotten character back on the input stream.
  67.  * cerror(), cwarn(), cfatal(), cierror(), ciwarn()
  68.  *              These routines format an print messages to the user.
  69.  *              cerror & cwarn take a format and a single string argument.
  70.  *              cierror & ciwarn take a format and a single int (char) argument.
  71.  *              cfatal takes a format and a single string argument.
  72.  */
  73.  
  74. /*
  75.  * This table must be rewritten for a non-Ascii machine.
  76.  *
  77.  * Note that several "non-visible" characters have special meaning:
  78.  * Hex 1D DEF_MAGIC -- a flag to prevent #define recursion.
  79.  * Hex 1E TOK_SEP   -- a delimiter for token concatenation
  80.  * Hex 1F COM_SEP   -- a zero-width whitespace for comment concatenation
  81.  */
  82. #if TOK_SEP != 0x1E || COM_SEP != 0x1F || DEF_MAGIC != 0x1D
  83.         << error type table isnt correct >>
  84. #endif
  85.  
  86. #if OK_DOLLAR
  87. #define DOL     LET
  88. #else
  89. #define DOL     000
  90. #endif
  91.  
  92. char type[256] = {              /* Character type codes    Hex          */
  93.    END,   000,   000,   000,   000,   000,   000,   000, /* 00          */
  94.    000,   SPA,   000,   000,   000,   000,   000,   000, /* 08          */
  95.    000,   000,   000,   000,   000,   000,   000,   000, /* 10          */
  96.    000,   000,   000,   000,   000,   LET,   000,   SPA, /* 18          */
  97.    SPA,OP_NOT,   QUO,   000,   DOL,OP_MOD,OP_AND,   QUO, /* 20  !"#$%&' */
  98. OP_LPA,OP_RPA,OP_MUL,OP_ADD,   000,OP_SUB,   DOT,OP_DIV, /* 28 ()*+,-./ */
  99.    DIG,   DIG,   DIG,   DIG,   DIG,   DIG,   DIG,   DIG, /* 30 01234567 */
  100.    DIG,   DIG,OP_COL,   000, OP_LT, OP_EQ, OP_GT,OP_QUE, /* 38 89:;<=>? */
  101.    000,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 40 @ABCDEFG */
  102.    LET,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 48 HIJKLMNO */
  103.    LET,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 50 PQRSTUVW */
  104.    LET,   LET,   LET,   000,   BSH,   000,OP_XOR,   LET, /* 58 XYZ[\]^_ */
  105.    000,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 60 `abcdefg */
  106.    LET,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 68 hijklmno */
  107.    LET,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 70 pqrstuvw */
  108.    LET,   LET,   LET,   000, OP_OR,   000,OP_NOT,   000, /* 78 xyz{|}~  */
  109.    000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF  */
  110.    000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF  */
  111.    000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF  */
  112.    000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF  */
  113.    000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF  */
  114.    000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF  */
  115.    000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF  */
  116.    000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF  */
  117. };
  118.  
  119. skipnl()
  120. /*
  121.  * Skip to the end of the current input line.
  122.  */
  123. {
  124.         register int            c;
  125.  
  126.         do {                            /* Skip to newline      */
  127.             c = get();
  128.         } while (c != '\n' && c != EOF_CHAR);
  129. }
  130.  
  131. int
  132. skipws()
  133. /*
  134.  * Skip over whitespace
  135.  */
  136. {
  137.         register int            c;
  138.  
  139.         do {                            /* Skip whitespace      */
  140.             c = get();
  141. #if COMMENT_INVISIBLE
  142.         } while (type[c] == SPA || c == COM_SEP);
  143. #else
  144.         } while (type[c] == SPA);
  145. #endif
  146.         return (c);
  147. }
  148.  
  149. scanid(c)
  150. register int    c;                              /* First char of id     */
  151. /*
  152.  * Get the next token (an id) into the token buffer.
  153.  * Note: this code is duplicated in lookid().
  154.  * Change one, change both.
  155.  */
  156. {
  157.         register char   *bp;
  158.  
  159.         if (c == DEF_MAGIC)                     /* Eat the magic token  */
  160.             c = get();                          /* undefiner.           */
  161.         bp = token;
  162.         do {
  163.             if (bp < &token[IDMAX])             /* token dim is IDMAX+1 */
  164.                 *bp++ = c;
  165.             c = get();
  166.         } while (type[c] == LET || type[c] == DIG);
  167.         unget();
  168.         *bp = EOS;
  169. }
  170.  
  171. int
  172. macroid(c)
  173. register int            c;
  174. /*
  175.  * If c is a letter, scan the id.  if it's #defined, expand it and scan
  176.  * the next character and try again.
  177.  *
  178.  * Else, return the character.  If type[c] is a LET, the token is in token.
  179.  */
  180. {
  181.         register DEFBUF *dp;
  182.  
  183.         if (infile != NULL && infile->fp != NULL)
  184.             recursion = 0;
  185.         while (type[c] == LET && (dp = lookid(c)) != NULL) {
  186.             expand(dp);
  187.             c = get();
  188.         }
  189.         return (c);
  190. }
  191.  
  192. int
  193. catenate()
  194. /*
  195.  * A token was just read (via macroid).
  196.  * If the next character is TOK_SEP, concatenate the next token
  197.  * return TRUE -- which should recall macroid after refreshing
  198.  * macroid's argument.  If it is not TOK_SEP, unget() the character
  199.  * and return FALSE.
  200.  */
  201. {
  202.         register int            c;
  203.         register char           *token1;
  204.  
  205. #if OK_CONCAT
  206.         if (get() != TOK_SEP) {                 /* Token concatenation  */
  207.             unget();
  208.             return (FALSE);
  209.         }
  210.         else {
  211.             token1 = savestring(token);         /* Save first token     */
  212.             c = macroid(get());                 /* Scan next token      */
  213.             switch(type[c]) {                   /* What was it?         */
  214.             case LET:                           /* An identifier, ...   */
  215.                 if (strlen(token1) + strlen(token) >= NWORK)
  216.                     cfatal("work buffer overflow doing %s #", token1);
  217.                 sprintf(work, "%s%s", token1, token);
  218.                 break;
  219.  
  220.             case DIG:                           /* A digit string       */
  221.                 strcpy(work, token1);
  222.                 workp = work + strlen(work);
  223.                 do {
  224.                     save(c);
  225.                 } while ((c = get()) != TOK_SEP);
  226.                 /*
  227.                  * The trailing TOK_SEP is no longer needed.
  228.                  */
  229.                 save(EOS);
  230.                 break;
  231.  
  232.             default:                            /* An error, ...        */
  233. #if ! COMMENT_INVISIBLE
  234.                 if (isprint(c))
  235.                     cierror("Strange character '%c' after #", c);
  236.                 else
  237.                     cierror("Strange character (%d.) after #", c);
  238. #endif
  239.                 strcpy(work, token1);
  240.                 unget();
  241.                 break;
  242.             }
  243.             /*
  244.              * work has the concatenated token and token1 has
  245.              * the first token (no longer needed).  Unget the
  246.              * new (concatenated) token after freeing token1.
  247.              * Finally, setup to read the new token.
  248.              */
  249.             free(token1);                       /* Free up memory       */
  250.             ungetstring(work);                  /* Unget the new thing, */
  251.             return (TRUE);
  252.         }
  253. #else
  254.         return (FALSE);                         /* Not supported        */
  255. #endif
  256. }
  257.  
  258. int
  259. scanstring(delim, outfun)
  260. register int    delim;                  /* ' or "                       */
  261. int             (*outfun)();            /* Output function              */
  262. /*
  263.  * Scan off a string.  Warning if terminated by newline or EOF.
  264.  * outfun() outputs the character -- to a buffer if in a macro.
  265.  * TRUE if ok, FALSE if error.
  266.  */
  267. {
  268.         register int            c;
  269.  
  270.         instring = TRUE;                /* Don't strip comments         */
  271.         (*outfun)(delim);
  272.         while ((c = get()) != delim
  273.              && c != '\n'
  274.              && c != EOF_CHAR) {
  275.  
  276.             if (c != DEF_MAGIC)
  277.             (*outfun)(c);
  278.             if (c == '\\')
  279.                 (*outfun)(get());
  280.         }
  281.         instring = FALSE;
  282.         if (c == delim) {
  283.             (*outfun)(c);
  284.             return (TRUE);
  285.         }
  286.         else {
  287.             cerror("Unterminated string", NULLST);
  288.             unget();
  289.             return (FALSE);
  290.         }
  291. }
  292.  
  293. scannumber(c, outfun)
  294. register int    c;                              /* First char of number */
  295. register int    (*outfun)();                    /* Output/store func    */
  296. /*
  297.  * Process a number.  We know that c is from 0 to 9 or dot.
  298.  * Algorithm from Dave Conroy's Decus C.
  299.  */
  300. {
  301.         register int    radix;                  /* 8, 10, or 16         */
  302.         int             expseen;                /* 'e' seen in floater  */
  303.         int             signseen;               /* '+' or '-' seen      */
  304.         int             octal89;                /* For bad octal test   */
  305.         int             dotflag;                /* TRUE if '.' was seen */
  306.  
  307.         expseen = FALSE;                        /* No exponent seen yet */
  308.         signseen = TRUE;                        /* No +/- allowed yet   */
  309.         octal89 = FALSE;                        /* No bad octal yet     */
  310.         radix = 10;                             /* Assume decimal       */
  311.         if ((dotflag = (c == '.')) != FALSE) {  /* . something?         */
  312.             (*outfun)('.');                     /* Always out the dot   */
  313.             if (type[(c = get())] != DIG) {     /* If not a float numb, */
  314.                 unget();                        /* Rescan strange char  */
  315.                 return;                         /* All done for now     */
  316.             }
  317.         }                                       /* End of float test    */
  318.         else if (c == '0') {                    /* Octal or hex?        */
  319.             (*outfun)(c);                       /* Stuff initial zero   */
  320.             radix = 8;                          /* Assume it's octal    */
  321.             c = get();                          /* Look for an 'x'      */
  322.             if (c == 'x' || c == 'X') {         /* Did we get one?      */
  323.                 radix = 16;                     /* Remember new radix   */
  324.                 (*outfun)(c);                   /* Stuff the 'x'        */
  325.                 c = get();                      /* Get next character   */
  326.             }
  327.         }
  328.         for (;;) {                              /* Process curr. char.  */
  329.             /*
  330.              * Note that this algorithm accepts "012e4" and "03.4"
  331.              * as legitimate floating-point numbers.
  332.              */
  333.             if (radix != 16 && (c == 'e' || c == 'E')) {
  334.                 if (expseen)                    /* Already saw 'E'?     */
  335.                     break;                      /* Exit loop, bad nbr.  */
  336.                 expseen = TRUE;                 /* Set exponent seen    */
  337.                 signseen = FALSE;               /* We can read '+' now  */
  338.                 radix = 10;                     /* Decimal exponent     */
  339.             }
  340.             else if (radix != 16 && c == '.') {
  341.                 if (dotflag)                    /* Saw dot already?     */
  342.                     break;                      /* Exit loop, two dots  */
  343.                 dotflag = TRUE;                 /* Remember the dot     */
  344.                 radix = 10;                     /* Decimal fraction     */
  345.             }
  346.             else if (c == '+' || c == '-') {    /* 1.0e+10              */
  347.                 if (signseen)                   /* Sign in wrong place? */
  348.                     break;                      /* Exit loop, not nbr.  */
  349.                 /* signseen = TRUE; */          /* Remember we saw it   */
  350.             }
  351.             else {                              /* Check the digit      */
  352.                 switch (c) {
  353.                 case '8': case '9':             /* Sometimes wrong      */
  354.                     octal89 = TRUE;             /* Do check later       */
  355.                 case '0': case '1': case '2': case '3':
  356.                 case '4': case '5': case '6': case '7':
  357.                     break;                      /* Always ok            */
  358.  
  359.                 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  360.                 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  361.                     if (radix == 16)            /* Alpha's are ok only  */
  362.                         break;                  /* if reading hex.      */
  363.                 default:                        /* At number end        */
  364.                     goto done;                  /* Break from for loop  */
  365.                 }                               /* End of switch        */
  366.             }                                   /* End general case     */
  367.             (*outfun)(c);                       /* Accept the character */
  368.             signseen = TRUE;                    /* Don't read sign now  */
  369.             c = get();                          /* Read another char    */
  370.         }                                       /* End of scan loop     */
  371.         /*
  372.          * When we break out of the scan loop, c contains the first
  373.          * character (maybe) not in the number.  If the number is an
  374.          * integer, allow a trailing 'L' for long and/or a trailing 'U'
  375.          * for unsigned.  If not those, push the trailing character back
  376.          * on the input stream.  Floating point numbers accept a trailing
  377.          * 'L' for "long double".
  378.          */
  379. done:   if (dotflag || expseen) {               /* Floating point?      */
  380.             if (c == 'l' || c == 'L') {
  381.                 (*outfun)(c);
  382.                 c = get();                      /* Ungotten later       */
  383.             }
  384.         }
  385.         else {                                  /* Else it's an integer */
  386.             /*
  387.              * We know that dotflag and expseen are both zero, now:
  388.              * dotflag signals "saw 'L'", and
  389.              * expseen signals "saw 'U'".
  390.              */
  391.             for (;;) {
  392.                 switch (c) {
  393.                 case 'l':
  394.                 case 'L':
  395.                     if (dotflag)
  396.                         goto nomore;
  397.                     dotflag = TRUE;
  398.                     break;
  399.  
  400.                 case 'u':
  401.                 case 'U':
  402.                     if (expseen)
  403.                         goto nomore;
  404.                     expseen = TRUE;
  405.                     break;
  406.  
  407.                 default:
  408.                     goto nomore;
  409.                 }
  410.                 (*outfun)(c);                   /* Got 'L' or 'U'.      */
  411.                 c = get();                      /* Look at next, too.   */
  412.             }
  413.         }
  414. nomore: unget();                                /* Not part of a number */
  415.         if (octal89 && radix == 8)
  416.             cwarn("Illegal digit in octal number", NULLST);
  417. }
  418.  
  419. save(c)
  420. register int    c;
  421. {
  422.         if (workp >= &work[NWORK]) {
  423.             work[NWORK-1] = '\0';
  424.             cfatal("Work buffer overflow:  %s", work);
  425.         }
  426.         else *workp++ = c;
  427. }
  428.  
  429. char *
  430. savestring(text)
  431. char            *text;
  432. /*
  433.  * Store a string into free memory.
  434.  */
  435. {
  436.         register char   *result;
  437.  
  438.         result = getmem(strlen(text) + 1);
  439.         strcpy(result, text);
  440.         return (result);
  441. }
  442.  
  443. FILEINFO        *
  444. getfile(bufsize, name)
  445. int             bufsize;                /* Line or define buffer size   */
  446. char            *name;                  /* File or macro name string    */
  447. /*
  448.  * Common FILEINFO buffer initialization for a new file or macro.
  449.  */
  450. {
  451.         register FILEINFO       *file;
  452.         register int            size;
  453.  
  454.         size = strlen(name);                    /* File/macro name      */
  455.         file = (FILEINFO *) getmem(sizeof (FILEINFO) + bufsize + size);
  456.         file->parent = infile;                  /* Chain files together */
  457.         file->fp = NULL;                        /* No file yet          */
  458.         file->filename = savestring(name);      /* Save file/macro name */
  459.         file->progname = NULL;                  /* No #line seen yet    */
  460.         file->unrecur = 0;                      /* No macro fixup       */
  461.         file->bptr = file->buffer;              /* Initialize line ptr  */
  462.         file->buffer[0] = EOS;                  /* Force first read     */
  463.         file->line = 0;                         /* (Not used just yet)  */
  464.         if (infile != NULL)                     /* If #include file     */
  465.             infile->line = line;                /* Save current line    */
  466.         infile = file;                          /* New current file     */
  467.         line = 1;                               /* Note first line      */
  468.         return (file);                          /* All done.            */
  469. }
  470.  
  471. char *
  472. getmem(size)
  473. int             size;
  474. /*
  475.  * Get a block of free memory.
  476.  */
  477. {
  478.         register char   *result;
  479.         extern char     *malloc();
  480.  
  481.         if ((result = malloc((unsigned) size)) == NULL)
  482.             cfatal("Out of memory", NULLST);
  483.         return (result);
  484. }
  485.  
  486. /*
  487.  *                      C P P   S y m b o l   T a b l e s
  488.  */
  489.  
  490. /*
  491.  * SBSIZE defines the number of hash-table slots for the symbol table.
  492.  * It must be a power of 2.
  493.  */
  494. #ifndef SBSIZE
  495. #define SBSIZE  64
  496. #endif
  497. #define SBMASK  (SBSIZE - 1)
  498. #if (SBSIZE ^ SBMASK) != ((SBSIZE * 2) - 1)
  499.         << error, SBSIZE must be a power of 2 >>
  500. #endif
  501.  
  502. static DEFBUF   *symtab[SBSIZE];        /* Symbol table queue headers   */
  503.  
  504. DEFBUF *
  505. lookid(c)
  506. int     c;                              /* First character of token     */
  507. /*
  508.  * Look for the next token in the symbol table.  Returns token in "token".
  509.  * If found, returns the table pointer;  Else returns NULL.
  510.  */
  511. {
  512.         register int            nhash;
  513.         register DEFBUF         *dp;
  514.         register char           *np;
  515.         int                     temp;
  516.         int                     isrecurse;      /* For #define foo foo  */
  517.  
  518.         np = token;
  519.         nhash = 0;
  520.         if ((isrecurse = (c == DEF_MAGIC)))     /* If recursive macro   */
  521.             c = get();                          /* hack, skip DEF_MAGIC */
  522.         do {
  523.             if (np < &token[IDMAX]) {           /* token dim is IDMAX+1 */
  524.                 *np++ = c;                      /* Store token byte     */
  525.                 nhash += c;                     /* Update hash value    */
  526.             }
  527.             c = get();                          /* And get another byte */
  528.         } while (type[c] == LET || type[c] == DIG);
  529.         unget();                                /* Rescan terminator    */
  530.         *np = EOS;                              /* Terminate token      */
  531.         if (isrecurse)                          /* Recursive definition */
  532.             return (NULL);                      /* undefined just now   */
  533.         nhash += (np - token);                  /* Fix hash value       */
  534.         dp = symtab[nhash & SBMASK];            /* Starting bucket      */
  535.         while (dp != (DEFBUF *) NULL) {         /* Search symbol table  */
  536.             if (dp->hash == nhash               /* Fast precheck        */
  537.              && (temp = strcmp(dp->name, token)) >= 0)
  538.                 break;
  539.             dp = dp->link;                      /* Nope, try next one   */
  540.         }
  541.         return ((temp == 0) ? dp : NULL);
  542. }
  543.  
  544. DEFBUF *
  545. defendel(name, delete)
  546. char            *name;
  547. int             delete;                 /* TRUE to delete a symbol      */
  548. /*
  549.  * Enter this name in the lookup table (delete = FALSE)
  550.  * or delete this name (delete = TRUE).
  551.  * Returns a pointer to the define block (delete = FALSE)
  552.  * Returns NULL if the symbol wasn't defined (delete = TRUE).
  553.  */
  554. {
  555.         register DEFBUF         *dp;
  556.         register DEFBUF         **prevp;
  557.         register char           *np;
  558.         int                     nhash;
  559.         int                     temp;
  560.         int                     size;
  561.  
  562.         for (nhash = 0, np = name; *np != EOS;)
  563.             nhash += *np++;
  564.         size = (np - name);
  565.         nhash += size;
  566.         prevp = &symtab[nhash & SBMASK];
  567.         while ((dp = *prevp) != (DEFBUF *) NULL) {
  568.             if (dp->hash == nhash
  569.              && (temp = strcmp(dp->name, name)) >= 0) {
  570.                 if (temp > 0)
  571.                     dp = NULL;                  /* Not found            */
  572.                 else {
  573.                     *prevp = dp->link;          /* Found, unlink and    */
  574.                     if (dp->repl != NULL)       /* Free the replacement */
  575.                         free(dp->repl);         /* if any, and then     */
  576.                     free((char *) dp);          /* Free the symbol      */
  577.                 }
  578.                 break;
  579.             }
  580.             prevp = &dp->link;
  581.         }
  582.         if (!delete) {
  583.             dp = (DEFBUF *) getmem(sizeof (DEFBUF) + size);
  584.             dp->link = *prevp;
  585.             *prevp = dp;
  586.             dp->hash = nhash;
  587.             dp->repl = NULL;
  588.             dp->nargs = 0;
  589.             strcpy(dp->name, name);
  590.         }
  591.         return (dp);
  592. }
  593.  
  594. #if DEBUG
  595.  
  596. dumpdef(why)
  597. char            *why;
  598. {
  599.         register DEFBUF         *dp;
  600.         register DEFBUF         **syp;
  601.  
  602.         printf("CPP symbol table dump %s\n", why);
  603.         for (syp = symtab; syp < &symtab[SBSIZE]; syp++) {
  604.             if ((dp = *syp) != (DEFBUF *) NULL) {
  605.                 printf("symtab[%d]\n", (syp - symtab));
  606.                 do {
  607.                     dumpadef((char *) NULL, dp);
  608.                 } while ((dp = dp->link) != (DEFBUF *) NULL);
  609.             }
  610.         }
  611. }
  612.  
  613. dumpadef(why, dp)
  614. char            *why;                   /* Notation                     */
  615. register DEFBUF *dp;
  616. {
  617.         register char           *cp;
  618.         register int            c;
  619.  
  620.         printf(" \"%s\" [%d]", dp->name, dp->nargs);
  621.         if (why != NULL)
  622.             printf(" (%s)", why);
  623.         if (dp->repl != NULL) {
  624.             printf(" => ");
  625.             for (cp = dp->repl; (c = *cp++ & 0xFF) != EOS;) {
  626.                 if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC))
  627.                     printf("<%d>", c - MAC_PARM);
  628.                 else if (isprint(c) || c == '\n' || c == '\t')
  629.                     putchar(c);
  630.                 else if (c < ' ')
  631.                     printf("<^%c>", c + '@');
  632.                 else
  633.                     printf("<\\0%o>", c);
  634.             }
  635.         }
  636.         else {
  637.             printf(", no replacement.");
  638.         }
  639.         putchar('\n');
  640. }
  641. #endif
  642.  
  643. /*
  644.  *                      G E T
  645.  */
  646.  
  647. int
  648. get()
  649. /*
  650.  * Return the next character from a macro or the current file.
  651.  * Handle end of file from #include files.
  652.  */
  653. {
  654.         register int            c;
  655.         register FILEINFO       *file;
  656.         register int            popped;         /* Recursion fixup      */
  657.  
  658.         popped = 0;
  659. get_from_file:
  660.         if ((file = infile) == NULL)
  661.             return (EOF_CHAR);
  662. newline:
  663. #if 0
  664.         printf("get(%s), recursion %d, line %d, bptr = %d, buffer \"%s\"\n",
  665.             file->filename, recursion, line,
  666.             file->bptr - file->buffer, file->buffer);
  667. #endif
  668.         /*
  669.          * Read a character from the current input line or macro.
  670.          * At EOS, either finish the current macro (freeing temp.
  671.          * storage) or read another line from the current input file.
  672.          * At EOF, exit the current file (#include) or, at EOF from
  673.          * the cpp input file, return EOF_CHAR to finish processing.
  674.          */
  675.         if ((c = *file->bptr++ & 0xFF) == EOS) {
  676.             /*
  677.              * Nothing in current line or macro.  Get next line (if
  678.              * input from a file), or do end of file/macro processing.
  679.              * In the latter case, jump back to restart from the top.
  680.              */
  681.             if (file->fp == NULL) {             /* NULL if macro        */
  682.                 popped++;
  683.                 recursion -= file->unrecur;
  684.                 if (recursion < 0)
  685.                     recursion = 0;
  686.                 infile = file->parent;          /* Unwind file chain    */
  687.             }
  688.             else {                              /* Else get from a file */
  689.                 if ((file->bptr = fgets(file->buffer, NBUFF, file->fp))
  690.                         != NULL) {
  691. #if DEBUG
  692.                     if (debug > 1) {            /* Dump it to stdout    */
  693.                         printf("\n#line %d (%s), %s",
  694.                             line, file->filename, file->buffer);
  695.                     }
  696. #endif
  697.                     goto newline;               /* process the line     */
  698.                 }
  699.                 else {
  700.                     fclose(file->fp);           /* Close finished file  */
  701.                     if ((infile = file->parent) != NULL) {
  702.                         /*
  703.                          * There is an "ungotten" newline in the current
  704.                          * infile buffer (set there by doinclude() in
  705.                          * cpp1.c).  Thus, we know that the mainline code
  706.                          * is skipping over blank lines and will do a
  707.                          * #line at its convenience.
  708.                          */
  709.                         wrongline = TRUE;       /* Need a #line now     */
  710.                     }
  711.                 }
  712.             }
  713.             /*
  714.              * Free up space used by the (finished) file or macro and
  715.              * restart input from the parent file/macro, if any.
  716.              */
  717.             free(file->filename);               /* Free name and        */
  718.             if (file->progname != NULL)         /* if a #line was seen, */
  719.                 free(file->progname);           /* free it, too.        */
  720.             free((char *) file);                /* Free file space      */
  721.             if (infile == NULL)                 /* If at end of file    */
  722.                 return (EOF_CHAR);              /* Return end of file   */
  723.             line = infile->line;                /* Reset line number    */
  724.             goto get_from_file;                 /* Get from the top.    */
  725.         }
  726.         /*
  727.          * Common processing for the new character.
  728.          */
  729.         if (c == DEF_MAGIC && file->fp != NULL) /* Don't allow delete   */
  730.             goto newline;                       /* from a file          */
  731.         if (file->parent != NULL) {             /* Macro or #include    */
  732.             if (popped != 0)
  733.                 file->parent->unrecur += popped;
  734.             else {
  735.                 recursion -= file->parent->unrecur;
  736.                 if (recursion < 0)
  737.                     recursion = 0;
  738.                 file->parent->unrecur = 0;
  739.             }
  740.         }
  741.         if (c == '\n')                          /* Maintain current     */
  742.             ++line;                             /* line counter         */
  743.         if (instring)                           /* Strings just return  */
  744.             return (c);                         /* the character.       */
  745.         else if (c == '/') {                    /* Comment?             */
  746.             instring = TRUE;                    /* So get() won't loop  */
  747.             if ((c = get()) != '*') {           /* Next byte '*'?       */
  748.                 instring = FALSE;               /* Nope, no comment     */
  749.                 unget();                        /* Push the char. back  */
  750.                 return ('/');                   /* Return the slash     */
  751.             }
  752.             if (keepcomments) {                 /* If writing comments  */
  753.                 putchar('/');                   /* Write out the        */
  754.                 putchar('*');                   /*   initializer        */
  755.             }
  756.             for (;;) {                          /* Eat a comment        */
  757.                 c = get();
  758. test:           if (keepcomments && c != EOF_CHAR)
  759.                     cput(c);
  760.                 switch (c) {
  761.                 case EOF_CHAR:
  762.                     cerror("EOF in comment", NULLST);
  763.                     return (EOF_CHAR);
  764.  
  765.                 case '/':
  766.                     if ((c = get()) != '*')     /* Don't let comments   */
  767.                         goto test;              /* Nest.                */
  768. #ifdef STRICT_COMMENTS
  769.                     cwarn("Nested comments", NULLST);
  770. #endif
  771.                                                 /* Fall into * stuff    */
  772.                 case '*':
  773.                     if ((c = get()) != '/')     /* If comment doesn't   */
  774.                         goto test;              /* end, look at next    */
  775.                     instring = FALSE;           /* End of comment,      */
  776.                     if (keepcomments) {         /* Put out the comment  */
  777.                         cput(c);                /* terminator, too      */
  778.                     }
  779.                     /*
  780.                      * A comment is syntactically "whitespace" --
  781.                      * however, there are certain strange sequences
  782.                      * such as
  783.                      *          #define foo(x)  (something)
  784.                      *                  foo|* comment *|(123)
  785.                      *       these are '/' ^           ^
  786.                      * where just returning space (or COM_SEP) will cause
  787.                      * problems.  This can be "fixed" by overwriting the
  788.                      * '/' in the input line buffer with ' ' (or COM_SEP)
  789.                      * but that may mess up an error message.
  790.                      * So, we peek ahead -- if the next character is
  791.                      * "whitespace" we just get another character, if not,
  792.                      * we modify the buffer.  All in the name of purity.
  793.                      */
  794.                     if (*file->bptr == '\n'
  795.                      || type[*file->bptr & 0xFF] == SPA)
  796.                         goto newline;
  797. #if COMMENT_INVISIBLE
  798.                     /*
  799.                      * Return magic (old-fashioned) syntactic space.
  800.                      */
  801.                     return ((file->bptr[-1] = COM_SEP));
  802. #else
  803.                     return ((file->bptr[-1] = ' '));
  804. #endif
  805.  
  806.                 case '\n':                      /* we'll need a #line   */
  807.                     if (!keepcomments)
  808.                         wrongline = TRUE;       /* later...             */
  809.                 default:                        /* Anything else is     */
  810.                     break;                      /* Just a character     */
  811.                 }                               /* End switch           */
  812.             }                                   /* End comment loop     */
  813.         }                                       /* End if in comment    */
  814.         else if (!inmacro && c == '\\') {       /* If backslash, peek   */
  815.             if ((c = get()) == '\n') {          /* for a <nl>.  If so,  */
  816.                 wrongline = TRUE;
  817.                 goto newline;
  818.             }
  819.             else {                              /* Backslash anything   */
  820.                 unget();                        /* Get it later         */
  821.                 return ('\\');                  /* Return the backslash */
  822.             }
  823.         }
  824.         else if (c == '\f' || c == VT)          /* Form Feed, Vertical  */
  825.             c = ' ';                            /* Tab are whitespace   */
  826.         return (c);                             /* Just return the char */
  827. }
  828.  
  829. unget()
  830. /*
  831.  * Backup the pointer to reread the last character.  Fatal error
  832.  * (code bug) if we backup too far.  unget() may be called,
  833.  * without problems, at end of file.  Only one character may
  834.  * be ungotten.  If you need to unget more, call ungetstring().
  835.  */
  836. {
  837.         register FILEINFO       *file;
  838.  
  839.         if ((file = infile) == NULL)
  840.             return;                     /* Unget after EOF              */
  841.         if (--file->bptr < file->buffer)
  842.             cfatal("Too much pushback", NULLST);
  843.         if (*file->bptr == '\n')        /* Ungetting a newline?         */
  844.             --line;                     /* Unget the line number, too   */
  845. }
  846.  
  847. ungetstring(text)
  848. char            *text;
  849. /*
  850.  * Push a string back on the input stream.  This is done by treating
  851.  * the text as if it were a macro.
  852.  */
  853. {
  854.         register FILEINFO       *file;
  855.         extern FILEINFO         *getfile();
  856.  
  857.         file = getfile(strlen(text) + 1, "");
  858.         strcpy(file->buffer, text);
  859. }
  860.  
  861. int
  862. cget()
  863. /*
  864.  * Get one character, absorb "funny space" after comments or
  865.  * token concatenation
  866.  */
  867. {
  868.         register int    c;
  869.  
  870.         do {
  871.             c = get();
  872. #if COMMENT_INVISIBLE
  873.         } while (c == TOK_SEP || c == COM_SEP);
  874. #else
  875.         } while (c == TOK_SEP);
  876. #endif
  877.         return (c);
  878. }
  879.  
  880. /*
  881.  * Error messages and other hacks.  The first byte of severity
  882.  * is 'S' for string arguments and 'I' for int arguments.  This
  883.  * is needed for portability with machines that have int's that
  884.  * are shorter than  char *'s.
  885.  */
  886.  
  887. static
  888. domsg(severity, format, arg)
  889. char            *severity;              /* "Error", "Warning", "Fatal"  */
  890. char            *format;                /* Format for the error message */
  891. char            *arg;                   /* Something for the message    */
  892. /*
  893.  * Print filenames, macro names, and line numbers for error messages.
  894.  */
  895. {
  896.         register char           *tp;
  897.         register FILEINFO       *file;
  898.  
  899.         fprintf(stderr, "%sline %d, %s: ", MSG_PREFIX, line, &severity[1]);
  900.         if (*severity == 'S')
  901.             fprintf(stderr, format, arg);
  902.         else
  903.             fprintf(stderr, format, (int) arg);
  904.         putc('\n', stderr);
  905.         if ((file = infile) == NULL)
  906.             return;                             /* At end of file       */
  907.         if (file->fp != NULL) {
  908.             tp = file->buffer;                  /* Print current file   */
  909.             fprintf(stderr, "%s", tp);          /* name, making sure    */
  910.             if (tp[strlen(tp) - 1] != '\n')     /* there's a newline    */
  911.                 putc('\n', stderr);
  912.         }
  913.         while ((file = file->parent) != NULL) { /* Print #includes, too */
  914.             if (file->fp == NULL)
  915.                 fprintf(stderr, "from macro %s\n", file->filename);
  916.             else {
  917.                 tp = file->buffer;
  918.                 fprintf(stderr, "from file %s, line %d:\n%s",
  919.                     (file->progname != NULL)
  920.                         ? file->progname : file->filename,
  921.                     file->line, tp);
  922.                 if (tp[strlen(tp) - 1] != '\n')
  923.                     putc('\n', stderr);
  924.             }
  925.         }
  926. }
  927.  
  928. cerror(format, sarg)
  929. char            *format;
  930. char            *sarg;          /* Single string argument               */
  931. /*
  932.  * Print a normal error message, string argument.
  933.  */
  934. {
  935.         domsg("SError", format, sarg);
  936.         errors++;
  937. }
  938.  
  939. cierror(format, narg)
  940. char            *format;
  941. int             narg;           /* Single numeric argument              */
  942. /*
  943.  * Print a normal error message, numeric argument.
  944.  */
  945. {
  946.         domsg("IError", format, (char *) narg);
  947.         errors++;
  948. }
  949.  
  950. cfatal(format, sarg)
  951. char            *format;
  952. char            *sarg;                  /* Single string argument       */
  953. /*
  954.  * A real disaster
  955.  */
  956. {
  957.         domsg("SFatal error", format, sarg);
  958.         exit(IO_ERROR);
  959. }
  960.  
  961. cwarn(format, sarg)
  962. char            *format;
  963. char            *sarg;                  /* Single string argument       */
  964. /*
  965.  * A non-fatal error, string argument.
  966.  */
  967. {
  968.         domsg("SWarning", format, sarg);
  969. }
  970.  
  971. ciwarn(format, narg)
  972. char            *format;
  973. int             narg;                   /* Single numeric argument      */
  974. /*
  975.  * A non-fatal error, numeric argument.
  976.  */
  977. {
  978.         domsg("IWarning", format, (char *) narg);
  979. }
  980.  
  981.  
  982.  
  983.