]> granicus.if.org Git - nethack/commitdiff
*** empty log message ***
authorjwalz <jwalz>
Sat, 5 Jan 2002 21:05:59 +0000 (21:05 +0000)
committerjwalz <jwalz>
Sat, 5 Jan 2002 21:05:59 +0000 (21:05 +0000)
sys/unix/cpp2.shr [new file with mode: 0644]

diff --git a/sys/unix/cpp2.shr b/sys/unix/cpp2.shr
new file mode 100644 (file)
index 0000000..ccadfc0
--- /dev/null
@@ -0,0 +1,1763 @@
+# This is a shell archive.  Save it in a file, remove anything before
+# this line, and then unpack it by entering "sh file".  Note, it may
+# create directories; files and directories will be owned by you and
+# have default permissions.
+#
+# This archive contains:
+#
+#      cpp1.c
+#      cpp3.c
+#      cpp4.c
+#
+echo x - cpp1.c
+sed 's/^X//' >cpp1.c << 'END-of-cpp1.c'
+X/*
+X * CPP main program.
+X *
+X * Edit history
+X * 21-May-84  MM      "Field test" release
+X * 23-May-84  MM      Some minor hacks.
+X * 30-May-84  ARF     Didn't get enough memory for __DATE__
+X *                    Added code to read stdin if no input
+X *                    files are provided.
+X * 29-Jun-84  MM      Added ARF's suggestions, Unixifying cpp.
+X * 11-Jul-84  MM      "Official" first release (that's what I thought!)
+X * 22-Jul-84  MM/ARF/SCK Fixed line number bugs, added cpp recognition
+X *                    of #line, fixed problems with #include.
+X * 23-Jul-84  MM      More (minor) include hacking, some documentation.
+X *                    Also, redid cpp's #include files
+X * 25-Jul-84  MM      #line filename isn't used for #include searchlist
+X *                    #line format is <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\f
+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\f
+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\f
+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\f
+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\f
+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\f
+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\f
+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\f
+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\f
+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\f
+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\f
+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\f
+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\f
+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\f
+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\f
+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