From: jwalz Date: Sat, 5 Jan 2002 21:05:59 +0000 (+0000) Subject: *** empty log message *** X-Git-Tag: MOVE2GIT~3657 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a686293a4a09bc9df62ec66f8f4d89b3db8f0965;p=nethack *** empty log message *** --- diff --git a/sys/unix/cpp2.shr b/sys/unix/cpp2.shr new file mode 100644 index 000000000..ccadfc054 --- /dev/null +++ b/sys/unix/cpp2.shr @@ -0,0 +1,1763 @@ +# This is a shell archive. Save it in a file, remove anything before +# this line, and then unpack it by entering "sh file". Note, it may +# create directories; files and directories will be owned by you and +# have default permissions. +# +# This archive contains: +# +# cpp1.c +# cpp3.c +# cpp4.c +# +echo x - cpp1.c +sed 's/^X//' >cpp1.c << 'END-of-cpp1.c' +X/* +X * CPP main program. +X * +X * Edit history +X * 21-May-84 MM "Field test" release +X * 23-May-84 MM Some minor hacks. +X * 30-May-84 ARF Didn't get enough memory for __DATE__ +X * Added code to read stdin if no input +X * files are provided. +X * 29-Jun-84 MM Added ARF's suggestions, Unixifying cpp. +X * 11-Jul-84 MM "Official" first release (that's what I thought!) +X * 22-Jul-84 MM/ARF/SCK Fixed line number bugs, added cpp recognition +X * of #line, fixed problems with #include. +X * 23-Jul-84 MM More (minor) include hacking, some documentation. +X * Also, redid cpp's #include files +X * 25-Jul-84 MM #line filename isn't used for #include searchlist +X * #line format is +X * 25-Jul-84 ARF/MM Various bugs, mostly serious. Removed homemade doprint +X * 01-Aug-84 MM Fixed recursion bug, remove extra newlines and +X * leading whitespace from cpp output. +X * 02-Aug-84 MM Hacked (i.e. optimized) out blank lines and unneeded +X * whitespace in general. Cleaned up unget()'s. +X * 03-Aug-84 Keie Several bug fixes from Ed Keizer, Vrije Universitet. +X * -- corrected arg. count in -D and pre-defined +X * macros. Also, allow \n inside macro actual parameter +X * lists. +X * 06-Aug-84 MM If debugging, dump the preset vector at startup. +X * 12-Aug-84 MM/SCK Some small changes from Sam Kendall +X * 15-Aug-84 Keie/MM cerror, cwarn, etc. take a single string arg. +X * cierror, etc. take a single int. arg. +X * changed LINE_PREFIX slightly so it can be +X * changed in the makefile. +X * 31-Aug-84 MM USENET net.sources release. +X * 7-Sep-84 SCH/ado Lint complaints +X * 10-Sep-84 Keie Char's can't be signed in some implementations +X * 11-Sep-84 ado Added -C flag, pathological line number fix +X * 13-Sep-84 ado Added -E flag (does nothing) and "-" file for stdin. +X * 14-Sep-84 MM Allow # 123 as a synonym for #line 123 +X * 19-Sep-84 MM scanid always reads to token, make sure #line is +X * written to a new line, even if -C switch given. +X * Also, cpp - - reads stdin, writes stdout. +X * 03-Oct-84 ado/MM Several changes to line counting and keepcomments +X * stuff. Also a rewritten control() hasher -- much +X * simpler and no less "perfect". Note also changes +X * in cpp3.c to fix numeric scanning. +X * 04-Oct-84 MM Added recognition of macro formal parameters if +X * they are the only thing in a string, per the +X * draft standard. +X * 08-Oct-84 MM One more attack on scannumber +X * 15-Oct-84 MM/ado Added -N to disable predefined symbols. Fixed +X * linecount if COMMENT_INVISIBLE enabled. +X * 22-Oct-84 MM Don't evaluate the #if/#ifdef argument if +X * compilation is supressed. This prevents +X * unnecessary error messages in sequences such as +X * #ifdef FOO -- undefined +X * #if FOO == 10 -- shouldn't print warning +X * 25-Oct-84 MM Fixed bug in false ifdef supression. On vms, +X * #include should open foo.h -- this duplicates +X * the behavior of Vax-C +X * 31-Oct-84 ado/MM Parametized $ in indentifiers. Added a better +X * token concatenator and took out the trial +X * concatenation code. Also improved #ifdef code +X * and cleaned up the macro recursion tester. +X * 2-Nov-84 MM/ado Some bug fixes in token concatenation, also +X * a variety of minor (uninteresting) hacks. +X * 6-Nov-84 MM Happy Birthday. Broke into 4 files and added +X * #if sizeof (basic_types) +X * 9-Nov-84 MM Added -S* for pointer type sizes +X * 13-Nov-84 MM Split cpp1.c, added vms defaulting +X * 23-Nov-84 MM/ado -E supresses error exit, added CPP_INCLUDE, +X * fixed strncpy bug. +X * 3-Dec-84 ado/MM Added OLD_PREPROCESSOR +X * 7-Dec-84 MM Stuff in Nov 12 Draft Standard +X * 17-Dec-84 george Fixed problems with recursive macros +X * 17-Dec-84 MM Yet another attack on #if's (f/t)level removed. +X * 07-Jan-85 ado Init defines before doing command line options +X * so -Uunix works. +X */ +X +X/*)BUILD +X $(PROGRAM) = cpp +X $(FILES) = { cpp1 cpp2 cpp3 cpp4 cpp5 cpp6 } +X $(INCLUDE) = { cppdef.h cpp.h } +X $(STACK) = 2000 +X $(TKBOPTIONS) = { +X STACK = 2000 +X } +X*/ +X +X#ifdef DOCUMENTATION +X +Xtitle cpp C Pre-Processor +Xindex C pre-processor +X +Xsynopsis +X .s.nf +X cpp [-options] [infile [outfile]] +X .s.f +Xdescription +X +X CPP reads a C source file, expands macros and include +X files, and writes an input file for the C compiler. +X If no file arguments are given, CPP reads from stdin +X and writes to stdout. If one file argument is given, +X it will define the input file, while two file arguments +X define both input and output files. The file name "-" +X is a synonym for stdin or stdout as appropriate. +X +X The following options are supported. Options may +X be given in either case. +X .lm +16 +X .p -16 +X -C If set, source-file comments are written +X to the output file. This allows the output of CPP to be +X used as the input to a program, such as lint, that expects +X commands embedded in specially-formatted comments. +X .p -16 +X -Dname=value Define the name as if the programmer wrote +X +X #define name value +X +X at the start of the first file. If "=value" is not +X given, a value of "1" will be used. +X +X On non-unix systems, all alphabetic text will be forced +X to upper-case. +X .p -16 +X -E Always return "success" to the operating +X system, even if errors were detected. Note that some fatal +X errors, such as a missing #include file, will terminate +X CPP, returning "failure" even if the -E option is given. +X .p -16 +X -Idirectory Add this directory to the list of +X directories searched for #include "..." and #include <...> +X commands. Note that there is no space between the +X "-I" and the directory string. More than one -I command +X is permitted. On non-Unix systems "directory" is forced +X to upper-case. +X .p -16 +X -N CPP normally predefines some symbols defining +X the target computer and operating system. If -N is specified, +X no symbols will be predefined. If -N -N is specified, the +X "always present" symbols, __LINE__, __FILE__, and __DATE__ +X are not defined. +X .p -16 +X -Stext CPP normally assumes that the size of +X the target computer's basic variable types is the same as the size +X of these types of the host computer. (This can be overridden +X when CPP is compiled, however.) The -S option allows dynamic +X respecification of these values. "text" is a string of +X numbers, separated by commas, that specifies correct sizes. +X The sizes must be specified in the exact order: +X +X char short int long float double +X +X If you specify the option as "-S*text", pointers to these +X types will be specified. -S* takes one additional argument +X for pointer to function (e.g. int (*)()) +X +X For example, to specify sizes appropriate for a PDP-11, +X you would write: +X +X c s i l f d func +X -S1,2,2,2,4,8, +X -S*2,2,2,2,2,2,2 +X +X Note that all values must be specified. +X .p -16 +X -Uname Undefine the name as if +X +X #undef name +X +X were given. On non-Unix systems, "name" will be forced to +X upper-case. +X .p -16 +X -Xnumber Enable debugging code. If no value is +X given, a value of 1 will be used. (For maintenence of +X CPP only.) +X .s.lm -16 +X +XPre-Defined Variables +X +X When CPP begins processing, the following variables will +X have been defined (unless the -N option is specified): +X .s +X Target computer (as appropriate): +X .s +X pdp11, vax, M68000 m68000 m68k +X .s +X Target operating system (as appropriate): +X .s +X rsx, rt11, vms, unix +X .s +X Target compiler (as appropriate): +X .s +X decus, vax11c +X .s +X The implementor may add definitions to this list. +X The default definitions match the definition of the +X host computer, operating system, and C compiler. +X .s +X The following are always available unless undefined (or +X -N was specified twice): +X .lm +16 +X .p -12 +X __FILE__ The input (or #include) file being compiled +X (as a quoted string). +X .p -12 +X __LINE__ The line number being compiled. +X .p -12 +X __DATE__ The date and time of compilation as +X a Unix ctime quoted string (the trailing newline is removed). +X Thus, +X .s +X printf("Bug at line %s,", __LINE__); +X printf(" source file %s", __FILE__); +X printf(" compiled on %s", __DATE__); +X .s.lm -16 +X +XDraft Proposed Ansi Standard Considerations +X +X The current version of the Draft Proposed Standard +X explicitly states that "readers are requested not to specify +X or claim conformance to this draft." Readers and users +X of Decus CPP should not assume that Decus CPP conforms +X to the standard, or that it will conform to the actual +X C Language Standard. +X +X When CPP is itself compiled, many features of the Draft +X Proposed Standard that are incompatible with existing +X preprocessors may be disabled. See the comments in CPP's +X source for details. +X +X The latest version of the Draft Proposed Standard (as reflected +X in Decus CPP) is dated November 12, 1984. +X +X Comments are removed from the input text. The comment +X is replaced by a single space character. The -C option +X preserves comments, writing them to the output file. +X +X The '$' character is considered to be a letter. This is +X a permitted extension. +X +X The following new features of C are processed by CPP: +X .s.comment Note: significant spaces, not tabs, .br quotes #if, #elif +X .br;####_#elif expression (_#else _#if) +X .br;####'_\xNNN' (Hexadecimal constant) +X .br;####'_\a' (Ascii BELL) +X .br;####'_\v' (Ascii Vertical Tab) +X .br;####_#if defined NAME 1 if defined, 0 if not +X .br;####_#if defined (NAME) 1 if defined, 0 if not +X .br;####_#if sizeof (basic type) +X .br;####unary + +X .br;####123U, 123LU Unsigned ints and longs. +X .br;####12.3L Long double numbers +X .br;####token_#token Token concatenation +X .br;####_#include token Expands to filename +X +X The Draft Proposed Standard has extended C, adding a constant +X string concatenation operator, where +X +X "foo" "bar" +X +X is regarded as the single string "foobar". (This does not +X affect CPP's processing but does permit a limited form of +X macro argument substitution into strings as will be discussed.) +X +X The Standard Committee plans to add token concatenation +X to #define command lines. One suggested implementation +X is as follows: the sequence "Token1#Token2" is treated +X as if the programmer wrote "Token1Token2". This could +X be used as follows: +X +X #line 123 +X #define ATLINE foo#__LINE__ +X +X ATLINE would be defined as foo123. +X +X Note that "Token2" must either have the format of an +X identifier or be a string of digits. Thus, the string +X +X #define ATLINE foo#1x3 +X +X generates two tokens: "foo1" and "x3". +X +X If the tokens T1 and T2 are concatenated into T3, +X this implementation operates as follows: +X +X 1. Expand T1 if it is a macro. +X 2. Expand T2 if it is a macro. +X 3. Join the tokens, forming T3. +X 4. Expand T3 if it is a macro. +X +X A macro formal parameter will be substituted into a string +X or character constant if it is the only component of that +X constant: +X +X #define VECSIZE 123 +X #define vprint(name, size) \ +X printf("name" "[" "size" "] = {\n") +X ... vprint(vector, VECSIZE); +X +X expands (effectively) to +X +X vprint("vector[123] = {\n"); +X +X Note that this will be useful if your C compiler supports +X the new string concatenation operation noted above. +X As implemented here, if you write +X +X #define string(arg) "arg" +X ... string("foo") ... +X +X This implementation generates "foo", rather than the strictly +X correct ""foo"" (which will probably generate an error message). +X This is, strictly speaking, an error in CPP and may be removed +X from future releases. +X +Xerror messages +X +X Many. CPP prints warning or error messages if you try to +X use multiple-byte character constants (non-transportable) +X if you #undef a symbol that was not defined, or if your +X program has potentially nested comments. +X +Xauthor +X +X Martin Minow +X +Xbugs +X +X The #if expression processor uses signed integers only. +X I.e, #if 0xFFFFu < 0 may be TRUE. +X +X#endif +X +X#include +X#include +X#include "cppdef.h" +X#include "cpp.h" +X +X/* +X * Commonly used global variables: +X * line is the current input line number. +X * wrongline is set in many places when the actual output +X * line is out of sync with the numbering, e.g, +X * when expanding a macro with an embedded newline. +X * +X * token holds the last identifier scanned (which might +X * be a candidate for macro expansion). +X * errors is the running cpp error counter. +X * infile is the head of a linked list of input files (extended by +X * #include and macros being expanded). infile always points +X * to the current file/macro. infile->parent to the includer, +X * etc. infile->fd is NULL if this input stream is a macro. +X */ +Xint line; /* Current line number */ +Xint wrongline; /* Force #line to compiler */ +Xchar token[IDMAX + 1]; /* Current input token */ +Xint errors; /* cpp error counter */ +XFILEINFO *infile = NULL; /* Current input file */ +X#if DEBUG +Xint debug; /* TRUE if debugging now */ +X#endif +X/* +X * This counter is incremented when a macro expansion is initiated. +X * If it exceeds a built-in value, the expansion stops -- this tests +X * for a runaway condition: +X * #define X Y +X * #define Y X +X * X +X * This can be disabled by falsifying rec_recover. (Nothing does this +X * currently: it is a hook for an eventual invocation flag.) +X */ +Xint recursion; /* Infinite recursion counter */ +Xint rec_recover = TRUE; /* Unwind recursive macros */ +X +X/* +X * instring is set TRUE when a string is scanned. It modifies the +X * behavior of the "get next character" routine, causing all characters +X * to be passed to the caller (except ). Note especially that +X * comments and \ are not removed from the source. (This +X * prevents cpp output lines from being arbitrarily long). +X * +X * inmacro is set by #define -- it absorbs comments and converts +X * form-feed and vertical-tab to space, but returns \ +X * to the caller. Strictly speaking, this is a bug as \ +X * shouldn't delimit tokens, but we'll worry about that some other +X * time -- it is more important to prevent infinitly long output lines. +X * +X * instring and inmarcor are parameters to the get() routine which +X * were made global for speed. +X */ +Xint instring = FALSE; /* TRUE if scanning string */ +Xint inmacro = FALSE; /* TRUE if #defining a macro */ +X +X/* +X * work[] and workp are used to store one piece of text in a temporay +X * buffer. To initialize storage, set workp = work. To store one +X * character, call save(c); (This will fatally exit if there isn't +X * room.) To terminate the string, call save(EOS). Note that +X * the work buffer is used by several subroutines -- be sure your +X * data won't be overwritten. The extra byte in the allocation is +X * needed for string formal replacement. +X */ +Xchar work[NWORK + 1]; /* Work buffer */ +Xchar *workp; /* Work buffer pointer */ +X +X/* +X * keepcomments is set TRUE by the -C option. If TRUE, comments +X * are written directly to the output stream. This is needed if +X * the output from cpp is to be passed to lint (which uses commands +X * embedded in comments). cflag contains the permanent state of the +X * -C flag. keepcomments is always falsified when processing #control +X * commands and when compilation is supressed by a false #if +X * +X * If eflag is set, CPP returns "success" even if non-fatal errors +X * were detected. +X * +X * If nflag is non-zero, no symbols are predefined except __LINE__. +X * __FILE__, and __DATE__. If nflag > 1, absolutely no symbols +X * are predefined. +X */ +Xint keepcomments = FALSE; /* Write out comments flag */ +Xint cflag = FALSE; /* -C option (keep comments) */ +Xint eflag = FALSE; /* -E option (never fail) */ +Xint nflag = 0; /* -N option (no predefines) */ +X +X/* +X * ifstack[] holds information about nested #if's. It is always +X * accessed via *ifptr. The information is as follows: +X * WAS_COMPILING state of compiling flag at outer level. +X * ELSE_SEEN set TRUE when #else seen to prevent 2nd #else. +X * TRUE_SEEN set TRUE when #if or #elif succeeds +X * ifstack[0] holds the compiling flag. It is TRUE if compilation +X * is currently enabled. Note that this must be initialized TRUE. +X */ +Xchar ifstack[BLK_NEST] = { TRUE }; /* #if information */ +Xchar *ifptr = ifstack; /* -> current ifstack[] */ +X +X/* +X * incdir[] stores the -i directories (and the system-specific +X * #include <...> directories. +X */ +Xchar *incdir[NINCLUDE]; /* -i directories */ +Xchar **incend = incdir; /* -> free space in incdir[] */ +X +X/* +X * This is the table used to predefine target machine and operating +X * system designators. It may need hacking for specific circumstances. +X * Note: it is not clear that this is part of the Ansi Standard. +X * The -N option supresses preset definitions. +X */ +Xchar *preset[] = { /* names defined at cpp start */ +X#ifdef MACHINE +X MACHINE, +X#endif +X#ifdef SYSTEM +X SYSTEM, +X#endif +X#ifdef COMPILER +X COMPILER, +X#endif +X#if DEBUG +X "decus_cpp", /* Ourselves! */ +X#endif +X NULL /* Must be last */ +X}; +X +X/* +X * The value of these predefined symbols must be recomputed whenever +X * they are evaluated. The order must not be changed. +X */ +Xchar *magic[] = { /* Note: order is important */ +X "__LINE__", +X "__FILE__", +X NULL /* Must be last */ +X}; +X +Xmain(argc, argv) +Xint argc; +Xchar *argv[]; +X{ +X register int i; +X +X#if HOST == SYS_VMS +X argc = getredirection(argc, argv); /* vms >file and stdin */ +X /* +X * Open input file, "-" means use stdin. +X */ +X if (!streq(argv[1], "-")) { +X if (freopen(argv[1], "r", stdin) == NULL) { +X perror(argv[1]); +X cerror("Can't open input file \"%s\"", argv[1]); +X exit(IO_ERROR); +X } +X strcpy(work, argv[1]); /* Remember input filename */ +X break; +X } /* Else, just get stdin */ +X case 0: /* No args? */ +X case 1: /* No files, stdin -> stdout */ +X#if HOST == SYS_UNIX +X work[0] = EOS; /* Unix can't find stdin name */ +X#else +X fgetname(stdin, work); /* Vax-11C, Decus C know name */ +X#endif +X break; +X +X default: +X exit(IO_ERROR); /* Can't happen */ +X } +X setincdirs(); /* Setup -I include directories */ +X addfile(stdin, work); /* "open" main input file */ +X#if DEBUG +X if (debug > 0) +X dumpdef("preset #define symbols"); +X#endif +X cppmain(); /* Process main file */ +X if ((i = (ifptr - &ifstack[0])) != 0) { +X#if OLD_PREPROCESSOR +X ciwarn("Inside #ifdef block at end of input, depth = %d", i); +X#else +X cierror("Inside #ifdef block at end of input, depth = %d", i); +X#endif +X } +X fclose(stdout); +X if (errors > 0) { +X fprintf(stderr, (errors == 1) +X ? "%d error in preprocessor\n" +X : "%d errors in preprocessor\n", errors); +X if (!eflag) +X exit(IO_ERROR); +X } +X exit(IO_NORMAL); /* No errors or -E option set */ +X} +X +XFILE_LOCAL +Xcppmain() +X/* +X * Main process for cpp -- copies tokens from the current input +X * stream (main file, include file, or a macro) to the output +X * file. +X */ +X{ +X register int c; /* Current character */ +X register int counter; /* newlines and spaces */ +X extern int output(); /* Output one character */ +X +X /* +X * Explicitly output a #line at the start of cpp output so +X * that lint (etc.) knows the name of the original source +X * file. If we don't do this explicitly, we may get +X * the name of the first #include file instead. +X */ +X sharp(); +X /* +X * This loop is started "from the top" at the beginning of each line +X * wrongline is set TRUE in many places if it is necessary to write +X * a #line record. (But we don't write them when expanding macros.) +X * +X * The counter variable has two different uses: at +X * the start of a line, it counts the number of blank lines that +X * have been skipped over. These are then either output via +X * #line records or by outputting explicit blank lines. +X * When expanding tokens within a line, the counter remembers +X * whether a blank/tab has been output. These are dropped +X * at the end of the line, and replaced by a single blank +X * within lines. +X */ +X for (;;) { +X counter = 0; /* Count empty lines */ +X for (;;) { /* For each line, ... */ +X while (type[(c = get())] == SPA) /* Skip leading blanks */ +X ; /* in this line. */ +X if (c == '\n') /* If line's all blank, */ +X ++counter; /* Do nothing now */ +X else if (c == '#') { /* Is 1st non-space '#' */ +X keepcomments = FALSE; /* Don't pass comments */ +X counter = control(counter); /* Yes, do a #command */ +X keepcomments = (cflag && compiling); +X } +X else if (c == EOF_CHAR) /* At end of file? */ +X break; +X else if (!compiling) { /* #ifdef false? */ +X skipnl(); /* Skip to newline */ +X counter++; /* Count it, too. */ +X } +X else { +X break; /* Actual token */ +X } +X } +X if (c == EOF_CHAR) /* Exit process at */ +X break; /* End of file */ +X /* +X * If the loop didn't terminate because of end of file, we +X * know there is a token to compile. First, clean up after +X * absorbing newlines. counter has the number we skipped. +X */ +X if ((wrongline && infile->fp != NULL) || counter > 4) +X sharp(); /* Output # line number */ +X else { /* If just a few, stuff */ +X while (--counter >= 0) /* them out ourselves */ +X putchar('\n'); +X } +X /* +X * Process each token on this line. +X */ +X unget(); /* Reread the char. */ +X for (;;) { /* For the whole line, */ +X do { /* Token concat. loop */ +X for (counter = 0; (type[(c = get())] == SPA);) { +X#if COMMENT_INVISIBLE +X if (c != COM_SEP) +X counter++; +X#else +X counter++; /* Skip over blanks */ +X#endif +X } +X if (c == EOF_CHAR || c == '\n') +X goto end_line; /* Exit line loop */ +X else if (counter > 0) /* If we got any spaces */ +X putchar(' '); /* Output one space */ +X c = macroid(c); /* Grab the token */ +X } while (type[c] == LET && catenate()); +X if (c == EOF_CHAR || c == '\n') /* From macro exp error */ +X goto end_line; /* Exit line loop */ +X switch (type[c]) { +X case LET: +X fputs(token, stdout); /* Quite ordinary token */ +X break; +X +X +X case DIG: /* Output a number */ +X case DOT: /* Dot may begin floats */ +X scannumber(c, output); +X break; +X +X case QUO: /* char or string const */ +X scanstring(c, output); /* Copy it to output */ +X break; +X +X default: /* Some other character */ +X cput(c); /* Just output it */ +X break; +X } /* Switch ends */ +X } /* Line for loop */ +Xend_line: if (c == '\n') { /* Compiling at EOL? */ +X putchar('\n'); /* Output newline, if */ +X if (infile->fp == NULL) /* Expanding a macro, */ +X wrongline = TRUE; /* Output # line later */ +X } +X } /* Continue until EOF */ +X} +X +Xoutput(c) +Xint c; +X/* +X * Output one character to stdout -- output() is passed as an +X * argument to scanstring() +X */ +X{ +X#if COMMENT_INVISIBLE +X if (c != TOK_SEP && c != COM_SEP) +X#else +X if (c != TOK_SEP) +X#endif +X putchar(c); +X} +X +Xstatic char *sharpfilename = NULL; +X +XFILE_LOCAL +Xsharp() +X/* +X * Output a line number line. +X */ +X{ +X register char *name; +X +X if (keepcomments) /* Make sure # comes on */ +X putchar('\n'); /* a fresh, new line. */ +X printf("#%s %d", LINE_PREFIX, line); +X if (infile->fp != NULL) { +X name = (infile->progname != NULL) +X ? infile->progname : infile->filename; +X if (sharpfilename == NULL +X || sharpfilename != NULL && !streq(name, sharpfilename)) { +X if (sharpfilename != NULL) +X free(sharpfilename); +X sharpfilename = savestring(name); +X printf(" \"%s\"", name); +X } +X } +X putchar('\n'); +X wrongline = FALSE; +X} +END-of-cpp1.c +echo x - cpp3.c +sed 's/^X//' >cpp3.c << 'END-of-cpp3.c' +X/* +X * C P P 3 . C +X * +X * File open and command line options +X * +X * Edit history +X * 13-Nov-84 MM Split from cpp1.c +X */ +X +X#include +X#include +X#include "cppdef.h" +X#include "cpp.h" +X#if DEBUG && (HOST == SYS_VMS || HOST == SYS_UNIX) +X#include +Xextern int abort(); /* For debugging */ +X#endif +X +Xint +Xopenfile(filename) +Xchar *filename; +X/* +X * Open a file, add it to the linked list of open files. +X * This is called only from openfile() above. +X */ +X{ +X register FILE *fp; +X +X if ((fp = fopen(filename, "r")) == NULL) { +X#if DEBUG +X perror(filename); +X#endif +X return (FALSE); +X } +X#if DEBUG +X if (debug) +X fprintf(stderr, "Reading from \"%s\"\n", filename); +X#endif +X addfile(fp, filename); +X return (TRUE); +X} +X +Xaddfile(fp, filename) +XFILE *fp; /* Open file pointer */ +Xchar *filename; /* Name of the file */ +X/* +X * Initialize tables for this open file. This is called from openfile() +X * above (for #include files), and from the entry to cpp to open the main +X * input file. It calls a common routine, getfile() to build the FILEINFO +X * structure which is used to read characters. (getfile() is also called +X * to setup a macro replacement.) +X */ +X{ +X register FILEINFO *file; +X extern FILEINFO *getfile(); +X +X file = getfile(NBUFF, filename); +X file->fp = fp; /* Better remember FILE * */ +X file->buffer[0] = EOS; /* Initialize for first read */ +X line = 1; /* Working on line 1 now */ +X wrongline = TRUE; /* Force out initial #line */ +X} +X +Xsetincdirs() +X/* +X * Append system-specific directories to the include directory list. +X * Called only when cpp is started. +X */ +X{ +X +X#ifdef CPP_INCLUDE +X *incend++ = CPP_INCLUDE; +X#define IS_INCLUDE 1 +X#else +X#define IS_INCLUDE 0 +X#endif +X +X#if HOST == SYS_UNIX +X *incend++ = "/usr/include"; +X#define MAXINCLUDE (NINCLUDE - 1 - IS_INCLUDE) +X#endif +X +X#if HOST == SYS_VMS +X extern char *getenv(); +X +X if (getenv("C$LIBRARY") != NULL) +X *incend++ = "C$LIBRARY:"; +X *incend++ = "SYS$LIBRARY:"; +X#define MAXINCLUDE (NINCLUDE - 2 - IS_INCLUDE) +X#endif +X +X#if HOST == SYS_RSX +X extern int $$rsts; /* TRUE on RSTS/E */ +X extern int $$pos; /* TRUE on PRO-350 P/OS */ +X extern int $$vms; /* TRUE on VMS compat. */ +X +X if ($$pos) { /* P/OS? */ +X *incend++ = "SY:[ZZDECUSC]"; /* C #includes */ +X *incend++ = "LB:[1,5]"; /* RSX library */ +X } +X else if ($$rsts) { /* RSTS/E? */ +X *incend++ = "SY:@"; /* User-defined account */ +X *incend++ = "C:"; /* Decus-C library */ +X *incend++ = "LB:[1,1]"; /* RSX library */ +X } +X else if ($$vms) { /* VMS compatibility? */ +X *incend++ = "C:"; +X } +X else { /* Plain old RSX/IAS */ +X *incend++ = "LB:[1,1]"; +X } +X#define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE) +X#endif +X +X#if HOST == SYS_RT11 +X extern int $$rsts; /* RSTS/E emulation? */ +X +X if ($$rsts) +X *incend++ = "SY:@"; /* User-defined account */ +X *incend++ = "C:"; /* Decus-C library disk */ +X *incend++ = "SY:"; /* System (boot) disk */ +X#define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE) +X#endif +X} +X +Xint +Xdooptions(argc, argv) +Xint argc; +Xchar *argv[]; +X/* +X * dooptions is called to process command line arguments (-Detc). +X * It is called only at cpp startup. +X */ +X{ +X register char *ap; +X register DEFBUF *dp; +X register int c; +X int i, j; +X char *arg; +X SIZES *sizp; /* For -S */ +X int size; /* For -S */ +X int isdatum; /* FALSE for -S* */ +X int endtest; /* For -S */ +X +X for (i = j = 1; i < argc; i++) { +X arg = ap = argv[i]; +X if (*ap++ != '-' || *ap == EOS) +X argv[j++] = argv[i]; +X else { +X c = *ap++; /* Option byte */ +X if (islower(c)) /* Normalize case */ +X c = toupper(c); +X switch (c) { /* Command character */ +X case 'C': /* Keep comments */ +X cflag = TRUE; +X keepcomments = TRUE; +X break; +X +X case 'D': /* Define symbol */ +X#if HOST != SYS_UNIX +X zap_uc(ap); /* Force define to U.C. */ +X#endif +X /* +X * If the option is just "-Dfoo", make it -Dfoo=1 +X */ +X while (*ap != EOS && *ap != '=') +X ap++; +X if (*ap == EOS) +X ap = "1"; +X else +X *ap++ = EOS; +X /* +X * Now, save the word and its definition. +X */ +X dp = defendel(argv[i] + 2, FALSE); +X dp->repl = savestring(ap); +X dp->nargs = DEF_NOARGS; +X break; +X +X case 'E': /* Ignore non-fatal */ +X eflag = TRUE; /* errors. */ +X break; +X +X case 'I': /* Include directory */ +X if (incend >= &incdir[MAXINCLUDE]) +X cfatal("Too many include directories", NULLST); +X *incend++ = ap; +X break; +X +X case 'N': /* No predefineds */ +X nflag++; /* Repeat to undefine */ +X break; /* __LINE__, etc. */ +X +X case 'S': +X sizp = size_table; +X if (isdatum = (*ap != '*')) /* If it's just -S, */ +X endtest = T_FPTR; /* Stop here */ +X else { /* But if it's -S* */ +X ap++; /* Step over '*' */ +X endtest = 0; /* Stop at end marker */ +X } +X while (sizp->bits != endtest && *ap != EOS) { +X if (!isdigit(*ap)) { /* Skip to next digit */ +X ap++; +X continue; +X } +X size = 0; /* Compile the value */ +X while (isdigit(*ap)) { +X size *= 10; +X size += (*ap++ - '0'); +X } +X if (isdatum) +X sizp->size = size; /* Datum size */ +X else +X sizp->psize = size; /* Pointer size */ +X sizp++; +X } +X if (sizp->bits != endtest) +X cwarn("-S, too few values specified in %s", argv[i]); +X else if (*ap != EOS) +X cwarn("-S, too many values, \"%s\" unused", ap); +X break; +X +X case 'U': /* Undefine symbol */ +X#if HOST != SYS_UNIX +X zap_uc(ap); +X#endif +X if (defendel(ap, TRUE) == NULL) +X cwarn("\"%s\" wasn't defined", ap); +X break; +X +X#if DEBUG +X case 'X': /* Debug */ +X debug = (isdigit(*ap)) ? atoi(ap) : 1; +X#if (HOST == SYS_VMS || HOST == SYS_UNIX) +X signal(SIGINT, abort); /* Trap "interrupt" */ +X#endif +X fprintf(stderr, "Debug set to %d\n", debug); +X break; +X#endif +X +X default: /* What is this one? */ +X cwarn("Unknown option \"%s\"", arg); +X fprintf(stderr, "The following options are valid:\n\ +X -C\t\t\tWrite source file comments to output\n\ +X -Dsymbol=value\tDefine a symbol with the given (optional) value\n\ +X -Idirectory\t\tAdd a directory to the #include search list\n\ +X -N\t\t\tDon't predefine target-specific names\n\ +X -Stext\t\tSpecify sizes for #if sizeof\n\ +X -Usymbol\t\tUndefine symbol\n"); +X#if DEBUG +X fprintf(stderr, " -Xvalue\t\tSet internal debug flag\n"); +X#endif +X break; +X } /* Switch on all options */ +X } /* If it's a -option */ +X } /* For all arguments */ +X if (j > 3) { +X cerror( +X "Too many file arguments. Usage: cpp [input [output]]", +X NULLST); +X } +X return (j); /* Return new argc */ +X} +X +X#if HOST != SYS_UNIX +XFILE_LOCAL +Xzap_uc(ap) +Xregister char *ap; +X/* +X * Dec operating systems mangle upper-lower case in command lines. +X * This routine forces the -D and -U arguments to uppercase. +X * It is called only on cpp startup by dooptions(). +X */ +X{ +X while (*ap != EOS) { +X /* +X * Don't use islower() here so it works with Multinational +X */ +X if (*ap >= 'a' && *ap <= 'z') +X *ap = toupper(*ap); +X ap++; +X } +X} +X#endif +X +Xinitdefines() +X/* +X * Initialize the built-in #define's. There are two flavors: +X * #define decus 1 (static definitions) +X * #define __FILE__ ?? (dynamic, evaluated by magic) +X * Called only on cpp startup. +X * +X * Note: the built-in static definitions are supressed by the -N option. +X * __LINE__, __FILE__, and __DATE__ are always present. +X */ +X{ +X register char **pp; +X register char *tp; +X register DEFBUF *dp; +X int i; +X long tvec; +X extern char *ctime(); +X +X /* +X * Predefine the built-in symbols. Allow the +X * implementor to pre-define a symbol as "" to +X * eliminate it. +X */ +X if (nflag == 0) { +X for (pp = preset; *pp != NULL; pp++) { +X if (*pp[0] != EOS) { +X dp = defendel(*pp, FALSE); +X dp->repl = savestring("1"); +X dp->nargs = DEF_NOARGS; +X } +X } +X } +X /* +X * The magic pre-defines (__FILE__ and __LINE__ are +X * initialized with negative argument counts. expand() +X * notices this and calls the appropriate routine. +X * DEF_NOARGS is one greater than the first "magic" definition. +X */ +X if (nflag < 2) { +X for (pp = magic, i = DEF_NOARGS; *pp != NULL; pp++) { +X dp = defendel(*pp, FALSE); +X dp->nargs = --i; +X } +X#if OK_DATE +X /* +X * Define __DATE__ as today's date. +X */ +X dp = defendel("__DATE__", FALSE); +X dp->repl = tp = getmem(27); +X dp->nargs = DEF_NOARGS; +X time(&tvec); +X *tp++ = '"'; +X strcpy(tp, ctime(&tvec)); +X tp[24] = '"'; /* Overwrite newline */ +X#endif +X } +X} +X +X#if HOST == SYS_VMS +X/* +X * getredirection() is intended to aid in porting C programs +X * to VMS (Vax-11 C) which does not support '>' and '<' +X * I/O redirection. With suitable modification, it may +X * useful for other portability problems as well. +X */ +X +Xint +Xgetredirection(argc, argv) +Xint argc; +Xchar **argv; +X/* +X * Process vms redirection arg's. Exit if any error is seen. +X * If getredirection() processes an argument, it is erased +X * from the vector. getredirection() returns a new argc value. +X * +X * Warning: do not try to simplify the code for vms. The code +X * presupposes that getredirection() is called before any data is +X * read from stdin or written to stdout. +X * +X * Normal usage is as follows: +X * +X * main(argc, argv) +X * int argc; +X * char *argv[]; +X * { +X * argc = getredirection(argc, argv); +X * } +X */ +X{ +X register char *ap; /* Argument pointer */ +X int i; /* argv[] index */ +X int j; /* Output index */ +X int file; /* File_descriptor */ +X extern int errno; /* Last vms i/o error */ +X +X for (j = i = 1; i < argc; i++) { /* Do all arguments */ +X switch (*(ap = argv[i])) { +X case '<': /* ': /* >file or >>file */ +X if (*++ap == '>') { /* >>file */ +X /* +X * If the file exists, and is writable by us, +X * call freopen to append to the file (using the +X * file's current attributes). Otherwise, create +X * a new file with "vanilla" attributes as if the +X * argument was given as ">filename". +X * access(name, 2) returns zero if we can write on +X * the specified file. +X */ +X if (access(++ap, 2) == 0) { +X if (freopen(ap, "a", stdout) != NULL) +X break; /* Exit case statement */ +X perror(ap); /* Error, can't append */ +X exit(errno); /* After access test */ +X } /* If file accessable */ +X } +X /* +X * On vms, we want to create the file using "standard" +X * record attributes. creat(...) creates the file +X * using the caller's default protection mask and +X * "variable length, implied carriage return" +X * attributes. dup2() associates the file with stdout. +X */ +X if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1 +X || dup2(file, fileno(stdout)) == -1) { +X perror(ap); /* Can't create file */ +X exit(errno); /* is a fatal error */ +X } /* If '>' creation */ +X break; /* Exit case test */ +X +X default: +X argv[j++] = ap; /* Not a redirector */ +X break; /* Exit case test */ +X } +X } /* For all arguments */ +X argv[j] = NULL; /* Terminate argv[] */ +X return (j); /* Return new argc */ +X} +X#endif +X +X +X +END-of-cpp3.c +echo x - cpp4.c +sed 's/^X//' >cpp4.c << 'END-of-cpp4.c' +X/* +X * C P P 4 . C +X * M a c r o D e f i n i t i o n s +X * +X * Edit History +X * 31-Aug-84 MM USENET net.sources release +X * 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring() +X * so they work correctly with token concatenation. +X * Added string formal recognition. +X * 25-Oct-84 MM "Short-circuit" evaluate #if's so that we +X * don't print unnecessary error messages for +X * #if !defined(FOO) && FOO != 0 && 10 / FOO ... +X * 31-Oct-84 ado/MM Added token concatenation +X * 6-Nov-84 MM Split off eval stuff +X */ +X +X#include +X#include +X#include "cppdef.h" +X#include "cpp.h" +X/* +X * parm[], parmp, and parlist[] are used to store #define() argument +X * lists. nargs contains the actual number of parameters stored. +X */ +Xstatic char parm[NPARMWORK + 1]; /* define param work buffer */ +Xstatic char *parmp; /* Free space in parm */ +Xstatic char *parlist[LASTPARM]; /* -> start of each parameter */ +Xstatic int nargs; /* Parameters for this macro */ +X +Xdodefine() +X/* +X * Called from control when a #define is scanned. This module +X * parses formal parameters and the replacement string. When +X * the formal parameter name is encountered in the replacement +X * string, it is replaced by a character in the range 128 to +X * 128+NPARAM (this allows up to 32 parameters within the +X * Dec Multinational range). If cpp is ported to an EBCDIC +X * machine, you will have to make other arrangements. +X * +X * There is some special case code to distinguish +X * #define foo bar +X * from #define foo() bar +X * +X * Also, we make sure that +X * #define foo foo +X * expands to "foo" but doesn't put cpp into an infinite loop. +X * +X * A warning message is printed if you redefine a symbol to a +X * different text. I.e, +X * #define foo 123 +X * #define foo 123 +X * is ok, but +X * #define foo 123 +X * #define foo +123 +X * is not. +X * +X * The following subroutines are called from define(): +X * checkparm called when a token is scanned. It checks through the +X * array of formal parameters. If a match is found, the +X * token is replaced by a control byte which will be used +X * to locate the parameter when the macro is expanded. +X * textput puts a string in the macro work area (parm[]), updating +X * parmp to point to the first free byte in parm[]. +X * textput() tests for work buffer overflow. +X * charput puts a single character in the macro work area (parm[]) +X * in a manner analogous to textput(). +X */ +X{ +X register int c; +X register DEFBUF *dp; /* -> new definition */ +X int isredefine; /* TRUE if redefined */ +X char *old; /* Remember redefined */ +X extern int save(); /* Save char in work[] */ +X +X if (type[(c = skipws())] != LET) +X goto bad_define; +X isredefine = FALSE; /* Set if redefining */ +X if ((dp = lookid(c)) == NULL) /* If not known now */ +X dp = defendel(token, FALSE); /* Save the name */ +X else { /* It's known: */ +X isredefine = TRUE; /* Remember this fact */ +X old = dp->repl; /* Remember replacement */ +X dp->repl = NULL; /* No replacement now */ +X } +X parlist[0] = parmp = parm; /* Setup parm buffer */ +X if ((c = get()) == '(') { /* With arguments? */ +X nargs = 0; /* Init formals counter */ +X do { /* Collect formal parms */ +X if (nargs >= LASTPARM) +X cfatal("Too many arguments for macro", NULLST); +X else if ((c = skipws()) == ')') +X break; /* Got them all */ +X else if (type[c] != LET) /* Bad formal syntax */ +X goto bad_define; +X scanid(c); /* Get the formal param */ +X parlist[nargs++] = parmp; /* Save its start */ +X textput(token); /* Save text in parm[] */ +X } while ((c = skipws()) == ','); /* Get another argument */ +X if (c != ')') /* Must end at ) */ +X goto bad_define; +X c = ' '; /* Will skip to body */ +X } +X else { +X /* +X * DEF_NOARGS is needed to distinguish between +X * "#define foo" and "#define foo()". +X */ +X nargs = DEF_NOARGS; /* No () parameters */ +X } +X if (type[c] == SPA) /* At whitespace? */ +X c = skipws(); /* Not any more. */ +X workp = work; /* Replacement put here */ +X inmacro = TRUE; /* Keep \ now */ +X while (c != EOF_CHAR && c != '\n') { /* Compile macro body */ +X#if OK_CONCAT +X if (c == '#') { /* Token concatenation? */ +X while (workp > work && type[workp[-1]] == SPA) +X --workp; /* Erase leading spaces */ +X save(TOK_SEP); /* Stuff a delimiter */ +X c = skipws(); /* Eat whitespace */ +X if (type[c] == LET) /* Another token here? */ +X ; /* Stuff it normally */ +X else if (type[c] == DIG) { /* Digit string after? */ +X while (type[c] == DIG) { /* Stuff the digits */ +X save(c); +X c = get(); +X } +X save(TOK_SEP); /* Delimit 2nd token */ +X } +X else { +X ciwarn("Strange character after # (%d.)", c); +X } +X continue; +X } +X#endif +X switch (type[c]) { +X case LET: +X checkparm(c, dp); /* Might be a formal */ +X break; +X +X case DIG: /* Number in mac. body */ +X case DOT: /* Maybe a float number */ +X scannumber(c, save); /* Scan it off */ +X break; +X +X case QUO: /* String in mac. body */ +X#if STRING_FORMAL +X stparmscan(c, dp); /* Do string magic */ +X#else +X stparmscan(c); +X#endif +X break; +X +X case BSH: /* Backslash */ +X save('\\'); +X if ((c = get()) == '\n') +X wrongline = TRUE; +X save(c); +X break; +X +X case SPA: /* Absorb whitespace */ +X /* +X * Note: the "end of comment" marker is passed on +X * to allow comments to separate tokens. +X */ +X if (workp[-1] == ' ') /* Absorb multiple */ +X break; /* spaces */ +X else if (c == '\t') +X c = ' '; /* Normalize tabs */ +X /* Fall through to store character */ +X default: /* Other character */ +X save(c); +X break; +X } +X c = get(); +X } +X inmacro = FALSE; /* Stop newline hack */ +X unget(); /* For control check */ +X if (workp > work && workp[-1] == ' ') /* Drop trailing blank */ +X workp--; +X *workp = EOS; /* Terminate work */ +X dp->repl = savestring(work); /* Save the string */ +X dp->nargs = nargs; /* Save arg count */ +X#if DEBUG +X if (debug) +X dumpadef("macro definition", dp); +X#endif +X if (isredefine) { /* Error if redefined */ +X if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl)) +X || (old == NULL && dp->repl != NULL) +X || (old != NULL && dp->repl == NULL)) { +X cerror("Redefining defined variable \"%s\"", dp->name); +X } +X if (old != NULL) /* We don't need the */ +X free(old); /* old definition now. */ +X } +X return; +X +Xbad_define: +X cerror("#define syntax error", NULLST); +X inmacro = FALSE; /* Stop hack */ +X} +X +Xcheckparm(c, dp) +Xregister int c; +XDEFBUF *dp; +X/* +X * Replace this param if it's defined. Note that the macro name is a +X * possible replacement token. We stuff DEF_MAGIC in front of the token +X * which is treated as a LETTER by the token scanner and eaten by +X * the output routine. This prevents the macro expander from +X * looping if someone writes "#define foo foo". +X */ +X{ +X register int i; +X register char *cp; +X +X scanid(c); /* Get parm to token[] */ +X for (i = 0; i < nargs; i++) { /* For each argument */ +X if (streq(parlist[i], token)) { /* If it's known */ +X save(i + MAC_PARM); /* Save a magic cookie */ +X return; /* And exit the search */ +X } +X } +X if (streq(dp->name, token)) /* Macro name in body? */ +X save(DEF_MAGIC); /* Save magic marker */ +X for (cp = token; *cp != EOS;) /* And save */ +X save(*cp++); /* The token itself */ +X} +X +X#if STRING_FORMAL +Xstparmscan(delim, dp) +Xint delim; +Xregister DEFBUF *dp; +X/* +X * Scan the string (starting with the given delimiter). +X * The token is replaced if it is the only text in this string or +X * character constant. The algorithm follows checkparm() above. +X * Note that scanstring() has approved of the string. +X */ +X{ +X register int c; +X +X /* +X * Warning -- this code hasn't been tested for a while. +X * It exists only to preserve compatibility with earlier +X * implementations of cpp. It is not part of the Draft +X * ANSI Standard C language. +X */ +X save(delim); +X instring = TRUE; +X while ((c = get()) != delim +X && c != '\n' +X && c != EOF_CHAR) { +X if (type[c] == LET) /* Maybe formal parm */ +X checkparm(c, dp); +X else { +X save(c); +X if (c == '\\') +X save(get()); +X } +X } +X instring = FALSE; +X if (c != delim) +X cerror("Unterminated string in macro body", NULLST); +X save(c); +X} +X#else +Xstparmscan(delim) +Xint delim; +X/* +X * Normal string parameter scan. +X */ +X{ +X register char *wp; +X register int i; +X extern int save(); +X +X wp = workp; /* Here's where it starts */ +X if (!scanstring(delim, save)) +X return; /* Exit on scanstring error */ +X workp[-1] = EOS; /* Erase trailing quote */ +X wp++; /* -> first string content byte */ +X for (i = 0; i < nargs; i++) { +X if (streq(parlist[i], wp)) { +X *wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */ +X *wp++ = (i + MAC_PARM); /* Make a formal marker */ +X *wp = wp[-3]; /* Add on closing quote */ +X workp = wp + 1; /* Reset string end */ +X return; +X } +X } +X workp[-1] = wp[-1]; /* Nope, reset end quote. */ +X} +X#endif +X +Xdoundef() +X/* +X * Remove the symbol from the defined list. +X * Called from the #control processor. +X */ +X{ +X register int c; +X +X if (type[(c = skipws())] != LET) +X cerror("Illegal #undef argument", NULLST); +X else { +X scanid(c); /* Get name to token[] */ +X if (defendel(token, TRUE) == NULL) { +X cwarn("Symbol \"%s\" not defined in #undef", token); +X } +X } +X} +X +Xtextput(text) +Xchar *text; +X/* +X * Put the string in the parm[] buffer. +X */ +X{ +X register int size; +X +X size = strlen(text) + 1; +X if ((parmp + size) >= &parm[NPARMWORK]) +X cfatal("Macro work area overflow", NULLST); +X else { +X strcpy(parmp, text); +X parmp += size; +X } +X} +X +Xcharput(c) +Xregister int c; +X/* +X * Put the byte in the parm[] buffer. +X */ +X{ +X if (parmp >= &parm[NPARMWORK]) +X cfatal("Macro work area overflow", NULLST); +X else { +X *parmp++ = c; +X } +X} +X +X/* +X * M a c r o E x p a n s i o n +X */ +X +Xstatic DEFBUF *macro; /* Catches start of infinite macro */ +X +Xexpand(tokenp) +Xregister DEFBUF *tokenp; +X/* +X * Expand a macro. Called from the cpp mainline routine (via subroutine +X * macroid()) when a token is found in the symbol table. It calls +X * expcollect() to parse actual parameters, checking for the correct number. +X * It then creates a "file" containing a single line containing the +X * macro with actual parameters inserted appropriately. This is +X * "pushed back" onto the input stream. (When the get() routine runs +X * off the end of the macro line, it will dismiss the macro itself.) +X */ +X{ +X register int c; +X register FILEINFO *file; +X extern FILEINFO *getfile(); +X +X#if DEBUG +X if (debug) +X dumpadef("expand entry", tokenp); +X#endif +X /* +X * If no macro is pending, save the name of this macro +X * for an eventual error message. +X */ +X if (recursion++ == 0) +X macro = tokenp; +X else if (recursion == RECURSION_LIMIT) { +X cerror("Recursive macro definition of \"%s\"", tokenp->name); +X fprintf(stderr, "(Defined by \"%s\")\n", macro->name); +X if (rec_recover) { +X do { +X c = get(); +X } while (infile != NULL && infile->fp == NULL); +X unget(); +X recursion = 0; +X return; +X } +X } +X /* +X * Here's a macro to expand. +X */ +X nargs = 0; /* Formals counter */ +X parmp = parm; /* Setup parm buffer */ +X switch (tokenp->nargs) { +X case (-2): /* __LINE__ */ +X sprintf(work, "%d", line); +X ungetstring(work); +X break; +X +X case (-3): /* __FILE__ */ +X for (file = infile; file != NULL; file = file->parent) { +X if (file->fp != NULL) { +X sprintf(work, "\"%s\"", (file->progname != NULL) +X ? file->progname : file->filename); +X ungetstring(work); +X break; +X } +X } +X break; +X +X default: +X /* +X * Nothing funny about this macro. +X */ +X if (tokenp->nargs < 0) +X cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name); +X while ((c = skipws()) == '\n') /* Look for (, skipping */ +X wrongline = TRUE; /* spaces and newlines */ +X if (c != '(') { +X /* +X * If the programmer writes +X * #define foo() ... +X * ... +X * foo [no ()] +X * just write foo to the output stream. +X */ +X unget(); +X cwarn("Macro \"%s\" needs arguments", tokenp->name); +X fputs(tokenp->name, stdout); +X return; +X } +X else if (expcollect()) { /* Collect arguments */ +X if (tokenp->nargs != nargs) { /* Should be an error? */ +X cwarn("Wrong number of macro arguments for \"%s\"", +X tokenp->name); +X } +X#if DEBUG +X if (debug) +X dumpparm("expand"); +X#endif +X } /* Collect arguments */ +X case DEF_NOARGS: /* No parameters just stuffs */ +X expstuff(tokenp); /* Do actual parameters */ +X } /* nargs switch */ +X} +X +XFILE_LOCAL int +Xexpcollect() +X/* +X * Collect the actual parameters for this macro. TRUE if ok. +X */ +X{ +X register int c; +X register int paren; /* For embedded ()'s */ +X extern int charput(); +X +X for (;;) { +X paren = 0; /* Collect next arg. */ +X while ((c = skipws()) == '\n') /* Skip over whitespace */ +X wrongline = TRUE; /* and newlines. */ +X if (c == ')') { /* At end of all args? */ +X /* +X * Note that there is a guard byte in parm[] +X * so we don't have to check for overflow here. +X */ +X *parmp = EOS; /* Make sure terminated */ +X break; /* Exit collection loop */ +X } +X else if (nargs >= LASTPARM) +X cfatal("Too many arguments in macro expansion", NULLST); +X parlist[nargs++] = parmp; /* At start of new arg */ +X for (;; c = cget()) { /* Collect arg's bytes */ +X if (c == EOF_CHAR) { +X cerror("end of file within macro argument", NULLST); +X return (FALSE); /* Sorry. */ +X } +X else if (c == '\\') { /* Quote next character */ +X charput(c); /* Save the \ for later */ +X charput(cget()); /* Save the next char. */ +X continue; /* And go get another */ +X } +X else if (type[c] == QUO) { /* Start of string? */ +X scanstring(c, charput); /* Scan it off */ +X continue; /* Go get next char */ +X } +X else if (c == '(') /* Worry about balance */ +X paren++; /* To know about commas */ +X else if (c == ')') { /* Other side too */ +X if (paren == 0) { /* At the end? */ +X unget(); /* Look at it later */ +X break; /* Exit arg getter. */ +X } +X paren--; /* More to come. */ +X } +X else if (c == ',' && paren == 0) /* Comma delimits args */ +X break; +X else if (c == '\n') /* Newline inside arg? */ +X wrongline = TRUE; /* We'll need a #line */ +X charput(c); /* Store this one */ +X } /* Collect an argument */ +X charput(EOS); /* Terminate argument */ +X#if DEBUG +X if (debug) +X printf("parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]); +X#endif +X } /* Collect all args. */ +X return (TRUE); /* Normal return */ +X} +X +XFILE_LOCAL +Xexpstuff(tokenp) +XDEFBUF *tokenp; /* Current macro being expanded */ +X/* +X * Stuff the macro body, replacing formal parameters by actual parameters. +X */ +X{ +X register int c; /* Current character */ +X register char *inp; /* -> repl string */ +X register char *defp; /* -> macro output buff */ +X int size; /* Actual parm. size */ +X char *defend; /* -> output buff end */ +X int string_magic; /* String formal hack */ +X FILEINFO *file; /* Funny #include */ +X extern FILEINFO *getfile(); +X +X file = getfile(NBUFF, tokenp->name); +X inp = tokenp->repl; /* -> macro replacement */ +X defp = file->buffer; /* -> output buffer */ +X defend = defp + (NBUFF - 1); /* Note its end */ +X if (inp != NULL) { +X while ((c = (*inp++ & 0xFF)) != EOS) { +X if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) { +X string_magic = (c == (MAC_PARM + PAR_MAC)); +X if (string_magic) +X c = (*inp++ & 0xFF); +X /* +X * Replace formal parameter by actual parameter string. +X */ +X if ((c -= MAC_PARM) < nargs) { +X size = strlen(parlist[c]); +X if ((defp + size) >= defend) +X goto nospace; +X /* +X * Erase the extra set of quotes. +X */ +X if (string_magic && defp[-1] == parlist[c][0]) { +X strcpy(defp-1, parlist[c]); +X defp += (size - 2); +X } +X else { +X strcpy(defp, parlist[c]); +X defp += size; +X } +X } +X } +X else if (defp >= defend) { +Xnospace: cfatal("Out of space in macro \"%s\" arg expansion", +X tokenp->name); +X } +X else { +X *defp++ = c; +X } +X } +X } +X *defp = EOS; +X#if DEBUG +X if (debug > 1) +X printf("macroline: \"%s\"\n", file->buffer); +X#endif +X} +X +X#if DEBUG +Xdumpparm(why) +Xchar *why; +X/* +X * Dump parameter list. +X */ +X{ +X register int i; +X +X printf("dump of %d parameters (%d bytes total) %s\n", +X nargs, parmp - parm, why); +X for (i = 0; i < nargs; i++) { +X printf("parm[%d] (%d) = \"%s\"\n", +X i + 1, strlen(parlist[i]), parlist[i]); +X } +X} +X#endif +END-of-cpp4.c +exit