# 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