# 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
