1764 lines
52 KiB
Plaintext
1764 lines
52 KiB
Plaintext
# 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 <number> <optional name>
|
||
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 <foo> 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 <stdio.h>
|
||
X#include <ctype.h>
|
||
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 <DEF_MAGIC>). Note especially that
|
||
X * comments and \<newline> 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 \<newline>
|
||
X * to the caller. Strictly speaking, this is a bug as \<newline>
|
||
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 <file */
|
||
X#endif
|
||
X initdefines(); /* O.S. specific def's */
|
||
X i = dooptions(argc, argv); /* Command line -flags */
|
||
X switch (i) {
|
||
X case 3:
|
||
X /*
|
||
X * Get output file, "-" means use stdout.
|
||
X */
|
||
X if (!streq(argv[2], "-")) {
|
||
X#if HOST == SYS_VMS
|
||
X /*
|
||
X * On vms, reopen stdout with "vanilla rms" attributes.
|
||
X */
|
||
X if ((i = creat(argv[2], 0, "rat=cr", "rfm=var")) == -1
|
||
X || dup2(i, fileno(stdout)) == -1) {
|
||
X#else
|
||
X if (freopen(argv[2], "w", stdout) == NULL) {
|
||
X#endif
|
||
X perror(argv[2]);
|
||
X cerror("Can't open output file \"%s\"", argv[2]);
|
||
X exit(IO_ERROR);
|
||
X }
|
||
X } /* Continue by opening input */
|
||
X case 2: /* One file -> 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 <stdio.h>
|
||
X#include <ctype.h>
|
||
X#include "cppdef.h"
|
||
X#include "cpp.h"
|
||
X#if DEBUG && (HOST == SYS_VMS || HOST == SYS_UNIX)
|
||
X#include <signal.h>
|
||
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 */
|
||
X if (freopen(++ap, "r", stdin) == NULL) {
|
||
X perror(ap); /* Can't find file */
|
||
X exit(errno); /* Is a fatal error */
|
||
X }
|
||
X break;
|
||
X
|
||
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 <stdio.h>
|
||
X#include <ctype.h>
|
||
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 \<newline> 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 <newline> 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
|