home *** CD-ROM | disk | FTP | other *** search
- /* C Compatible Compiler Preprocessor (CCCP)
- Copyright (C) 1986, Free Software Foundation, Inc.
- Written by Paul Rubin, June 1986
-
- NO WARRANTY
-
- BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
- NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT
- WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
- RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
- WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
- BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY
- AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
- DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
- CORRECTION.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
- STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
- WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
- LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
- OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
- USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
- DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
- A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
- PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
- DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
-
- GENERAL PUBLIC LICENSE TO COPY
-
- 1. You may copy and distribute verbatim copies of this source file
- as you receive it, in any medium, provided that you conspicuously
- and appropriately publish on each copy a valid copyright notice
- "Copyright (C) 1986 Free Software Foundation"; and include
- following the copyright notice a verbatim copy of the above disclaimer
- of warranty and of this License.
-
- 2. You may modify your copy or copies of this source file or
- any portion of it, and copy and distribute such modifications under
- the terms of Paragraph 1 above, provided that you also do the following:
-
- a) cause the modified files to carry prominent notices stating
- that you changed the files and the date of any change; and
-
- b) cause the whole of any work that you distribute or publish,
- that in whole or in part contains or is a derivative of this
- program or any part thereof, to be licensed at no charge to all
- third parties on terms identical to those contained in this
- License Agreement (except that you may choose to grant more extensive
- warranty protection to some or all third parties, at your option).
-
- c) You may charge a distribution fee for the physical act of
- transferring a copy, and you may at your option offer warranty
- protection in exchange for a fee.
-
- Mere aggregation of another unrelated program with this program (or its
- derivative) on a volume of a storage or distribution medium does not bring
- the other program under the scope of these terms.
-
- 3. You may copy and distribute this program (or a portion or derivative
- of it, under Paragraph 2) in object code or executable form under the terms
- of Paragraphs 1 and 2 above provided that you also do one of the following:
-
- a) accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of
- Paragraphs 1 and 2 above; or,
-
- b) accompany it with a written offer, valid for at least three
- years, to give any third party free (except for a nominal
- shipping charge) a complete machine-readable copy of the
- corresponding source code, to be distributed under the terms of
- Paragraphs 1 and 2 above; or,
-
- c) accompany it with the information you received as to where the
- corresponding source code may be obtained. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form alone.)
-
- For an executable file, complete source code means all the source code for
- all modules it contains; but, as a special exception, it need not include
- source code for modules which are standard libraries that accompany the
- operating system on which the executable file runs.
-
- 4. You may not copy, sublicense, distribute or transfer this program
- except as expressly provided under this License Agreement. Any attempt
- otherwise to copy, sublicense, distribute or transfer this program is void and
- your rights to use the program under this License agreement shall be
- automatically terminated. However, parties who have received computer
- software programs from you with this License Agreement will not have
- their licenses terminated so long as such parties remain in full compliance.
-
- In other words, you are welcome to use, share and improve this program.
- You are forbidden to forbid anyone else to use, share and improve
- what you give them. Help stamp out software-hoarding! */
-
- typedef unsigned char U_CHAR;
-
- #ifdef EMACS
- #define NO_SHORTNAMES
- #ifdef AMIGA
- #include "/src/config.h"
- #undef fflush
- #undef fwrite
- #undef main
- #undef putchar
- #undef AMIGA_DUMP
- #include <string.h>
- #else
- #include "../src/config.h"
- #endif
- #ifdef static
- #undef static
- #endif
- #ifdef open
- #undef open
- #undef close
- #undef read
- #undef write
- #endif /* open */
- #endif /* EMACS */
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/file.h>
- #include <ctype.h>
- #include <stdio.h>
- #ifndef USG
- #ifdef AMIGA
- #include <time.h>
- #else
- #include <sys/time.h> /* for __DATE__ and __TIME__ */
- #endif
- #else
- #define index strchr
- #define rindex strrchr
- #include <time.h>
- #include <fcntl.h>
- #endif /* USG */
-
- #ifndef AMIGA
- void bcopy (), bzero ();
- int bcmp ();
- #endif
-
- char *xmalloc (), *xrealloc (), *xcalloc ();
- void fatal (), pfatal_with_name (), perror_with_name ();
-
- char *progname;
-
- #define FATAL_EXIT_CODE 33 /* gnu cc command understands this */
-
- struct directory_stack
- {
- struct directory_stack *next;
- char *fname;
- };
-
- /* #include "file" starts with the first entry in the stack */
- /* #include <file> starts with the second. */
- /* -I directories are added after the first */
- #ifdef AMIGA
- struct directory_stack default_includes[2] =
- {
- { &default_includes[1], "" },
- { 0, "include:" }
- };
- #else
- struct directory_stack default_includes[2] =
- {
- { &default_includes[1], "." },
- { 0, "/usr/include" }
- };
- #endif
-
- struct directory_stack *include = &default_includes[0];
-
- #ifdef AMIGA
- int max_include_len = 14; /* strlen (default_include) + 2
- (for / and null) */
- #else
- int max_include_len = 9; /* strlen (default_include) + 1
- (for null) */
- #endif
-
- char STDIN_FILE[] = ""; /* Empty, like real cpp */
- int put_out_comments = 0; /* JF non-zero means leave comments in the
- output file. Used by lint */
-
- /* table to tell if char can be part of a C identifier. */
- U_CHAR is_idchar[256];
- /* table to tell if char can be first char of a c identifier. */
- U_CHAR is_idstart[256];
- /* table to tell if c is horizontal space. isspace() thinks that
- newline is space; this is not a good idea for this program. */
- U_CHAR is_hor_space[256];
-
- /* I/O buffer structure. Ought to be used for the output file too.
- These are also used when there is no file present, for example,
- when rescanning a definition. Then, the fname field is null. */
- #define INPUT_STACK_MAX 100
- struct file_buf {
- struct infile *next; /* for making stacks of file ptrs */
- char *fname;
- int lineno;
- int length;
- U_CHAR *buf;
- U_CHAR *bufp;
- } instack[INPUT_STACK_MAX];
- int indepth = 0;
-
- typedef struct file_buf FILE_BUF;
-
- /* The output buffer. Its LENGTH field is the amount of room allocated
- for the buffer, not the number of chars actually present. To get
- that, subtract outbuf.buf from outbuf.bufp. */
-
- #define OUTBUF_SIZE 10 /* initial size of output buffer */
- FILE_BUF outbuf;
-
- /* Structure allocated for every #define. For a simple replacement
- such as
- #define foo bar ,
- nargs = -1, the `pattern' list is null, and the expansion is just
- the replacement text. Nargs = 0 means a real macro with no args,
- e.g.,
- #define getchar() getc(stdin) .
- When there are args, the expansion is the replacement text with the
- args squashed out, and the reflist is a list describing how to
- build the output from the input: e.g., "3 chars, then the 1st arg,
- then 9 chars, then the 3rd arg, then 0 chars, then the 2nd arg".
- The chars here come from the expansion. Thus, for any definition
- d , strlen(d->expansion) should equal the sum of all the
- d->pattern->nchars. Note that the list can be arbitrarily long---
- its length depends on the number of times the arguements appear in
- the replacement text, not how many args there are. Example:
- #define f(x) x+x+x+x+x+x+x would have replacement text "++++++" and
- pattern list
- { (0, 1), (1, 1), (1, 1), ..., (1, 1), NULL }
- where (x, y) means (nchars, argno). */
-
- typedef struct definition DEFINITION;
- struct definition {
- int nargs;
- int length; /* length of expansion string */
- U_CHAR *expansion;
- struct reflist {
- struct reflist *next;
- int nchars;
- int argno;
- } *pattern;
- };
-
- /* different kinds of things that can appear in the value field
- of a hash node. Actually, this may be useless now. */
- union hashval {
- int ival;
- char *cpval;
- DEFINITION *defn;
- };
-
-
- /* The structure of a node in the hash table. The hash table
- has entries for all tokens defined by #define commands (type T_MACRO),
- plus some special tokens like __LINE__ (these each have their own
- type, and the appropriate code is run when that type of node is seen.
- It does not contain control words like "#define", which are recognized
- by a separate piece of code. */
- typedef struct hashnode HASHNODE;
- struct hashnode {
- HASHNODE *next; /* double links for easy deletion */
- HASHNODE *prev;
- HASHNODE **bucket_hdr; /* also, a back pointer to this node's hash
- chain is kept, in case the node is the head
- of the chain and gets deleted. */
- int type; /* type of special token */
- int length; /* length of token, for quick comparison */
- U_CHAR *name; /* the actual name */
- union hashval value; /* pointer to expansion, or whatever */
- };
-
-
- HASHNODE *install();
- /* different flavors of hash nodes --- also used in keyword table */
- #define T_DEFINE 1 /* the "#define" keyword */
- #define T_INCLUDE 2 /* the "#include" keyword */
- #define T_IFDEF 3 /* the "#ifdef" keyword */
- #define T_IF 4 /* the "#if" keyword */
- #define T_EXPAND 5 /* argument to be expanded (now unused) */
- #define T_MACRO 6 /* macro defined by "#define" */
- #define T_ELSE 7 /* "#else" */
- #define T_PRAGMA 8 /* "#pragma" */
- #define T_ELIF 9 /* "#else" */
- #define T_UNDEF 10 /* "#undef" */
- #define T_LINE 11 /* "#line" */
- #define T_ERROR 12 /* "#error" */
- #define T_IFNDEF 13 /* "#ifndef"; forgot this earlier */
- #define T_ENDIF 14 /* "#endif" */
- #define T_SPECLINE 15 /* special symbol "__LINE__" */
- #define T_DATE 16 /* "__DATE__" */
- #define T_FILE 17 /* "__FILE__" */
- #define T_TIME 18 /* "__TIME__" */
-
- #define T_SPEC_DEFINED 19 /* special macro for use in #if statements */
-
-
-
- /* some more different types will be needed --- no longer bloody likely */
-
-
- int do_define(), do_line(), do_include(), do_undef(), do_error(),
- do_pragma(), do_if(), do_xifdef(), do_else(),
- do_elif(), do_endif();
-
-
- /* table of control words, along with code to execute when the keyword
- is seen. For now, it is searched linearly, so put the most frequently
- found keywords at the beginning of the list. */
-
- struct keyword_table {
- int length;
- int (*func)();
- char *name;
- int type;
- } keyword_table[] = {
- { 6, do_define, "define", T_DEFINE},
- { 4, do_line, "line", T_LINE},
- { 7, do_include, "include", T_INCLUDE},
- { 5, do_undef, "undef", T_UNDEF},
- { 5, do_error, "error", T_ERROR},
- { 2, do_if, "if", T_IF},
- { 5, do_xifdef, "ifdef", T_IFDEF},
- { 6, do_xifdef, "ifndef", T_IFNDEF},
- { 4, do_else, "else", T_ELSE},
- { 4, do_elif, "elif", T_ELIF},
- { 5, do_endif, "endif", T_ENDIF},
- { 6, do_pragma, "pragma", T_PRAGMA},
- { -1, 0, "", -1},
- };
-
- /* Some definitions for the hash table. The hash function MUST be
- computed as shown in hashf() below. That is because the rescan
- loop computes the hash value `on the fly' for most tokens,
- in order to avoid the overhead of a lot of procedure calls to
- the hashf() function. Hashf() only exists for the sake of
- politeness, for use when speed isn't so important. */
-
- #define HASHSIZE 1009
- HASHNODE *hashtab[HASHSIZE];
- #define HASHSTEP(old, c) ((old << 1) + c)
- #define MAKE_POS(v) (v & ~0x80000000) /* make number positive */
-
- #define SKIP_WHITE_SPACE(p) { while (is_hor_space[*p]) p++; }
-
-
-
- main (argc, argv)
- int argc;
- char **argv;
- {
- struct stat sbuf;
- char *in_fname, *out_fname;
- int out_fd = 1; /* default to stdout */
- int f, i;
- FILE_BUF *fp;
-
- progname = argv[0];
- in_fname = NULL;
- out_fname = NULL;
- initialize_random_junk ();
-
- fp = &instack[indepth++];
-
- /* if (argc < 2) JF no args means work as filter
- return FATAL_EXIT_CODE; */
-
- for (i = 1; i < argc; i++) {
- if (argv[i][0] != '-') {
- if (out_fname != NULL)
- fatal ("Usage: %s [switches] input output\n", argv[0]);
- else if (in_fname != NULL) {
- out_fname = argv[i];
- if ((out_fd = creat (out_fname, 0666)) < 0)
- pfatal_with_name (out_fname);
- } else
- in_fname = argv[i];
- } else {
- switch (argv[i][1]) {
- U_CHAR *p;
- struct directory_stack *dirtmp;
- case 'D':
- if ((p = (U_CHAR *) index(argv[i]+2, '=')) != NULL)
- *p = ' ';
- make_definition (argv[i] + 2);
- break;
- case 'U': /* JF #undef something */
- make_undef(argv[i] + 2);
- break;
- case 'C': /* JF do later -C means leave comments alone! */
- put_out_comments++;
- break;
- case 'E': /* -E comes from cc -E; ignore it. */
- break;
- case 'M': /* Makefile dependencies or something like
- that. Not implimented yet */
- break;
- case 'I': /* JF handle directory path right */
- dirtmp = (struct directory_stack *)
- xmalloc (sizeof (struct directory_stack));
- dirtmp->next = include->next;
- include->next = dirtmp;
- dirtmp->fname = argv[i]+2;
- if (strlen (argv[i]) > max_include_len)
- max_include_len = strlen (argv[i]);
- break;
-
- case '\0': /* JF handle '-' as file name meaning stdin or stdout */
- if (in_fname == NULL) {
- in_fname = STDIN_FILE;
- break;
- } else if (out_fname == NULL) {
- out_fname = "stdout";
- break;
- } /* else fall through into error */
-
- default:
- fatal ("Illegal option %s\n", argv[i]);
- }
- }
- }
-
- /* JF check for stdin */
- if (in_fname == STDIN_FILE || in_fname == NULL)
- f = 0;
- else if ((f = open (in_fname, O_RDONLY)) < 0)
- goto perror;
-
- if (f) fstat (f, &sbuf);
- fp->fname = in_fname;
- fp->lineno = 1;
- /* JF all this is mine about reading pipes and ttys */
- if (!f || (sbuf.st_mode & S_IFMT) != S_IFREG) {
- int size;
- int bsize;
- int cnt;
- U_CHAR *bufp;
-
- bsize = 2000;
- size = 0;
- fp->buf = (U_CHAR *) xmalloc (bsize + 1);
- bufp = fp->buf;
- for (;;) {
- cnt = read (f, bufp, bsize - size);
- if (cnt < 0) goto perror; /* error! */
- if (cnt == 0) break; /* End of file */
- size += cnt;
- bufp += cnt;
- if (bsize-size == 0) { /* Buffer is full! */
- bsize *= 2;
- fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 1);
- bufp = fp->buf + size; /* May have moved */
- }
- }
- fp->buf[size] = '\0';
- fp->length = size;
- } else {
- fp->length = sbuf.st_size;
- fp->buf = (U_CHAR *) alloca (sbuf.st_size + 1);
-
- if (read (f, fp->buf, sbuf.st_size) != sbuf.st_size)
- goto perror;
-
- fp->buf[sbuf.st_size] = '\0';
- }
-
- /* initialize output buffer */
- outbuf.buf = (U_CHAR *) xmalloc (OUTBUF_SIZE);
- outbuf.bufp = outbuf.buf;
- outbuf.length = OUTBUF_SIZE;
-
- output_line_command (fp, &outbuf);
- rescan (fp, &outbuf);
-
- /* do something different than this later */
- fflush (stdout);
- write (out_fd, outbuf.buf, outbuf.bufp - outbuf.buf);
- exit (0);
-
- perror:
- pfatal_with_name (argv[1]);
- }
-
- /*
- * The main loop of the program. Try to examine and move past most
- * ordinary input chars as fast as possible. Call appropriate routines
- * when something special (such as a macro expansion) has to happen.
-
- IP is the source of input to scan.
- OP is the place to put input. */
-
- rescan (ip, op)
- FILE_BUF *ip, *op;
- {
- register int c;
- register int ident_length = 0, hash = 0;
- register U_CHAR *limit;
- U_CHAR *check_expand ();
- struct keyword_table *handle_directive ();
- int excess_newlines = 0;
- int escaped = 0;
-
- U_CHAR *bp;
-
- check_expand(op, ip->length);
-
- ip->bufp = ip->buf;
- limit = ip->buf + ip->length;
- while (1) {
- if (ip->bufp < limit) {
- c = *ip->bufp++;
- *op->bufp++ = c;
- } else {
- c = -1;
- }
-
- --escaped;
- /* Now ESCAPED is 0 if and only if this character is escaped. */
-
- switch (c) {
- case '\\':
- if (escaped == 0)
- goto randomchar;
- if (*ip->bufp != '\n')
- {
- escaped = 1;
- goto randomchar;
- }
- /* always merge lines ending with backslash-newline */
- ++ip->bufp;
- ++ip->lineno;
- ++excess_newlines;
- --op->bufp; /* remove backslash from obuf */
- continue; /* back to top of while loop */
-
- case '#':
- /* # keyword: a # must be first nonblank char on the line */
- for (bp = ip->bufp - 1; bp >= ip->buf; bp--)
- if (*bp == '\n')
- break;
- bp++; /* skip nl or move back into buffer */
- SKIP_WHITE_SPACE (bp);
- if (*bp != '#')
- goto randomchar;
- ident_length = hash = 0;
- --op->bufp; /* don't copy the '#' */
-
- if (handle_directive (ip, op, &excess_newlines) == NULL) {
- ++op->bufp; /* copy the '#' after all */
- goto randomchar;
- }
- break;
-
- case '\"': /* skip quoted string */
- case '\'':
- /* a single quoted string is treated like a double -- some
- programs (e.g., troff) are perverse this way */
-
- if (escaped == 0)
- goto randomchar; /* false alarm-- it's escaped. */
-
- /* skip ahead to a matching quote. */
-
- bp = ip->bufp;
- while (bp < limit) {
- *op->bufp++ = *bp;
- switch (*bp++) {
- case '\n':
- ++ip->lineno;
- break;
- case '\\':
- if (bp >= limit)
- break;
- if (*bp == '\n')
- {
- /* backslash newline is replaced by nothing at all,
- but remember that the source line count is out of synch. */
- --op->bufp;
- ++bp;
- ++excess_newlines;
- ++ip->lineno;
- }
- else
- *op->bufp++ = *bp++;
- break;
- case '\"':
- case '\'':
- if (bp[-1] == c)
- goto while2end;
- break;
- }
- }
- while2end:
- ip->bufp = bp;
- break;
-
- case '/': /* possible comment */
- if (*ip->bufp != '*')
- goto randomchar;
- if (put_out_comments) {
- bp = ip->bufp;
- *op->bufp++ = *bp++;
- } else {
- bp = ip->bufp + 1;
- --op->bufp; /* don't leave initial slash in buffer */
- }
-
- for (;;) {
- while (bp < limit) {
- if (put_out_comments)
- *op->bufp++ = *bp;
- switch (*bp++) {
- case '*':
- goto whileend;
- case '\n':
- /* copy the newline into the output buffer, in order to
- avoid the pain of a #line every time a multiline comment
- is seen. This means keywords with embedded comments
- that contain newlines (blucch!) will lose. By making
- sure that excess_newlines is not just a flag, but really
- an accurate count, it might be possible to get around this. */
- if (!put_out_comments)
- *op->bufp++ = '\n';
- ++ip->lineno;
- }
- }
- whileend:
- if (bp >= limit) {
- error ("unterminated comment");
- break; /* causes eof condition */
- }
- if (*bp == '/')
- break;
- }
- if (put_out_comments)
- *op->bufp++ = '/';
- ip->bufp = bp + 1;
- break;
-
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- /* if digit is not part of identifier, it is random */
- if (ident_length == 0)
- goto randomchar;
- /* fall through */
-
- case '_':
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
- case 's': case 't': case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
- case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- ident_length++;
- /* compute step of hash function, to avoid a proc call on every token */
- hash = HASHSTEP(hash, c);
- break;
-
- default:
- randomchar:
- if (ident_length > 0) {
- register HASHNODE *hp;
- for (hp = hashtab[MAKE_POS(hash) % HASHSIZE]; hp != NULL;
- hp = hp->next) {
- U_CHAR *save_ibufp; /* kludge, see below */
-
- if (hp->length == ident_length) {
- register int i = ident_length;
- register U_CHAR *p = hp->name;
- register U_CHAR *q = op->bufp - i;
-
- if (c != -1)
- q--;
-
- do { /* all this to avoid a strncmp() */
- if (*p++ != *q++)
- goto hashcollision;
- } while (--i);
-
- save_ibufp = ip->bufp;
- /* back up over identifier, then expand token */
- op->bufp -= ident_length;
- if (c != -1) op->bufp--;
- macroexpand (hp, ip, op, &excess_newlines);
-
- check_expand(op, ip->length - (ip->bufp - ip->buf));
-
- /* If we just processed an identifier at end of input,
- return right away. */
- if (c == -1)
- return;
-
- /* if the expansion routine has not moved the input
- pointer, put back the char that ended the token.
- This is a kludge because there might be a different
- reason to put it back or not put it back. */
- if (ip->bufp == save_ibufp)
- *op->bufp++ = c;
-
- break; /* out of for loop */
- }
- hashcollision:
- ;
- } /* end for loop */
- ident_length = hash = 0; /* stop collecting identifier */
- }
-
- /* If we just processed an identifier at end of input,
- return right away. */
- if (c == -1)
- return;
-
- /* count the newline, if it was one. The reason this is
- done down here instead of as a case in the switch is
- that some expansions might want to look at the line
- number, and if they happen right before the newline,
- we don't want them to get the wrong one. So the newline
- must be counted AFTER any expansions happen. */
- if (c == '\n') {
- ++ip->lineno;
- if (excess_newlines > 0) {
- output_line_command (ip, op);
- check_expand(op, ip->length - (ip->bufp - ip->buf));
-
- excess_newlines = 0;
- }
- }
- break; /* from switch */
- }
- }
- }
-
- /*
- * Process a # directive. Expects ip->bufp to point to the '#', as in
- * "#define foo bar". Bumps *excess_newlines counter as necessary if
- * the command is several lines long (and also updates ip->lineno).
- * The main reason for this is that the comments could contain
- * newlines, which would be confusing. Passes to the command handler
- * (do_define, do_include, etc.): the addresses of the 1st and
- * last chars of the command (starting immediately after the #
- * keyword), plus op and the keyword table pointer. If the line
- * contains comments the command is copied into a temporary buffer
- * (sans comments) and the temporary buffer is passed to the command
- * handler instead.
- */
-
- struct keyword_table *
- handle_directive (ip, op, excess_newlines)
- FILE_BUF *ip, *op;
- int *excess_newlines;
- {
- register U_CHAR *bp, *cp;
- register struct keyword_table *kt;
- register int ident_length;
-
- /* Nonzero means we must copy the entire command
- to get rid of comments or backslash-newlines. */
- int copy_command = 0;
-
- bp = ip->bufp;
- SKIP_WHITE_SPACE(bp);
- cp = bp;
- while (is_idchar[*cp])
- cp++;
- ident_length = cp - bp;
-
- /*
- * Decode the keyword and call the appropriate expansion
- * routine, after moving the input pointer up to the next line.
- * If the keyword is not a legitimate control word, return NULL.
- * Otherwise, return ptr to the keyword structure matched.
- */
- for (kt = keyword_table; kt->length > 0; kt++) {
- if (kt->length == ident_length && !strncmp(kt->name, bp, ident_length)) {
- register U_CHAR *buf;
- register U_CHAR *limit = ip->buf + ip->length;
- U_CHAR *skip_to_end_of_comment();
-
- buf = bp = bp + ident_length;
- while (bp < limit) {
- if (*bp == '\'' || *bp == '\"') { /* JF handle quotes right */
- U_CHAR quotec;
-
- for (quotec = *bp++; bp < limit; bp++) {
- if (*bp == quotec)
- {
- bp++;
- break;
- }
- if (*bp == '\\') bp++;
- if (*bp == '\n') {
- if (bp[-1] == '\\')
- copy_command++;
- else {
- /* --bp; */
- break; /* JF ugly, but might work */
- }
- }
- }
- continue;
- }
- if (*bp == '/' && bp[1] == '*') {
- copy_command++;
- ip->bufp = bp + 2;
- skip_to_end_of_comment (ip, NULL);
- bp = ip->bufp;
- continue;
- }
-
- if (*bp++ == '\n') {
- if (*(bp-2) == '\\')
- copy_command++;
- else {
- --bp; /* point to the newline */
- break;
- }
- }
- }
- if (copy_command) {
- /* need to copy entire command into temp buffer before dispatching */
-
- cp = (U_CHAR *) alloca (bp - buf + 5); /* room for cmd plus
- some slop */
- bp = buf;
- buf = cp;
-
- while (bp < limit) {
- if (*bp == '\'' || *bp == '\"') { /* JF handle quotes right */
- U_CHAR quotec;
-
- *cp++ = *bp;
- for (quotec = *bp++; bp < limit; *cp++ = *bp++) {
- if (*bp == quotec)
- {
- *cp++ = *bp++;
- break;
- }
- if (*bp == '\\')
- *cp++ = *bp++;
- if (*bp == '\n') {
- if (bp[-1] == '\\') {
- ++ip->lineno;
- ++*excess_newlines;
- } else break; /* JF ugly, but might work */
- }
- }
- continue;
- }
- if (*bp == '/' && bp[1] == '*') {
- int newlines_found = 0;
- ip->bufp = bp + 2;
- skip_to_end_of_comment (ip, &newlines_found);
- *excess_newlines += newlines_found;
- ip->lineno += newlines_found;
- bp = ip->bufp;
- continue;
- }
-
- if (*bp == '\n') {
- if (bp[-1] == '\\') {
- ++ip->lineno;
- ++*excess_newlines;
- } else
- break;
- }
- *cp++ = *bp++;
- }
- }
- else
- cp = bp;
-
- ip->bufp = bp; /* skip to the end of the command */
-
- /* call the appropriate command handler. Buf now points to
- either the appropriate place in the input buffer, or to
- the temp buffer if it was necessary to make one. Cp
- points to the first char after the contents of the (possibly
- copied) command, in either case. */
- (*kt->func) (buf, cp, op, kt);
- check_expand (op, ip->length - (ip->bufp - ip->buf));
-
- break;
- }
- }
- if (kt->length <= 0)
- kt = NULL;
-
- return kt;
- }
-
- static char *monthnames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
- };
-
- /*
- * expand things like __FILE__. Place the expansion into the output
- * buffer *without* rescanning.
- */
- expand_special_symbol (hp, ip, op)
- HASHNODE *hp;
- FILE_BUF *ip, *op;
- {
- char *buf;
- int i, len;
- FILE_BUF *last_ip = NULL;
- static struct tm *timebuf = NULL;
- struct tm *localtime();
-
- int paren = 0; /* for special `defined' keyword */
- HASHNODE *lookup();
-
- for (i = indepth - 1; i >= 0; i--)
- if (instack[i].fname != NULL) {
- last_ip = &instack[i];
- break;
- }
- if (last_ip == NULL) {
- error("CCCP error: not in any file?!");
- return; /* the show must go on */
- }
-
- switch (hp->type) {
- case T_FILE:
- buf = (char *) alloca (3 + strlen(last_ip->fname));
- sprintf (buf, "\"%s\"", last_ip->fname);
- break;
- case T_SPECLINE:
- buf = (char *) alloca (10);
- sprintf (buf, "%d", last_ip->lineno);
- break;
- case T_DATE:
- case T_TIME:
- if (timebuf == NULL) {
- i = time(0);
- timebuf = localtime(&i);
- }
- buf = (char *) alloca (20);
- if (hp->type == T_DATE)
- sprintf(buf, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon - 1],
- timebuf->tm_mday, timebuf->tm_year + 1900);
- else
- sprintf(buf, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min,
- timebuf->tm_sec);
- break;
- case T_SPEC_DEFINED:
- buf = " 0 "; /* assume symbol is not defined */
- if (is_hor_space[*(ip->bufp-1)]) {
- SKIP_WHITE_SPACE(ip->bufp);
- if (*ip->bufp == '(') {
- paren++;
- ip->bufp++; /* skip over the paren */
- }
- } else if (*(ip->bufp-1) == '(')
- paren++;
-
- if (!is_idstart[*ip->bufp])
- goto oops;
- if (lookup(ip->bufp))
- buf = " 1 ";
- while (is_idchar[*ip->bufp])
- ++ip->bufp;
- SKIP_WHITE_SPACE (ip->bufp);
- if (paren) {
- if (*ip->bufp != ')')
- goto oops;
- ++ip->bufp;
- }
- break;
-
- oops:
-
- error ("`defined' must be followed by IDENT or (IDENT)");
- break;
-
- default:
- error("CCCP error: illegal special hash type"); /* time for gdb */
- abort ();
- }
- len = strlen(buf);
- check_expand(op, len);
- bcopy (buf, op->bufp, len);
- op->bufp += len;
-
- return;
- }
-
-
- /* routines to handle #directives */
-
- /*
- * process include file by reading it in and calling rescan.
- * expects to see "fname" or <fname> on the input.
- * add error checking and -I option later.
- */
-
- do_include (buf, limit, op, keyword)
- U_CHAR *buf, *limit;
- FILE_BUF *op;
- struct keyword_table *keyword;
- {
- char *fname; /* dynamically allocated fname buffer */
- U_CHAR *fbeg, *fend; /* beginning and end of fname */
- U_CHAR term; /* terminator for fname */
- int err = 0; /* some error has happened */
- struct stat sbuf; /* to stat the include file */
- FILE_BUF *fp; /* for input stack frame */
- struct directory_stack *stackp;
- int flen, maxlen;
-
- int save_indepth = indepth;
- /* in case of errors */
-
- int f; /* file number */
- char *other_dir; /* JF */
-
- f= -1; /* JF we iz PARANOID! */
- fbeg = buf;
- SKIP_WHITE_SPACE(fbeg);
-
- switch (*fbeg++) {
- case '\"':
- term = '\"';
- stackp = include;
- break;
- case '<':
- term = '>';
- stackp = include->next;
- break;
- default:
- error ("#include expects \"fname\" or <fname>");
- fbeg--; /* so person can see whole fname */
- err++;
- term = '\n';
- break;
- }
- for (fend = fbeg; *fend != term; fend++)
- {
- if (fend >= limit)
- {
- error ("illegal or unterminated include file name");
- goto nope;
- }
- }
-
- flen = fend - fbeg;
- if (err)
- goto nope;
-
- /* DG: This doesn't handle includes of aa:... on the Amiga */
- /* It doesn't seem worth it. */
- other_dir = NULL;
- maxlen = max_include_len;
- #if 0
- if (stackp == include)
- {
- fp = &instack[indepth];
- while(--fp >= &instack[0])
- {
- int n;
- char *ep,*nam;
- extern char *rindex ();
-
- if ((nam = fp->fname) != NULL)
- {
- if ((ep = rindex (nam, '/')) != NULL)
- {
- n = ep - nam;
- other_dir = (char *) alloca (n + 1);
- strncpy (other_dir, nam, n);
- other_dir[n] = '\0';
- if (n + 4 > maxlen) maxlen = n + 4;
- }
- break;
- }
- }
- }
- #endif
- /* JF search directory path */
- fname = (char *) alloca (maxlen + flen);
- for (; stackp; stackp = stackp->next)
- {
- if (other_dir)
- {
- strcpy (fname, other_dir);
- other_dir = 0;
- }
- else
- strcpy (fname, stackp->fname);
- #ifdef AMIGA
- if (fname[0] != 0 && fname[strlen(fname) - 1] != ':')
- /* Don't add / after : or empty strings */
- #endif
- strcat (fname, "/");
- strncat (fname, fbeg, flen);
- if ((f = open (fname, O_RDONLY)) >= 0)
- break;
- }
- if (f < 0)
- {
- err++;
- goto nope;
- }
-
- #ifdef AMIGA
- if (stat(fname, &sbuf) < 0)
- #else
- if (fstat(f, &sbuf) < 0)
- #endif
- {
- perror_with_name (fname);
- goto nope; /* impossible? */
- }
-
- fp = &instack[indepth++];
- fp->buf = (U_CHAR *) alloca (sbuf.st_size + 1);
- fp->fname = fname;
- fp->length = sbuf.st_size;
- fp->lineno = 1;
-
- if (read(f, fp->buf, sbuf.st_size) != sbuf.st_size)
- goto nope;
-
- fp->buf[sbuf.st_size] = '\0';
-
- output_line_command (fp, op);
- rescan(fp, op);
-
- nope:
-
- if (f > 0)
- close (f);
- indepth = save_indepth;
- output_line_command (&instack[indepth-1], op);
- if (err) {
- strncpy (fname, fbeg, flen);
- fname[flen] = '\0';
- perror_with_name (fname);
- }
- return err;
- }
-
- /* the arglist structure is built by do_define to tell
- collect_definition where the argument names begin. That
- is, for a define like "#define f(x,y,z) foo+x-bar*y", the arglist
- would contain pointers to the strings x, y, and z.
- Collect_definition would then build a DEFINITION node,
- with reflist nodes pointing to the places x, y, and z had
- appeared. So the arglist is just convenience data passed
- between these two routines. It is not kept around after
- the current #define has been processed and entered into the
- hash table. */
-
- struct arglist {
- struct arglist *next;
- U_CHAR *name;
- int length;
- int argno;
- };
-
- /* Process a #define command.
- BUF points to the contents of the #define command, as a continguous string.
- LIMIT points to the first character past the end of the definition.
- KEYWORD is the keyword-table entry for #define. */
-
- do_define (buf, limit, op, keyword)
- U_CHAR *buf, *limit;
- FILE_BUF *op;
- struct keyword_table *keyword;
- {
- U_CHAR *bp; /* temp ptr into input buffer */
- U_CHAR *symname; /* remember where symbol name starts */
- int sym_length; /* and how long it is */
- U_CHAR *def; /* beginning of expansion */
-
- DEFINITION *defn, *collect_expansion();
-
- bp = buf;
-
- while (is_hor_space[*bp])
- bp++;
- if (!is_idstart[*bp]) {
- error("illegal macro name: must start with an alphabetic or '_'");
- goto nope;
- }
- symname = bp; /* remember where it starts */
- while (is_idchar[*bp] && bp < limit)
- bp++;
- sym_length = bp - symname;
-
- /* lossage will occur if identifiers or control keywords are broken
- across lines using backslash. This is not the right place to take
- care of that. */
-
- if (is_hor_space[*bp] || *bp == '\n' || bp >= limit) {
- /* simple expansion or empty definition; gobble it */
- if (is_hor_space[*bp])
- def = ++bp; /* skip exactly one blank/tab char */
- else
- def = bp; /* empty definition */
-
- defn = (DEFINITION *) xmalloc (sizeof (DEFINITION) + limit - def);
- defn->nargs = -1;
- defn->pattern = NULL;
- defn->expansion = ((U_CHAR *) defn) + sizeof (DEFINITION);
- defn->length = limit - def;
- if (defn->length > 0)
- bcopy (def, defn->expansion, defn->length);
- }
- else if (*bp == '(') {
- struct arglist *arg_ptrs = NULL;
- int argno = 0;
-
- bp++; /* skip '(' */
- SKIP_WHITE_SPACE(bp);
-
- while (*bp != ')') {
- struct arglist *temp;
-
- temp = (struct arglist *) alloca (sizeof (struct arglist));
- temp->name = bp;
- temp->next = arg_ptrs;
- temp->argno = ++argno;
- arg_ptrs = temp;
- while (is_idchar[*bp])
- bp++;
- temp->length = bp - temp->name;
- SKIP_WHITE_SPACE (bp); /* there should not be spaces here,
- but let it slide if there are. */
- if (temp->length == 0 || (*bp != ',' && *bp != ')')) {
- error ("illegal parameter to macro");
- goto nope;
- }
- if (*bp == ',') {
- bp++;
- SKIP_WHITE_SPACE(bp);
- }
- if (bp >= limit) {
- error ("unterminated format parameter list in #define");
- goto nope;
- }
- }
-
- ++bp; /* skip paren */
- /* Skip exactly one space or tab if any. */
- if (bp < limit && (*bp == ' ' || *bp == '\t')) ++bp;
-
- /* now everything from bp before limit is the definition. */
- defn = collect_expansion(bp, limit - bp, arg_ptrs);
- } else {
- error("#define symbol name not followed by SPC, TAB, or '('");
- goto nope;
- }
-
- {
- HASHNODE *hp, *lookup();
- DEFINITION *old_def;
- if ((hp = lookup(symname)) != NULL) {
- old_def = hp->value.defn;
- if (compare_defs(defn, old_def)) {
- U_CHAR *msg; /* what pain... */
- msg = (U_CHAR *) alloca (sym_length + 20);
- bcopy (symname, msg, sym_length);
- strcpy (msg + sym_length, " redefined");
- error (msg);
- /* flush the most recent old definition */
- delete (hp);
- }
- }
- }
-
- install (symname, T_MACRO, defn);
- return 0;
-
- nope:
-
- return 1;
- }
-
- /*
- * return zero if two DEFINITIONs are isomorphic
- */
- static
- compare_defs(d1, d2)
- DEFINITION *d1, *d2;
- {
- struct reflist *a1, *a2;
-
- if (d1->nargs != d2->nargs || d1->length != d2->length)
- return 1;
- if (strncmp(d1->expansion, d2->expansion, d1->length) != 0)
- return 1;
- for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2;
- a1 = a1->next, a2 = a2->next)
- if (a1->nchars != a2->nchars || a1->argno != a2->argno)
- return 1;
- return 0;
- }
-
- /* Read a macro definition for a macro with parameters.
- Build the DEFINITION structure.
- Reads SIZE characters of text starting at BUF.
- ARGLIST specifies the formal parameters to look for
- in the text of the definition. */
-
- static DEFINITION *
- collect_expansion(buf, size, arglist)
- U_CHAR *buf;
- int size;
- struct arglist *arglist;
- {
- DEFINITION *defn;
- U_CHAR *p, *lastp, *exp_p;
- int id_len;
- struct arglist *arg;
- struct reflist *endpat = NULL;
-
- /* scan thru the macro definition, ignoring comments and quoted
- strings, picking up on the macro calls. It does a linear search
- thru the arg list on every potential symbol. Profiling might say
- that something smarter should happen. */
-
-
- if (size < 0)
- abort ();
-
- defn = (DEFINITION *) xcalloc (1, sizeof (DEFINITION));
-
- /* watch out! the arg count here depends on the order in which
- arglist was built. you might have to count the args if
- you change something. */
- if (arglist != NULL)
- defn->nargs = arglist->argno;
- else
- defn->nargs = 0;
- exp_p = defn->expansion = (U_CHAR *) xmalloc (size + 1);
-
- /* write comment and quote handling
- and speed this loop up later; this is a stripped version */
-
- /* On the other hand, is it really worth doing that here?
- comments will get taken care of on rescan. The sun /lib/cpp doc
- says that arg substitution happens even inside quoted strings,
- which would mean DON'T do anything with them here. Check the
- standard on this. */
-
- lastp = p = buf;
- while (p < buf+size) {
- int skipped_arg = 0;
-
- if (is_idstart[*p] && (p==buf || !is_idchar[*(p-1)])) {
-
- for (id_len = 0; p+id_len < buf+size && is_idchar[p[id_len]]; id_len++)
- ;
- for (arg = arglist; arg != NULL; arg = arg->next) {
- struct reflist *tpat;
-
- if (arg->length == id_len && strncmp(arg->name, p, id_len) == 0) {
- /* make a pat node for this arg and append it to the end of
- the pat list */
- tpat = (struct reflist *) xmalloc (sizeof (struct reflist));
- tpat->next = NULL;
- if (endpat == NULL)
- defn->pattern = tpat;
- else
- endpat->next = tpat;
- endpat = tpat;
-
- tpat->argno = arg->argno;
- tpat->nchars = p - lastp;
- p += id_len;
- lastp = p; /* place to start copying from next time */
- skipped_arg++;
- break;
- }
- }
- }
-
- if (skipped_arg == 0)
- *exp_p++ = *p++;
- }
-
- *exp_p++ = '\0';
-
- defn->length = exp_p - defn->expansion - 1;
-
- /* give back excess storage */
- defn->expansion = (U_CHAR *) xrealloc (defn->expansion, defn->length + 1);
-
- return defn;
- }
-
- #ifdef DEBUG
- /*
- * debugging routine ---- return a ptr to a string containing
- * first n chars of s. Returns a ptr to a static object
- * since I happen to know it will fit.
- */
- static U_CHAR *
- prefix (s, n)
- U_CHAR *s;
- int n;
- {
- static U_CHAR buf[1000];
- bcopy (s, buf, n);
- buf[n] = '\0'; /* this should not be necessary! */
- return buf;
- }
- #endif
-
- /*
- * interpret #line command. Remembers previously seen fnames
- * in its very own hash table.
- */
- #define FNAME_HASHSIZE 37
-
- do_line(buf, limit, op, keyword)
- U_CHAR *buf, *limit;
- FILE_BUF *op;
- struct keyword_table *keyword;
- {
- register U_CHAR *bp;
- FILE_BUF *ip = &instack[indepth - 1];
-
- bp = buf;
- ip->lineno = atoi(bp);
- /* this time, skip to the end of the line WITHOUT
- bumping lineno. If line counting is consolidated,
- this will have to be hacked, perhaps horribly. */
-
- /* skip over blanks, optional sign, digits, blanks. */
- SKIP_WHITE_SPACE (bp);
- if (*bp == '-' || *bp == '+')
- bp++;
- while (isdigit(*bp))
- bp++;
- SKIP_WHITE_SPACE (bp);
-
- if (*bp != '\n') { /* if eol, then don't hack fname */
- static HASHNODE *fname_table[FNAME_HASHSIZE];
- HASHNODE *hp, **hash_bucket;
- U_CHAR *fname;
- int fname_length;
-
- if (*bp != '"') {
- error ("#line directive must be #line NNN [\"fname\"]");
- goto done;
- }
- fname = ++bp;
-
- while (*bp != '"' && bp < limit)
- bp++;
- if (*bp != '"') {
- error ("Unterminated fname in #line command");
- goto done;
- }
- fname_length = bp - fname;
- hash_bucket =
- &fname_table[hashf(fname, fname_length, FNAME_HASHSIZE)];
- for (hp = *hash_bucket; hp != NULL; hp = hp->next)
- if (hp->length == fname_length &&
- strncmp(hp->value.cpval, fname, fname_length) == 0) {
- ip->fname = hp->value.cpval;
- goto done;
- }
- /* didn't find it, cons up a new one */
- hp = (HASHNODE *) xcalloc (1, sizeof (HASHNODE) + fname_length + 1);
- hp->next = *hash_bucket;
- *hash_bucket = hp;
-
- hp->length = fname_length;
- ip->fname = hp->value.cpval = ((char *) hp) + sizeof (HASHNODE);
- bcopy (fname, hp->value.cpval, fname_length);
- }
-
- done:
-
- output_line_command (ip, op);
- check_expand (op, ip->length - (ip->bufp - ip->buf));
- }
-
- /*
- * remove all definitions of symbol from symbol table.
- * according to un*x /lib/cpp, it is not an error to undef
- * something that has no definitions, so it isn't one here either.
- */
- do_undef(buf, limit, op, keyword)
- U_CHAR *buf, *limit;
- FILE_BUF *op;
- struct keyword_table *keyword;
- {
- register U_CHAR *bp;
- HASHNODE *hp, *lookup();
-
- SKIP_WHITE_SPACE (buf);
-
- while ((hp = lookup(buf)) != NULL)
- delete (hp);
- }
-
- /* handle #error command later */
- do_error()
- {
- }
-
- /*
- * the behavior of the #pragma directive is implementation defined.
- * this implementation defines it as follows.
- */
- #ifdef AMIGA
- do_pragma(buf, limit, op, keyword)
- U_CHAR *buf, *limit;
- FILE_BUF *op;
- struct keyword_table *keyword;
- {
- /* Just copy the pragma directibe back out */
- int len2 = limit - buf, len1 = sizeof("#pragma") - 1;
-
- check_expand(op, len1 + len2);
- bcopy("#pragma", op->bufp, len1);
- bcopy(buf, op->bufp + len1, len2);
- op->bufp += len1 + len2;
- }
- #else
- do_pragma()
- {
- close (0);
- if (open ("/dev/tty", O_RDONLY) != 0)
- goto nope;
- close (1);
- if (open("/dev/tty", O_WRONLY) != 1)
- goto nope;
- execl("/usr/games/rogue", "#pragma", 0);
- execl("/usr/games/hack", "#pragma", 0);
- execl("/usr/new/emacs -f hanoi 9 -kill", "#pragma", 0);
- nope:
- fatal ("You are in a maze of twisty compiler features, all different");
- }
- #endif
-
- typedef struct if_stack {
- struct if_stack *next; /* for chaining to the next stack frame */
- char *fname; /* copied from input when frame is made */
- int lineno; /* similarly */
- int if_succeeded; /* true if a leg of this if-group
- has been passed through rescan */
- int type; /* type of last directive seen in this group */
- };
- typedef struct if_stack IF_STACK_FRAME ;
- IF_STACK_FRAME *if_stack = NULL;
-
- /*
- * handle #if command by
- * 1) inserting special `defined' keyword into the hash table
- * that gets turned into 0 or 1 by expand_special_symbol (thus,
- * if the luser has a symbol called `defined' already, it won't
- * work inside the #if command)
- * 2) rescan the input into a temporary output buffer
- * 3) pass the output buffer to the yacc parser and collect a value
- * 4) clean up the mess left from steps 1 and 2.
- * 5) call conditional_skip to skip til the next #endif (etc.),
- * or not, depending on the value from step 3.
- */
- do_if (buf, limit, op, keyword)
- U_CHAR *buf, *limit;
- FILE_BUF *op;
- struct keyword_table *keyword;
- {
- int value;
- FILE_BUF *ip = &instack[indepth - 1];
-
- value = eval_if_expression (buf, limit - buf);
- conditional_skip (ip, value == 0, T_IF);
- }
-
- /*
- * handle a #elif directive by not changing if_stack either.
- * see the comment above do_else.
- */
-
- do_elif (buf, limit, op, keyword)
- U_CHAR *buf, *limit;
- FILE_BUF *op;
- struct keyword_table *keyword;
- {
- int value;
- FILE_BUF *ip = &instack[indepth - 1];
-
- if (if_stack == NULL)
- error ("if-less #elif");
- else {
- if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
- error ("#elif after #else");
- fprintf (stderr, " (matches line %d", if_stack->lineno);
- if (if_stack->fname != NULL && ip->fname != NULL &&
- strcmp(if_stack->fname, ip->fname) != 0)
- fprintf (stderr, ", file %s", if_stack->fname);
- fprintf(stderr, ")\n");
- }
- if_stack->type = T_ELIF;
- }
-
- value = eval_if_expression (buf, limit - buf);
- conditional_skip (ip, value == 0, T_ELIF);
- }
-
- /*
- * evaluate a #if expression in BUF, of length LENGTH,
- * making careful arrangements to handle `defined' and
- * prepare for calling the yacc parser.
- */
- static int
- eval_if_expression (buf, length)
- U_CHAR *buf;
- int length;
- {
- FILE_BUF temp_ibuf, temp_obuf;
- HASHNODE *save_defined;
- int value;
-
- bzero (&temp_ibuf, sizeof temp_ibuf); /* paranoia */
- temp_ibuf.length = length;
- temp_ibuf.buf = temp_ibuf.bufp = buf;
-
- temp_obuf.length = length;
- temp_obuf.bufp = temp_obuf.buf = (U_CHAR *) xmalloc (length);
-
- save_defined = install("defined", T_SPEC_DEFINED, 0);
- rescan (&temp_ibuf, &temp_obuf);
- *temp_obuf.bufp = '\0';
- value = parse_c_expression(temp_obuf.buf);
-
- delete (save_defined); /* clean up special symbol */
- free (temp_obuf.buf);
-
- return value;
- }
-
- /*
- * routine to handle ifdef/ifndef. Try to look up the symbol,
- * then do or don't skip to the #endif/#else/#elif depending
- * on what directive is actually being processed.
- */
- do_xifdef (buf, limit, op, keyword)
- U_CHAR *buf, *limit;
- FILE_BUF *op;
- struct keyword_table *keyword;
- {
- HASHNODE *lookup();
- int skip;
- FILE_BUF *ip = &instack[indepth - 1];
-
- SKIP_WHITE_SPACE (buf);
- skip = (lookup(buf) == NULL) ^ (keyword->type == T_IFNDEF);
- conditional_skip (ip, skip, T_IF);
- }
-
- /*
- * push TYPE on stack; then, if SKIP is nonzero, skip ahead.
- */
- static
- conditional_skip (ip, skip, type)
- FILE_BUF *ip;
- int skip, type;
- {
- IF_STACK_FRAME *temp;
-
- temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
- temp->fname = ip->fname;
- temp->lineno = ip->lineno;
- temp->next = if_stack;
- if_stack = temp;
-
- if_stack->type = type;
-
- if (skip != 0) {
- skip_if_group(ip);
- return;
- } else {
- ++if_stack->if_succeeded;
- output_line_command(ip, &outbuf); /* JF */
- }
- }
-
- /*
- * skip to #endif, #else, or #elif. adjust line numbers, etc.
- * leaves input ptr at the sharp sign found.
- */
- static
- skip_if_group(ip)
- FILE_BUF *ip;
- {
- register U_CHAR *bp = ip->bufp, *cp;
- register U_CHAR *endb = ip->buf + ip->length;
- struct keyword_table *kt;
- U_CHAR *save_sharp, *skip_to_end_of_comment (), *skip_quoted_string ();
- IF_STACK_FRAME *save_if_stack = if_stack; /* don't pop past here */
-
- while (bp <= endb) {
- switch (*bp++) {
- case '/': /* possible comment */
- if (*bp == '*') {
- ip->bufp = ++bp;
- bp = skip_to_end_of_comment (ip, &ip->lineno);
- }
- break;
- case '\"':
- case '\'':
- ip->bufp = bp - 1;
- bp = skip_quoted_string (ip, NULL); /* JF was (ip) */
- break;
- case '\n':
- ++ip->lineno;
- break;
- case '#':
- /* # keyword: the # must be first nonblank char on the line */
- for (cp = bp - 1; cp >= ip->buf; cp--)
- if (*cp == '\n')
- break;
- cp++; /* skip nl or move back into buffer */
- SKIP_WHITE_SPACE (cp);
- if (cp != bp - 1) /* ????? */
- break;
-
- save_sharp = cp; /* point at '#' */
- SKIP_WHITE_SPACE (bp);
- for (kt = keyword_table; kt->length >= 0; kt++) {
- IF_STACK_FRAME *temp;
- if (strncmp(bp, kt->name, kt->length) == 0
- && !is_idchar[bp[kt->length]]) {
- switch (kt->type) {
- case T_IF:
- case T_IFDEF:
- case T_IFNDEF:
- temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
- temp->next = if_stack;
- if_stack = temp;
- temp->lineno = ip->lineno;
- temp->fname = ip->fname;
- temp->type = kt->type;
- break;
- case T_ELSE:
- case T_ELIF:
- case T_ENDIF:
- ip->bufp = save_sharp;
- if (if_stack == NULL) {
- U_CHAR msg[50];
- sprintf (msg, "if-less #%s", kt->name);
- error (msg);
- break;
- }
- else if (if_stack == save_if_stack)
- return; /* found what we came for */
-
- if (kt->type != T_ENDIF) {
- if (if_stack->type == T_ELSE)
- error ("#else or #elif after #else");
- if_stack->type = kt->type;
- break;
- }
-
- temp = if_stack;
- if_stack = if_stack->next;
- free (temp);
- break;
- }
- }
- }
- }
- }
- ip->bufp = bp;
- ip->lineno = instack->lineno; /* bufp won't be right, though */
- error ("unterminated #if/#ifdef/#ifndef conditional");
- /* after this returns, the main loop will exit because ip->bufp
- now points to the end of the buffer. I am not sure whether
- this is dirty or not. */
- return;
- }
-
- /*
- * handle a #else directive. Do this by just continuing processing
- * without changing if_stack ; this is so that the error message
- * for missing #endif's etc. will point to the original #if. It
- * is possible that something different would be better.
- */
- do_else(buf, limit, op, keyword)
- U_CHAR *buf, *limit;
- FILE_BUF *op;
- struct keyword_table *keyword;
- {
- register U_CHAR *bp;
- FILE_BUF *ip = &instack[indepth - 1];
-
- if (if_stack == NULL) {
- error ("if-less #else");
- return;
- } else {
- if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
- error ("#else after #else");
- fprintf (stderr, " (matches line %d", if_stack->lineno);
- if (strcmp(if_stack->fname, ip->fname) != 0)
- fprintf (stderr, ", file %s", if_stack->fname);
- fprintf(stderr, ")\n");
- }
- if_stack->type = T_ELSE;
- }
-
- if (if_stack->if_succeeded)
- skip_if_group (ip);
- else {
- ++if_stack->if_succeeded; /* continue processing input */
- output_line_command(ip, op); /* JF try to keep line #s right? */
- }
- }
-
- /*
- * unstack after #endif command
- */
- do_endif(buf, limit, op, keyword)
- U_CHAR *buf, *limit;
- FILE_BUF *op;
- struct keyword_table *keyword;
- {
- register U_CHAR *bp;
-
- if (if_stack == NULL)
- error ("if-less #endif");
- else {
- IF_STACK_FRAME *temp = if_stack;
- if_stack = if_stack->next;
- free (temp);
- /* JF try to keep line #s right? */
- output_line_command (&instack[indepth - 1], op);
- }
- }
-
- /*
- * Skip a comment, assuming the input ptr immediately follows the
- * initial slash-star. Bump line counter as necessary.
- * (The canonical line counter is &ip->lineno).
- * Don't use this routine (or the next one) if bumping the line
- * counter is not sufficient to deal with newlines in the string.
- */
- U_CHAR *
- skip_to_end_of_comment (ip, line_counter)
- register FILE_BUF *ip;
- int *line_counter; /* place to remember newlines, or NULL */
- {
- register U_CHAR *limit = ip->buf + ip->length;
- register U_CHAR *bp = ip->bufp;
- FILE_BUF *op = &outbuf; /* JF */
-
- /* JF this line_counter stuff is a crock to make sure the
- comment is only put out once, no matter how many times
- the comment is skipped. It almost works */
- if (put_out_comments && !line_counter) {
- *op->bufp++ = '/';
- *op->bufp++ = '*';
- }
- while (bp < limit) {
- if (put_out_comments && !line_counter)
- *op->bufp++ = *bp;
- switch (*bp++) {
- case '\n':
- if (line_counter != NULL)
- ++*line_counter;
- break;
- case '*':
- if (*bp == '/') {
- if (put_out_comments && !line_counter)
- *op->bufp++ = '/';
- ip->bufp = ++bp;
- return bp;
- }
- break;
- }
- }
- ip->bufp = bp;
- return bp;
- }
- /*
- * skip over a quoted string. Unlike skip_to_end_of_comment, this
- * wants ip->bufp at the beginning quote, not after it. this is so we
- * can tell what kind of quote to match. return if unescaped eol is
- * encountered --- it is probably some sort of error in the input.
- */
- U_CHAR *
- skip_quoted_string (ip, count_newlines)
- register FILE_BUF *ip;
- int count_newlines;
- {
- register U_CHAR *limit = ip->buf + ip->length;
- register U_CHAR *bp = ip->bufp;
- register U_CHAR c, match;
-
- match = *bp++;
- while (bp < limit) {
- c = *bp++;
- if (c == '\\') {
- if (*bp++ == '\n' && count_newlines)
- ++ip->lineno;
- } else if (c == '\n') {
- bp -= 2; /* whoa! back up to eol and punt. */
- break;
- } else if (c == match)
- break;
- }
- ip->bufp = bp;
- return bp;
- }
-
- /*
- * write out a #line command, for instance, after an #include file.
- */
- static
- output_line_command (ip, op)
- FILE_BUF *ip, *op;
- {
- int len, line_cmd_buf[500];
-
- if (ip->fname == NULL)
- return;
-
- #ifdef OUTPUT_LINE_COMMANDS
- sprintf(line_cmd_buf, "#line %d \"%s\"\n", ip->lineno, ip->fname);
- #else
- sprintf(line_cmd_buf, "# %d \"%s\"\n", ip->lineno, ip->fname);
- #endif
- len = strlen(line_cmd_buf);
- check_expand (op, len);
- if (op->bufp > op->buf && op->bufp[-1] != '\n') /* JF make sure */
- *op->bufp++ = '\n';
- bcopy (line_cmd_buf, op->bufp, len);
- op->bufp += len;
- }
-
-
- /* Expand a macro call.
- HP points to the symbol that is the macro being called.
- IP is the input source for reading the arguments of the macro.
- Send the result of the expansion to OP.
- EXCESS_NEWLINES_PTR points to an integer;
- we increment that integer once for each newline swallowed
- in the process of reading this macro call. */
-
- macroexpand (hp, ip, op, excess_newlines_ptr)
- HASHNODE *hp;
- FILE_BUF *ip, *op;
- int *excess_newlines_ptr;
- {
- FILE_BUF *ip2;
- int nargs;
- DEFINITION *defn = hp->value.defn;
- int newlines_found = 0;
-
- /* it might not actually be a macro. */
- if (hp->type != T_MACRO)
- return expand_special_symbol (hp, ip, op);
-
- ip2 = &instack[indepth++];
- bzero (ip2, sizeof (FILE_BUF)); /* paranoia */
-
- nargs = defn->nargs;
-
- if (nargs >= 0)
- {
- register U_CHAR *bp, *xbuf;
- U_CHAR *skip_macro_argument ();
- register int i;
- int xbuf_len;
- int offset; /* offset in expansion,
- copied a piece at a time */
- int totlen; /* total amount of exp buffer filled so far */
-
- register struct reflist *ap;
- struct argptrs {
- U_CHAR *argstart;
- int length;
- } *args;
-
- args = (struct argptrs *) alloca ((nargs + 1) * sizeof (struct argptrs));
- if (ip->bufp >= ip->buf+ip->length)
- { /* JF evil magic to make things work! */
- ip = &instack[indepth-3];
- }
- bp = ip->bufp;
-
- /* make sure it really was a macro call. */
- if (isspace(bp[-1])) {
- while (isspace (*bp)) {
- if (*bp == '\n')
- ++newlines_found;
- bp++;
- }
- if (*bp != '(')
- goto nope;
- bp++; /* skip over the paren */
- }
- else if (*(bp-1) != '(')
- goto nope;
-
- for (i = 0; i < nargs; i++) {
- args[i].argstart = bp;
- bp = skip_macro_argument(bp, ip, &newlines_found);
- args[i].length = bp - args[i].argstart;
- if (*bp == ',')
- bp++;
- }
- args[nargs].argstart = bp;
- if (*bp++ != ')')
- goto nope;
-
- /* make a rescan buffer with enough room for the pattern plus
- all the arg strings. */
- xbuf_len = defn->length + 1;
- for (ap = defn->pattern; ap != NULL; ap = ap->next)
- xbuf_len += args[ap->argno - 1].length;
- xbuf = (U_CHAR *) alloca (xbuf_len);
-
- offset = totlen = 0;
- for (ap = defn->pattern; ap != NULL; ap = ap->next) {
- bcopy (defn->expansion + offset, xbuf + totlen, ap->nchars);
- totlen += ap->nchars;
- offset += ap->nchars;
-
- if (ap->argno > 0) {
- bcopy (args[ap->argno - 1].argstart, xbuf + totlen,
- args[ap->argno - 1].length);
- totlen += args[ap->argno - 1].length;
- }
-
- if (totlen > xbuf_len)
- {
- /* impossible */
- error ("cpp impossible internal error: expansion too large");
- goto nope; /* this can't happen??? */
- }
- }
-
- /* if there is anything left after handling the arg list,
- copy that in too. */
- if (offset < defn->length) {
- bcopy (defn->expansion + offset, xbuf + totlen,
- defn->length - offset);
- totlen += defn->length - offset;
- }
-
- ip2->buf = xbuf;
- ip2->length = totlen;
-
- /* skip the input over the whole macro call. */
- ip->bufp = bp;
-
- }
- else
- {
- ip2->buf = ip2->bufp = defn->expansion;
- ip2->length = defn->length;
- }
-
- rescan (ip2, op);
- --indepth;
- *excess_newlines_ptr += newlines_found;
- ip->lineno += newlines_found;
-
- return 0;
-
- nope:
- error ("argument mismatch");
- --indepth;
- return 1;
- }
-
- /*
- * skip a balanced paren string up to the next comma.
- */
- U_CHAR *
- skip_macro_argument(bp, ip, newlines)
- U_CHAR *bp;
- FILE_BUF *ip;
- int *newlines;
- {
- int paren = 0;
- int quotec = 0;
-
- while (bp < ip->buf + ip->length) {
- switch (*bp) {
- case '(':
- paren++;
- break;
- case ')':
- if (--paren < 0)
- return bp;
- break;
- case '\n':
- ++*newlines;
- break;
- case '/':
- if (bp[1] != '*' || bp + 1 >= ip->buf + ip->length)
- break;
- bp += 2;
- while ((bp[0] != '*' || bp[1] != '/')
- && bp + 1 < ip->buf + ip->length)
- {
- if (*bp == '\n') ++*newlines;
- bp++;
- }
- break;
- case '\'': /* JF handle quotes right */
- case '\"':
- for (quotec = *bp++; bp < ip->buf + ip->length && *bp != quotec; bp++)
- {
- if (*bp == '\\') bp++;
- if (*bp == '\n')
- ++*newlines;
- }
- break;
- case ',':
- if (paren == 0)
- return bp;
- break;
- }
- bp++;
- }
- return bp;
- }
-
- /*
- * error - print out message. also make print on stderr. Uses stdout
- * now for debugging convenience.
- */
- error (msg)
- U_CHAR *msg;
- {
- int i;
- FILE_BUF *ip = NULL;
-
- for (i = indepth - 1; i >= 0; i--)
- if (instack[i].fname != NULL) {
- ip = &instack[i];
- break;
- }
-
- if (ip != NULL)
- fprintf(stderr, "file %s, offset %d (line %d): ",
- ip->fname, ip->bufp - ip->buf, ip->lineno);
- fprintf(stderr, "%s\n", msg);
- return 0;
- }
-
- /*
- * if OBUF doesn't have NEEDED bytes after OPTR, make it bigger
- * this should be a macro, for speed.
- * The "expand" in the name of this routine means buffer expansion,
- * not macro expansion. It may become necessary to have some hacky
- * mechanism for flushing out the output buffer if it gets too big.
- *
- * As things stand, nothing is ever placed in the output buffer to be
- * removed again except when it's KNOWN to be part of an identifier,
- * so flushing and moving down everything left, instead of expanding,
- * should work ok.
- */
- U_CHAR *
- check_expand(obuf, needed)
- register FILE_BUF *obuf;
- register int needed;
- {
- register int i;
- register U_CHAR *p;
-
- if (obuf->length - (obuf->bufp - obuf->buf) > needed)
- return obuf->buf;
-
- i = 2 * obuf->length;
- if (needed >= i)
- i += (3 * needed) / 2;
-
- if ((p = (U_CHAR *) xrealloc (obuf->buf, i)) == NULL)
- return NULL;
- obuf->bufp = p + (obuf->bufp - obuf->buf);
- obuf->buf = p;
- obuf->length = i;
-
- return p;
- }
-
- /*
- * install a name in the main hash table, even if it is already there.
- * name stops with first non alphanumeric, except leading '#'.
- * caller must check against redefinition if that is desired.
- * delete() removes things installed by install() in fifo order.
- * this is important because of the `defined' special symbol used
- * in #if, and also if pushdef/popdef directives are ever implemented.
- */
- HASHNODE *
- install (name, type, value)
- U_CHAR *name;
- int type;
- int value;
- /* watch out here if sizeof(U_CHAR *) != sizeof (int) */
- {
- HASHNODE *hp;
- int i, len = 0, bucket;
- register U_CHAR *p;
-
- p = name;
- while (is_idchar[*p])
- p++;
- len = p - name;
-
- i = sizeof (HASHNODE) + len + 1;
- hp = (HASHNODE *) xmalloc (i);
- bucket = hashf(name, len, HASHSIZE);
- hp->bucket_hdr = &hashtab[bucket];
- hp->next = hashtab[bucket];
- hashtab[bucket] = hp;
- hp->prev = NULL;
- if (hp->next != NULL)
- hp->next->prev = hp;
- hp->type = type;
- hp->length = len;
- hp->value.ival = value;
- hp->name = ((U_CHAR *) hp) + sizeof (HASHNODE);
- bcopy (name, hp->name, len);
- return hp;
- }
- /*
- * find the most recent hash node for name name (ending with first
- * non-identifier char) installed by install
- */
- HASHNODE *
- lookup (name)
- U_CHAR *name;
- {
- register U_CHAR *bp;
- register HASHNODE *bucket;
- int len;
-
- for (bp = name; is_idchar[*bp]; bp++)
- ;
- len = bp - name;
- bucket = hashtab[hashf(name, len, HASHSIZE)];
- while (bucket) {
- if (bucket->length == len && strncmp(bucket->name, name, len) == 0)
- return bucket;
- bucket = bucket->next;
- }
- return NULL;
- }
-
- /*
- * Delete a hash node. Some weirdness to free junk from macros.
- * More such weirdness will have to be added if you define more hash
- * types that need it.
- */
- delete(hp)
- HASHNODE *hp;
- {
-
- if (hp->prev != NULL)
- hp->prev->next = hp->next;
- if (hp->next != NULL)
- hp->next->prev = hp->prev;
-
- /* make sure that the bucket chain header that
- the deleted guy was on points to the right thing afterwards. */
- if (hp == *hp->bucket_hdr)
- *hp->bucket_hdr = hp->next;
-
- if (hp->type == T_MACRO) {
- DEFINITION *d = hp->value.defn;
- struct reflist *ap, *nextap;
-
- for (ap = d->pattern; ap != NULL; ap = nextap) {
- nextap = ap->next;
- free (ap);
- }
- free (d);
- }
- }
-
- /*
- * return hash function on name. must be compatible with the one
- * computed a step at a time, elsewhere
- */
- int
- hashf(name, len, hashsize)
- register U_CHAR *name;
- register int len;
- int hashsize;
- {
- register int r = 0;
-
- while (len--)
- r = HASHSTEP(r, *name++);
-
- return MAKE_POS(r) % hashsize;
- }
-
-
- /*
- * initialize random junk in the hash table and maybe other places
- */
- initialize_random_junk()
- {
- register int i;
-
- /*
- * Set up is_idchar and is_idstart tables. These should be
- * faster than saying (is_alpha(c) || c == '_'), etc.
- * Must do set up these things before calling any routines tthat
- * refer to them.
- */
- for (i = 'a'; i <= 'z'; i++) {
- ++is_idchar[i - 'a' + 'A'];
- ++is_idchar[i];
- ++is_idstart[i - 'a' + 'A'];
- ++is_idstart[i];
- }
- for (i = '0'; i <= '9'; i++)
- ++is_idchar[i];
- ++is_idchar['_'];
- ++is_idstart['_'];
-
- /* horizontal space table */
- ++is_hor_space[' '];
- ++is_hor_space['\t'];
-
- install("__LINE__", T_SPECLINE, 0);
- install("__DATE__", T_DATE, 0);
- install("__FILE__", T_FILE, 0);
- install("__TIME__", T_TIME, 0);
-
- #ifdef vax
- make_definition("vax 1");
- #endif
-
- #ifdef unix
- make_definition("unix 1");
- #endif
-
- /* is there more? */
-
- }
-
- /*
- * process a given definition string, for initialization
- */
- make_definition(str)
- U_CHAR *str;
- {
- FILE_BUF *ip;
- struct keyword_table *kt;
-
- ip = &instack[indepth++];
- ip->fname = "*Initialization*";
-
- ip->buf = ip->bufp = str;
- ip->length = strlen(str);
- ip->lineno = 1;
-
- for (kt = keyword_table; kt->type != T_DEFINE; kt++)
- ;
-
- /* pass NULL as output ptr to do_define since we KNOW it never
- does any output.... */
- do_define (str, str + strlen(str) /* - 1 JF */ , NULL, kt);
- --indepth;
- }
-
- /* JF, this does the work for the -U option */
- make_undef(str)
- U_CHAR *str;
- {
- FILE_BUF *ip;
- struct keyword_table *kt;
-
- ip = &instack[indepth++];
- ip->fname = "*undef*";
-
- ip->buf = ip->bufp = str;
- ip->length = strlen(str);
- ip->lineno = 1;
-
- for(kt = keyword_table; kt->type != T_UNDEF; kt++)
- ;
-
- do_undef(str,str + strlen(str) - 1, NULL, kt);
- --indepth;
- }
-
-
- #ifndef BSD
- #ifndef BSTRING
-
- void
- bzero (b, length)
- register char *b;
- register int length;
- {
- #ifdef VMS
- short zero = 0;
- long max_str = 65535;
-
- while (length > max_str)
- {
- (void) LIB$MOVC5 (&zero, &zero, &zero, &max_str, b);
- length -= max_str;
- b += max_str;
- }
- (void) LIB$MOVC5 (&zero, &zero, &zero, &length, b);
- #else
- while (length-- > 0)
- *b++ = 0;
- #endif /* not VMS */
- }
-
- void
- bcopy (b1, b2, length)
- register char *b1;
- register char *b2;
- register int length;
- {
- #ifdef VMS
- long max_str = 65535;
-
- while (length > max_str)
- {
- (void) LIB$MOVC3 (&max_str, b1, b2);
- length -= max_str;
- b1 += max_str;
- b2 += max_str;
- }
- (void) LIB$MOVC3 (&length, b1, b2);
- #else
- while (length-- > 0)
- *b2++ = *b1++;
- #endif /* not VMS */
- }
-
- int
- bcmp (b1, b2, length) /* This could be a macro! */
- register char *b1;
- register char *b2;
- register int length;
- {
- #ifdef VMS
- struct dsc$descriptor_s src1 = {length, DSC$K_DTYPE_T, DSC$K_CLASS_S, b1};
- struct dsc$descriptor_s src2 = {length, DSC$K_DTYPE_T, DSC$K_CLASS_S, b2};
-
- return STR$COMPARE (&src1, &src2);
- #else
- while (length-- > 0)
- if (*b1++ != *b2++)
- return 1;
-
- return 0;
- #endif /* not VMS */
- }
- #endif /* not BSTRING */
- #endif /* not BSD */
-
-
- void
- fatal (str, arg)
- char *str, *arg;
- {
- fprintf (stderr, "%s: ", progname);
- fprintf (stderr, str, arg);
- fprintf (stderr, "\n");
- exit (FATAL_EXIT_CODE);
- }
-
- void
- perror_with_name (name)
- char *name;
- {
- extern int errno, sys_nerr;
- extern char *sys_errlist[];
-
- fprintf (stderr, "%s: ", progname);
- if (errno < sys_nerr)
- fprintf (stderr, "%s for %s\n", sys_errlist[errno], name);
- else
- fprintf (stderr, "cannot open %s\n", sys_errlist[errno], name);
- }
-
- void
- pfatal_with_name (name)
- char *name;
- {
- perror_with_name (name);
- exit (FATAL_EXIT_CODE);
- }
-
-
- static void
- memory_full ()
- {
- fatal ("Memory exhausted.");
- }
-
-
- char *
- xmalloc (size)
- int size;
- {
- extern char *malloc ();
- register char *ptr = malloc (size);
- if (ptr != 0) return (ptr);
- memory_full ();
- /*NOTREACHED*/
- }
-
- char *
- xrealloc (old, size)
- char *old;
- int size;
- {
- extern char *realloc ();
- register char *ptr = realloc (old, size);
- if (ptr != 0) return (ptr);
- memory_full ();
- /*NOTREACHED*/
- }
-
- char *
- xcalloc (number, size)
- int number, size;
- {
- extern char *malloc ();
- register int total = number * size;
- register char *ptr = malloc (total);
- if (ptr != 0)
- {
- bzero (ptr, total);
- return (ptr);
- }
- memory_full ();
- /*NOTREACHED*/
- }
-