Annotation of GNUtools/emacs/cpp/cccp.c, revision 1.1

1.1     ! root        1: /* C Compatible Compiler Preprocessor (CCCP)
        !             2: Copyright (C) 1986, Free Software Foundation, Inc.
        !             3:                     Written by Paul Rubin, June 1986
        !             4: 
        !             5:                       NO WARRANTY
        !             6: 
        !             7:   BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
        !             8: NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
        !             9: WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
        !            10: RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
        !            11: WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
        !            12: BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
        !            13: FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
        !            14: AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
        !            15: DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
        !            16: CORRECTION.
        !            17: 
        !            18:  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
        !            19: STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
        !            20: WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
        !            21: LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
        !            22: OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
        !            23: USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
        !            24: DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
        !            25: A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
        !            26: PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
        !            27: DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
        !            28: 
        !            29:                GENERAL PUBLIC LICENSE TO COPY
        !            30: 
        !            31:   1. You may copy and distribute verbatim copies of this source file
        !            32: as you receive it, in any medium, provided that you conspicuously
        !            33: and appropriately publish on each copy a valid copyright notice
        !            34: "Copyright (C) 1986 Free Software Foundation"; and include
        !            35: following the copyright notice a verbatim copy of the above disclaimer
        !            36: of warranty and of this License.
        !            37: 
        !            38:   2. You may modify your copy or copies of this source file or
        !            39: any portion of it, and copy and distribute such modifications under
        !            40: the terms of Paragraph 1 above, provided that you also do the following:
        !            41: 
        !            42:     a) cause the modified files to carry prominent notices stating
        !            43:     that you changed the files and the date of any change; and
        !            44: 
        !            45:     b) cause the whole of any work that you distribute or publish,
        !            46:     that in whole or in part contains or is a derivative of this
        !            47:     program or any part thereof, to be licensed at no charge to all
        !            48:     third parties on terms identical to those contained in this
        !            49:     License Agreement (except that you may choose to grant more extensive
        !            50:     warranty protection to some or all third parties, at your option).
        !            51: 
        !            52:     c) You may charge a distribution fee for the physical act of
        !            53:     transferring a copy, and you may at your option offer warranty
        !            54:     protection in exchange for a fee.
        !            55: 
        !            56: Mere aggregation of another unrelated program with this program (or its
        !            57: derivative) on a volume of a storage or distribution medium does not bring
        !            58: the other program under the scope of these terms.
        !            59: 
        !            60:   3. You may copy and distribute this program (or a portion or derivative
        !            61: of it, under Paragraph 2) in object code or executable form under the terms
        !            62: of Paragraphs 1 and 2 above provided that you also do one of the following:
        !            63: 
        !            64:     a) accompany it with the complete corresponding machine-readable
        !            65:     source code, which must be distributed under the terms of
        !            66:     Paragraphs 1 and 2 above; or,
        !            67: 
        !            68:     b) accompany it with a written offer, valid for at least three
        !            69:     years, to give any third party free (except for a nominal
        !            70:     shipping charge) a complete machine-readable copy of the
        !            71:     corresponding source code, to be distributed under the terms of
        !            72:     Paragraphs 1 and 2 above; or,
        !            73: 
        !            74:     c) accompany it with the information you received as to where the
        !            75:     corresponding source code may be obtained.  (This alternative is
        !            76:     allowed only for noncommercial distribution and only if you
        !            77:     received the program in object code or executable form alone.)
        !            78: 
        !            79: For an executable file, complete source code means all the source code for
        !            80: all modules it contains; but, as a special exception, it need not include
        !            81: source code for modules which are standard libraries that accompany the
        !            82: operating system on which the executable file runs.
        !            83: 
        !            84:   4. You may not copy, sublicense, distribute or transfer this program
        !            85: except as expressly provided under this License Agreement.  Any attempt
        !            86: otherwise to copy, sublicense, distribute or transfer this program is void and
        !            87: your rights to use the program under this License agreement shall be
        !            88: automatically terminated.  However, parties who have received computer
        !            89: software programs from you with this License Agreement will not have
        !            90: their licenses terminated so long as such parties remain in full compliance.
        !            91: 
        !            92:  In other words, you are welcome to use, share and improve this program.
        !            93:  You are forbidden to forbid anyone else to use, share and improve
        !            94:  what you give them.   Help stamp out software-hoarding!  */
        !            95: 
        !            96: typedef unsigned char U_CHAR;
        !            97: 
        !            98: #ifdef EMACS
        !            99: #define NO_SHORTNAMES
        !           100: #include "../src/config.h"
        !           101: #ifdef static
        !           102: #undef static
        !           103: #endif
        !           104: #ifdef open
        !           105: #undef open
        !           106: #undef close
        !           107: #undef read
        !           108: #undef write
        !           109: #endif /* open */
        !           110: #endif /* EMACS */
        !           111: 
        !           112: #include <sys/types.h>
        !           113: #include <sys/stat.h>
        !           114: #include <sys/file.h>
        !           115: #include <ctype.h>
        !           116: #include <stdio.h>
        !           117: #ifndef USG
        !           118: #include <sys/time.h>          /* for __DATE__ and __TIME__ */
        !           119: #else
        !           120: #define index strchr
        !           121: #define rindex strrchr
        !           122: #include <time.h>
        !           123: #include <fcntl.h>
        !           124: #endif /* USG */
        !           125: 
        !           126: void bcopy (), bzero ();
        !           127: int bcmp ();
        !           128: 
        !           129: char *xmalloc (), *xrealloc (), *xcalloc ();
        !           130: void fatal (), pfatal_with_name (), perror_with_name ();
        !           131: 
        !           132: char *progname;
        !           133: 
        !           134: #define FATAL_EXIT_CODE 33     /* gnu cc command understands this */
        !           135: 
        !           136: struct directory_stack
        !           137:   {
        !           138:     struct directory_stack *next;
        !           139:     char *fname;
        !           140:   };
        !           141: 
        !           142: /* #include "file" starts with the first entry in the stack */
        !           143: /* #include <file> starts with the second. */
        !           144: /* -I directories are added after the first */
        !           145: struct directory_stack default_includes[2] =
        !           146:   {
        !           147:     { &default_includes[1], "." },
        !           148:     { 0, "/usr/include" }
        !           149:   };
        !           150: struct directory_stack *include = &default_includes[0];
        !           151: 
        !           152: int max_include_len = 14;      /* strlen (default_include) + 2
        !           153:                                                        (for / and null) */
        !           154: 
        !           155: char STDIN_FILE[] = "";                /* Empty, like real cpp */
        !           156: int put_out_comments = 0;      /* JF non-zero means leave comments in the
        !           157:                                   output file.  Used by lint */
        !           158: 
        !           159: /* table to tell if char can be part of a C identifier. */
        !           160: U_CHAR is_idchar[256];
        !           161: /* table to tell if char can be first char of a c identifier. */
        !           162: U_CHAR is_idstart[256];
        !           163: /* table to tell if c is horizontal space.  isspace() thinks that
        !           164:    newline is space; this is not a good idea for this program. */
        !           165: U_CHAR is_hor_space[256];
        !           166: 
        !           167: /* I/O buffer structure.  Ought to be used for the output file too.
        !           168:    These are also used when there is no file present, for example,
        !           169:    when rescanning a definition.  Then, the fname field is null. */
        !           170: #define INPUT_STACK_MAX 100
        !           171: struct file_buf {
        !           172:   struct infile *next; /* for making stacks of file ptrs */
        !           173:   char *fname;
        !           174:   int lineno;
        !           175:   int length;
        !           176:   U_CHAR *buf;
        !           177:   U_CHAR *bufp;
        !           178: } instack[INPUT_STACK_MAX];
        !           179: int indepth = 0;
        !           180: 
        !           181: typedef struct file_buf FILE_BUF;
        !           182: 
        !           183: /* The output buffer.  Its LENGTH field is the amount of room allocated
        !           184:    for the buffer, not the number of chars actually present.  To get
        !           185:    that, subtract outbuf.buf from outbuf.bufp. */
        !           186: 
        !           187: #define OUTBUF_SIZE 10 /* initial size of output buffer */
        !           188: FILE_BUF outbuf;
        !           189: 
        !           190: /* Structure allocated for every #define.  For a simple replacement
        !           191:    such as
        !           192:        #define foo bar ,
        !           193:    nargs = -1, the `pattern' list is null, and the expansion is just
        !           194:    the replacement text.  Nargs = 0 means a real macro with no args,
        !           195:    e.g.,
        !           196:        #define getchar() getc(stdin) .
        !           197:    When there are args, the expansion is the replacement text with the
        !           198:    args squashed out, and the reflist is a list describing how to
        !           199:    build the output from the input: e.g., "3 chars, then the 1st arg,
        !           200:    then 9 chars, then the 3rd arg, then 0 chars, then the 2nd arg".
        !           201:    The chars here come from the expansion.  Thus, for any definition
        !           202:    d , strlen(d->expansion) should equal the sum of all the
        !           203:    d->pattern->nchars.  Note that the list can be arbitrarily long---
        !           204:    its length depends on the number of times the arguements appear in
        !           205:    the replacement text, not how many args there are.  Example:
        !           206:    #define f(x) x+x+x+x+x+x+x would have replacement text "++++++" and
        !           207:    pattern list
        !           208:      { (0, 1), (1, 1), (1, 1), ..., (1, 1), NULL }
        !           209:    where (x, y) means (nchars, argno). */
        !           210: 
        !           211: typedef struct definition DEFINITION;
        !           212: struct definition {
        !           213:   int nargs;
        !           214:   int length;                  /* length of expansion string */
        !           215:   U_CHAR *expansion;
        !           216:   struct reflist {
        !           217:     struct reflist *next;
        !           218:     int nchars;
        !           219:     int argno;
        !           220:   } *pattern;
        !           221: };
        !           222: 
        !           223: /* different kinds of things that can appear in the value field
        !           224:    of a hash node.  Actually, this may be useless now. */
        !           225: union hashval {
        !           226:   int ival;
        !           227:   char *cpval;
        !           228:   DEFINITION *defn;
        !           229: };
        !           230: 
        !           231: 
        !           232: /* The structure of a node in the hash table.  The hash table
        !           233:    has entries for all tokens defined by #define commands (type T_MACRO),
        !           234:    plus some special tokens like __LINE__ (these each have their own
        !           235:    type, and the appropriate code is run when that type of node is seen.
        !           236:    It does not contain control words like "#define", which are recognized
        !           237:    by a separate piece of code. */
        !           238: typedef struct hashnode HASHNODE;
        !           239: struct hashnode {
        !           240:   HASHNODE *next;              /* double links for easy deletion */
        !           241:   HASHNODE *prev;
        !           242:   HASHNODE **bucket_hdr;       /* also, a back pointer to this node's hash
        !           243:                                   chain is kept, in case the node is the head
        !           244:                                   of the chain and gets deleted. */
        !           245:   int type;                    /* type of special token */
        !           246:   int length;                  /* length of token, for quick comparison */
        !           247:   U_CHAR *name;                        /* the actual name */
        !           248:   union hashval value;         /* pointer to expansion, or whatever */
        !           249: };
        !           250: 
        !           251: 
        !           252: HASHNODE *install();
        !           253: /* different flavors of hash nodes --- also used in keyword table */
        !           254: #define T_DEFINE       1       /* the "#define" keyword */
        !           255: #define T_INCLUDE      2       /* the "#include" keyword */
        !           256: #define T_IFDEF                3       /* the "#ifdef" keyword */
        !           257: #define T_IF           4       /* the "#if" keyword */
        !           258: #define T_EXPAND       5       /* argument to be expanded (now unused) */
        !           259: #define T_MACRO                6       /* macro defined by "#define" */
        !           260: #define T_ELSE         7       /* "#else" */
        !           261: #define T_PRAGMA       8       /* "#pragma" */
        !           262: #define T_ELIF         9       /* "#else" */
        !           263: #define T_UNDEF                10      /* "#undef" */
        !           264: #define T_LINE         11      /* "#line" */
        !           265: #define T_ERROR                12      /* "#error" */
        !           266: #define T_IFNDEF       13      /* "#ifndef"; forgot this earlier */
        !           267: #define T_ENDIF                14      /* "#endif" */
        !           268: #define T_SPECLINE     15      /* special symbol "__LINE__" */
        !           269: #define T_DATE         16      /* "__DATE__" */
        !           270: #define T_FILE         17      /* "__FILE__" */
        !           271: #define T_TIME         18      /* "__TIME__" */
        !           272: 
        !           273: #define T_SPEC_DEFINED 19      /* special macro for use in #if statements */
        !           274: 
        !           275: 
        !           276: 
        !           277: /* some more different types will be needed --- no longer bloody likely */
        !           278: 
        !           279: 
        !           280: int do_define(), do_line(), do_include(), do_undef(), do_error(),
        !           281:   do_pragma(), do_if(), do_xifdef(), do_else(),
        !           282:   do_elif(), do_endif();
        !           283: 
        !           284: 
        !           285: /* table of control words, along with code to execute when the keyword
        !           286:    is seen.  For now, it is searched linearly, so put the most frequently
        !           287:    found keywords at the beginning of the list. */
        !           288: 
        !           289: struct keyword_table {
        !           290:   int length;
        !           291:   int (*func)();
        !           292:   char *name;
        !           293:   int type;
        !           294: } keyword_table[] = {
        !           295:   {  6, do_define, "define", T_DEFINE},
        !           296:   {  4, do_line, "line", T_LINE},
        !           297:   {  7, do_include, "include", T_INCLUDE},
        !           298:   {  5, do_undef, "undef", T_UNDEF},
        !           299:   {  5, do_error, "error", T_ERROR},
        !           300:   {  2, do_if, "if", T_IF},
        !           301:   {  5, do_xifdef, "ifdef", T_IFDEF},
        !           302:   {  6, do_xifdef, "ifndef", T_IFNDEF},
        !           303:   {  4, do_else, "else", T_ELSE},
        !           304:   {  4, do_elif, "elif", T_ELIF},
        !           305:   {  5, do_endif, "endif", T_ENDIF},
        !           306:   {  6, do_pragma, "pragma", T_PRAGMA},
        !           307:   {  -1, 0, "", -1},
        !           308: };
        !           309: 
        !           310: /* Some definitions for the hash table.  The hash function MUST be
        !           311:    computed as shown in hashf() below.  That is because the rescan
        !           312:    loop computes the hash value `on the fly' for most tokens,
        !           313:    in order to avoid the overhead of a lot of procedure calls to
        !           314:    the hashf() function.  Hashf() only exists for the sake of
        !           315:    politeness, for use when speed isn't so important. */
        !           316: 
        !           317: #define HASHSIZE 1009
        !           318: HASHNODE *hashtab[HASHSIZE];
        !           319: #define HASHSTEP(old, c) ((old << 1) + c)
        !           320: #define MAKE_POS(v) (v & ~0x80000000) /* make number positive */
        !           321: 
        !           322: #define SKIP_WHITE_SPACE(p) { while (is_hor_space[*p]) p++; }
        !           323: 
        !           324: 
        !           325: 
        !           326: main (argc, argv)
        !           327:      int argc;
        !           328:      char **argv;
        !           329: {
        !           330:   struct stat sbuf;
        !           331:   char *in_fname, *out_fname;
        !           332:   int out_fd = 1;      /* default to stdout */
        !           333:   int f, i;
        !           334:   FILE_BUF *fp;
        !           335: 
        !           336:   progname = argv[0];
        !           337:   in_fname = NULL;
        !           338:   out_fname = NULL;
        !           339:   initialize_random_junk ();
        !           340: 
        !           341:   fp = &instack[indepth++];
        !           342: 
        !           343: /*  if (argc < 2)              JF no args means work as filter
        !           344:     return FATAL_EXIT_CODE; */
        !           345: 
        !           346:   for (i = 1; i < argc; i++) {
        !           347:     if (argv[i][0] != '-') {
        !           348:       if (out_fname != NULL)
        !           349:        fatal ("Usage: %s [switches] input output\n", argv[0]);
        !           350:       else if (in_fname != NULL) {
        !           351:        out_fname = argv[i];
        !           352:        if ((out_fd = creat (out_fname, 0666)) < 0)
        !           353:          pfatal_with_name (out_fname);
        !           354:       } else
        !           355:        in_fname = argv[i];
        !           356:     } else {
        !           357:       switch (argv[i][1]) {
        !           358:        U_CHAR *p;
        !           359:        struct directory_stack *dirtmp;
        !           360:       case 'D':
        !           361:        if ((p = (U_CHAR *) index(argv[i]+2, '=')) != NULL)
        !           362:          *p = ' ';
        !           363:        make_definition (argv[i] + 2);
        !           364:        break;
        !           365:       case 'U':                /* JF #undef something */
        !           366:        make_undef(argv[i] + 2);
        !           367:        break;
        !           368:       case 'C':                /* JF do later -C means leave comments alone! */
        !           369:        put_out_comments++;
        !           370:        break;
        !           371:       case 'E':                        /* -E comes from cc -E; ignore it.  */
        !           372:        break;
        !           373:       case 'M':                        /* Makefile dependencies or something like
        !           374:                                   that.  Not implimented yet */
        !           375:        break;
        !           376:       case 'I':                        /* JF handle directory path right */
        !           377:         dirtmp = (struct directory_stack *)
        !           378:                        xmalloc (sizeof (struct directory_stack));
        !           379:        dirtmp->next = include->next;
        !           380:        include->next = dirtmp;
        !           381:        dirtmp->fname = argv[i]+2;
        !           382:        include = dirtmp;
        !           383:        if (strlen (argv[i]) > max_include_len)
        !           384:          max_include_len = strlen (argv[i]);
        !           385:        break;
        !           386: 
        !           387:       case '\0': /* JF handle '-' as file name meaning stdin or stdout */
        !           388:        if (in_fname == NULL) {
        !           389:          in_fname = STDIN_FILE;
        !           390:          break;
        !           391:        } else if (out_fname == NULL) {
        !           392:          out_fname = "stdout";
        !           393:          break;
        !           394:        }       /* else fall through into error */
        !           395: 
        !           396:       default:
        !           397:        fatal ("Illegal option %s\n", argv[i]);
        !           398:       }
        !           399:     }
        !           400:   }
        !           401: 
        !           402:   /* JF check for stdin */
        !           403:   if (in_fname == STDIN_FILE || in_fname == NULL)
        !           404:     f = 0;
        !           405:   else if ((f = open (in_fname, O_RDONLY)) < 0)
        !           406:     goto perror;
        !           407: 
        !           408:   fstat (f, &sbuf);
        !           409:   fp->fname = in_fname;
        !           410:   fp->lineno = 1;
        !           411:   /* JF all this is mine about reading pipes and ttys */
        !           412:   if ((sbuf.st_mode & S_IFMT) != S_IFREG) {
        !           413:     int size;
        !           414:     int bsize;
        !           415:     int cnt;
        !           416:     U_CHAR *bufp;
        !           417: 
        !           418:     bsize = 2000;
        !           419:     size = 0;
        !           420:     fp->buf = (U_CHAR *) xmalloc (bsize + 1);
        !           421:     bufp = fp->buf;
        !           422:     for (;;) {
        !           423:       cnt = read (f, bufp, bsize - size);
        !           424:       if (cnt < 0) goto perror;        /* error! */
        !           425:       if (cnt == 0) break;     /* End of file */
        !           426:       size += cnt;
        !           427:       bufp += cnt;
        !           428:       if (bsize-size == 0) {   /* Buffer is full! */
        !           429:         bsize *= 2;
        !           430:         fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 1);
        !           431:        bufp = fp->buf + size;  /* May have moved */
        !           432:       }
        !           433:     }
        !           434:     fp->buf[size] = '\0';
        !           435:     fp->length = size;
        !           436:   } else {
        !           437:     fp->length = sbuf.st_size;
        !           438:     fp->buf = (U_CHAR *) alloca (sbuf.st_size + 1);
        !           439: 
        !           440:     if (read (f, fp->buf, sbuf.st_size) != sbuf.st_size)
        !           441:       goto perror;
        !           442: 
        !           443:     fp->buf[sbuf.st_size] = '\0';
        !           444:   }
        !           445: 
        !           446:   /* initialize output buffer */
        !           447:   outbuf.buf = (U_CHAR *) xmalloc (OUTBUF_SIZE);
        !           448:   outbuf.bufp = outbuf.buf;
        !           449:   outbuf.length = OUTBUF_SIZE;
        !           450: 
        !           451:   output_line_command (fp, &outbuf);
        !           452:   rescan (fp, &outbuf);
        !           453: 
        !           454:   /* do something different than this later */
        !           455:   fflush (stdout);
        !           456:   write (out_fd, outbuf.buf, outbuf.bufp - outbuf.buf);
        !           457:   exit (0);
        !           458: 
        !           459:  perror:
        !           460:   pfatal_with_name (argv[1]);
        !           461: }
        !           462: 
        !           463: /*
        !           464:  * The main loop of the program.  Try to examine and move past most
        !           465:  * ordinary input chars as fast as possible.  Call appropriate routines
        !           466:  * when something special (such as a macro expansion) has to happen.
        !           467: 
        !           468: IP is the source of input to scan.
        !           469: OP is the place to put input. */
        !           470: 
        !           471: rescan (ip, op)
        !           472:      FILE_BUF *ip, *op;
        !           473: {
        !           474:   register int c;
        !           475:   register int ident_length = 0, hash = 0;
        !           476:   register U_CHAR *limit;
        !           477:   U_CHAR *check_expand ();
        !           478:   struct keyword_table *handle_directive ();
        !           479:   int excess_newlines = 0;
        !           480:   int escaped = 0;
        !           481:   
        !           482:   U_CHAR *bp;
        !           483:   
        !           484:   check_expand(op, ip->length);
        !           485:   
        !           486:   ip->bufp = ip->buf;
        !           487:   limit = ip->buf + ip->length;
        !           488:   while (1) {
        !           489:     if (ip->bufp < limit) {
        !           490:       c = *ip->bufp++;
        !           491:       *op->bufp++ = c;
        !           492:     } else {
        !           493:       c = -1;
        !           494:     }
        !           495: 
        !           496:     --escaped;
        !           497:     /* Now ESCAPED is 0 if and only if this character is escaped.  */
        !           498: 
        !           499:     switch (c) {
        !           500:     case '\\':
        !           501:       if (escaped == 0)
        !           502:        goto randomchar;
        !           503:       if (*ip->bufp != '\n')
        !           504:        {
        !           505:          escaped = 1;
        !           506:          goto randomchar;
        !           507:        }
        !           508:       /* always merge lines ending with backslash-newline */
        !           509:       ++ip->bufp;
        !           510:       ++ip->lineno;
        !           511:       ++excess_newlines;
        !           512:       --op->bufp;              /* remove backslash from obuf */
        !           513:       continue;                        /* back to top of while loop */
        !           514: 
        !           515:     case '#':
        !           516:       /* # keyword: a # must be first nonblank char on the line */
        !           517:       for (bp = ip->bufp - 1; bp >= ip->buf; bp--)
        !           518:        if (*bp == '\n')
        !           519:          break;
        !           520:       bp++;                    /* skip nl or move back into buffer */
        !           521:       SKIP_WHITE_SPACE (bp);
        !           522:       if (*bp != '#')
        !           523:        goto randomchar;
        !           524:       ident_length = hash = 0;
        !           525:       --op->bufp;              /* don't copy the '#' */
        !           526: 
        !           527:       if (handle_directive (ip, op, &excess_newlines) == NULL) {
        !           528:        ++op->bufp;             /* copy the '#' after all */
        !           529:        goto randomchar;
        !           530:       }
        !           531:       break;
        !           532: 
        !           533:     case '\"':                 /* skip quoted string */
        !           534:     case '\'':
        !           535:       /* a single quoted string is treated like a double -- some
        !           536:         programs (e.g., troff) are perverse this way */
        !           537: 
        !           538:       if (escaped == 0)
        !           539:        goto randomchar;        /* false alarm-- it's escaped. */
        !           540: 
        !           541:       /* skip ahead to a matching quote.  */
        !           542: 
        !           543:       bp = ip->bufp;
        !           544:       while (bp < limit) {
        !           545:        *op->bufp++ = *bp;
        !           546:        switch (*bp++) {
        !           547:        case '\n':
        !           548:          ++ip->lineno;
        !           549:          break;
        !           550:        case '\\':
        !           551:          if (bp >= limit)
        !           552:            break;
        !           553:          if (*bp == '\n')
        !           554:            {
        !           555:              /* backslash newline is replaced by nothing at all,
        !           556:                 but remember that the source line count is out of synch.  */
        !           557:              --op->bufp;
        !           558:              ++bp;
        !           559:              ++excess_newlines;
        !           560:              ++ip->lineno;
        !           561:            }
        !           562:          else
        !           563:            *op->bufp++ = *bp++;
        !           564:          break;
        !           565:        case '\"':
        !           566:        case '\'':
        !           567:          if (bp[-1] == c)
        !           568:            goto while2end;
        !           569:          break;
        !           570:        }
        !           571:       }
        !           572:     while2end:
        !           573:       ip->bufp = bp;
        !           574:       break;
        !           575: 
        !           576:     case '/':                  /* possible comment */
        !           577:       if (*ip->bufp != '*')
        !           578:        goto randomchar;
        !           579:       if (put_out_comments) {
        !           580:         bp = ip->bufp;
        !           581:        *op->bufp++ = *bp++;
        !           582:       } else {
        !           583:        bp = ip->bufp + 1;
        !           584:        --op->bufp;             /* don't leave initial slash in buffer */
        !           585:       }
        !           586: 
        !           587:       for (;;) {
        !           588:        while (bp < limit) {
        !           589:          if (put_out_comments)
        !           590:            *op->bufp++ = *bp;
        !           591:          switch (*bp++) {
        !           592:          case '*':
        !           593:            goto whileend;
        !           594:          case '\n':
        !           595:            /* copy the newline into the output buffer, in order to
        !           596:               avoid the pain of a #line every time a multiline comment
        !           597:               is seen.  This means keywords with embedded comments
        !           598:               that contain newlines (blucch!) will lose.  By making
        !           599:               sure that excess_newlines is not just a flag, but really
        !           600:               an accurate count, it might be possible to get around this. */
        !           601:            if (!put_out_comments)
        !           602:              *op->bufp++ = '\n';
        !           603:            ++ip->lineno;
        !           604:          }
        !           605:        }
        !           606:       whileend:
        !           607:        if (bp >= limit) {
        !           608:          error ("unterminated comment");
        !           609:          break;                /* causes eof condition */
        !           610:        }
        !           611:        if (*bp == '/')
        !           612:          break;
        !           613:       }
        !           614:       if (put_out_comments)
        !           615:         *op->bufp++ = '/';
        !           616:       ip->bufp = bp + 1;
        !           617:       break;
        !           618:       
        !           619:     case '0': case '1': case '2': case '3': case '4':
        !           620:     case '5': case '6': case '7': case '8': case '9':
        !           621:       /* if digit is not part of identifier, it is random */
        !           622:       if (ident_length == 0)
        !           623:        goto randomchar;
        !           624:       /* fall through */
        !           625:       
        !           626:     case '_':
        !           627:     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
        !           628:     case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
        !           629:     case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
        !           630:     case 's': case 't': case 'u': case 'v': case 'w': case 'x':
        !           631:     case 'y': case 'z': 
        !           632:     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
        !           633:     case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
        !           634:     case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
        !           635:     case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
        !           636:     case 'Y': case 'Z': 
        !           637:       ident_length++;
        !           638:       /* compute step of hash function, to avoid a proc call on every token */
        !           639:       hash = HASHSTEP(hash, c);
        !           640:       break;
        !           641: 
        !           642:     default:
        !           643: randomchar:
        !           644:       if (ident_length > 0) {
        !           645:        register HASHNODE *hp;
        !           646:        for (hp = hashtab[MAKE_POS(hash) % HASHSIZE]; hp != NULL;
        !           647:             hp = hp->next) {
        !           648:           U_CHAR *save_ibufp;  /* kludge, see below */
        !           649:              
        !           650:           if (hp->length == ident_length) {
        !           651:            register int i = ident_length;
        !           652:            register U_CHAR *p = hp->name;
        !           653:            register U_CHAR *q = op->bufp - i;
        !           654: 
        !           655:            if (c != (U_CHAR) -1)
        !           656:              q--;
        !           657: 
        !           658:            do {                /* all this to avoid a strncmp() */
        !           659:              if (*p++ != *q++)
        !           660:                goto hashcollision;
        !           661:            } while (--i);
        !           662:         
        !           663:            save_ibufp = ip->bufp;
        !           664:            /* back up over identifier, then expand token */
        !           665:            op->bufp -= ident_length;
        !           666:            if (c != (U_CHAR) -1) op->bufp--;
        !           667:            macroexpand (hp, ip, op, &excess_newlines);
        !           668: 
        !           669:            check_expand(op, ip->length - (ip->bufp - ip->buf));
        !           670:            
        !           671:            /* If we just processed an identifier at end of input,
        !           672:               return right away.  */
        !           673:            if (c == (U_CHAR) -1)
        !           674:              return;
        !           675: 
        !           676:            /* if the expansion routine has not moved the input
        !           677:               pointer, put back the char that ended the token.
        !           678:               This is a kludge because there might be a different
        !           679:               reason to put it back or not put it back. */
        !           680:            if (ip->bufp == save_ibufp)
        !           681:              *op->bufp++ = c;
        !           682:            
        !           683:            break;              /* out of for loop */
        !           684:          }
        !           685: hashcollision:
        !           686:               ;
        !           687:        }                       /* end for loop */
        !           688:        ident_length = hash = 0; /* stop collecting identifier */
        !           689:       }
        !           690:            
        !           691:       /* If we just processed an identifier at end of input,
        !           692:         return right away.  */
        !           693:       if (c == -1)
        !           694:        return;
        !           695: 
        !           696:       /* count the newline, if it was one.  The reason this is
        !           697:         done down here instead of as a case in the switch is
        !           698:         that some expansions might want to look at the line
        !           699:         number, and if they happen right before the newline,
        !           700:         we don't want them to get the wrong one.  So the newline
        !           701:         must be counted AFTER any expansions happen. */
        !           702:       if (c == '\n') {
        !           703:        ++ip->lineno;
        !           704:        if (excess_newlines > 0) {
        !           705:          output_line_command (ip, op);
        !           706:          check_expand(op, ip->length - (ip->bufp - ip->buf));
        !           707: 
        !           708:          excess_newlines = 0;
        !           709:        }
        !           710:       }
        !           711:       break;                   /* from switch */
        !           712:     }
        !           713:   }
        !           714: }
        !           715: 
        !           716: /*
        !           717:  * Process a # directive.  Expects ip->bufp to point to the '#', as in
        !           718:  * "#define foo bar".  Bumps *excess_newlines counter as necessary if
        !           719:  * the command is several lines long (and also updates ip->lineno).
        !           720:  * The main reason for this is that the comments could contain
        !           721:  * newlines, which would be confusing.  Passes to the command handler
        !           722:  * (do_define, do_include, etc.): the addresses of the 1st and
        !           723:  * last chars of the command (starting immediately after the #
        !           724:  * keyword), plus op and the keyword table pointer.  If the line
        !           725:  * contains comments the command is copied into a temporary buffer
        !           726:  * (sans comments) and the temporary buffer is passed to the command
        !           727:  * handler instead.
        !           728:  */
        !           729: 
        !           730: struct keyword_table *
        !           731: handle_directive (ip, op, excess_newlines)
        !           732:      FILE_BUF *ip, *op;
        !           733:      int *excess_newlines;
        !           734: {
        !           735:   register U_CHAR *bp, *cp;
        !           736:   register struct keyword_table *kt;
        !           737:   register int ident_length;
        !           738: 
        !           739:   /* Nonzero means we must copy the entire command
        !           740:      to get rid of comments or backslash-newlines.  */
        !           741:   int copy_command = 0;
        !           742: 
        !           743:   bp = ip->bufp;
        !           744:   SKIP_WHITE_SPACE(bp);
        !           745:   cp = bp;
        !           746:   while (is_idchar[*cp])
        !           747:     cp++;
        !           748:   ident_length = cp - bp;
        !           749:   
        !           750:   /*
        !           751:    * Decode the keyword and call the appropriate expansion
        !           752:    * routine, after moving the input pointer up to the next line.
        !           753:    * If the keyword is not a legitimate control word, return NULL.
        !           754:    * Otherwise, return ptr to the keyword structure matched.
        !           755:    */
        !           756:   for (kt = keyword_table; kt->length > 0; kt++) {
        !           757:     if (kt->length == ident_length && !strncmp(kt->name, bp, ident_length)) {
        !           758:       register U_CHAR *buf;
        !           759:       register U_CHAR *limit = ip->buf + ip->length;
        !           760:       U_CHAR *skip_to_end_of_comment();
        !           761:       
        !           762:       buf = bp = bp + ident_length;
        !           763:       while (bp < limit) {
        !           764:        if (*bp == '\'' || *bp == '\"') {               /* JF handle quotes right  */
        !           765:          U_CHAR quotec;
        !           766: 
        !           767:          for (quotec = *bp++; bp < limit && *bp != quotec; bp++) {
        !           768:            if (*bp == '\\') bp++;
        !           769:            if (*bp == '\n') {
        !           770:              if (bp[-1] == '\\')
        !           771:                copy_command++;
        !           772:              else {
        !           773:                /* --bp; */
        !           774:                break;  /* JF ugly, but might work */
        !           775:              }
        !           776:            }
        !           777:          }
        !           778:          continue;
        !           779:        }
        !           780:        if (*bp == '/' && bp[1] == '*') {
        !           781:          copy_command++;
        !           782:          ip->bufp = bp + 2;
        !           783:          skip_to_end_of_comment (ip, NULL);
        !           784:          bp = ip->bufp;
        !           785:          continue;
        !           786:        }
        !           787: 
        !           788:        if (*bp++ == '\n') {
        !           789:          if (*(bp-2) == '\\')
        !           790:            copy_command++;
        !           791:          else {
        !           792:            --bp;               /* point to the newline */
        !           793:            break;
        !           794:          }
        !           795:        }
        !           796:       }
        !           797:       if (copy_command) {
        !           798:        /* need to copy entire command into temp buffer before dispatching */
        !           799: 
        !           800:        cp = (U_CHAR *) alloca (bp - buf + 5); /* room for cmd plus
        !           801:                                                  some slop */
        !           802:        bp = buf;
        !           803:        buf = cp;
        !           804:        
        !           805:        while (bp < limit) {
        !           806:          if (*bp == '\'' || *bp == '\"') {     /* JF handle quotes right  */
        !           807:            U_CHAR quotec;
        !           808: 
        !           809:            *cp++ = *bp;
        !           810:            for (quotec = *bp++; bp < limit && *bp != quotec; *cp++ = *bp++) {
        !           811:              if (*bp == '\\')
        !           812:                *cp++ = *bp++;
        !           813:              if (*bp == '\n') {
        !           814:                if (bp[-1] == '\\') {
        !           815:                  ++ip->lineno;
        !           816:                  ++*excess_newlines;
        !           817:                } else break;   /* JF ugly, but might work */
        !           818:              }
        !           819:            }
        !           820:            continue;
        !           821:          }
        !           822:          if (*bp == '/' && bp[1] == '*') {
        !           823:            int newlines_found = 0;
        !           824:            ip->bufp = bp + 2;
        !           825:            skip_to_end_of_comment (ip, &newlines_found);
        !           826:            *excess_newlines += newlines_found;
        !           827:            ip->lineno += newlines_found;
        !           828:            bp = ip->bufp;
        !           829:            continue;
        !           830:          }
        !           831: 
        !           832:          if (*bp == '\n') {
        !           833:            if (bp[-1] == '\\') {
        !           834:              ++ip->lineno;
        !           835:              ++*excess_newlines;
        !           836:            } else
        !           837:              break;
        !           838:          }
        !           839:          *cp++ = *bp++;
        !           840:        }
        !           841:       }
        !           842:       else
        !           843:        cp = bp;
        !           844: 
        !           845:       ip->bufp = bp;           /* skip to the end of the command */
        !           846: 
        !           847:       /* call the appropriate command handler.  Buf now points to
        !           848:         either the appropriate place in the input buffer, or to
        !           849:         the temp buffer if it was necessary to make one.  Cp
        !           850:         points to the first char after the contents of the (possibly
        !           851:         copied) command, in either case. */
        !           852:       (*kt->func) (buf, cp, op, kt);
        !           853:       check_expand (op, ip->length - (ip->bufp - ip->buf));
        !           854: 
        !           855:       break;
        !           856:     }
        !           857:   }
        !           858:   if (kt->length <= 0)
        !           859:     kt = NULL;
        !           860: 
        !           861:   return kt;
        !           862: }
        !           863: 
        !           864: static char *monthnames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
        !           865:                             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
        !           866:                            };
        !           867: 
        !           868: /*
        !           869:  * expand things like __FILE__.  Place the expansion into the output
        !           870:  * buffer *without* rescanning.
        !           871:  */
        !           872: expand_special_symbol (hp, ip, op)
        !           873:      HASHNODE *hp;
        !           874:      FILE_BUF *ip, *op;
        !           875: {
        !           876:   char *buf;
        !           877:   int i, len;
        !           878:   FILE_BUF *last_ip = NULL;
        !           879:   static struct tm *timebuf = NULL;
        !           880:   struct tm *localtime();
        !           881: 
        !           882:   int paren = 0;               /* for special `defined' keyword */
        !           883:   HASHNODE *lookup();
        !           884: 
        !           885:   for (i = indepth - 1; i >= 0; i--)
        !           886:     if (instack[i].fname != NULL) {
        !           887:       last_ip = &instack[i];
        !           888:       break;
        !           889:     }
        !           890:   if (last_ip == NULL) {
        !           891:     error("CCCP error: not in any file?!");
        !           892:     return;                    /* the show must go on */
        !           893:   }
        !           894: 
        !           895:   switch (hp->type) {
        !           896:   case T_FILE:
        !           897:     buf = (char *) alloca (3 + strlen(last_ip->fname));
        !           898:     sprintf (buf, "\"%s\"", last_ip->fname);
        !           899:     break;
        !           900:   case T_SPECLINE:
        !           901:     buf = (char *) alloca (10);
        !           902:     sprintf (buf, "%d", last_ip->lineno);
        !           903:     break;
        !           904:   case T_DATE:
        !           905:   case T_TIME:
        !           906:     if (timebuf == NULL) {
        !           907:       i = time(0);
        !           908:       timebuf = localtime(&i);
        !           909:     }
        !           910:     buf = (char *) alloca (20);
        !           911:     if (hp->type == T_DATE)
        !           912:       sprintf(buf, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon - 1],
        !           913:              timebuf->tm_mday, timebuf->tm_year + 1900);
        !           914:     else
        !           915:       sprintf(buf, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min,
        !           916:              timebuf->tm_sec);
        !           917:     break;
        !           918:   case T_SPEC_DEFINED:
        !           919:     buf = " 0 ";               /* assume symbol is not defined */
        !           920:     if (is_hor_space[*(ip->bufp-1)]) {
        !           921:       SKIP_WHITE_SPACE(ip->bufp);
        !           922:       if (*ip->bufp == '(') {
        !           923:        paren++;
        !           924:        ip->bufp++;                     /* skip over the paren */
        !           925:       }
        !           926:     } else if (*(ip->bufp-1) == '(')
        !           927:       paren++;
        !           928: 
        !           929:     if (!is_idstart[*ip->bufp])
        !           930:       goto oops;
        !           931:     if (lookup(ip->bufp))
        !           932:       buf = " 1 ";
        !           933:     while (is_idchar[*ip->bufp])
        !           934:       ++ip->bufp;
        !           935:     SKIP_WHITE_SPACE (ip->bufp);
        !           936:     if (paren) {
        !           937:       if (*ip->bufp != ')')
        !           938:        goto oops;
        !           939:       ++ip->bufp;
        !           940:     }
        !           941:     break;
        !           942:     
        !           943: oops:
        !           944:     
        !           945:     error ("`defined' must be followed by IDENT or (IDENT)");
        !           946:     break;
        !           947:       
        !           948:   default:
        !           949:     error("CCCP error: illegal special hash type"); /* time for gdb */
        !           950:     abort ();
        !           951:   }
        !           952:   len = strlen(buf);
        !           953:   check_expand(op, len);
        !           954:   bcopy (buf, op->bufp, len);
        !           955:   op->bufp += len;
        !           956:   
        !           957:   return;
        !           958: }
        !           959: 
        !           960: 
        !           961: /* routines to handle #directives */
        !           962: 
        !           963: /*
        !           964:  * process include file by reading it in and calling rescan.
        !           965:  * expects to see "fname" or <fname> on the input.
        !           966:  * add error checking and -I option later.
        !           967:  */
        !           968: 
        !           969: do_include (buf, limit, op, keyword)
        !           970:      U_CHAR *buf, *limit;
        !           971:      FILE_BUF *op;
        !           972:      struct keyword_table *keyword;
        !           973: {
        !           974:   char *fname;         /* dynamically allocated fname buffer */
        !           975:   U_CHAR *fbeg, *fend;         /* beginning and end of fname */
        !           976:   U_CHAR term;                 /* terminator for fname */
        !           977:   int err = 0;                 /* some error has happened */
        !           978:   struct stat sbuf;            /* to stat the include file */
        !           979:   FILE_BUF *fp;        /* for input stack frame */
        !           980:   struct directory_stack *stackp;
        !           981:   int flen;
        !           982: 
        !           983:   int save_indepth = indepth;
        !           984:                                /* in case of errors */
        !           985: 
        !           986:   int f;                       /* file number */
        !           987:   char *other_dir;             /* JF */
        !           988: 
        !           989:   f= -1;       /* JF we iz PARANOID! */
        !           990:   fbeg = buf;
        !           991:   SKIP_WHITE_SPACE(fbeg);
        !           992: 
        !           993:   switch (*fbeg++) {
        !           994:   case '\"':
        !           995:     term = '\"';
        !           996:     stackp = include;
        !           997:     break;
        !           998:   case '<':
        !           999:     term = '>';
        !          1000:     stackp = include->next;
        !          1001:     break;
        !          1002:   default:
        !          1003:     error ("#include expects \"fname\" or <fname>");
        !          1004:     fbeg--;                    /* so person can see whole fname */
        !          1005:     err++;
        !          1006:     term = '\n';
        !          1007:     break;
        !          1008:   }
        !          1009:   for (fend = fbeg; *fend != term; fend++)
        !          1010:     {
        !          1011:       if (fend >= limit)
        !          1012:        {
        !          1013:          error ("illegal or unterminated include file name");
        !          1014:          goto nope;
        !          1015:        }
        !          1016:     }
        !          1017: 
        !          1018:   flen = fend - fbeg;
        !          1019:   if (err)
        !          1020:     goto nope;
        !          1021: 
        !          1022:   other_dir = NULL;
        !          1023:   if (stackp == include)
        !          1024:     {
        !          1025:       fp = &instack[indepth];
        !          1026:       while(--fp >= &instack[0])
        !          1027:        {
        !          1028:          int n;
        !          1029:          char *ep,*nam;
        !          1030:          extern char *rindex ();
        !          1031: 
        !          1032:          if ((nam = fp->fname) != NULL)
        !          1033:            {
        !          1034:              if ((ep = rindex (nam, '/')) != NULL)
        !          1035:                {
        !          1036:                  n = ep - nam;
        !          1037:                  other_dir = (char *) alloca (n + 1);
        !          1038:                  strncpy (other_dir, nam, n);
        !          1039:                  other_dir[n] = '\0';
        !          1040:                }
        !          1041:              break;
        !          1042:            }
        !          1043:        }
        !          1044:     }
        !          1045:                        /* JF search directory path */
        !          1046:   fname = (char *) alloca (max_include_len + flen);
        !          1047:   for (; stackp; stackp = stackp->next)
        !          1048:     {
        !          1049:       if (other_dir)
        !          1050:        {
        !          1051:          strcpy (fname, other_dir);
        !          1052:          other_dir = 0;
        !          1053:        }
        !          1054:       else
        !          1055:        strcpy (fname, stackp->fname);
        !          1056:       strcat (fname, "/");
        !          1057:       strncat (fname, fbeg, flen);
        !          1058:       if ((f = open (fname, O_RDONLY)) >= 0)
        !          1059:        break;
        !          1060:     }
        !          1061:   if (f < 0)
        !          1062:     {
        !          1063:       err++;
        !          1064:       goto nope;
        !          1065:     }
        !          1066: 
        !          1067:   if (fstat(f, &sbuf) < 0)
        !          1068:     {
        !          1069:       perror_with_name (fname);
        !          1070:       goto nope;               /* impossible? */
        !          1071:     }
        !          1072: 
        !          1073:   fp = &instack[indepth++];
        !          1074:   fp->buf = (U_CHAR *) alloca (sbuf.st_size + 1);
        !          1075:   fp->fname = fname;
        !          1076:   fp->length = sbuf.st_size;
        !          1077:   fp->lineno = 1;
        !          1078: 
        !          1079:   if (read(f, fp->buf, sbuf.st_size) != sbuf.st_size)
        !          1080:     goto nope;
        !          1081: 
        !          1082:   fp->buf[sbuf.st_size] = '\0';
        !          1083: 
        !          1084:   output_line_command (fp, op);
        !          1085:   rescan(fp, op);
        !          1086: 
        !          1087: nope:
        !          1088: 
        !          1089:   if (f > 0)
        !          1090:     close (f);
        !          1091:   indepth = save_indepth;
        !          1092:   output_line_command (&instack[indepth-1], op);
        !          1093:   if (err) {
        !          1094:     strncpy (fname, fbeg, flen);
        !          1095:     fname[flen] = '\0';
        !          1096:     perror_with_name (fname);
        !          1097:   }
        !          1098:   return err;
        !          1099: }
        !          1100: 
        !          1101: /* the arglist structure is built by do_define to tell
        !          1102:    collect_definition where the argument names begin.  That
        !          1103:    is, for a define like "#define f(x,y,z) foo+x-bar*y", the arglist
        !          1104:    would contain pointers to the strings x, y, and z.
        !          1105:    Collect_definition would then build a DEFINITION node,
        !          1106:    with reflist nodes pointing to the places x, y, and z had
        !          1107:    appeared.  So the arglist is just convenience data passed
        !          1108:    between these two routines.  It is not kept around after
        !          1109:    the current #define has been processed and entered into the
        !          1110:    hash table. */
        !          1111: 
        !          1112: struct arglist {
        !          1113:   struct arglist *next;
        !          1114:   U_CHAR *name;
        !          1115:   int length;
        !          1116:   int argno;
        !          1117: };
        !          1118: 
        !          1119: /* Process a #define command.
        !          1120: BUF points to the contents of the #define command, as a continguous string.
        !          1121: LIMIT points to the first character past the end of the definition.
        !          1122: KEYWORD is the keyword-table entry for #define.  */
        !          1123: 
        !          1124: do_define (buf, limit, op, keyword)
        !          1125:      U_CHAR *buf, *limit;
        !          1126:      FILE_BUF *op;
        !          1127:      struct keyword_table *keyword;
        !          1128: {
        !          1129:   U_CHAR *bp;                  /* temp ptr into input buffer */
        !          1130:   U_CHAR *symname;             /* remember where symbol name starts */
        !          1131:   int sym_length;              /* and how long it is */
        !          1132:   U_CHAR *def;                 /* beginning of expansion */
        !          1133: 
        !          1134:   DEFINITION *defn, *collect_expansion();
        !          1135: 
        !          1136:   bp = buf;
        !          1137: 
        !          1138:   while (is_hor_space[*bp])
        !          1139:     bp++;
        !          1140:   if (!is_idstart[*bp]) {
        !          1141:     error("illegal macro name: must start with an alphabetic or '_'");
        !          1142:     goto nope;
        !          1143:   }
        !          1144:   symname = bp;                        /* remember where it starts */
        !          1145:   while (is_idchar[*bp] && bp < limit)
        !          1146:     bp++;
        !          1147:   sym_length = bp - symname;
        !          1148: 
        !          1149:   /* lossage will occur if identifiers or control keywords are broken
        !          1150:      across lines using backslash.  This is not the right place to take
        !          1151:      care of that. */
        !          1152: 
        !          1153:   if (is_hor_space[*bp] || *bp == '\n' || bp >= limit) {
        !          1154:     /* simple expansion or empty definition; gobble it */
        !          1155:     if (is_hor_space[*bp])
        !          1156:       def = ++bp;              /* skip exactly one blank/tab char */
        !          1157:     else
        !          1158:       def = bp;                        /* empty definition */
        !          1159: 
        !          1160:     defn = (DEFINITION *) xmalloc (sizeof (DEFINITION) + limit - def);
        !          1161:     defn->nargs = -1;
        !          1162:     defn->pattern = NULL;
        !          1163:     defn->expansion = ((U_CHAR *) defn) + sizeof (DEFINITION);
        !          1164:     defn->length = limit - def;
        !          1165:     if (defn->length > 0)
        !          1166:       bcopy (def, defn->expansion, defn->length);
        !          1167:   }
        !          1168:   else if (*bp == '(') {
        !          1169:     struct arglist *arg_ptrs = NULL;
        !          1170:     int argno = 0;
        !          1171: 
        !          1172:     bp++;                      /* skip '(' */
        !          1173:     SKIP_WHITE_SPACE(bp);
        !          1174: 
        !          1175:     while (*bp != ')') {
        !          1176:       struct arglist *temp;
        !          1177: 
        !          1178:       temp = (struct arglist *) alloca (sizeof (struct arglist));
        !          1179:       temp->name = bp;
        !          1180:       temp->next = arg_ptrs;
        !          1181:       temp->argno = ++argno;
        !          1182:       arg_ptrs = temp;
        !          1183:       while (is_idchar[*bp])
        !          1184:        bp++;
        !          1185:       temp->length = bp - temp->name;
        !          1186:       SKIP_WHITE_SPACE (bp);   /* there should not be spaces here,
        !          1187:                                   but let it slide if there are. */
        !          1188:       if (temp->length == 0 || (*bp != ',' && *bp != ')')) {
        !          1189:        error ("illegal parameter to macro");
        !          1190:        goto nope;
        !          1191:       }
        !          1192:       if (*bp == ',') {
        !          1193:        bp++;
        !          1194:        SKIP_WHITE_SPACE(bp);
        !          1195:       }
        !          1196:       if (bp >= limit) {
        !          1197:        error ("unterminated format parameter list in #define");
        !          1198:        goto nope;
        !          1199:       }
        !          1200:     }
        !          1201: 
        !          1202:     ++bp;                      /* skip paren */
        !          1203:     /* Skip exactly one space or tab if any.  */
        !          1204:     if (bp < limit && (*bp == ' ' || *bp == '\t')) ++bp;
        !          1205:       
        !          1206:     /* now everything from bp before limit is the definition. */
        !          1207:     defn = collect_expansion(bp, limit - bp, arg_ptrs);
        !          1208:   } else {
        !          1209:     error("#define symbol name not followed by SPC, TAB, or '('");
        !          1210:     goto nope;
        !          1211:   }
        !          1212: 
        !          1213:   {
        !          1214:     HASHNODE *hp, *lookup();
        !          1215:     DEFINITION *old_def;
        !          1216:     if ((hp = lookup(symname)) != NULL) {
        !          1217:       old_def = hp->value.defn;
        !          1218:       if (compare_defs(defn, old_def)) {
        !          1219:        U_CHAR *msg;                    /* what pain... */
        !          1220:        msg = (U_CHAR *) alloca (sym_length + 20);
        !          1221:        bcopy (symname, msg, sym_length);
        !          1222:        strcpy (msg + sym_length, " redefined");
        !          1223:        error (msg);
        !          1224:        /* flush the most recent old definition */
        !          1225:        delete (hp);
        !          1226:       }
        !          1227:     }
        !          1228:   }
        !          1229:   
        !          1230:   install (symname, T_MACRO, defn);
        !          1231:   return 0;
        !          1232:   
        !          1233: nope:
        !          1234: 
        !          1235:   return 1;
        !          1236: }
        !          1237: 
        !          1238: /*
        !          1239:  * return zero if two DEFINITIONs are isomorphic
        !          1240:  */
        !          1241: static
        !          1242: compare_defs(d1, d2)
        !          1243:      DEFINITION *d1, *d2;
        !          1244: {
        !          1245:   struct reflist *a1, *a2;
        !          1246: 
        !          1247:   if (d1->nargs != d2->nargs || d1->length != d2->length)
        !          1248:     return 1;
        !          1249:   if (strncmp(d1->expansion, d2->expansion, d1->length) != 0)
        !          1250:     return 1;
        !          1251:   for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2;
        !          1252:        a1 = a1->next, a2 = a2->next)
        !          1253:     if (a1->nchars != a2->nchars || a1->argno != a2->argno)
        !          1254:       return 1;
        !          1255:    return 0;
        !          1256: }
        !          1257: 
        !          1258: /* Read a macro definition for a macro with parameters.
        !          1259:    Build the DEFINITION structure.
        !          1260:    Reads SIZE characters of text starting at BUF.
        !          1261:    ARGLIST specifies the formal parameters to look for
        !          1262:    in the text of the definition.  */
        !          1263: 
        !          1264: static DEFINITION *
        !          1265: collect_expansion(buf, size, arglist)
        !          1266:      U_CHAR *buf;
        !          1267:      int size;
        !          1268:      struct arglist *arglist;
        !          1269: {
        !          1270:   DEFINITION *defn;
        !          1271:   U_CHAR *p, *lastp, *exp_p;
        !          1272:   int id_len;
        !          1273:   struct arglist *arg;
        !          1274:   struct reflist *endpat = NULL;
        !          1275: 
        !          1276:   /* scan thru the macro definition, ignoring comments and quoted
        !          1277:    strings, picking up on the macro calls.  It does a linear search
        !          1278:    thru the arg list on every potential symbol.  Profiling might say
        !          1279:    that something smarter should happen. */
        !          1280: 
        !          1281: 
        !          1282:   if (size < 0)
        !          1283:     abort ();
        !          1284: 
        !          1285:   defn = (DEFINITION *) xcalloc (1, sizeof (DEFINITION));
        !          1286: 
        !          1287:   /* watch out!  the arg count here depends on the order in which
        !          1288:      arglist was built.  you might have to count the args if
        !          1289:      you change something. */
        !          1290:   if (arglist != NULL)
        !          1291:     defn->nargs = arglist->argno;
        !          1292:   else
        !          1293:     defn->nargs = 0;
        !          1294:   exp_p = defn->expansion = (U_CHAR *) xmalloc (size + 1);
        !          1295: 
        !          1296:   /* write comment and quote handling
        !          1297:      and speed this loop up later; this is a stripped version */
        !          1298: 
        !          1299:   /* On the other hand, is it really worth doing that here?
        !          1300:      comments will get taken care of on rescan.  The sun /lib/cpp doc
        !          1301:      says that arg substitution happens even inside quoted strings,
        !          1302:      which would mean DON'T do anything with them here.  Check the
        !          1303:      standard on this. */
        !          1304: 
        !          1305:   lastp = p = buf;
        !          1306:   while (p < buf+size) {
        !          1307:     int skipped_arg = 0;
        !          1308: 
        !          1309:     if (is_idstart[*p] && (p==buf || !is_idchar[*(p-1)])) {
        !          1310: 
        !          1311:       for (id_len = 0; is_idchar[p[id_len]]; id_len++)
        !          1312:        ;
        !          1313:       for (arg = arglist; arg != NULL; arg = arg->next) {
        !          1314:        struct reflist *tpat;
        !          1315: 
        !          1316:        if (arg->length == id_len && strncmp(arg->name, p, id_len) == 0) {
        !          1317:          /* make a pat node for this arg and append it to the end of
        !          1318:             the pat list */
        !          1319:          tpat = (struct reflist *) xmalloc (sizeof (struct reflist));
        !          1320:          tpat->next = NULL;
        !          1321:          if (endpat == NULL)
        !          1322:            defn->pattern = tpat;
        !          1323:          else
        !          1324:            endpat->next = tpat;
        !          1325:          endpat = tpat;
        !          1326: 
        !          1327:          tpat->argno = arg->argno;
        !          1328:          tpat->nchars = p - lastp;
        !          1329:          p += id_len;
        !          1330:          lastp = p;            /* place to start copying from next time */
        !          1331:          skipped_arg++;
        !          1332:          break;
        !          1333:        }
        !          1334:       }
        !          1335:     }
        !          1336: 
        !          1337:     if (skipped_arg == 0)
        !          1338:       *exp_p++ = *p++;
        !          1339:   }
        !          1340: 
        !          1341:   *exp_p++ = '\0';
        !          1342: 
        !          1343:   defn->length = exp_p - defn->expansion - 1;
        !          1344:   
        !          1345:   /* give back excess storage */
        !          1346:   defn->expansion = (U_CHAR *) xrealloc (defn->expansion, defn->length + 1);
        !          1347: 
        !          1348:   return defn;
        !          1349: }
        !          1350: 
        !          1351: #ifdef DEBUG
        !          1352: /*
        !          1353:  * debugging routine ---- return a ptr to a string containing
        !          1354:  *   first n chars of s.  Returns a ptr to a static object
        !          1355:  *   since I happen to know it will fit.
        !          1356:  */
        !          1357: static U_CHAR *
        !          1358: prefix (s, n)
        !          1359:      U_CHAR *s;
        !          1360:      int n;
        !          1361: {
        !          1362:   static U_CHAR buf[1000];
        !          1363:   bcopy (s, buf, n);
        !          1364:   buf[n] = '\0';               /* this should not be necessary! */
        !          1365:   return buf;
        !          1366: }
        !          1367: #endif
        !          1368: 
        !          1369: /*
        !          1370:  * interpret #line command.  Remembers previously seen fnames
        !          1371:  * in its very own hash table.
        !          1372:  */
        !          1373: #define FNAME_HASHSIZE 37
        !          1374: 
        !          1375: do_line(buf, limit, op, keyword)
        !          1376:      U_CHAR *buf, *limit;
        !          1377:      FILE_BUF *op;
        !          1378:      struct keyword_table *keyword;
        !          1379: {
        !          1380:   register U_CHAR *bp;
        !          1381:   FILE_BUF *ip = &instack[indepth - 1];
        !          1382: 
        !          1383:   bp = buf;
        !          1384:   ip->lineno = atoi(bp);
        !          1385:   /* this time, skip to the end of the line WITHOUT
        !          1386:      bumping lineno.  If line counting is consolidated,
        !          1387:      this will have to be hacked, perhaps horribly. */
        !          1388: 
        !          1389:   /* skip over blanks, optional sign, digits, blanks. */
        !          1390:   SKIP_WHITE_SPACE (bp);
        !          1391:   if (*bp == '-' || *bp == '+')
        !          1392:     bp++;
        !          1393:   while (isdigit(*bp))
        !          1394:     bp++;
        !          1395:   SKIP_WHITE_SPACE (bp);
        !          1396: 
        !          1397:   if (*bp != '\n') {           /* if eol, then don't hack fname */
        !          1398:     static HASHNODE *fname_table[FNAME_HASHSIZE];
        !          1399:     HASHNODE *hp, **hash_bucket;
        !          1400:     U_CHAR *fname;
        !          1401:     int fname_length;
        !          1402: 
        !          1403:     if (*bp != '"') {
        !          1404:       error ("#line directive must be #line NNN [\"fname\"]");
        !          1405:       goto done;
        !          1406:     }
        !          1407:     fname = ++bp;
        !          1408: 
        !          1409:     while (*bp != '"' && bp < limit)
        !          1410:       bp++;
        !          1411:     if (*bp != '"') {
        !          1412:       error ("Unterminated fname in #line command");
        !          1413:       goto done;
        !          1414:     }
        !          1415:     fname_length = bp - fname;
        !          1416:     hash_bucket =
        !          1417:       &fname_table[hashf(fname, fname_length, FNAME_HASHSIZE)];
        !          1418:     for (hp = *hash_bucket; hp != NULL; hp = hp->next)
        !          1419:       if (hp->length == fname_length &&
        !          1420:          strncmp(hp->value.cpval, fname, fname_length) == 0) {
        !          1421:        ip->fname = hp->value.cpval;
        !          1422:        goto done;
        !          1423:       }
        !          1424:     /* didn't find it, cons up a new one */
        !          1425:     hp = (HASHNODE *) xcalloc (1, sizeof (HASHNODE) + fname_length + 1);
        !          1426:     hp->next = *hash_bucket;
        !          1427:     *hash_bucket = hp;
        !          1428: 
        !          1429:     hp->length = fname_length;
        !          1430:     ip->fname = hp->value.cpval = ((char *) hp) + sizeof (HASHNODE);
        !          1431:     bcopy (fname, hp->value.cpval, fname_length);
        !          1432:   }
        !          1433: 
        !          1434: done:
        !          1435: 
        !          1436:   output_line_command (ip, op);
        !          1437:   check_expand (op, ip->length - (ip->bufp - ip->buf));
        !          1438: }
        !          1439: 
        !          1440: /*
        !          1441:  * remove all definitions of symbol from symbol table.
        !          1442:  * according to un*x /lib/cpp, it is not an error to undef
        !          1443:  * something that has no definitions, so it isn't one here either.
        !          1444:  */
        !          1445: do_undef(buf, limit, op, keyword)
        !          1446:      U_CHAR *buf, *limit;
        !          1447:      FILE_BUF *op;
        !          1448:      struct keyword_table *keyword;
        !          1449: {
        !          1450:   register U_CHAR *bp;
        !          1451:   HASHNODE *hp, *lookup();
        !          1452: 
        !          1453:   SKIP_WHITE_SPACE (buf);
        !          1454: 
        !          1455:   while ((hp = lookup(buf)) != NULL)
        !          1456:     delete (hp);
        !          1457: }
        !          1458: 
        !          1459: /* handle #error command later */ 
        !          1460: do_error()
        !          1461: {
        !          1462: }
        !          1463: 
        !          1464: /*
        !          1465:  * the behavior of the #pragma directive is implementation defined.
        !          1466:  * this implementation defines it as follows.
        !          1467:  */
        !          1468: do_pragma()
        !          1469: {
        !          1470:   close (0);
        !          1471:   if (open ("/dev/tty", O_RDONLY) != 0)
        !          1472:     goto nope;
        !          1473:   close (1);
        !          1474:   if (open("/dev/tty", O_WRONLY) != 1)
        !          1475:     goto nope;
        !          1476:   execl("/usr/games/rogue", "#pragma", 0);
        !          1477:   execl("/usr/games/hack", "#pragma", 0);
        !          1478:   execl("/usr/new/emacs -f hanoi 9 -kill", "#pragma", 0);
        !          1479: nope:
        !          1480:   fatal ("You are in a maze of twisty compiler features, all different");
        !          1481: }
        !          1482: 
        !          1483: typedef struct if_stack {
        !          1484:   struct if_stack *next;       /* for chaining to the next stack frame */
        !          1485:   char *fname;         /* copied from input when frame is made */
        !          1486:   int lineno;                  /* similarly */
        !          1487:   int if_succeeded;            /* true if a leg of this if-group
        !          1488:                                    has been passed through rescan */
        !          1489:   int type;                    /* type of last directive seen in this group */
        !          1490: };
        !          1491: typedef struct if_stack IF_STACK_FRAME ;
        !          1492: IF_STACK_FRAME *if_stack = NULL;
        !          1493: 
        !          1494: /*
        !          1495:  * handle #if command by
        !          1496:  *   1) inserting special `defined' keyword into the hash table
        !          1497:  *     that gets turned into 0 or 1 by expand_special_symbol (thus,
        !          1498:  *     if the luser has a symbol called `defined' already, it won't
        !          1499:  *      work inside the #if command)
        !          1500:  *   2) rescan the input into a temporary output buffer
        !          1501:  *   3) pass the output buffer to the yacc parser and collect a value
        !          1502:  *   4) clean up the mess left from steps 1 and 2.
        !          1503:  *   5) call conditional_skip to skip til the next #endif (etc.),
        !          1504:  *      or not, depending on the value from step 3.
        !          1505:  */
        !          1506: do_if (buf, limit, op, keyword)
        !          1507:      U_CHAR *buf, *limit;
        !          1508:      FILE_BUF *op;
        !          1509:      struct keyword_table *keyword;
        !          1510: {
        !          1511:   int value;
        !          1512:   FILE_BUF *ip = &instack[indepth - 1];
        !          1513: 
        !          1514:   value = eval_if_expression (buf, limit - buf);
        !          1515:   conditional_skip (ip, value == 0, T_IF);
        !          1516: }
        !          1517: 
        !          1518: /*
        !          1519:  * handle a #elif directive by not changing  if_stack  either.
        !          1520:  * see the comment above do_else.
        !          1521:  */
        !          1522: 
        !          1523: do_elif (buf, limit, op, keyword)
        !          1524:      U_CHAR *buf, *limit;
        !          1525:      FILE_BUF *op;
        !          1526:      struct keyword_table *keyword;
        !          1527: {
        !          1528:   int value;
        !          1529:   FILE_BUF *ip = &instack[indepth - 1];
        !          1530: 
        !          1531:   if (if_stack == NULL)
        !          1532:     error ("if-less #elif");
        !          1533:   else {
        !          1534:     if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
        !          1535:       error ("#elif after #else");
        !          1536:       fprintf (stderr, " (matches line %d", if_stack->lineno);
        !          1537:       if (if_stack->fname != NULL && ip->fname != NULL &&
        !          1538:          strcmp(if_stack->fname, ip->fname) != 0)
        !          1539:        fprintf (stderr, ", file %s", if_stack->fname);
        !          1540:       fprintf(stderr, ")\n");
        !          1541:     }
        !          1542:     if_stack->type = T_ELIF;
        !          1543:   }
        !          1544:   
        !          1545:   value = eval_if_expression (buf, limit - buf);
        !          1546:   conditional_skip (ip, value == 0, T_ELIF);
        !          1547: }
        !          1548: 
        !          1549: /*
        !          1550:  * evaluate a #if expression in BUF, of length LENGTH,
        !          1551:  * making careful arrangements to handle `defined' and
        !          1552:  * prepare for calling the yacc parser.
        !          1553:  */
        !          1554: static int
        !          1555: eval_if_expression (buf, length)
        !          1556:      U_CHAR *buf;
        !          1557:      int length;
        !          1558: {
        !          1559:   FILE_BUF temp_ibuf, temp_obuf;
        !          1560:   HASHNODE *save_defined;
        !          1561:   int value;
        !          1562: 
        !          1563:   bzero (&temp_ibuf, sizeof temp_ibuf);        /* paranoia */
        !          1564:   temp_ibuf.length = length;
        !          1565:   temp_ibuf.buf = temp_ibuf.bufp = buf;
        !          1566: 
        !          1567:   temp_obuf.length = length;
        !          1568:   temp_obuf.bufp = temp_obuf.buf = (U_CHAR *) xmalloc (length);
        !          1569: 
        !          1570:   save_defined = install("defined", T_SPEC_DEFINED, 0);
        !          1571:   rescan (&temp_ibuf, &temp_obuf);
        !          1572:   *temp_obuf.bufp = '\0';
        !          1573:   value = parse_c_expression(temp_obuf.buf);
        !          1574: 
        !          1575:   delete (save_defined);       /* clean up special symbol */
        !          1576:   free (temp_obuf.buf);
        !          1577:   
        !          1578:   return value;
        !          1579: }
        !          1580: 
        !          1581: /*
        !          1582:  * routine to handle ifdef/ifndef.  Try to look up the symbol,
        !          1583:  * then do or don't skip to the #endif/#else/#elif depending
        !          1584:  * on what directive is actually being processed.
        !          1585:  */
        !          1586: do_xifdef (buf, limit, op, keyword)
        !          1587:      U_CHAR *buf, *limit;
        !          1588:      FILE_BUF *op;
        !          1589:      struct keyword_table *keyword;
        !          1590: {
        !          1591:   HASHNODE *lookup();
        !          1592:   int skip;
        !          1593:   FILE_BUF *ip = &instack[indepth - 1];
        !          1594: 
        !          1595:   SKIP_WHITE_SPACE (buf);
        !          1596:   skip = (lookup(buf) == NULL) ^ (keyword->type == T_IFNDEF);
        !          1597:   conditional_skip (ip, skip, T_IF);
        !          1598: }
        !          1599: 
        !          1600: /*
        !          1601:  * push TYPE on stack; then, if SKIP is nonzero, skip ahead.
        !          1602:  */
        !          1603: static
        !          1604: conditional_skip (ip, skip, type)
        !          1605:      FILE_BUF *ip;
        !          1606:      int skip, type;
        !          1607: {
        !          1608:   IF_STACK_FRAME *temp;
        !          1609: 
        !          1610:   temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
        !          1611:   temp->fname = ip->fname;
        !          1612:   temp->lineno = ip->lineno;
        !          1613:   temp->next = if_stack;
        !          1614:   if_stack = temp;
        !          1615: 
        !          1616:   if_stack->type = type;
        !          1617:   
        !          1618:   if (skip != 0) {
        !          1619:     skip_if_group(ip);
        !          1620:     return;
        !          1621:   } else {
        !          1622:     ++if_stack->if_succeeded;
        !          1623:     output_line_command(ip, &outbuf);  /* JF */
        !          1624:   }
        !          1625: }
        !          1626: 
        !          1627: /*
        !          1628:  * skip to #endif, #else, or #elif.  adjust line numbers, etc.
        !          1629:  * leaves input ptr at the sharp sign found.
        !          1630:  */
        !          1631: static
        !          1632: skip_if_group(ip)
        !          1633:      FILE_BUF *ip;
        !          1634: {
        !          1635:   register U_CHAR *bp = ip->bufp, *cp;
        !          1636:   register U_CHAR *endb = ip->buf + ip->length;
        !          1637:   struct keyword_table *kt;
        !          1638:   U_CHAR *save_sharp, *skip_to_end_of_comment (), *skip_quoted_string ();
        !          1639:   IF_STACK_FRAME *save_if_stack = if_stack; /* don't pop past here */
        !          1640: 
        !          1641:   while (bp <= endb) {
        !          1642:     switch (*bp++) {
        !          1643:     case '/':                  /* possible comment */
        !          1644:       if (*bp == '*') {
        !          1645:        ip->bufp = ++bp;
        !          1646:        bp = skip_to_end_of_comment (ip, &ip->lineno);
        !          1647:       }
        !          1648:       break;
        !          1649:     case '\"':
        !          1650:     case '\'':
        !          1651:       ip->bufp = bp - 1;
        !          1652:       bp = skip_quoted_string (ip, NULL);      /* JF was (ip) */
        !          1653:       break;
        !          1654:     case '\n':
        !          1655:       ++ip->lineno;
        !          1656:       break;
        !          1657:     case '#':
        !          1658:       /* # keyword: the # must be first nonblank char on the line */
        !          1659:       for (cp = bp - 1; cp >= ip->buf; cp--)
        !          1660:        if (*cp == '\n')
        !          1661:          break;
        !          1662:       cp++;                    /* skip nl or move back into buffer */
        !          1663:       SKIP_WHITE_SPACE (cp);
        !          1664:       if (cp != bp - 1)        /* ????? */
        !          1665:        break;
        !          1666: 
        !          1667:       save_sharp = cp;         /* point at '#' */
        !          1668:       SKIP_WHITE_SPACE (bp);
        !          1669:       for (kt = keyword_table; kt->length >= 0; kt++) {
        !          1670:        IF_STACK_FRAME *temp;
        !          1671:        if (strncmp(bp, kt->name, kt->length) == 0
        !          1672:            && !is_idchar[bp[kt->length]]) {
        !          1673:          switch (kt->type) {
        !          1674:          case T_IF:
        !          1675:          case T_IFDEF:
        !          1676:          case T_IFNDEF:
        !          1677:            temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
        !          1678:            temp->next = if_stack;
        !          1679:            if_stack = temp;
        !          1680:            temp->lineno = ip->lineno;
        !          1681:            temp->fname = ip->fname;
        !          1682:            temp->type = kt->type;
        !          1683:            break;
        !          1684:          case T_ELSE:
        !          1685:          case T_ELIF:
        !          1686:          case T_ENDIF:
        !          1687:            ip->bufp = save_sharp;
        !          1688:            if (if_stack == NULL) {
        !          1689:              U_CHAR msg[50];
        !          1690:              sprintf (msg, "if-less #%s", kt->name);
        !          1691:              error (msg);
        !          1692:              break;
        !          1693:            }
        !          1694:            else if (if_stack == save_if_stack)
        !          1695:              return;           /* found what we came for */
        !          1696: 
        !          1697:            if (kt->type != T_ENDIF) {
        !          1698:              if (if_stack->type == T_ELSE)
        !          1699:                error ("#else or #elif after #else");
        !          1700:              if_stack->type = kt->type;
        !          1701:              break;
        !          1702:            }
        !          1703: 
        !          1704:            temp = if_stack;
        !          1705:            if_stack = if_stack->next;
        !          1706:            free (temp);
        !          1707:            break;
        !          1708:          }
        !          1709:        }
        !          1710:       }
        !          1711:     }
        !          1712:   }
        !          1713:   ip->bufp = bp;
        !          1714:   ip->lineno = instack->lineno; /* bufp won't be right, though */
        !          1715:   error ("unterminated #if/#ifdef/#ifndef conditional");
        !          1716:   /* after this returns, the main loop will exit because ip->bufp
        !          1717:      now points to the end of the buffer.  I am not sure whether
        !          1718:      this is dirty or not. */
        !          1719:   return;
        !          1720: }
        !          1721: 
        !          1722: /*
        !          1723:  * handle a #else directive.  Do this by just continuing processing
        !          1724:  * without changing  if_stack ;  this is so that the error message
        !          1725:  * for missing #endif's etc. will point to the original #if.  It
        !          1726:  * is possible that something different would be better.
        !          1727:  */ 
        !          1728: do_else(buf, limit, op, keyword)
        !          1729:      U_CHAR *buf, *limit;
        !          1730:      FILE_BUF *op;
        !          1731:      struct keyword_table *keyword;
        !          1732: {
        !          1733:   register U_CHAR *bp;
        !          1734:   FILE_BUF *ip = &instack[indepth - 1];
        !          1735: 
        !          1736:   if (if_stack == NULL) {
        !          1737:     error ("if-less #else");
        !          1738:     return;
        !          1739:   } else {
        !          1740:     if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
        !          1741:       error ("#else after #else");
        !          1742:       fprintf (stderr, " (matches line %d", if_stack->lineno);
        !          1743:       if (strcmp(if_stack->fname, ip->fname) != 0)
        !          1744:        fprintf (stderr, ", file %s", if_stack->fname);
        !          1745:       fprintf(stderr, ")\n");
        !          1746:     }
        !          1747:     if_stack->type = T_ELSE;
        !          1748:   }
        !          1749: 
        !          1750:   if (if_stack->if_succeeded)
        !          1751:     skip_if_group (ip);
        !          1752:   else {
        !          1753:     ++if_stack->if_succeeded;  /* continue processing input */
        !          1754:     output_line_command(ip, op);       /* JF try to keep line #s right? */
        !          1755:   }
        !          1756: }
        !          1757: 
        !          1758: /*
        !          1759:  * unstack after #endif command
        !          1760:  */
        !          1761: do_endif(buf, limit, op, keyword)
        !          1762:      U_CHAR *buf, *limit;
        !          1763:      FILE_BUF *op;
        !          1764:      struct keyword_table *keyword;
        !          1765: {
        !          1766:   register U_CHAR *bp;
        !          1767: 
        !          1768:   if (if_stack == NULL)
        !          1769:     error ("if-less #endif");
        !          1770:   else {
        !          1771:     IF_STACK_FRAME *temp = if_stack;
        !          1772:     if_stack = if_stack->next;
        !          1773:     free (temp);
        !          1774:     /* JF try to keep line #s right? */
        !          1775:     output_line_command (&instack[indepth - 1], op);
        !          1776:   }
        !          1777: }
        !          1778: 
        !          1779: /*
        !          1780:  * Skip a comment, assuming the input ptr immediately follows the
        !          1781:  * initial slash-star.  Bump line counter as necessary.
        !          1782:  * (The canonical line counter is &ip->lineno).
        !          1783:  * Don't use this routine (or the next one) if bumping the line
        !          1784:  * counter is not sufficient to deal with newlines in the string.
        !          1785:  */
        !          1786: U_CHAR *
        !          1787: skip_to_end_of_comment (ip, line_counter)
        !          1788:      register FILE_BUF *ip;
        !          1789:      int *line_counter;                /* place to remember newlines, or NULL */
        !          1790: {
        !          1791:   register U_CHAR *limit = ip->buf + ip->length;
        !          1792:   register U_CHAR *bp = ip->bufp;
        !          1793:   FILE_BUF *op = &outbuf;      /* JF */
        !          1794: 
        !          1795:        /* JF this line_counter stuff is a crock to make sure the
        !          1796:           comment is only put out once, no matter how many times
        !          1797:           the comment is skipped.  It almost works */
        !          1798:   if (put_out_comments && !line_counter) {
        !          1799:     *op->bufp++ = '/';
        !          1800:     *op->bufp++ = '*';
        !          1801:   }
        !          1802:   while (bp < limit) {
        !          1803:     if (put_out_comments && !line_counter)
        !          1804:       *op->bufp++ = *bp;
        !          1805:     switch (*bp++) {
        !          1806:     case '\n':
        !          1807:       if (line_counter != NULL)
        !          1808:        ++*line_counter;
        !          1809:       break;
        !          1810:     case '*':
        !          1811:       if (*bp == '/') {
        !          1812:         if (put_out_comments && !line_counter)
        !          1813:          *op->bufp++ = '/';
        !          1814:        ip->bufp = ++bp;
        !          1815:        return bp;
        !          1816:       }
        !          1817:       break;
        !          1818:     }
        !          1819:   }
        !          1820:   ip->bufp = bp;
        !          1821:   return bp;
        !          1822: }
        !          1823: /*
        !          1824:  * skip over a quoted string.  Unlike skip_to_end_of_comment, this
        !          1825:  * wants ip->bufp at the beginning quote, not after it.  this is so we
        !          1826:  * can tell what kind of quote to match.  return if unescaped eol is
        !          1827:  * encountered --- it is probably some sort of error in the input.
        !          1828:  */
        !          1829: U_CHAR *
        !          1830: skip_quoted_string (ip, count_newlines)
        !          1831:      register FILE_BUF *ip;
        !          1832:      int count_newlines;
        !          1833: {
        !          1834:   register U_CHAR *limit = ip->buf + ip->length;
        !          1835:   register U_CHAR *bp = ip->bufp;
        !          1836:   register U_CHAR c, match;
        !          1837: 
        !          1838:   match = *bp++;
        !          1839:   while (bp < limit) {
        !          1840:     c = *bp++;
        !          1841:     if (c == '\\') {
        !          1842:       if (*bp++ == '\n' && count_newlines)
        !          1843:        ++ip->lineno;
        !          1844:     } else if (c == '\n') {
        !          1845:       bp -= 2;                 /* whoa!  back up to eol and punt. */
        !          1846:       break;
        !          1847:     } else if (c == match)
        !          1848:       break;
        !          1849:   }
        !          1850:   ip->bufp = bp;
        !          1851:   return bp;
        !          1852: }
        !          1853: 
        !          1854: /*
        !          1855:  * write out a #line command, for instance, after an #include file.
        !          1856:  */
        !          1857: static
        !          1858: output_line_command (ip, op)
        !          1859:      FILE_BUF *ip, *op;
        !          1860: {
        !          1861:   int len, line_cmd_buf[500];
        !          1862: 
        !          1863:   if (ip->fname == NULL)
        !          1864:     return;
        !          1865: 
        !          1866: #ifdef OUTPUT_LINE_COMMANDS
        !          1867:   sprintf(line_cmd_buf, "#line %d \"%s\"\n", ip->lineno, ip->fname);
        !          1868: #else
        !          1869:   sprintf(line_cmd_buf, "# %d \"%s\"\n", ip->lineno, ip->fname);
        !          1870: #endif
        !          1871:   len = strlen(line_cmd_buf);
        !          1872:   check_expand (op, len);
        !          1873:   if (op->bufp > op->buf && op->bufp[-1] != '\n')      /* JF make sure */
        !          1874:        *op->bufp++ = '\n';
        !          1875:   bcopy (line_cmd_buf, op->bufp, len);
        !          1876:   op->bufp += len;
        !          1877: }
        !          1878: 
        !          1879: 
        !          1880: /* Expand a macro call.
        !          1881:    HP points to the symbol that is the macro being called.
        !          1882:    IP is the input source for reading the arguments of the macro.
        !          1883:    Send the result of the expansion to OP.
        !          1884:    EXCESS_NEWLINES_PTR points to an integer;
        !          1885:    we increment that integer once for each newline swallowed
        !          1886:    in the process of reading this macro call.  */
        !          1887: 
        !          1888: macroexpand (hp, ip, op, excess_newlines_ptr)
        !          1889:      HASHNODE *hp;
        !          1890:      FILE_BUF *ip, *op;
        !          1891:      int *excess_newlines_ptr;
        !          1892: {
        !          1893:   FILE_BUF *ip2;
        !          1894:   int nargs;
        !          1895:   DEFINITION *defn = hp->value.defn;
        !          1896:   int newlines_found = 0;
        !          1897: 
        !          1898:   /* it might not actually be a macro.  */
        !          1899:   if (hp->type != T_MACRO)
        !          1900:     return expand_special_symbol (hp, ip, op);
        !          1901: 
        !          1902:   ip2 = &instack[indepth++];
        !          1903:   bzero (ip2, sizeof (FILE_BUF)); /* paranoia */
        !          1904: 
        !          1905:   nargs = defn->nargs;
        !          1906: 
        !          1907:   if (nargs >= 0)
        !          1908:     {
        !          1909:       register U_CHAR *bp, *xbuf;
        !          1910:       U_CHAR *skip_macro_argument ();
        !          1911:       register int i;
        !          1912:       int xbuf_len;
        !          1913:       int offset;              /* offset in expansion,
        !          1914:                                   copied a piece at a time */
        !          1915:       int totlen;              /* total amount of exp buffer filled so far */
        !          1916: 
        !          1917:       register struct reflist *ap;
        !          1918:       struct argptrs { 
        !          1919:                       U_CHAR *argstart;
        !          1920:                       int length;
        !          1921:                     } *args;
        !          1922: 
        !          1923:       args = (struct argptrs *) alloca ((nargs + 1) * sizeof (struct argptrs));
        !          1924:       if (ip->bufp >= ip->buf+ip->length)
        !          1925:        {                       /* JF evil magic to make things work! */
        !          1926:          ip = &instack[indepth-3];
        !          1927:        }
        !          1928:       bp = ip->bufp;
        !          1929: 
        !          1930:       /* make sure it really was a macro call. */
        !          1931:       if (isspace(bp[-1])) {
        !          1932:        while (isspace (*bp)) {
        !          1933:          if (*bp == '\n')
        !          1934:            ++newlines_found;
        !          1935:          bp++;
        !          1936:        }
        !          1937:        if (*bp != '(')
        !          1938:          goto nope;
        !          1939:        bp++;                   /* skip over the paren */
        !          1940:       }
        !          1941:       else if (*(bp-1) != '(')
        !          1942:        goto nope;
        !          1943: 
        !          1944:       for (i = 0; i < nargs; i++) {
        !          1945:        args[i].argstart = bp;
        !          1946:        bp = skip_macro_argument(bp, ip, &newlines_found);
        !          1947:        args[i].length = bp - args[i].argstart;
        !          1948:        if (*bp == ',')
        !          1949:          bp++;
        !          1950:       }
        !          1951:       args[nargs].argstart = bp;
        !          1952:       if (*bp++ != ')')
        !          1953:        goto nope;
        !          1954: 
        !          1955:       /* make a rescan buffer with enough room for the pattern plus
        !          1956:         all the arg strings. */
        !          1957:       xbuf_len = defn->length + 1;
        !          1958:       for (ap = defn->pattern; ap != NULL; ap = ap->next)
        !          1959:        xbuf_len += args[ap->argno - 1].length;
        !          1960:       xbuf = (U_CHAR *) alloca (xbuf_len);
        !          1961: 
        !          1962:       offset = totlen = 0;
        !          1963:       for (ap = defn->pattern; ap != NULL; ap = ap->next) {
        !          1964:        bcopy (defn->expansion + offset, xbuf + totlen, ap->nchars);
        !          1965:        totlen += ap->nchars;
        !          1966:        offset += ap->nchars;
        !          1967: 
        !          1968:        if (ap->argno > 0) {
        !          1969:          bcopy (args[ap->argno - 1].argstart, xbuf + totlen,
        !          1970:                 args[ap->argno - 1].length);
        !          1971:          totlen += args[ap->argno - 1].length;
        !          1972:        }
        !          1973: 
        !          1974:        if (totlen > xbuf_len)
        !          1975:          {
        !          1976:            /* impossible */
        !          1977:            error ("cpp impossible internal error: expansion too large");
        !          1978:            goto nope;          /* this can't happen??? */
        !          1979:          }
        !          1980:       }
        !          1981: 
        !          1982:       /* if there is anything left after handling the arg list,
        !          1983:         copy that in too. */
        !          1984:       if (offset < defn->length) {
        !          1985:        bcopy (defn->expansion + offset, xbuf + totlen,
        !          1986:               defn->length - offset);
        !          1987:        totlen += defn->length - offset;
        !          1988:       }
        !          1989: 
        !          1990:       ip2->buf = xbuf;
        !          1991:       ip2->length = totlen;
        !          1992: 
        !          1993:       /* skip the input over the whole macro call. */
        !          1994:       ip->bufp = bp;
        !          1995: 
        !          1996:     }
        !          1997:   else
        !          1998:     {
        !          1999:       ip2->buf = ip2->bufp = defn->expansion;
        !          2000:       ip2->length = defn->length;
        !          2001:     }
        !          2002:   
        !          2003:   rescan (ip2, op);
        !          2004:   --indepth;
        !          2005:   *excess_newlines_ptr += newlines_found;
        !          2006:   ip->lineno += newlines_found;
        !          2007: 
        !          2008:   return 0;
        !          2009: 
        !          2010:  nope:
        !          2011:   error ("argument mismatch");
        !          2012:   --indepth;
        !          2013:   return 1;
        !          2014: }
        !          2015: 
        !          2016: /*
        !          2017:  * skip a balanced paren string up to the next comma.
        !          2018:  */
        !          2019: U_CHAR *
        !          2020: skip_macro_argument(bp, ip, newlines)
        !          2021:      U_CHAR *bp;
        !          2022:      FILE_BUF *ip;
        !          2023:      int *newlines;
        !          2024: {
        !          2025:   int paren = 0;
        !          2026:   int quotec = 0;
        !          2027:   
        !          2028:   while (bp < ip->buf + ip->length) {
        !          2029:     switch (*bp) {
        !          2030:     case '(':
        !          2031:       paren++;
        !          2032:       break;
        !          2033:     case ')':
        !          2034:       if (--paren < 0)
        !          2035:        return bp;
        !          2036:       break;
        !          2037:     case '\n':
        !          2038:       ++*newlines;
        !          2039:       break;
        !          2040:     case '/':
        !          2041:       if (bp[1] != '*' || bp + 1 >= ip->buf + ip->length)
        !          2042:        break;
        !          2043:       bp += 2;
        !          2044:       while ((bp[0] != '*' || bp[1] != '/')
        !          2045:             && bp + 1 < ip->buf + ip->length)
        !          2046:        {
        !          2047:          if (*bp == '\n') ++*newlines;
        !          2048:          bp++;
        !          2049:        }
        !          2050:       break;
        !          2051:     case '\'':         /* JF handle quotes right  */
        !          2052:     case '\"':
        !          2053:       for (quotec = *bp++; bp < ip->buf + ip->length && *bp != quotec; bp++)
        !          2054:        {
        !          2055:          if (*bp == '\\') bp++;
        !          2056:          if (*bp == '\n')
        !          2057:            ++*newlines;
        !          2058:        }
        !          2059:       break;
        !          2060:     case ',':
        !          2061:       if (paren == 0)
        !          2062:        return bp;
        !          2063:       break;
        !          2064:     }
        !          2065:     bp++;
        !          2066:   }
        !          2067:   return bp;
        !          2068: }
        !          2069: 
        !          2070: /*
        !          2071:  * error - print out message.  also make print on stderr.  Uses stdout
        !          2072:  * now for debugging convenience.
        !          2073:  */
        !          2074: error (msg)
        !          2075:      U_CHAR *msg;
        !          2076: {
        !          2077:   int i;
        !          2078:   FILE_BUF *ip = NULL;
        !          2079: 
        !          2080:   for (i = indepth - 1; i >= 0; i--)
        !          2081:     if (instack[i].fname != NULL) {
        !          2082:       ip = &instack[i];
        !          2083:       break;
        !          2084:     }
        !          2085: 
        !          2086:   if (ip != NULL)
        !          2087:     fprintf(stdout, "file %s, offset %d (line %d): ",
        !          2088:            ip->fname, ip->bufp - ip->buf, ip->lineno);
        !          2089:   fprintf(stdout, "%s\n", msg);
        !          2090:   return 0;
        !          2091: }
        !          2092: 
        !          2093: /*
        !          2094:  * if OBUF doesn't have NEEDED bytes after OPTR, make it bigger
        !          2095:  *    this should be a macro, for speed.
        !          2096:  * The "expand" in the name of this routine means buffer expansion,
        !          2097:  * not macro expansion.  It may become necessary to have some hacky
        !          2098:  * mechanism for flushing out the output buffer if it gets too big.
        !          2099:  *
        !          2100:  * As things stand, nothing is ever placed in the output buffer to be
        !          2101:  * removed again except when it's KNOWN to be part of an identifier,
        !          2102:  * so flushing and moving down everything left, instead of expanding,
        !          2103:  * should work ok.
        !          2104:  */
        !          2105: U_CHAR *
        !          2106: check_expand(obuf, needed)
        !          2107:      register FILE_BUF *obuf;
        !          2108:      register int needed;
        !          2109: {
        !          2110:   register int i;
        !          2111:   register U_CHAR *p;
        !          2112:   
        !          2113:   if (obuf->length - (obuf->bufp - obuf->buf) > needed)
        !          2114:     return obuf->buf;
        !          2115: 
        !          2116:   i = 2 * obuf->length;
        !          2117:   if (needed >= i)
        !          2118:     i += (3 * needed) / 2;
        !          2119: 
        !          2120:   if ((p = (U_CHAR *) xrealloc (obuf->buf, i)) == NULL)
        !          2121:     return NULL;
        !          2122:   obuf->bufp = p + (obuf->bufp - obuf->buf);
        !          2123:   obuf->buf = p;
        !          2124:   obuf->length = i;
        !          2125: 
        !          2126:   return p;
        !          2127: }
        !          2128:   
        !          2129: /*
        !          2130:  * install a name in the main hash table, even if it is already there.
        !          2131:  *   name stops with first non alphanumeric, except leading '#'.
        !          2132:  * caller must check against redefinition if that is desired.
        !          2133:  * delete() removes things installed by install() in fifo order.
        !          2134:  * this is important because of the `defined' special symbol used
        !          2135:  * in #if, and also if pushdef/popdef directives are ever implemented.
        !          2136:  */
        !          2137: HASHNODE *
        !          2138: install (name, type, value)
        !          2139:      U_CHAR *name;
        !          2140:      int type;
        !          2141:      int value;
        !          2142:         /* watch out here if sizeof(U_CHAR *) != sizeof (int) */
        !          2143: {
        !          2144:   HASHNODE *hp;
        !          2145:   int i, len = 0, bucket;
        !          2146:   register U_CHAR *p;
        !          2147: 
        !          2148:   p = name;
        !          2149:   while (is_idchar[*p])
        !          2150:     p++;
        !          2151:   len = p - name;
        !          2152: 
        !          2153:   i = sizeof (HASHNODE) + len + 1;
        !          2154:   hp = (HASHNODE *) xmalloc (i);
        !          2155:   bucket = hashf(name, len, HASHSIZE);
        !          2156:   hp->bucket_hdr = &hashtab[bucket];
        !          2157:   hp->next = hashtab[bucket];
        !          2158:   hashtab[bucket] = hp;
        !          2159:   hp->prev = NULL;
        !          2160:   if (hp->next != NULL)
        !          2161:     hp->next->prev = hp;
        !          2162:   hp->type = type;
        !          2163:   hp->length = len;
        !          2164:   hp->value.ival = value;
        !          2165:   hp->name = ((U_CHAR *) hp) + sizeof (HASHNODE);
        !          2166:   bcopy (name, hp->name, len);
        !          2167:   return hp;
        !          2168: }
        !          2169: /*
        !          2170:  * find the most recent hash node for name name (ending with first
        !          2171:  * non-identifier char) installed by install
        !          2172:  */
        !          2173: HASHNODE *
        !          2174: lookup (name)
        !          2175:      U_CHAR *name;
        !          2176: {
        !          2177:   register U_CHAR *bp;
        !          2178:   register HASHNODE *bucket;
        !          2179:   int len;
        !          2180: 
        !          2181:   for (bp = name; is_idchar[*bp]; bp++)
        !          2182:     ;
        !          2183:   len = bp - name;
        !          2184:   bucket = hashtab[hashf(name, len, HASHSIZE)];
        !          2185:   while (bucket) {
        !          2186:     if (bucket->length == len && strncmp(bucket->name, name, len) == 0)
        !          2187:       return bucket;
        !          2188:     bucket = bucket->next;
        !          2189:   }
        !          2190:   return NULL;
        !          2191: }
        !          2192: 
        !          2193: /*
        !          2194:  * Delete a hash node.  Some weirdness to free junk from macros.
        !          2195:  * More such weirdness will have to be added if you define more hash
        !          2196:  * types that need it.
        !          2197:  */
        !          2198: delete(hp)
        !          2199:      HASHNODE *hp;
        !          2200: {
        !          2201:   
        !          2202:   if (hp->prev != NULL)
        !          2203:     hp->prev->next = hp->next;
        !          2204:   if (hp->next != NULL)
        !          2205:     hp->next->prev = hp->prev;
        !          2206: 
        !          2207:   /* make sure that the bucket chain header that
        !          2208:      the deleted guy was on points to the right thing afterwards. */
        !          2209:   if (hp == *hp->bucket_hdr)
        !          2210:     *hp->bucket_hdr = hp->next;
        !          2211: 
        !          2212:   if (hp->type == T_MACRO) {
        !          2213:     DEFINITION *d = hp->value.defn;
        !          2214:     struct reflist *ap, *nextap;
        !          2215: 
        !          2216:     for (ap = d->pattern; ap != NULL; ap = nextap) {
        !          2217:       nextap = ap->next;
        !          2218:       free (ap);
        !          2219:     }
        !          2220:     free (d);
        !          2221:   }
        !          2222: }
        !          2223: 
        !          2224: /*
        !          2225:  * return hash function on name.  must be compatible with the one
        !          2226:  * computed a step at a time, elsewhere
        !          2227:  */
        !          2228: int
        !          2229: hashf(name, len, hashsize)
        !          2230:      register U_CHAR *name;
        !          2231:      register int len;
        !          2232:      int hashsize;
        !          2233: {
        !          2234:   register int r = 0;
        !          2235:   
        !          2236:   while (len--)
        !          2237:     r = HASHSTEP(r, *name++);
        !          2238:   
        !          2239:   return MAKE_POS(r) % hashsize;
        !          2240: }
        !          2241: 
        !          2242: 
        !          2243: /*
        !          2244:  * initialize random junk in the hash table and maybe other places
        !          2245:  */
        !          2246: initialize_random_junk()
        !          2247: {
        !          2248:   register int i;
        !          2249: 
        !          2250:   /*
        !          2251:    * Set up is_idchar and is_idstart tables.  These should be
        !          2252:    * faster than saying (is_alpha(c) || c == '_'), etc.
        !          2253:    * Must do set up these things before calling any routines tthat
        !          2254:    * refer to them.
        !          2255:    */
        !          2256:   for (i = 'a'; i <= 'z'; i++) {
        !          2257:     ++is_idchar[i - 'a' + 'A'];
        !          2258:     ++is_idchar[i];
        !          2259:     ++is_idstart[i - 'a' + 'A'];
        !          2260:     ++is_idstart[i];
        !          2261:   }
        !          2262:   for (i = '0'; i <= '9'; i++)
        !          2263:     ++is_idchar[i];
        !          2264:   ++is_idchar['_'];
        !          2265:   ++is_idstart['_'];
        !          2266: 
        !          2267:   /* horizontal space table */
        !          2268:   ++is_hor_space[' '];
        !          2269:   ++is_hor_space['\t'];
        !          2270: 
        !          2271:   install("__LINE__", T_SPECLINE, 0);
        !          2272:   install("__DATE__", T_DATE, 0);
        !          2273:   install("__FILE__", T_FILE, 0);
        !          2274:   install("__TIME__", T_TIME, 0);
        !          2275: 
        !          2276: #ifdef vax
        !          2277:   make_definition("vax 1");
        !          2278: #endif
        !          2279: 
        !          2280: #ifdef unix
        !          2281:   make_definition("unix 1");
        !          2282: #endif
        !          2283: 
        !          2284:   /* is there more? */
        !          2285:   
        !          2286: }
        !          2287: 
        !          2288: /*
        !          2289:  * process a given definition string, for initialization
        !          2290:  */
        !          2291: make_definition(str)
        !          2292:      U_CHAR *str;
        !          2293: {
        !          2294:   FILE_BUF *ip;
        !          2295:   struct keyword_table *kt;
        !          2296: 
        !          2297:   ip = &instack[indepth++];
        !          2298:   ip->fname = "*Initialization*";
        !          2299: 
        !          2300:   ip->buf = ip->bufp = str;
        !          2301:   ip->length = strlen(str);
        !          2302:   ip->lineno = 1;
        !          2303: 
        !          2304:   for (kt = keyword_table; kt->type != T_DEFINE; kt++)
        !          2305:     ;
        !          2306: 
        !          2307:   /* pass NULL as output ptr to do_define since we KNOW it never
        !          2308:      does any output.... */
        !          2309:   do_define (str, str + strlen(str) /* - 1 JF */ , NULL, kt);
        !          2310:   --indepth;
        !          2311: }
        !          2312: 
        !          2313: /* JF, this does the work for the -U option */
        !          2314: make_undef(str)
        !          2315:      U_CHAR *str;
        !          2316: {
        !          2317:   FILE_BUF *ip;
        !          2318:   struct keyword_table *kt;
        !          2319: 
        !          2320:   ip = &instack[indepth++];
        !          2321:   ip->fname = "*undef*";
        !          2322: 
        !          2323:   ip->buf = ip->bufp = str;
        !          2324:   ip->length = strlen(str);
        !          2325:   ip->lineno = 1;
        !          2326: 
        !          2327:   for(kt = keyword_table; kt->type != T_UNDEF; kt++)
        !          2328:     ;
        !          2329: 
        !          2330:   do_undef(str,str + strlen(str) - 1, NULL, kt);
        !          2331:   --indepth;
        !          2332: }
        !          2333: 
        !          2334: 
        !          2335: #ifndef BSD
        !          2336: #ifndef BSTRING
        !          2337: 
        !          2338: void
        !          2339: bzero (b, length)
        !          2340:      register char *b;
        !          2341:      register int length;
        !          2342: {
        !          2343: #ifdef VMS
        !          2344:   short zero = 0;
        !          2345:   long max_str = 65535;
        !          2346: 
        !          2347:   while (length > max_str)
        !          2348:     {
        !          2349:       (void) LIB$MOVC5 (&zero, &zero, &zero, &max_str, b);
        !          2350:       length -= max_str;
        !          2351:       b += max_str;
        !          2352:     }
        !          2353:   (void) LIB$MOVC5 (&zero, &zero, &zero, &length, b);
        !          2354: #else
        !          2355:   while (length-- > 0)
        !          2356:     *b++ = 0;
        !          2357: #endif /* not VMS */
        !          2358: }
        !          2359: 
        !          2360: void 
        !          2361: bcopy (b1, b2, length)
        !          2362:      register char *b1;
        !          2363:      register char *b2;
        !          2364:      register int length;
        !          2365: {
        !          2366: #ifdef VMS
        !          2367:   long max_str = 65535;
        !          2368: 
        !          2369:   while (length > max_str)
        !          2370:     {
        !          2371:       (void) LIB$MOVC3 (&max_str, b1, b2);
        !          2372:       length -= max_str;
        !          2373:       b1 += max_str;
        !          2374:       b2 += max_str;
        !          2375:     }
        !          2376:   (void) LIB$MOVC3 (&length, b1, b2);
        !          2377: #else
        !          2378:   while (length-- > 0)
        !          2379:     *b2++ = *b1++;
        !          2380: #endif /* not VMS */
        !          2381: }
        !          2382:  
        !          2383: int
        !          2384: bcmp (b1, b2, length)  /* This could be a macro! */
        !          2385:      register char *b1;
        !          2386:      register char *b2;
        !          2387:       register int length;
        !          2388:  {
        !          2389: #ifdef VMS
        !          2390:    struct dsc$descriptor_s src1 = {length, DSC$K_DTYPE_T, DSC$K_CLASS_S, b1};
        !          2391:    struct dsc$descriptor_s src2 = {length, DSC$K_DTYPE_T, DSC$K_CLASS_S, b2};
        !          2392: 
        !          2393:    return STR$COMPARE (&src1, &src2);
        !          2394: #else
        !          2395:    while (length-- > 0)
        !          2396:      if (*b1++ != *b2++)
        !          2397:        return 1;
        !          2398: 
        !          2399:    return 0;
        !          2400: #endif /* not VMS */
        !          2401: }
        !          2402: #endif /* not BSTRING */
        !          2403: #endif /* not BSD */
        !          2404: 
        !          2405: 
        !          2406: void
        !          2407: fatal (str, arg)
        !          2408:      char *str, *arg;
        !          2409: {
        !          2410:   fprintf (stderr, "%s: ", progname);
        !          2411:   fprintf (stderr, str, arg);
        !          2412:   fprintf (stderr, "\n");
        !          2413:   exit (FATAL_EXIT_CODE);
        !          2414: }
        !          2415: 
        !          2416: void
        !          2417: perror_with_name (name)
        !          2418:      char *name;
        !          2419: {
        !          2420:   extern int errno, sys_nerr;
        !          2421:   extern char *sys_errlist[];
        !          2422: 
        !          2423:   fprintf (stderr, "%s: ", progname);
        !          2424:   if (errno < sys_nerr)
        !          2425:     fprintf (stderr, "%s for %s\n", sys_errlist[errno], name);
        !          2426:   else
        !          2427:     fprintf (stderr, "cannot open %s\n", sys_errlist[errno], name);
        !          2428: }
        !          2429: 
        !          2430: void
        !          2431: pfatal_with_name (name)
        !          2432:      char *name;
        !          2433: {
        !          2434:   perror_with_name (name);
        !          2435:   exit (FATAL_EXIT_CODE);
        !          2436: }
        !          2437: 
        !          2438: 
        !          2439: static void
        !          2440: memory_full ()
        !          2441: {
        !          2442:   fatal ("Memory exhausted.");
        !          2443: }
        !          2444: 
        !          2445: 
        !          2446: char *
        !          2447: xmalloc (size)
        !          2448:      int size;
        !          2449: {
        !          2450:   extern char *malloc ();
        !          2451:   register char *ptr = malloc (size);
        !          2452:   if (ptr != 0) return (ptr);
        !          2453:   memory_full ();
        !          2454:   /*NOTREACHED*/
        !          2455: }
        !          2456: 
        !          2457: char *
        !          2458: xrealloc (old, size)
        !          2459:      char *old;
        !          2460:      int size;
        !          2461: {
        !          2462:   extern char *realloc ();
        !          2463:   register char *ptr = realloc (old, size);
        !          2464:   if (ptr != 0) return (ptr);
        !          2465:   memory_full ();
        !          2466:   /*NOTREACHED*/
        !          2467: }
        !          2468: 
        !          2469: char *
        !          2470: xcalloc (number, size)
        !          2471:      int number, size;
        !          2472: {
        !          2473:   extern char *malloc ();
        !          2474:   register int total = number * size;
        !          2475:   register char *ptr = malloc (total);
        !          2476:   if (ptr != 0)
        !          2477:     {
        !          2478:       bzero (ptr, total);
        !          2479:       return (ptr);
        !          2480:     }
        !          2481:   memory_full ();
        !          2482:   /*NOTREACHED*/
        !          2483: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.