OSDN Git Service

no bone
[nethackexpress/trunk.git] / sys / unix / cpp3.shr
1 # This is a shell archive.  Save it in a file, remove anything before
2 # this line, and then unpack it by entering "sh file".  Note, it may
3 # create directories; files and directories will be owned by you and
4 # have default permissions.
5 #
6 # This archive contains:
7 #
8 #       cpp5.c
9 #       cpp6.c
10 #
11 echo x - cpp5.c
12 sed 's/^X//' >cpp5.c << 'END-of-cpp5.c'
13 X/*
14 X *                         C P P 5 . C
15 X *             E x p r e s s i o n   E v a l u a t i o n
16 X *
17 X * Edit History
18 X * 31-Aug-84   MM      USENET net.sources release
19 X * 04-Oct-84   MM      __LINE__ and __FILE__ must call ungetstring()
20 X *                     so they work correctly with token concatenation.
21 X *                     Added string formal recognition.
22 X * 25-Oct-84   MM      "Short-circuit" evaluate #if's so that we
23 X *                     don't print unnecessary error messages for
24 X *                     #if !defined(FOO) && FOO != 0 && 10 / FOO ...
25 X * 31-Oct-84   ado/MM  Added token concatenation
26 X *  6-Nov-84   MM      Split from #define stuff, added sizeof stuff
27 X * 19-Nov-84   ado     #if error returns TRUE for (sigh) compatibility
28 X */
29 X
30 X#include       <stdio.h>
31 X#include       <ctype.h>
32 X#include       "cppdef.h"
33 X#include       "cpp.h"
34 X
35 X/*
36 X * Evaluate an #if expression.
37 X */
38 X
39 Xstatic char    *opname[] = {           /* For debug and error messages */
40 X"end of expression", "val", "id",
41 X  "+",   "-",  "*",  "/",  "%",
42 X  "<<", ">>",  "&",  "|",  "^",
43 X  "==", "!=",  "<", "<=", ">=",  ">",
44 X  "&&", "||",  "?",  ":",  ",",
45 X  "unary +", "unary -", "~", "!",  "(",  ")", "(none)",
46 X};
47 X
48 X/*
49 X * opdope[] has the operator precedence:
50 X *     Bits
51 X *       7     Unused (so the value is always positive)
52 X *     6-2     Precedence (000x .. 017x)
53 X *     1-0     Binary op. flags:
54 X *         01  The binop flag should be set/cleared when this op is seen.
55 X *         10  The new value of the binop flag.
56 X * Note:  Expected, New binop
57 X * constant    0       1       Binop, end, or ) should follow constants
58 X * End of line 1       0       End may not be preceeded by an operator
59 X * binary      1       0       Binary op follows a value, value follows.
60 X * unary       0       0       Unary op doesn't follow a value, value follows
61 X *   (         0       0       Doesn't follow value, value or unop follows
62 X *   )         1       1       Follows value.  Op follows.
63 X */
64 X
65 Xstatic char    opdope[OP_MAX] = {
66 X  0001,                                        /* End of expression            */
67 X  0002,                                        /* Digit                        */
68 X  0000,                                        /* Letter (identifier)          */
69 X  0141, 0141, 0151, 0151, 0151,                /* ADD, SUB, MUL, DIV, MOD      */
70 X  0131, 0131, 0101, 0071, 0071,                /* ASL, ASR, AND,  OR, XOR      */
71 X  0111, 0111, 0121, 0121, 0121,        0121,   /*  EQ,  NE,  LT,  LE,  GE,  GT */
72 X  0061, 0051, 0041, 0041, 0031,                /* ANA, ORO, QUE, COL, CMA      */
73 X/*
74 X * Unary op's follow
75 X */
76 X  0160, 0160, 0160, 0160,              /* NEG, PLU, COM, NOT           */
77 X  0170, 0013, 0023,                    /* LPA, RPA, END                */
78 X};
79 X/*
80 X * OP_QUE and OP_RPA have alternate precedences:
81 X */
82 X#define        OP_RPA_PREC     0013
83 X#define OP_QUE_PREC    0034
84 X
85 X/*
86 X * S_ANDOR and S_QUEST signal "short-circuit" boolean evaluation, so that
87 X *     #if FOO != 0 && 10 / FOO ...
88 X * doesn't generate an error message.  They are stored in optab.skip.
89 X */
90 X#define S_ANDOR                2
91 X#define S_QUEST                1
92 X
93 Xtypedef struct optab {
94 X    char       op;                     /* Operator                     */
95 X    char       prec;                   /* Its precedence               */
96 X    char       skip;                   /* Short-circuit: TRUE to skip  */
97 X} OPTAB;
98 Xstatic int     evalue;                 /* Current value from evallex() */
99 X
100 X#ifdef nomacargs
101 XFILE_LOCAL int
102 Xisbinary(op)
103 Xregister int   op;
104 X{
105 X       return (op >= FIRST_BINOP && op <= LAST_BINOP);
106 X}
107 X
108 XFILE_LOCAL int
109 Xisunary(op)
110 Xregister int   op;
111 X{
112 X       return (op >= FIRST_UNOP && op <= LAST_UNOP);
113 X}
114 X#else
115 X#define        isbinary(op)    (op >= FIRST_BINOP && op <= LAST_BINOP)
116 X#define        isunary(op)     (op >= FIRST_UNOP  && op <= LAST_UNOP)
117 X#endif
118 X\f
119 X/*
120 X * The following definitions are used to specify basic variable sizes.
121 X */
122 X
123 X#ifndef        S_CHAR
124 X#define        S_CHAR          (sizeof (char))
125 X#endif
126 X#ifndef        S_SINT
127 X#define        S_SINT          (sizeof (short int))
128 X#endif
129 X#ifndef        S_INT
130 X#define        S_INT           (sizeof (int))
131 X#endif
132 X#ifndef        S_LINT
133 X#define        S_LINT          (sizeof (long int))
134 X#endif
135 X#ifndef        S_FLOAT
136 X#define        S_FLOAT         (sizeof (float))
137 X#endif
138 X#ifndef        S_DOUBLE
139 X#define        S_DOUBLE        (sizeof (double))
140 X#endif
141 X#ifndef        S_PCHAR
142 X#define        S_PCHAR         (sizeof (char *))
143 X#endif
144 X#ifndef        S_PSINT
145 X#define        S_PSINT         (sizeof (short int *))
146 X#endif
147 X#ifndef        S_PINT
148 X#define        S_PINT          (sizeof (int *))
149 X#endif
150 X#ifndef        S_PLINT
151 X#define        S_PLINT         (sizeof (long int *))
152 X#endif
153 X#ifndef        S_PFLOAT
154 X#define        S_PFLOAT        (sizeof (float *))
155 X#endif
156 X#ifndef        S_PDOUBLE
157 X#define        S_PDOUBLE       (sizeof (double *))
158 X#endif
159 X#ifndef        S_PFPTR
160 X#define S_PFPTR                (sizeof (int (*)()))
161 X#endif
162 X\f
163 Xtypedef struct types {
164 X    short      type;                   /* This is the bit if           */
165 X    char       *name;                  /* this is the token word       */
166 X} TYPES;
167 X
168 Xstatic TYPES basic_types[] = {
169 X       { T_CHAR,       "char",         },
170 X       { T_INT,        "int",          },
171 X       { T_FLOAT,      "float",        },
172 X       { T_DOUBLE,     "double",       },
173 X       { T_SHORT,      "short",        },
174 X       { T_LONG,       "long",         },
175 X       { T_SIGNED,     "signed",       },
176 X       { T_UNSIGNED,   "unsigned",     },
177 X       { 0,            NULL,           },      /* Signal end           */
178 X};
179 X
180 X/*
181 X * Test_table[] is used to test for illegal combinations.
182 X */
183 Xstatic short test_table[] = {
184 X       T_FLOAT | T_DOUBLE | T_LONG | T_SHORT,
185 X       T_FLOAT | T_DOUBLE | T_CHAR | T_INT,
186 X       T_FLOAT | T_DOUBLE | T_SIGNED | T_UNSIGNED,
187 X       T_LONG  | T_SHORT  | T_CHAR,
188 X       0                                               /* end marker   */
189 X};
190 X
191 X/*
192 X * The order of this table is important -- it is also referenced by
193 X * the command line processor to allow run-time overriding of the
194 X * built-in size values.  The order must not be changed:
195 X *     char, short, int, long, float, double (func pointer)
196 X */
197 XSIZES size_table[] = {
198 X    { T_CHAR,  S_CHAR,         S_PCHAR         },      /* char         */
199 X    { T_SHORT, S_SINT,         S_PSINT         },      /* short int    */
200 X    { T_INT,   S_INT,          S_PINT          },      /* int          */
201 X    { T_LONG,  S_LINT,         S_PLINT         },      /* long         */
202 X    { T_FLOAT, S_FLOAT,        S_PFLOAT        },      /* float        */
203 X    { T_DOUBLE,        S_DOUBLE,       S_PDOUBLE       },      /* double       */
204 X    { T_FPTR,  0,              S_PFPTR         },      /* int (*())    */
205 X    { 0,       0,              0               },      /* End of table */
206 X};
207 X\f
208 Xint
209 Xeval()
210 X/*
211 X * Evaluate an expression.  Straight-forward operator precedence.
212 X * This is called from control() on encountering an #if statement.
213 X * It calls the following routines:
214 X * evallex     Lexical analyser -- returns the type and value of
215 X *             the next input token.
216 X * evaleval    Evaluate the current operator, given the values on
217 X *             the value stack.  Returns a pointer to the (new)
218 X *             value stack.
219 X * For compatiblity with older cpp's, this return returns 1 (TRUE)
220 X * if a syntax error is detected.
221 X */
222 X{
223 X       register int    op;             /* Current operator             */
224 X       register int    *valp;          /* -> value vector              */
225 X       register OPTAB  *opp;           /* Operator stack               */
226 X       int             prec;           /* Op precedence                */
227 X       int             binop;          /* Set if binary op. needed     */
228 X       int             op1;            /* Operand from stack           */
229 X       int             skip;           /* For short-circuit testing    */
230 X       int             value[NEXP];    /* Value stack                  */
231 X       OPTAB           opstack[NEXP];  /* Operand stack                */
232 X       extern int      *evaleval();    /* Does actual evaluation       */
233 X
234 X       valp = value;
235 X       opp = opstack;
236 X       opp->op = OP_END;               /* Mark bottom of stack         */
237 X       opp->prec = opdope[OP_END];     /* And its precedence           */
238 X       opp->skip = 0;                  /* Not skipping now             */
239 X       binop = 0;
240 Xagain: ;
241 X#ifdef DEBUG_EVAL
242 X       printf("In #if at again: skip = %d, binop = %d, line is: %s",
243 X           opp->skip, binop, infile->bptr);
244 X#endif
245 X       if ((op = evallex(opp->skip)) == OP_SUB && binop == 0)
246 X           op = OP_NEG;                        /* Unary minus          */
247 X       else if (op == OP_ADD && binop == 0)
248 X           op = OP_PLU;                        /* Unary plus           */
249 X       else if (op == OP_FAIL)
250 X           return (1);                         /* Error in evallex     */
251 X#ifdef DEBUG_EVAL
252 X       printf("op = %s, opdope = %03o, binop = %d, skip = %d\n",
253 X           opname[op], opdope[op], binop, opp->skip);
254 X#endif
255 X       if (op == DIG) {                        /* Value?               */
256 X           if (binop != 0) {
257 X               cerror("misplaced constant in #if", NULLST);
258 X               return (1);
259 X           }
260 X           else if (valp >= &value[NEXP-1]) {
261 X               cerror("#if value stack overflow", NULLST);
262 X               return (1);
263 X           }
264 X           else {
265 X#ifdef DEBUG_EVAL
266 X               printf("pushing %d onto value stack[%d]\n",
267 X                   evalue, valp - value);
268 X#endif
269 X               *valp++ = evalue;
270 X               binop = 1;
271 X           }
272 X           goto again;
273 X       }
274 X       else if (op > OP_END) {
275 X           cerror("Illegal #if line", NULLST);
276 X           return (1);
277 X       }
278 X       prec = opdope[op];
279 X       if (binop != (prec & 1)) {
280 X           cerror("Operator %s in incorrect context", opname[op]);
281 X           return (1);
282 X       }
283 X       binop = (prec & 2) >> 1;
284 X       for (;;) {
285 X#ifdef DEBUG_EVAL
286 X           printf("op %s, prec %d., stacked op %s, prec %d, skip %d\n",
287 X               opname[op], prec, opname[opp->op], opp->prec, opp->skip);
288 X#endif
289 X           if (prec > opp->prec) {
290 X               if (op == OP_LPA)
291 X                   prec = OP_RPA_PREC;
292 X               else if (op == OP_QUE)
293 X                   prec = OP_QUE_PREC;
294 X               op1 = opp->skip;                /* Save skip for test   */
295 X               /*
296 X                * Push operator onto op. stack.
297 X                */
298 X               opp++;
299 X               if (opp >= &opstack[NEXP]) {
300 X                   cerror("expression stack overflow at op \"%s\"",
301 X                       opname[op]);
302 X                   return (1);
303 X               }
304 X               opp->op = op;
305 X               opp->prec = prec;
306 X               skip = (valp[-1] != 0);         /* Short-circuit tester */
307 X               /*
308 X                * Do the short-circuit stuff here.  Short-circuiting
309 X                * stops automagically when operators are evaluated.
310 X                */
311 X               if ((op == OP_ANA && !skip)
312 X                || (op == OP_ORO && skip))
313 X                   opp->skip = S_ANDOR;        /* And/or skip starts   */
314 X               else if (op == OP_QUE)          /* Start of ?: operator */
315 X                   opp->skip = (op1 & S_ANDOR) | ((!skip) ? S_QUEST : 0);
316 X               else if (op == OP_COL) {        /* : inverts S_QUEST    */
317 X                   opp->skip = (op1 & S_ANDOR)
318 X                             | (((op1 & S_QUEST) != 0) ? 0 : S_QUEST);
319 X               }
320 X               else {                          /* Other ops leave      */
321 X                   opp->skip = op1;            /*  skipping unchanged. */
322 X               }
323 X#ifdef DEBUG_EVAL
324 X               printf("stacking %s, valp[-1] == %d at %s",
325 X                   opname[op], valp[-1], infile->bptr);
326 X               dumpstack(opstack, opp, value, valp);
327 X#endif
328 X               goto again;
329 X           }
330 X           /*
331 X            * Pop operator from op. stack and evaluate it.
332 X            * End of stack and '(' are specials.
333 X            */
334 X           skip = opp->skip;                   /* Remember skip value  */
335 X           switch ((op1 = opp->op)) {          /* Look at stacked op   */
336 X           case OP_END:                        /* Stack end marker     */
337 X               if (op == OP_EOE)
338 X                   return (valp[-1]);          /* Finished ok.         */
339 X               goto again;                     /* Read another op.     */
340 X
341 X           case OP_LPA:                        /* ( on stack           */
342 X               if (op != OP_RPA) {             /* Matches ) on input   */
343 X                   cerror("unbalanced paren's, op is \"%s\"", opname[op]);
344 X                   return (1);
345 X               }
346 X               opp--;                          /* Unstack it           */
347 X               /* goto again;                  -- Fall through         */
348 X
349 X           case OP_QUE:
350 X               goto again;                     /* Evaluate true expr.  */
351 X
352 X           case OP_COL:                        /* : on stack.          */
353 X               opp--;                          /* Unstack :            */
354 X               if (opp->op != OP_QUE) {        /* Matches ? on stack?  */
355 X                   cerror("Misplaced '?' or ':', previous operator is %s",
356 X                       opname[opp->op]);
357 X                   return (1);
358 X               }
359 X               /*
360 X                * Evaluate op1.
361 X                */
362 X           default:                            /* Others:              */
363 X               opp--;                          /* Unstack the operator */
364 X#ifdef DEBUG_EVAL
365 X               printf("Stack before evaluation of %s\n", opname[op1]);
366 X               dumpstack(opstack, opp, value, valp);
367 X#endif
368 X               valp = evaleval(valp, op1, skip);
369 X#ifdef DEBUG_EVAL
370 X               printf("Stack after evaluation\n");
371 X               dumpstack(opstack, opp, value, valp);
372 X#endif
373 X           }                                   /* op1 switch end       */
374 X       }                                       /* Stack unwind loop    */
375 X}
376 X\f
377 XFILE_LOCAL int
378 Xevallex(skip)
379 Xint            skip;           /* TRUE if short-circuit evaluation     */
380 X/*
381 X * Return next eval operator or value.  Called from eval().  It
382 X * calls a special-purpose routines for 'char' strings and
383 X * numeric values:
384 X * evalchar    called to evaluate 'x'
385 X * evalnum     called to evaluate numbers.
386 X */
387 X{
388 X       register int    c, c1, t;
389 X
390 Xagain:  do {                                   /* Collect the token    */
391 X           c = skipws();
392 X           if ((c = macroid(c)) == EOF_CHAR || c == '\n') {
393 X               unget();
394 X               return (OP_EOE);                /* End of expression    */
395 X           }
396 X       } while ((t = type[c]) == LET && catenate());
397 X       if (t == INV) {                         /* Total nonsense       */
398 X           if (!skip) {
399 X               if (isascii(c) && isprint(c))
400 X                   cierror("illegal character '%c' in #if", c);
401 X               else
402 X                   cierror("illegal character (%d decimal) in #if", c);
403 X           }
404 X           return (OP_FAIL);
405 X       }
406 X       else if (t == QUO) {                    /* ' or "               */
407 X           if (c == '\'') {                    /* Character constant   */
408 X               evalue = evalchar(skip);        /* Somewhat messy       */
409 X#ifdef DEBUG_EVAL
410 X               printf("evalchar returns %d.\n", evalue);
411 X#endif
412 X               return (DIG);                   /* Return a value       */
413 X           }
414 X           cerror("Can't use a string in an #if", NULLST);
415 X           return (OP_FAIL);
416 X       }
417 X       else if (t == LET) {                    /* ID must be a macro   */
418 X           if (streq(token, "defined")) {      /* Or defined name      */
419 X               c1 = c = skipws();
420 X               if (c == '(')                   /* Allow defined(name)  */
421 X                   c = skipws();
422 X               if (type[c] == LET) {
423 X                   evalue = (lookid(c) != NULL);
424 X                   if (c1 != '('               /* Need to balance      */
425 X                    || skipws() == ')')        /* Did we balance?      */
426 X                       return (DIG);           /* Parsed ok            */
427 X               }
428 X               cerror("Bad #if ... defined() syntax", NULLST);
429 X               return (OP_FAIL);
430 X           }
431 X           else if (streq(token, "sizeof"))    /* New sizeof hackery   */
432 X               return (dosizeof());            /* Gets own routine     */
433 X           /*
434 X            * The Draft ANSI C Standard says that an undefined symbol
435 X            * in an #if has the value zero.  We are a bit pickier,
436 X            * warning except where the programmer was careful to write
437 X            *          #if defined(foo) ? foo : 0
438 X            */
439 X#ifdef VERBOSE
440 X            if (!skip)
441 X               cwarn("undefined symbol \"%s\" in #if, 0 used", token);
442 X#endif
443 X           evalue = 0;
444 X           return (DIG);
445 X       }
446 X       else if (t == DIG) {                    /* Numbers are harder   */
447 X           evalue = evalnum(c);
448 X#ifdef DEBUG_EVAL
449 X           printf("evalnum returns %d.\n", evalue);
450 X#endif
451 X       }
452 X       else if (strchr("!=<>&|\\", c) != NULL) {
453 X           /*
454 X            * Process a possible multi-byte lexeme.
455 X            */
456 X           c1 = cget();                        /* Peek at next char    */
457 X           switch (c) {
458 X           case '!':
459 X               if (c1 == '=')
460 X                   return (OP_NE);
461 X               break;
462 X
463 X           case '=':
464 X               if (c1 != '=') {                /* Can't say a=b in #if */
465 X                   unget();
466 X                   cerror("= not allowed in #if", NULLST);
467 X                   return (OP_FAIL);
468 X               }
469 X               return (OP_EQ);
470 X
471 X           case '>':
472 X           case '<':
473 X               if (c1 == c)
474 X                   return ((c == '<') ? OP_ASL : OP_ASR);
475 X               else if (c1 == '=')
476 X                   return ((c == '<') ? OP_LE  : OP_GE);
477 X               break;
478 X
479 X           case '|':
480 X           case '&':
481 X               if (c1 == c)
482 X                   return ((c == '|') ? OP_ORO : OP_ANA);
483 X               break;
484 X
485 X           case '\\':
486 X               if (c1 == '\n')                 /* Multi-line if        */
487 X                   goto again;
488 X               cerror("Unexpected \\ in #if", NULLST);
489 X               return (OP_FAIL);
490 X           }
491 X           unget();
492 X       }
493 X       return (t);
494 X}
495 X\f
496 XFILE_LOCAL int
497 Xdosizeof()
498 X/*
499 X * Process the sizeof (basic type) operation in an #if string.
500 X * Sets evalue to the size and returns
501 X *     DIG             success
502 X *     OP_FAIL         bad parse or something.
503 X */
504 X{
505 X       register int    c;
506 X       register TYPES  *tp;
507 X       register SIZES  *sizp;
508 X       register short  *testp;
509 X       short           typecode;
510 X
511 X       if ((c = skipws()) != '(')
512 X           goto nogood;
513 X       /*
514 X        * Scan off the tokens.
515 X        */
516 X       typecode = 0;
517 X       while ((c = skipws())) {
518 X           if ((c = macroid(c)) == EOF_CHAR || c == '\n')
519 X               goto nogood;                    /* End of line is a bug */
520 X           else if (c == '(') {                /* thing (*)() func ptr */
521 X               if (skipws() == '*'
522 X                && skipws() == ')') {          /* We found (*)         */
523 X                   if (skipws() != '(')        /* Let () be optional   */
524 X                       unget();
525 X                   else if (skipws() != ')')
526 X                       goto nogood;
527 X                   typecode |= T_FPTR;         /* Function pointer     */
528 X               }
529 X               else {                          /* Junk is a bug        */
530 X                   goto nogood;
531 X               }
532 X           }
533 X           else if (type[c] != LET)            /* Exit if not a type   */
534 X               break;
535 X           else if (!catenate()) {             /* Maybe combine tokens */
536 X               /*
537 X                * Look for this unexpandable token in basic_types.
538 X                * The code accepts "int long" as well as "long int"
539 X                * which is a minor bug as bugs go (and one shared with
540 X                * a lot of C compilers).
541 X                */
542 X               for (tp = basic_types; tp->name != NULLST; tp++) {
543 X                   if (streq(token, tp->name))
544 X                       break;
545 X               }
546 X               if (tp->name == NULLST) {
547 X                   cerror("#if sizeof, unknown type \"%s\"", token);
548 X                   return (OP_FAIL);
549 X               }
550 X               typecode |= tp->type;           /* Or in the type bit   */
551 X           }
552 X       }
553 X       /*
554 X        * We are at the end of the type scan.  Chew off '*' if necessary.
555 X        */
556 X       if (c == '*') {
557 X           typecode |= T_PTR;
558 X           c = skipws();
559 X       }
560 X       if (c == ')') {                         /* Last syntax check    */
561 X           for (testp = test_table; *testp != 0; testp++) {
562 X               if (!bittest(typecode & *testp)) {
563 X                   cerror("#if ... sizeof: illegal type combination", NULLST);
564 X                   return (OP_FAIL);
565 X               }
566 X           }
567 X           /*
568 X            * We assume that all function pointers are the same size:
569 X            *          sizeof (int (*)()) == sizeof (float (*)())
570 X            * We assume that signed and unsigned don't change the size:
571 X            *          sizeof (signed int) == (sizeof unsigned int)
572 X            */
573 X           if ((typecode & T_FPTR) != 0)       /* Function pointer     */
574 X               typecode = T_FPTR | T_PTR;
575 X           else {                              /* Var or var * datum   */
576 X               typecode &= ~(T_SIGNED | T_UNSIGNED);
577 X               if ((typecode & (T_SHORT | T_LONG)) != 0)
578 X                   typecode &= ~T_INT;
579 X           }
580 X           if ((typecode & ~T_PTR) == 0) {
581 X               cerror("#if sizeof() error, no type specified", NULLST);
582 X               return (OP_FAIL);
583 X           }
584 X           /*
585 X            * Exactly one bit (and possibly T_PTR) may be set.
586 X            */
587 X           for (sizp = size_table; sizp->bits != 0; sizp++) {
588 X               if ((typecode & ~T_PTR) == sizp->bits) {
589 X                   evalue = ((typecode & T_PTR) != 0)
590 X                       ? sizp->psize : sizp->size;
591 X                   return (DIG);
592 X               }
593 X           }                                   /* We shouldn't fail    */
594 X           cierror("#if ... sizeof: bug, unknown type code 0x%x", typecode);
595 X           return (OP_FAIL);
596 X       }
597 X
598 Xnogood:        unget();
599 X       cerror("#if ... sizeof() syntax error", NULLST);
600 X       return (OP_FAIL);
601 X}
602 X
603 XFILE_LOCAL int
604 Xbittest(value)
605 X/*
606 X * TRUE if value is zero or exactly one bit is set in value.
607 X */
608 X{
609 X#if (4096 & ~(-4096)) == 0
610 X       return ((value & ~(-value)) == 0);
611 X#else
612 X       /*
613 X        * Do it the hard way (for non 2's complement machines)
614 X        */
615 X       return (value == 0 || value ^ (value - 1) == (value * 2 - 1));
616 X#endif
617 X}
618 X\f
619 XFILE_LOCAL int
620 Xevalnum(c)
621 Xregister int   c;
622 X/*
623 X * Expand number for #if lexical analysis.  Note: evalnum recognizes
624 X * the unsigned suffix, but only returns a signed int value.
625 X */
626 X{
627 X       register int    value;
628 X       register int    base;
629 X       register int    c1;
630 X
631 X       if (c != '0')
632 X           base = 10;
633 X       else if ((c = cget()) == 'x' || c == 'X') {
634 X               base = 16;
635 X               c = cget();
636 X       }
637 X       else base = 8;
638 X       value = 0;
639 X       for (;;) {
640 X           c1 = c;
641 X           if (isascii(c) && isupper(c1))
642 X               c1 = tolower(c1);
643 X           if (c1 >= 'a')
644 X               c1 -= ('a' - 10);
645 X           else c1 -= '0';
646 X           if (c1 < 0 || c1 >= base)
647 X               break;
648 X           value *= base;
649 X           value += c1;
650 X           c = cget();
651 X       }
652 X       if (c == 'u' || c == 'U')       /* Unsigned nonsense            */
653 X           c = cget();
654 X       unget();
655 X       return (value);
656 X}
657 X\f
658 XFILE_LOCAL int
659 Xevalchar(skip)
660 Xint            skip;           /* TRUE if short-circuit evaluation     */
661 X/*
662 X * Get a character constant
663 X */
664 X{
665 X       register int    c;
666 X       register int    value;
667 X       register int    count;
668 X
669 X       instring = TRUE;
670 X       if ((c = cget()) == '\\') {
671 X           switch ((c = cget())) {
672 X           case 'a':                           /* New in Standard      */
673 X#if ('a' == '\a' || '\a' == ALERT)
674 X               value = ALERT;                  /* Use predefined value */
675 X#else
676 X               value = '\a';                   /* Use compiler's value */
677 X#endif
678 X               break;
679 X
680 X           case 'b':
681 X               value = '\b';
682 X               break;
683 X
684 X           case 'f':
685 X               value = '\f';
686 X               break;
687 X
688 X           case 'n':
689 X               value = '\n';
690 X               break;
691 X
692 X           case 'r':
693 X               value = '\r';
694 X               break;
695 X
696 X           case 't':
697 X               value = '\t';
698 X               break;
699 X
700 X           case 'v':                           /* New in Standard      */
701 X#if ('v' == '\v' || '\v' == VT)
702 X               value = VT;                     /* Use predefined value */
703 X#else
704 X               value = '\v';                   /* Use compiler's value */
705 X#endif
706 X               break;
707 X
708 X           case 'x':                           /* '\xFF'               */
709 X               count = 3;
710 X               value = 0;
711 X               while ((((c = get()) >= '0' && c <= '9')
712 X                    || (c >= 'a' && c <= 'f')
713 X                    || (c >= 'A' && c <= 'F'))
714 X                   && (--count >= 0)) {
715 X                       value *= 16;
716 X                       value += (c <= '9') ? (c - '0') : ((c & 0xF) + 9);
717 X               }
718 X               unget();
719 X               break;
720 X
721 X           default:
722 X               if (c >= '0' && c <= '7') {
723 X                   count = 3;
724 X                   value = 0;
725 X                   while (c >= '0' && c <= '7' && --count >= 0) {
726 X                       value *= 8;
727 X                       value += (c - '0');
728 X                       c = get();
729 X                   }
730 X                   unget();
731 X               }
732 X               else value = c;
733 X               break;
734 X           }
735 X       }
736 X       else if (c == '\'')
737 X           value = 0;
738 X       else value = c;
739 X       /*
740 X        * We warn on multi-byte constants and try to hack
741 X        * (big|little)endian machines.
742 X        */
743 X#if BIG_ENDIAN
744 X       count = 0;
745 X#endif
746 X       while ((c = get()) != '\'' && c != EOF_CHAR && c != '\n') {
747 X           if (!skip)
748 X               ciwarn("multi-byte constant '%c' isn't portable", c);
749 X#if BIG_ENDIAN
750 X           count += BITS_CHAR;
751 X           value += (c << count);
752 X#else
753 X           value <<= BITS_CHAR;
754 X           value += c;
755 X#endif
756 X       }
757 X       instring = FALSE;
758 X       return (value);
759 X}
760 X\f
761 XFILE_LOCAL int *
762 Xevaleval(valp, op, skip)
763 Xregister int   *valp;
764 Xint            op;
765 Xint            skip;           /* TRUE if short-circuit evaluation     */
766 X/*
767 X * Apply the argument operator to the data on the value stack.
768 X * One or two values are popped from the value stack and the result
769 X * is pushed onto the value stack.
770 X *
771 X * OP_COL is a special case.
772 X *
773 X * evaleval() returns the new pointer to the top of the value stack.
774 X */
775 X{
776 X       register int    v1, v2;
777 X
778 X       if (isbinary(op))
779 X           v2 = *--valp;
780 X       v1 = *--valp;
781 X#ifdef DEBUG_EVAL
782 X       printf("%s op %s", (isbinary(op)) ? "binary" : "unary",
783 X           opname[op]);
784 X       if (isbinary(op))
785 X           printf(", v2 = %d.", v2);
786 X       printf(", v1 = %d.\n", v1);
787 X#endif
788 X       switch (op) {
789 X       case OP_EOE:
790 X            break;
791 X
792 X       case OP_ADD:
793 X           v1 += v2;
794 X           break;
795 X
796 X       case OP_SUB:
797 X           v1 -= v2;
798 X           break;
799 X
800 X       case OP_MUL:
801 X           v1 *= v2;
802 X           break;
803 X
804 X       case OP_DIV:
805 X       case OP_MOD:
806 X           if (v2 == 0) {
807 X               if (!skip) {
808 X                   cwarn("%s by zero in #if, zero result assumed",
809 X                       (op == OP_DIV) ? "divide" : "mod");
810 X               }
811 X               v1 = 0;
812 X           }
813 X           else if (op == OP_DIV)
814 X               v1 /= v2;
815 X           else
816 X               v1 %= v2;
817 X           break;
818 X
819 X       case OP_ASL:
820 X           v1 <<= v2;
821 X           break;
822 X
823 X       case OP_ASR:
824 X           v1 >>= v2;
825 X           break;
826 X
827 X       case OP_AND:
828 X           v1 &= v2;
829 X           break;
830 X
831 X       case OP_OR:
832 X           v1 |= v2;
833 X           break;
834 X
835 X       case OP_XOR:
836 X           v1 ^= v2;
837 X           break;
838 X
839 X       case OP_EQ:
840 X           v1 = (v1 == v2);
841 X           break;
842 X
843 X       case OP_NE:
844 X           v1 = (v1 != v2);
845 X           break;
846 X
847 X       case OP_LT:
848 X           v1 = (v1 < v2);
849 X           break;
850 X
851 X       case OP_LE:
852 X           v1 = (v1 <= v2);
853 X           break;
854 X
855 X       case OP_GE:
856 X           v1 = (v1 >= v2);
857 X           break;
858 X
859 X       case OP_GT:
860 X           v1 = (v1 > v2);
861 X           break;
862 X
863 X       case OP_ANA:
864 X           v1 = (v1 && v2);
865 X           break;
866 X
867 X       case OP_ORO:
868 X           v1 = (v1 || v2);
869 X           break;
870 X
871 X       case OP_COL:
872 X           /*
873 X            * v1 has the "true" value, v2 the "false" value.
874 X            * The top of the value stack has the test.
875 X            */
876 X           v1 = (*--valp) ? v1 : v2;
877 X           break;
878 X
879 X       case OP_NEG:
880 X           v1 = (-v1);
881 X           break;
882 X
883 X       case OP_PLU:
884 X           break;
885 X
886 X       case OP_COM:
887 X           v1 = ~v1;
888 X           break;
889 X
890 X       case OP_NOT:
891 X           v1 = !v1;
892 X           break;
893 X
894 X       default:
895 X           cierror("#if bug, operand = %d.", op);
896 X           v1 = 0;
897 X       }
898 X       *valp++ = v1;
899 X       return (valp);
900 X}
901 X\f
902 X#ifdef DEBUG_EVAL
903 Xdumpstack(opstack, opp, value, valp)
904 XOPTAB          opstack[NEXP];  /* Operand stack                */
905 Xregister OPTAB *opp;           /* Operator stack               */
906 Xint            value[NEXP];    /* Value stack                  */
907 Xregister int   *valp;          /* -> value vector              */
908 X{
909 X       printf("index op prec skip name -- op stack at %s", infile->bptr);
910 X       while (opp > opstack) {
911 X           printf(" [%2d] %2d  %03o    %d %s\n", opp - opstack,
912 X               opp->op, opp->prec, opp->skip, opname[opp->op]);
913 X           opp--;
914 X       }
915 X       while (--valp >= value) {
916 X           printf("value[%d] = %d\n", (valp - value), *valp);
917 X       }
918 X}
919 X#endif
920 X
921 END-of-cpp5.c
922 echo x - cpp6.c
923 sed 's/^X//' >cpp6.c << 'END-of-cpp6.c'
924 X/*
925 X *                         C P P 6 . C
926 X *             S u p p o r t   R o u t i n e s
927 X *
928 X * Edit History
929 X * 25-May-84 MM                Added 8-bit support to type table.
930 X * 30-May-84 ARF       sharp() should output filename in quotes
931 X * 02-Aug-84 MM                Newline and #line hacking.  sharp() now in cpp1.c
932 X * 31-Aug-84 MM                USENET net.sources release
933 X * 11-Sep-84 ado/MM    Keepcomments, also line number pathological
934 X * 12-Sep-84 ado/MM    bug if comment changes to space and we unget later.
935 X * 03-Oct-84 gkr/MM    Fixed scannumber bug for '.e' (as in struct.element).
936 X * 04-Oct-84 MM                Added ungetstring() for token concatenation
937 X * 08-Oct-84 MM                Yet another attack on number scanning
938 X * 31-Oct-84 ado       Parameterized $ in identifiers
939 X *  2-Nov-84 MM                Token concatenation is messier than I thought
940 X *  6-Dec-84 MM                \<nl> is everywhere invisible.
941 X */
942 X
943 X#include       <stdio.h>
944 X#include       <ctype.h>
945 X#include       "cppdef.h"
946 X#include       "cpp.h"
947 X
948 X/*
949 X * skipnl()    skips over input text to the end of the line.
950 X * skipws()    skips over "whitespace" (spaces or tabs), but
951 X *             not skip over the end of the line.  It skips over
952 X *             TOK_SEP, however (though that shouldn't happen).
953 X * scanid()    reads the next token (C identifier) into token[].
954 X *             The caller has already read the first character of
955 X *             the identifier.  Unlike macroid(), the token is
956 X *             never expanded.
957 X * macroid()   reads the next token (C identifier) into token[].
958 X *             If it is a #defined macro, it is expanded, and
959 X *             macroid() returns TRUE, otherwise, FALSE.
960 X * catenate()  Does the dirty work of token concatenation, TRUE if it did.
961 X * scanstring()        Reads a string from the input stream, calling
962 X *             a user-supplied function for each character.
963 X *             This function may be output() to write the
964 X *             string to the output file, or save() to save
965 X *             the string in the work buffer.
966 X * scannumber()        Reads a C numeric constant from the input stream,
967 X *             calling the user-supplied function for each
968 X *             character.  (output() or save() as noted above.)
969 X * save()      Save one character in the work[] buffer.
970 X * savestring()        Saves a string in malloc() memory.
971 X * getfile()   Initialize a new FILEINFO structure, called when
972 X *             #include opens a new file, or a macro is to be
973 X *             expanded.
974 X * getmem()    Get a specified number of bytes from malloc memory.
975 X * output()    Write one character to stdout (calling putchar) --
976 X *             implemented as a function so its address may be
977 X *             passed to scanstring() and scannumber().
978 X * lookid()    Scans the next token (identifier) from the input
979 X *             stream.  Looks for it in the #defined symbol table.
980 X *             Returns a pointer to the definition, if found, or NULL
981 X *             if not present.  The identifier is stored in token[].
982 X * defnedel()  Define enter/delete subroutine.  Updates the
983 X *             symbol table.
984 X * get()       Read the next byte from the current input stream,
985 X *             handling end of (macro/file) input and embedded
986 X *             comments appropriately.  Note that the global
987 X *             instring is -- essentially -- a parameter to get().
988 X * cget()      Like get(), but skip over TOK_SEP.
989 X * unget()     Push last gotten character back on the input stream.
990 X * cerror(), cwarn(), cfatal(), cierror(), ciwarn()
991 X *             These routines format an print messages to the user.
992 X *             cerror & cwarn take a format and a single string argument.
993 X *             cierror & ciwarn take a format and a single int (char) argument.
994 X *             cfatal takes a format and a single string argument.
995 X */
996 X\f
997 X/*
998 X * This table must be rewritten for a non-Ascii machine.
999 X *
1000 X * Note that several "non-visible" characters have special meaning:
1001 X * Hex 1D DEF_MAGIC -- a flag to prevent #define recursion.
1002 X * Hex 1E TOK_SEP   -- a delimiter for token concatenation
1003 X * Hex 1F COM_SEP   -- a zero-width whitespace for comment concatenation
1004 X */
1005 X#if TOK_SEP != 0x1E || COM_SEP != 0x1F || DEF_MAGIC != 0x1D
1006 X       << error type table isn't correct >>
1007 X#endif
1008 X
1009 X#if OK_DOLLAR
1010 X#define        DOL     LET
1011 X#else
1012 X#define        DOL     000
1013 X#endif
1014 X
1015 Xchar type[256] = {             /* Character type codes    Hex          */
1016 X   END,   000,   000,   000,   000,   000,   000,   000, /* 00         */
1017 X   000,   SPA,   000,   000,   000,   000,   000,   000, /* 08         */
1018 X   000,   000,   000,   000,   000,   000,   000,   000, /* 10         */
1019 X   000,   000,   000,   000,   000,   LET,   000,   SPA, /* 18         */
1020 X   SPA,OP_NOT,   QUO,   000,   DOL,OP_MOD,OP_AND,   QUO, /* 20  !"#$%&'        */
1021 XOP_LPA,OP_RPA,OP_MUL,OP_ADD,   000,OP_SUB,   DOT,OP_DIV, /* 28 ()*+,-./        */
1022 X   DIG,   DIG,   DIG,   DIG,   DIG,   DIG,   DIG,   DIG, /* 30 01234567        */
1023 X   DIG,   DIG,OP_COL,   000, OP_LT, OP_EQ, OP_GT,OP_QUE, /* 38 89:;<=>?        */
1024 X   000,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 40 @ABCDEFG        */
1025 X   LET,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 48 HIJKLMNO        */
1026 X   LET,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 50 PQRSTUVW        */
1027 X   LET,   LET,   LET,   000,   BSH,   000,OP_XOR,   LET, /* 58 XYZ[\]^_        */
1028 X   000,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 60 `abcdefg        */
1029 X   LET,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 68 hijklmno        */
1030 X   LET,   LET,   LET,   LET,   LET,   LET,   LET,   LET, /* 70 pqrstuvw        */
1031 X   LET,   LET,   LET,   000, OP_OR,   000,OP_NOT,   000, /* 78 xyz{|}~ */
1032 X   000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF */
1033 X   000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF */
1034 X   000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF */
1035 X   000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF */
1036 X   000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF */
1037 X   000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF */
1038 X   000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF */
1039 X   000,   000,   000,   000,   000,   000,   000,   000, /*   80 .. FF */
1040 X};
1041 X\f
1042 Xskipnl()
1043 X/*
1044 X * Skip to the end of the current input line.
1045 X */
1046 X{
1047 X       register int            c;
1048 X
1049 X       do {                            /* Skip to newline      */
1050 X           c = get();
1051 X       } while (c != '\n' && c != EOF_CHAR);
1052 X}
1053 X
1054 Xint
1055 Xskipws()
1056 X/*
1057 X * Skip over whitespace
1058 X */
1059 X{
1060 X       register int            c;
1061 X
1062 X       do {                            /* Skip whitespace      */
1063 X           c = get();
1064 X#if COMMENT_INVISIBLE
1065 X       } while (type[c] == SPA || c == COM_SEP);
1066 X#else
1067 X       } while (type[c] == SPA);
1068 X#endif
1069 X       return (c);
1070 X}
1071 X\f
1072 Xscanid(c)
1073 Xregister int   c;                              /* First char of id     */
1074 X/*
1075 X * Get the next token (an id) into the token buffer.
1076 X * Note: this code is duplicated in lookid().
1077 X * Change one, change both.
1078 X */
1079 X{
1080 X       register char   *bp;
1081 X
1082 X       if (c == DEF_MAGIC)                     /* Eat the magic token  */
1083 X           c = get();                          /* undefiner.           */
1084 X       bp = token;
1085 X       do {
1086 X           if (bp < &token[IDMAX])             /* token dim is IDMAX+1 */
1087 X               *bp++ = c;
1088 X           c = get();
1089 X       } while (type[c] == LET || type[c] == DIG);
1090 X       unget();
1091 X       *bp = EOS;
1092 X}
1093 X
1094 Xint
1095 Xmacroid(c)
1096 Xregister int           c;
1097 X/*
1098 X * If c is a letter, scan the id.  if it's #defined, expand it and scan
1099 X * the next character and try again.
1100 X *
1101 X * Else, return the character.  If type[c] is a LET, the token is in token.
1102 X */
1103 X{
1104 X       register DEFBUF *dp;
1105 X
1106 X       if (infile != NULL && infile->fp != NULL)
1107 X           recursion = 0;
1108 X       while (type[c] == LET && (dp = lookid(c)) != NULL) {
1109 X           expand(dp);
1110 X           c = get();
1111 X       }
1112 X       return (c);
1113 X}
1114 X\f
1115 Xint
1116 Xcatenate()
1117 X/*
1118 X * A token was just read (via macroid).
1119 X * If the next character is TOK_SEP, concatenate the next token
1120 X * return TRUE -- which should recall macroid after refreshing
1121 X * macroid's argument.  If it is not TOK_SEP, unget() the character
1122 X * and return FALSE.
1123 X */
1124 X{
1125 X       register int            c;
1126 X       register char           *token1;
1127 X
1128 X#if OK_CONCAT
1129 X       if (get() != TOK_SEP) {                 /* Token concatenation  */
1130 X           unget();
1131 X           return (FALSE);
1132 X       }
1133 X       else {
1134 X           token1 = savestring(token);         /* Save first token     */
1135 X           c = macroid(get());                 /* Scan next token      */
1136 X           switch(type[c]) {                   /* What was it?         */
1137 X           case LET:                           /* An identifier, ...   */
1138 X               if (strlen(token1) + strlen(token) >= NWORK)
1139 X                   cfatal("work buffer overflow doing %s #", token1);
1140 X               sprintf(work, "%s%s", token1, token);
1141 X               break;
1142 X
1143 X           case DIG:                           /* A digit string       */
1144 X               strcpy(work, token1);
1145 X               workp = work + strlen(work);
1146 X               do {
1147 X                   save(c);
1148 X               } while ((c = get()) != TOK_SEP);
1149 X               /*
1150 X                * The trailing TOK_SEP is no longer needed.
1151 X                */
1152 X               save(EOS);
1153 X               break;
1154 X
1155 X           default:                            /* An error, ...        */
1156 X               if (isprint(c))
1157 X                   cierror("Strange character '%c' after #", c);
1158 X               else
1159 X                   cierror("Strange character (%d.) after #", c);
1160 X               strcpy(work, token1);
1161 X               unget();
1162 X               break;
1163 X           }
1164 X           /*
1165 X            * work has the concatenated token and token1 has
1166 X            * the first token (no longer needed).  Unget the
1167 X            * new (concatenated) token after freeing token1.
1168 X            * Finally, setup to read the new token.
1169 X            */
1170 X           free(token1);                       /* Free up memory       */
1171 X           ungetstring(work);                  /* Unget the new thing, */
1172 X           return (TRUE);
1173 X       }
1174 X#else
1175 X       return (FALSE);                         /* Not supported        */
1176 X#endif
1177 X}
1178 X\f
1179 Xint
1180 Xscanstring(delim, outfun)
1181 Xregister int   delim;                  /* ' or "                       */
1182 Xint            (*outfun)();            /* Output function              */
1183 X/*
1184 X * Scan off a string.  Warning if terminated by newline or EOF.
1185 X * outfun() outputs the character -- to a buffer if in a macro.
1186 X * TRUE if ok, FALSE if error.
1187 X */
1188 X{
1189 X       register int            c;
1190 X
1191 X       instring = TRUE;                /* Don't strip comments         */
1192 X       (*outfun)(delim);
1193 X       while ((c = get()) != delim
1194 X            && c != '\n'
1195 X            && c != EOF_CHAR) {
1196 X           (*outfun)(c);
1197 X           if (c == '\\')
1198 X               (*outfun)(get());
1199 X       }
1200 X       instring = FALSE;
1201 X       if (c == delim) {
1202 X           (*outfun)(c);
1203 X           return (TRUE);
1204 X       }
1205 X       else {
1206 X           cerror("Unterminated string", NULLST);
1207 X           unget();
1208 X           return (FALSE);
1209 X       }
1210 X}
1211 X\f
1212 Xscannumber(c, outfun)
1213 Xregister int   c;                              /* First char of number */
1214 Xregister int   (*outfun)();                    /* Output/store func    */
1215 X/*
1216 X * Process a number.  We know that c is from 0 to 9 or dot.
1217 X * Algorithm from Dave Conroy's Decus C.
1218 X */
1219 X{
1220 X       register int    radix;                  /* 8, 10, or 16         */
1221 X       int             expseen;                /* 'e' seen in floater  */
1222 X       int             signseen;               /* '+' or '-' seen      */
1223 X       int             octal89;                /* For bad octal test   */
1224 X       int             dotflag;                /* TRUE if '.' was seen */
1225 X
1226 X       expseen = FALSE;                        /* No exponent seen yet */
1227 X       signseen = TRUE;                        /* No +/- allowed yet   */
1228 X       octal89 = FALSE;                        /* No bad octal yet     */
1229 X       radix = 10;                             /* Assume decimal       */
1230 X       if ((dotflag = (c == '.')) != FALSE) {  /* . something?         */
1231 X           (*outfun)('.');                     /* Always out the dot   */
1232 X           if (type[(c = get())] != DIG) {     /* If not a float numb, */
1233 X               unget();                        /* Rescan strange char  */
1234 X               return;                         /* All done for now     */
1235 X           }
1236 X       }                                       /* End of float test    */
1237 X       else if (c == '0') {                    /* Octal or hex?        */
1238 X           (*outfun)(c);                       /* Stuff initial zero   */
1239 X           radix = 8;                          /* Assume it's octal    */
1240 X           c = get();                          /* Look for an 'x'      */
1241 X           if (c == 'x' || c == 'X') {         /* Did we get one?      */
1242 X               radix = 16;                     /* Remember new radix   */
1243 X               (*outfun)(c);                   /* Stuff the 'x'        */
1244 X               c = get();                      /* Get next character   */
1245 X           }
1246 X       }
1247 X       for (;;) {                              /* Process curr. char.  */
1248 X           /*
1249 X            * Note that this algorithm accepts "012e4" and "03.4"
1250 X            * as legitimate floating-point numbers.
1251 X            */
1252 X           if (radix != 16 && (c == 'e' || c == 'E')) {
1253 X               if (expseen)                    /* Already saw 'E'?     */
1254 X                   break;                      /* Exit loop, bad nbr.  */
1255 X               expseen = TRUE;                 /* Set exponent seen    */
1256 X               signseen = FALSE;               /* We can read '+' now  */
1257 X               radix = 10;                     /* Decimal exponent     */
1258 X           }
1259 X           else if (radix != 16 && c == '.') {
1260 X               if (dotflag)                    /* Saw dot already?     */
1261 X                   break;                      /* Exit loop, two dots  */
1262 X               dotflag = TRUE;                 /* Remember the dot     */
1263 X               radix = 10;                     /* Decimal fraction     */
1264 X           }
1265 X           else if (c == '+' || c == '-') {    /* 1.0e+10              */
1266 X               if (signseen)                   /* Sign in wrong place? */
1267 X                   break;                      /* Exit loop, not nbr.  */
1268 X               /* signseen = TRUE; */          /* Remember we saw it   */
1269 X           }
1270 X           else {                              /* Check the digit      */
1271 X               switch (c) {
1272 X               case '8': case '9':             /* Sometimes wrong      */
1273 X                   octal89 = TRUE;             /* Do check later       */
1274 X               case '0': case '1': case '2': case '3':
1275 X               case '4': case '5': case '6': case '7':
1276 X                   break;                      /* Always ok            */
1277 X
1278 X               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1279 X               case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1280 X                   if (radix == 16)            /* Alpha's are ok only  */
1281 X                       break;                  /* if reading hex.      */
1282 X               default:                        /* At number end        */
1283 X                   goto done;                  /* Break from for loop  */
1284 X               }                               /* End of switch        */
1285 X           }                                   /* End general case     */
1286 X           (*outfun)(c);                       /* Accept the character */
1287 X           signseen = TRUE;                    /* Don't read sign now  */
1288 X           c = get();                          /* Read another char    */
1289 X       }                                       /* End of scan loop     */
1290 X       /*
1291 X        * When we break out of the scan loop, c contains the first
1292 X        * character (maybe) not in the number.  If the number is an
1293 X        * integer, allow a trailing 'L' for long and/or a trailing 'U'
1294 X        * for unsigned.  If not those, push the trailing character back
1295 X        * on the input stream.  Floating point numbers accept a trailing
1296 X        * 'L' for "long double".
1297 X        */
1298 Xdone:  if (dotflag || expseen) {               /* Floating point?      */
1299 X           if (c == 'l' || c == 'L') {
1300 X               (*outfun)(c);
1301 X               c = get();                      /* Ungotten later       */
1302 X           }
1303 X       }
1304 X       else {                                  /* Else it's an integer */
1305 X           /*
1306 X            * We know that dotflag and expseen are both zero, now:
1307 X            * dotflag signals "saw 'L'", and
1308 X            * expseen signals "saw 'U'".
1309 X            */
1310 X           for (;;) {
1311 X               switch (c) {
1312 X               case 'l':
1313 X               case 'L':
1314 X                   if (dotflag)
1315 X                       goto nomore;
1316 X                   dotflag = TRUE;
1317 X                   break;
1318 X
1319 X               case 'u':
1320 X               case 'U':
1321 X                   if (expseen)
1322 X                       goto nomore;
1323 X                   expseen = TRUE;
1324 X                   break;
1325 X
1326 X               default:
1327 X                   goto nomore;
1328 X               }
1329 X               (*outfun)(c);                   /* Got 'L' or 'U'.      */
1330 X               c = get();                      /* Look at next, too.   */
1331 X           }
1332 X       }
1333 Xnomore:        unget();                                /* Not part of a number */
1334 X       if (octal89 && radix == 8)
1335 X           cwarn("Illegal digit in octal number", NULLST);
1336 X}
1337 X\f
1338 Xsave(c)
1339 Xregister int   c;
1340 X{
1341 X       if (workp >= &work[NWORK])
1342 X           cfatal("Work buffer overflow", NULLST);
1343 X       else *workp++ = c;
1344 X}
1345 X
1346 Xchar *
1347 Xsavestring(text)
1348 Xchar           *text;
1349 X/*
1350 X * Store a string into free memory.
1351 X */
1352 X{
1353 X       register char   *result;
1354 X
1355 X       result = getmem(strlen(text) + 1);
1356 X       strcpy(result, text);
1357 X       return (result);
1358 X}
1359 X
1360 XFILEINFO       *
1361 Xgetfile(bufsize, name)
1362 Xint            bufsize;                /* Line or define buffer size   */
1363 Xchar           *name;                  /* File or macro name string    */
1364 X/*
1365 X * Common FILEINFO buffer initialization for a new file or macro.
1366 X */
1367 X{
1368 X       register FILEINFO       *file;
1369 X       register int            size;
1370 X
1371 X       size = strlen(name);                    /* File/macro name      */
1372 X       file = (FILEINFO *) getmem(sizeof (FILEINFO) + bufsize + size);
1373 X       file->parent = infile;                  /* Chain files together */
1374 X       file->fp = NULL;                        /* No file yet          */
1375 X       file->filename = savestring(name);      /* Save file/macro name */
1376 X       file->progname = NULL;                  /* No #line seen yet    */
1377 X       file->unrecur = 0;                      /* No macro fixup       */
1378 X       file->bptr = file->buffer;              /* Initialize line ptr  */
1379 X       file->buffer[0] = EOS;                  /* Force first read     */
1380 X       file->line = 0;                         /* (Not used just yet)  */
1381 X       if (infile != NULL)                     /* If #include file     */
1382 X           infile->line = line;                /* Save current line    */
1383 X       infile = file;                          /* New current file     */
1384 X       line = 1;                               /* Note first line      */
1385 X       return (file);                          /* All done.            */
1386 X}
1387 X
1388 Xchar *
1389 Xgetmem(size)
1390 Xint            size;
1391 X/*
1392 X * Get a block of free memory.
1393 X */
1394 X{
1395 X       register char   *result;
1396 X       extern char     *malloc();
1397 X
1398 X       if ((result = malloc((unsigned) size)) == NULL)
1399 X           cfatal("Out of memory", NULLST);
1400 X       return (result);
1401 X}
1402 X\f
1403 X/*
1404 X *                     C P P   S y m b o l   T a b l e s
1405 X */
1406 X
1407 X/*
1408 X * SBSIZE defines the number of hash-table slots for the symbol table.
1409 X * It must be a power of 2.
1410 X */
1411 X#ifndef        SBSIZE
1412 X#define        SBSIZE  64
1413 X#endif
1414 X#define        SBMASK  (SBSIZE - 1)
1415 X#if (SBSIZE ^ SBMASK) != ((SBSIZE * 2) - 1)
1416 X       << error, SBSIZE must be a power of 2 >>
1417 X#endif
1418 X
1419 Xstatic DEFBUF  *symtab[SBSIZE];        /* Symbol table queue headers   */
1420 X
1421 XDEFBUF *
1422 Xlookid(c)
1423 Xint    c;                              /* First character of token     */
1424 X/*
1425 X * Look for the next token in the symbol table.  Returns token in "token".
1426 X * If found, returns the table pointer;  Else returns NULL.
1427 X */
1428 X{
1429 X       register int            nhash;
1430 X       register DEFBUF         *dp;
1431 X       register char           *np;
1432 X       int                     temp;
1433 X       int                     isrecurse;      /* For #define foo foo  */
1434 X
1435 X       np = token;
1436 X       nhash = 0;
1437 X       if ((isrecurse = (c == DEF_MAGIC)))     /* If recursive macro   */
1438 X           c = get();                          /* hack, skip DEF_MAGIC */
1439 X       do {
1440 X           if (np < &token[IDMAX]) {           /* token dim is IDMAX+1 */
1441 X               *np++ = c;                      /* Store token byte     */
1442 X               nhash += c;                     /* Update hash value    */
1443 X           }
1444 X           c = get();                          /* And get another byte */
1445 X       } while (type[c] == LET || type[c] == DIG);
1446 X       unget();                                /* Rescan terminator    */
1447 X       *np = EOS;                              /* Terminate token      */
1448 X       if (isrecurse)                          /* Recursive definition */
1449 X           return (NULL);                      /* undefined just now   */
1450 X       nhash += (np - token);                  /* Fix hash value       */
1451 X       dp = symtab[nhash & SBMASK];            /* Starting bucket      */
1452 X       while (dp != (DEFBUF *) NULL) {         /* Search symbol table  */
1453 X           if (dp->hash == nhash               /* Fast precheck        */
1454 X            && (temp = strcmp(dp->name, token)) >= 0)
1455 X               break;
1456 X           dp = dp->link;                      /* Nope, try next one   */
1457 X       }
1458 X       return ((temp == 0) ? dp : NULL);
1459 X}
1460 X\f
1461 XDEFBUF *
1462 Xdefendel(name, delete)
1463 Xchar           *name;
1464 Xint            delete;                 /* TRUE to delete a symbol      */
1465 X/*
1466 X * Enter this name in the lookup table (delete = FALSE)
1467 X * or delete this name (delete = TRUE).
1468 X * Returns a pointer to the define block (delete = FALSE)
1469 X * Returns NULL if the symbol wasn't defined (delete = TRUE).
1470 X */
1471 X{
1472 X       register DEFBUF         *dp;
1473 X       register DEFBUF         **prevp;
1474 X       register char           *np;
1475 X       int                     nhash;
1476 X       int                     temp;
1477 X       int                     size;
1478 X
1479 X       for (nhash = 0, np = name; *np != EOS;)
1480 X           nhash += *np++;
1481 X       size = (np - name);
1482 X       nhash += size;
1483 X       prevp = &symtab[nhash & SBMASK];
1484 X       while ((dp = *prevp) != (DEFBUF *) NULL) {
1485 X           if (dp->hash == nhash
1486 X            && (temp = strcmp(dp->name, name)) >= 0) {
1487 X               if (temp > 0)
1488 X                   dp = NULL;                  /* Not found            */
1489 X               else {
1490 X                   *prevp = dp->link;          /* Found, unlink and    */
1491 X                   if (dp->repl != NULL)       /* Free the replacement */
1492 X                       free(dp->repl);         /* if any, and then     */
1493 X                   free((char *) dp);          /* Free the symbol      */
1494 X               }
1495 X               break;
1496 X           }
1497 X           prevp = &dp->link;
1498 X       }
1499 X       if (!delete) {
1500 X           dp = (DEFBUF *) getmem(sizeof (DEFBUF) + size);
1501 X           dp->link = *prevp;
1502 X           *prevp = dp;
1503 X           dp->hash = nhash;
1504 X           dp->repl = NULL;
1505 X           dp->nargs = 0;
1506 X           strcpy(dp->name, name);
1507 X       }
1508 X       return (dp);
1509 X}
1510 X\f
1511 X#if DEBUG
1512 X
1513 Xdumpdef(why)
1514 Xchar           *why;
1515 X{
1516 X       register DEFBUF         *dp;
1517 X       register DEFBUF         **syp;
1518 X
1519 X       printf("CPP symbol table dump %s\n", why);
1520 X       for (syp = symtab; syp < &symtab[SBSIZE]; syp++) {
1521 X           if ((dp = *syp) != (DEFBUF *) NULL) {
1522 X               printf("symtab[%d]\n", (syp - symtab));
1523 X               do {
1524 X                   dumpadef((char *) NULL, dp);
1525 X               } while ((dp = dp->link) != (DEFBUF *) NULL);
1526 X           }
1527 X       }
1528 X}
1529 X
1530 Xdumpadef(why, dp)
1531 Xchar           *why;                   /* Notation                     */
1532 Xregister DEFBUF        *dp;
1533 X{
1534 X       register char           *cp;
1535 X       register int            c;
1536 X
1537 X       printf(" \"%s\" [%d]", dp->name, dp->nargs);
1538 X       if (why != NULL)
1539 X           printf(" (%s)", why);
1540 X       if (dp->repl != NULL) {
1541 X           printf(" => ");
1542 X           for (cp = dp->repl; (c = *cp++ & 0xFF) != EOS;) {
1543 X               if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC))
1544 X                   printf("<%d>", c - MAC_PARM);
1545 X               else if (isprint(c) || c == '\n' || c == '\t')
1546 X                   putchar(c);
1547 X               else if (c < ' ')
1548 X                   printf("<^%c>", c + '@');
1549 X               else
1550 X                   printf("<\\0%o>", c);
1551 X           }
1552 X       }
1553 X       else {
1554 X           printf(", no replacement.");
1555 X       }
1556 X       putchar('\n');
1557 X}
1558 X#endif
1559 X\f
1560 X/*
1561 X *                     G E T
1562 X */
1563 X
1564 Xint
1565 Xget()
1566 X/*
1567 X * Return the next character from a macro or the current file.
1568 X * Handle end of file from #include files.
1569 X */
1570 X{
1571 X       register int            c;
1572 X       register FILEINFO       *file;
1573 X       register int            popped;         /* Recursion fixup      */
1574 X
1575 X       popped = 0;
1576 Xget_from_file:
1577 X       if ((file = infile) == NULL)
1578 X           return (EOF_CHAR);
1579 Xnewline:
1580 X#if 0
1581 X       printf("get(%s), recursion %d, line %d, bptr = %d, buffer \"%s\"\n",
1582 X           file->filename, recursion, line,
1583 X           file->bptr - file->buffer, file->buffer);
1584 X#endif
1585 X       /*
1586 X        * Read a character from the current input line or macro.
1587 X        * At EOS, either finish the current macro (freeing temp.
1588 X        * storage) or read another line from the current input file.
1589 X        * At EOF, exit the current file (#include) or, at EOF from
1590 X        * the cpp input file, return EOF_CHAR to finish processing.
1591 X        */
1592 X       if ((c = *file->bptr++ & 0xFF) == EOS) {
1593 X           /*
1594 X            * Nothing in current line or macro.  Get next line (if
1595 X            * input from a file), or do end of file/macro processing.
1596 X            * In the latter case, jump back to restart from the top.
1597 X            */
1598 X           if (file->fp == NULL) {             /* NULL if macro        */
1599 X               popped++;
1600 X               recursion -= file->unrecur;
1601 X               if (recursion < 0)
1602 X                   recursion = 0;
1603 X               infile = file->parent;          /* Unwind file chain    */
1604 X           }
1605 X           else {                              /* Else get from a file */
1606 X               if ((file->bptr = fgets(file->buffer, NBUFF, file->fp))
1607 X                       != NULL) {
1608 X#if DEBUG
1609 X                   if (debug > 1) {            /* Dump it to stdout    */
1610 X                       printf("\n#line %d (%s), %s",
1611 X                           line, file->filename, file->buffer);
1612 X                   }
1613 X#endif
1614 X                   goto newline;               /* process the line     */
1615 X               }
1616 X               else {
1617 X                   fclose(file->fp);           /* Close finished file  */
1618 X                   if ((infile = file->parent) != NULL) {
1619 X                       /*
1620 X                        * There is an "ungotten" newline in the current
1621 X                        * infile buffer (set there by doinclude() in
1622 X                        * cpp1.c).  Thus, we know that the mainline code
1623 X                        * is skipping over blank lines and will do a
1624 X                        * #line at its convenience.
1625 X                        */
1626 X                       wrongline = TRUE;       /* Need a #line now     */
1627 X                   }
1628 X               }
1629 X           }
1630 X           /*
1631 X            * Free up space used by the (finished) file or macro and
1632 X            * restart input from the parent file/macro, if any.
1633 X            */
1634 X           free(file->filename);               /* Free name and        */
1635 X           if (file->progname != NULL)         /* if a #line was seen, */
1636 X               free(file->progname);           /* free it, too.        */
1637 X           free((char *) file);                /* Free file space      */
1638 X           if (infile == NULL)                 /* If at end of file    */
1639 X               return (EOF_CHAR);              /* Return end of file   */
1640 X           line = infile->line;                /* Reset line number    */
1641 X           goto get_from_file;                 /* Get from the top.    */
1642 X       }
1643 X       /*
1644 X        * Common processing for the new character.
1645 X        */
1646 X       if (c == DEF_MAGIC && file->fp != NULL) /* Don't allow delete   */
1647 X           goto newline;                       /* from a file          */
1648 X       if (file->parent != NULL) {             /* Macro or #include    */
1649 X           if (popped != 0)
1650 X               file->parent->unrecur += popped;
1651 X           else {
1652 X               recursion -= file->parent->unrecur;
1653 X               if (recursion < 0)
1654 X                   recursion = 0;
1655 X               file->parent->unrecur = 0;
1656 X           }
1657 X       }
1658 X       if (c == '\n')                          /* Maintain current     */
1659 X           ++line;                             /* line counter         */
1660 X       if (instring)                           /* Strings just return  */
1661 X           return (c);                         /* the character.       */
1662 X       else if (c == '/') {                    /* Comment?             */
1663 X           instring = TRUE;                    /* So get() won't loop  */
1664 X           if ((c = get()) != '*') {           /* Next byte '*'?       */
1665 X               instring = FALSE;               /* Nope, no comment     */
1666 X               unget();                        /* Push the char. back  */
1667 X               return ('/');                   /* Return the slash     */
1668 X           }
1669 X           if (keepcomments) {                 /* If writing comments  */
1670 X               putchar('/');                   /* Write out the        */
1671 X               putchar('*');                   /*   initializer        */
1672 X           }
1673 X           for (;;) {                          /* Eat a comment        */
1674 X               c = get();
1675 Xtest:          if (keepcomments && c != EOF_CHAR)
1676 X                   cput(c);
1677 X               switch (c) {
1678 X               case EOF_CHAR:
1679 X                   cerror("EOF in comment", NULLST);
1680 X                   return (EOF_CHAR);
1681 X
1682 X               case '/':
1683 X                   if ((c = get()) != '*')     /* Don't let comments   */
1684 X                       goto test;              /* Nest.                */
1685 X#ifdef VERBOSE
1686 X                   cwarn("Nested comments", NULLST);
1687 X#endif
1688 X                                               /* Fall into * stuff    */
1689 X               case '*':
1690 X                   if ((c = get()) != '/')     /* If comment doesn't   */
1691 X                       goto test;              /* end, look at next    */
1692 X                   instring = FALSE;           /* End of comment,      */
1693 X                   if (keepcomments) {         /* Put out the comment  */
1694 X                       cput(c);                /* terminator, too      */
1695 X                   }
1696 X                   /*
1697 X                    * A comment is syntactically "whitespace" --
1698 X                    * however, there are certain strange sequences
1699 X                    * such as
1700 X                    *          #define foo(x)  (something)
1701 X                    *                  foo|* comment *|(123)
1702 X                    *       these are '/' ^           ^
1703 X                    * where just returning space (or COM_SEP) will cause
1704 X                    * problems.  This can be "fixed" by overwriting the
1705 X                    * '/' in the input line buffer with ' ' (or COM_SEP)
1706 X                    * but that may mess up an error message.
1707 X                    * So, we peek ahead -- if the next character is
1708 X                    * "whitespace" we just get another character, if not,
1709 X                    * we modify the buffer.  All in the name of purity.
1710 X                    */
1711 X                   if (*file->bptr == '\n'
1712 X                    || type[*file->bptr & 0xFF] == SPA)
1713 X                       goto newline;
1714 X#if COMMENT_INVISIBLE
1715 X                   /*
1716 X                    * Return magic (old-fashioned) syntactic space.
1717 X                    */
1718 X                   return ((file->bptr[-1] = COM_SEP));
1719 X#else
1720 X                   return ((file->bptr[-1] = ' '));
1721 X#endif
1722 X
1723 X               case '\n':                      /* we'll need a #line   */
1724 X                   if (!keepcomments)
1725 X                       wrongline = TRUE;       /* later...             */
1726 X               default:                        /* Anything else is     */
1727 X                   break;                      /* Just a character     */
1728 X               }                               /* End switch           */
1729 X           }                                   /* End comment loop     */
1730 X       }                                       /* End if in comment    */
1731 X       else if (!inmacro && c == '\\') {       /* If backslash, peek   */
1732 X           if ((c = get()) == '\n') {          /* for a <nl>.  If so,  */
1733 X               wrongline = TRUE;
1734 X               goto newline;
1735 X           }
1736 X           else {                              /* Backslash anything   */
1737 X               unget();                        /* Get it later         */
1738 X               return ('\\');                  /* Return the backslash */
1739 X           }
1740 X       }
1741 X       else if (c == '\f' || c == VT)          /* Form Feed, Vertical  */
1742 X           c = ' ';                            /* Tab are whitespace   */
1743 X       return (c);                             /* Just return the char */
1744 X}
1745 X\f
1746 Xunget()
1747 X/*
1748 X * Backup the pointer to reread the last character.  Fatal error
1749 X * (code bug) if we backup too far.  unget() may be called,
1750 X * without problems, at end of file.  Only one character may
1751 X * be ungotten.  If you need to unget more, call ungetstring().
1752 X */
1753 X{
1754 X       register FILEINFO       *file;
1755 X
1756 X       if ((file = infile) == NULL)
1757 X           return;                     /* Unget after EOF              */
1758 X       if (--file->bptr < file->buffer)
1759 X           cfatal("Too much pushback", NULLST);
1760 X       if (*file->bptr == '\n')        /* Ungetting a newline?         */
1761 X           --line;                     /* Unget the line number, too   */
1762 X}
1763 X
1764 Xungetstring(text)
1765 Xchar           *text;
1766 X/*
1767 X * Push a string back on the input stream.  This is done by treating
1768 X * the text as if it were a macro.
1769 X */
1770 X{
1771 X       register FILEINFO       *file;
1772 X       extern FILEINFO         *getfile();
1773 X
1774 X       file = getfile(strlen(text) + 1, "");
1775 X       strcpy(file->buffer, text);
1776 X}
1777 X
1778 Xint
1779 Xcget()
1780 X/*
1781 X * Get one character, absorb "funny space" after comments or
1782 X * token concatenation
1783 X */
1784 X{
1785 X       register int    c;
1786 X
1787 X       do {
1788 X           c = get();
1789 X#if COMMENT_INVISIBLE
1790 X       } while (c == TOK_SEP || c == COM_SEP);
1791 X#else
1792 X       } while (c == TOK_SEP);
1793 X#endif
1794 X       return (c);
1795 X}
1796 X\f
1797 X/*
1798 X * Error messages and other hacks.  The first byte of severity
1799 X * is 'S' for string arguments and 'I' for int arguments.  This
1800 X * is needed for portability with machines that have int's that
1801 X * are shorter than  char *'s.
1802 X */
1803 X
1804 Xstatic
1805 Xdomsg(severity, format, arg)
1806 Xchar           *severity;              /* "Error", "Warning", "Fatal"  */
1807 Xchar           *format;                /* Format for the error message */
1808 Xchar           *arg;                   /* Something for the message    */
1809 X/*
1810 X * Print filenames, macro names, and line numbers for error messages.
1811 X */
1812 X{
1813 X       register char           *tp;
1814 X       register FILEINFO       *file;
1815 X
1816 X       fprintf(stderr, "%sline %d, %s: ", MSG_PREFIX, line, &severity[1]);
1817 X       if (*severity == 'S')
1818 X           fprintf(stderr, format, arg);
1819 X       else
1820 X           fprintf(stderr, format, (int) arg);
1821 X       putc('\n', stderr);
1822 X       if ((file = infile) == NULL)
1823 X           return;                             /* At end of file       */
1824 X       if (file->fp != NULL) {
1825 X           tp = file->buffer;                  /* Print current file   */
1826 X           fprintf(stderr, "%s", tp);          /* name, making sure    */
1827 X           if (tp[strlen(tp) - 1] != '\n')     /* there's a newline    */
1828 X               putc('\n', stderr);
1829 X       }
1830 X       while ((file = file->parent) != NULL) { /* Print #includes, too */
1831 X           if (file->fp == NULL)
1832 X               fprintf(stderr, "from macro %s\n", file->filename);
1833 X           else {
1834 X               tp = file->buffer;
1835 X               fprintf(stderr, "from file %s, line %d:\n%s",
1836 X                   (file->progname != NULL)
1837 X                       ? file->progname : file->filename,
1838 X                   file->line, tp);
1839 X               if (tp[strlen(tp) - 1] != '\n')
1840 X                   putc('\n', stderr);
1841 X           }
1842 X       }
1843 X}
1844 X
1845 Xcerror(format, sarg)
1846 Xchar           *format;
1847 Xchar           *sarg;          /* Single string argument               */
1848 X/*
1849 X * Print a normal error message, string argument.
1850 X */
1851 X{
1852 X       domsg("SError", format, sarg);
1853 X       errors++;
1854 X}
1855 X
1856 Xcierror(format, narg)
1857 Xchar           *format;
1858 Xint            narg;           /* Single numeric argument              */
1859 X/*
1860 X * Print a normal error message, numeric argument.
1861 X */
1862 X{
1863 X       domsg("IError", format, (char *) narg);
1864 X       errors++;
1865 X}
1866 X
1867 Xcfatal(format, sarg)
1868 Xchar           *format;
1869 Xchar           *sarg;                  /* Single string argument       */
1870 X/*
1871 X * A real disaster
1872 X */
1873 X{
1874 X       domsg("SFatal error", format, sarg);
1875 X       exit(IO_ERROR);
1876 X}
1877 X
1878 Xcwarn(format, sarg)
1879 Xchar           *format;
1880 Xchar           *sarg;                  /* Single string argument       */
1881 X/*
1882 X * A non-fatal error, string argument.
1883 X */
1884 X{
1885 X       domsg("SWarning", format, sarg);
1886 X}
1887 X
1888 Xciwarn(format, narg)
1889 Xchar           *format;
1890 Xint            narg;                   /* Single numeric argument      */
1891 X/*
1892 X * A non-fatal error, numeric argument.
1893 X */
1894 X{
1895 X       domsg("IWarning", format, (char *) narg);
1896 X}
1897 X
1898 X
1899 X
1900 END-of-cpp6.c
1901 exit