1784 lines
53 KiB
Plaintext
1784 lines
53 KiB
Plaintext
# This is a shell archive. Save it in a file, remove anything before
|
||
# this line, and then unpack it by entering "sh file". Note, it may
|
||
# create directories; files and directories will be owned by you and
|
||
# have default permissions.
|
||
#
|
||
# This archive contains:
|
||
#
|
||
# makefile.txt
|
||
# readme.txt
|
||
# cpp.mem
|
||
# cpp.h
|
||
# cppdef.h
|
||
# cpp2.c
|
||
#
|
||
echo x - makefile.txt
|
||
sed 's/^X//' >makefile.txt << 'END-of-makefile.txt'
|
||
X#
|
||
X# The redefinition of strchr() and strrchr() are needed for
|
||
X# Ultrix-32, Unix 4.2 bsd (and maybe some other Unices).
|
||
X#
|
||
XBSDDEFINE = -Dstrchr=index -Dstrrchr=rindex
|
||
X#
|
||
X# On certain systems, such as Unix System III, you may need to define
|
||
X# $(LINTFLAGS) in the make command line to set system-specific lint flags.
|
||
X#
|
||
X# This Makefile assumes cpp will replace the "standard" preprocessor.
|
||
X# Delete the reference to -DLINE_PREFIX=\"\" if cpp is used stand-alone.
|
||
X# LINEFIX is a sed script filter that reinserts #line -- used for testing
|
||
X# if LINE_PREFIX is set to "". Note that we must stand on our heads to
|
||
X# match the # and a line had better not begin with $. By the way, what
|
||
X# we really want is
|
||
X# LINEFIX = | sed "s/^#/#line/"
|
||
X#
|
||
XCPPDEFINE = -DLINE_PREFIX=\"\"
|
||
XLINEFIX = | sed "s/^[^ !\"%-~]/&line/"
|
||
X#
|
||
X# Define OLD_PREPROCESSOR non-zero to make a preprocessor which is
|
||
X# "as compatible as possible" with the standard Unix V7 or Ultrix
|
||
X# preprocessors. This is needed to rebuild 4.2bsd, for example, as
|
||
X# the preprocessor is used to modify assembler code, rather than C.
|
||
X# This is not recommended for current development. OLD_PREPROCESSOR
|
||
X# forces the following definitions:
|
||
X# OK_DOLLAR FALSE $ is not allowed in variables
|
||
X# OK_CONCAT FALSE # cannot concatenate tokens
|
||
X# COMMENT_INVISIBLE TRUE old-style comment concatenation
|
||
X# STRING_FORMAL TRUE old-style string expansion
|
||
X#
|
||
XOLDDEFINE = -DOLD_PREPROCESSOR=1
|
||
X#
|
||
X# DEFINES collects all -D arguments for cc and lint:
|
||
X# Change DEFINES = $(BSDDEFINE) $(CPPDEFINE) $(OLDDEFINE)
|
||
X# for an old-style preprocessor.
|
||
X#
|
||
X# DEFINES = $(BSDDEFINE) $(CPPDEFINE)
|
||
XDEFINES = $(CPPDEFINE)
|
||
X
|
||
XCFLAGS = -O $(DEFINES)
|
||
X
|
||
X#
|
||
X# ** compile cpp
|
||
X#
|
||
XSRCS = cpp1.c cpp2.c cpp3.c cpp4.c cpp5.c cpp6.c
|
||
XOBJS = cpp1.o cpp2.o cpp3.o cpp4.o cpp5.o cpp6.o
|
||
Xcpp: $(OBJS)
|
||
X $(CC) $(CFLAGS) $(OBJS) -o cpp
|
||
X
|
||
X#
|
||
X# ** Test cpp by preprocessing itself, compiling the result,
|
||
X# ** repeating the process and diff'ing the result. Note: this
|
||
X# ** is not a good test of cpp, but a simple verification.
|
||
X# ** The diff's should not report any changes.
|
||
X# ** Note that a sed script may be executed for each compile
|
||
X#
|
||
Xtest:
|
||
X cpp cpp1.c $(LINEFIX) >old.tmp1.c
|
||
X cpp cpp2.c $(LINEFIX) >old.tmp2.c
|
||
X cpp cpp3.c $(LINEFIX) >old.tmp3.c
|
||
X cpp cpp4.c $(LINEFIX) >old.tmp4.c
|
||
X cpp cpp5.c $(LINEFIX) >old.tmp5.c
|
||
X cpp cpp6.c $(LINEFIX) >old.tmp6.c
|
||
X $(CC) $(CFLAGS) old.tmp[123456].c
|
||
X a.out cpp1.c >new.tmp1.c
|
||
X a.out cpp2.c >new.tmp2.c
|
||
X a.out cpp3.c >new.tmp3.c
|
||
X a.out cpp4.c >new.tmp4.c
|
||
X a.out cpp5.c >new.tmp5.c
|
||
X a.out cpp6.c >new.tmp6.c
|
||
X diff old.tmp1.c new.tmp1.c
|
||
X diff old.tmp2.c new.tmp2.c
|
||
X diff old.tmp3.c new.tmp3.c
|
||
X diff old.tmp4.c new.tmp4.c
|
||
X diff old.tmp5.c new.tmp5.c
|
||
X diff old.tmp6.c new.tmp6.c
|
||
X rm a.out old.tmp[123456].* new.tmp[123456].*
|
||
X
|
||
X#
|
||
X# A somewhat more extensive test is provided by the "clock"
|
||
X# program (which is not distributed). Substitute your favorite
|
||
X# macro-rich program here.
|
||
X#
|
||
Xclock: clock.c cpp
|
||
X cpp clock.c $(LINEFIX) >temp.cpp.c
|
||
X cc temp.cpp.c -lcurses -ltermcap -o clock
|
||
X rm temp.cpp.c
|
||
X
|
||
X#
|
||
X# ** Lint the code
|
||
X#
|
||
X
|
||
Xlint: $(SRCS)
|
||
X lint $(LINTFLAGS) $(DEFINES) $(SRCS)
|
||
X
|
||
X#
|
||
X# ** Remove unneeded files
|
||
X#
|
||
Xclean:
|
||
X rm -f $(OBJS) cpp
|
||
X
|
||
X#
|
||
X# ** Rebuild the archive files needed to distribute cpp
|
||
X# ** Uses the Decus C archive utility.
|
||
X#
|
||
X
|
||
Xarchc: archc.c
|
||
X $(CC) $(CFLAGS) archc.c -o archc
|
||
X
|
||
Xarchx: archx.c
|
||
X $(CC) $(CFLAGS) archx.c -o archx
|
||
X
|
||
Xarchive: archc
|
||
X archc readme.txt cpp.mem archx.c archc.c cpp.rno makefile.txt \
|
||
X cpp*.h >cpp1.arc
|
||
X archc cpp1.c cpp2.c cpp3.c >cpp2.arc
|
||
X archc cpp4.c cpp5.c cpp6.c >cpp3.arc
|
||
X
|
||
X#
|
||
X# Object module dependencies
|
||
X#
|
||
X
|
||
Xcpp1.o : cpp1.c cpp.h cppdef.h
|
||
X
|
||
Xcpp2.o : cpp2.c cpp.h cppdef.h
|
||
X
|
||
Xcpp3.o : cpp3.c cpp.h cppdef.h
|
||
X
|
||
Xcpp4.o : cpp4.c cpp.h cppdef.h
|
||
X
|
||
Xcpp5.o : cpp5.c cpp.h cppdef.h
|
||
X
|
||
Xcpp6.o : cpp6.c cpp.h cppdef.h
|
||
X
|
||
X
|
||
END-of-makefile.txt
|
||
echo x - readme.txt
|
||
sed 's/^X//' >readme.txt << 'END-of-readme.txt'
|
||
X
|
||
XDecus cpp is a public-domain implementation of the C preprocessor.
|
||
XIt runs on VMS native (Vax C), VMS compatibilty mode (Decus C),
|
||
XRSX-11M, RSTS/E, P/OS, and RT11, as well as on several varieties
|
||
Xof Unix, including Ultrix. Decus cpp attempts to implement features
|
||
Xin the Draft ANSI Standard for the C language. It should be noted,
|
||
Xhowever, that this standard is under active development: the current
|
||
Xdraft of the standard explicitly states that "readers are requested
|
||
Xnot to specify or claim conformance to this draft." Thus readers
|
||
Xand users of Decus cpp should not assume that it conforms to the
|
||
Xdraft standard, or that it will conform to the actual C language
|
||
Xstandard.
|
||
X
|
||
XThese notes describe how to extract the cpp source files, configure it
|
||
Xfor your needs, and mention a few design decisions that may be of interest
|
||
Xto maintainers.
|
||
X
|
||
X Installation
|
||
X
|
||
XBecause the primary development of cpp was not on Unix, it
|
||
Xis distributed using the Decus C archive program (quite similar
|
||
Xto the archiver published in Kernighan and Plauger's Software
|
||
XTools). To extract the files from the net.sources distribution,
|
||
Xsave this message as cpp1.arc and the other two distribution
|
||
Xfiles as cpp2.arc and cpp3.arc. Then, using your favorite editor,
|
||
Xlocate the archx.c program, just following the line beginning with
|
||
X"-h- archx.c" -- the format of the distribution is just:
|
||
X
|
||
X -h- readme.txt
|
||
X ... this file
|
||
X -h- cpp.mem
|
||
X ... description of cpp
|
||
X -h- archx.c
|
||
X ... archx.c program -- extracts archives
|
||
X -h- archc.c
|
||
X ... archc.c program -- creates archives
|
||
X
|
||
XCompile archx.c -- it shouldn't require any special editing.
|
||
XThen run it as follows:
|
||
X
|
||
X archx *.arc
|
||
X
|
||
XYou do not need to remove mail headers from the saved messages.
|
||
X
|
||
XYou should then read through cppdef.h to make sure the HOST and
|
||
XTARGET (and other implementation-specific) definitions are set
|
||
Xcorrectly for your machine, editing them as needed.
|
||
X
|
||
XYou may then copy makefile.txt to Makefile, editing it as needed
|
||
Xfor your particular system. On Unix, cpp should be compiled
|
||
Xby make without further difficulty. On other operating systems,
|
||
Xyou should compile the six source modules, linking them together.
|
||
XNote that, on Decus C based systems, you must extend the default
|
||
Xstack allocation. The Decus C build utility will create the
|
||
Xappropriate command file.
|
||
X
|
||
X Support Notes
|
||
X
|
||
XThe USENET distribution kit was designed to keep all submissions around
|
||
X50,000 bytes:
|
||
X
|
||
Xcpp1.arc:
|
||
X readme.txt This file
|
||
X cpp.mem Documentation page (see below)
|
||
X archx.c Archive extraction program
|
||
X archc.c Archive construction program
|
||
X cpp.rno Source for cpp.mem (see below)
|
||
X makefile.txt Unix makefile -- copy to Makefile
|
||
X cpp.h Main header file (structure def's and globals)
|
||
X cppdef.h Configuration file (host and target definitions)
|
||
X
|
||
Xcpp2.arc:
|
||
X cpp1.c Mainline code, documentation master sources
|
||
X cpp2.c most #control processing
|
||
X cpp3.c filename stuff and command line parsing
|
||
Xcpp3.arc:
|
||
X cpp4.c #define processor
|
||
X cpp5.c #if <expr> processor
|
||
X cpp6.c Support code (symbol table and I/O routines)
|
||
X
|
||
XCpp intentionally does not rely on the presence of a full-scale
|
||
Xmacro preprocessor, it does require the simple parameter substitution
|
||
Xpreprocessor capabilities of Unix V6 and Decus C. If your C
|
||
Xlanguage lacks full preprocessing, you should make sure "nomacargs"
|
||
Xis #define'd in cpp.h. (This is done automatically by the Decus C
|
||
Xcompiler.)
|
||
X
|
||
XThe documentation (manual page) for cpp is included as cpp.mem
|
||
Xand cpp.rno. Cpp.rno is in Dec Runoff format, built by a Decus C
|
||
Xutility (getrno) from original source which is embedded in cpp1.c.
|
||
XTo my knowledge, there is no equivalent program that creates
|
||
Xthe nroff source appropriate for Unix.
|
||
X
|
||
XI would be happy to receive fixes to any problems you encounter.
|
||
XAs I do not maintain distribution kit base-levels, bare-bones
|
||
Xdiff listings without sufficient context are not very useful.
|
||
XIt is unlikely that I can find time to help you with other
|
||
Xdifficulties.
|
||
X
|
||
X Acknowledgements
|
||
X
|
||
XI received a great deal of help from many people in debugging cpp.
|
||
XAlan Feuer and Sam Kendall used "state of the art" run-time code
|
||
Xcheckers to locate several errors. Ed Keiser found problems when
|
||
Xcpp was used on machines with different int and pointer sizes.
|
||
XDave Conroy helped with the initial debugging, while Arthur Olsen
|
||
Xand George Rosenberg found (and solved) several problems in the
|
||
Xfirst USENET release.
|
||
X
|
||
XMartin Minow
|
||
Xdecvax!minow
|
||
X
|
||
END-of-readme.txt
|
||
echo x - cpp.mem
|
||
sed 's/^X//' >cpp.mem << 'END-of-cpp.mem'
|
||
X
|
||
X
|
||
X
|
||
X
|
||
X 1.0 C Pre-Processor
|
||
X
|
||
X
|
||
X
|
||
X *******
|
||
X * cpp *
|
||
X *******
|
||
X
|
||
X
|
||
X
|
||
X NAME: cpp -- C Pre-Processor
|
||
X
|
||
X SYNOPSIS:
|
||
X
|
||
X cpp [-options] [infile [outfile]]
|
||
X
|
||
X DESCRIPTION:
|
||
X
|
||
X CPP reads a C source file, expands macros and include
|
||
X files, and writes an input file for the C compiler. If
|
||
X no file arguments are given, CPP reads from stdin and
|
||
X writes to stdout. If one file argument is given, it
|
||
X 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 be
|
||
X given in either case.
|
||
X
|
||
X -C If set, source-file comments are written
|
||
X to the output file. This allows the
|
||
X output of CPP to be used as the input to
|
||
X a program, such as lint, that expects
|
||
X commands embedded in specially-formatted
|
||
X comments.
|
||
X
|
||
X -Dname=value Define the name as if the programmer
|
||
X wrote
|
||
X
|
||
X #define name value
|
||
X
|
||
X at the start of the first file. If
|
||
X "=value" is not given, a value of "1"
|
||
X will be used.
|
||
X
|
||
X On non-unix systems, all alphabetic text
|
||
X will be forced to upper-case.
|
||
X
|
||
X -E Always return "success" to the operating
|
||
X system, even if errors were detected.
|
||
X Note that some fatal errors, such as a
|
||
X missing #include file, will terminate
|
||
X CPP, returning "failure" even if the -E
|
||
X option is given.
|
||
X Page 2
|
||
X cpp C Pre-Processor
|
||
X
|
||
X
|
||
X -Idirectory Add this directory to the list of
|
||
X directories searched for #include "..."
|
||
X and #include <...> commands. Note that
|
||
X there is no space between the "-I" and
|
||
X the directory string. More than one -I
|
||
X command is permitted. On non-Unix
|
||
X systems "directory" is forced to
|
||
X upper-case.
|
||
X
|
||
X -N CPP normally predefines some symbols
|
||
X defining the target computer and
|
||
X operating system. If -N is specified,
|
||
X no symbols will be predefined. If -N -N
|
||
X is specified, the "always present"
|
||
X symbols, __LINE__, __FILE__, and
|
||
X __DATE__ are not defined.
|
||
X
|
||
X -Stext CPP normally assumes that the size of
|
||
X the target computer's basic variable
|
||
X types is the same as the size of these
|
||
X types of the host computer. (This can
|
||
X be overridden when CPP is compiled,
|
||
X however.) The -S option allows dynamic
|
||
X respecification of these values. "text"
|
||
X is a string of numbers, separated by
|
||
X commas, that specifies correct sizes.
|
||
X The sizes must be specified in the exact
|
||
X order:
|
||
X
|
||
X char short int long float double
|
||
X
|
||
X If you specify the option as "-S*text",
|
||
X pointers to these types will be
|
||
X specified. -S* takes one additional
|
||
X argument for pointer to function (e.g.
|
||
X int (*)())
|
||
X
|
||
X For example, to specify sizes
|
||
X appropriate for a PDP-11, you would
|
||
X 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
|
||
X -Uname Undefine the name as if
|
||
X
|
||
X #undef name
|
||
X
|
||
X were given. On non-Unix systems, "name"
|
||
X will be forced to upper-case.
|
||
X Page 3
|
||
X cpp C Pre-Processor
|
||
X
|
||
X
|
||
X -Xnumber Enable debugging code. If no value is
|
||
X given, a value of 1 will be used. (For
|
||
X maintenence of CPP only.)
|
||
X
|
||
X
|
||
X PRE-DEFINED VARIABLES:
|
||
X
|
||
X When CPP begins processing, the following variables will
|
||
X have been defined (unless the -N option is specified):
|
||
X
|
||
X Target computer (as appropriate):
|
||
X
|
||
X pdp11, vax, M68000 m68000 m68k
|
||
X
|
||
X Target operating system (as appropriate):
|
||
X
|
||
X rsx, rt11, vms, unix
|
||
X
|
||
X Target compiler (as appropriate):
|
||
X
|
||
X decus, vax11c
|
||
X
|
||
X The implementor may add definitions to this list. The
|
||
X default definitions match the definition of the host
|
||
X computer, operating system, and C compiler.
|
||
X
|
||
X The following are always available unless undefined (or
|
||
X -N was specified twice):
|
||
X
|
||
X __FILE__ The input (or #include) file being
|
||
X compiled (as a quoted string).
|
||
X
|
||
X __LINE__ The line number being compiled.
|
||
X
|
||
X __DATE__ The date and time of compilation as a
|
||
X Unix ctime quoted string (the trailing
|
||
X newline is removed). Thus,
|
||
X
|
||
X printf("Bug at line %s,", __LINE__);
|
||
X printf(" source file %s", __FILE__);
|
||
X printf(" compiled on %s", __DATE__);
|
||
X
|
||
X
|
||
X DRAFT PROPOSED ANSI STANDARD CONSIDERATIONS:
|
||
X
|
||
X The current version of the Draft Proposed Standard
|
||
X explicitly states that "readers are requested not to
|
||
X specify or claim conformance to this draft." Readers and
|
||
X users of Decus CPP should not assume that Decus CPP
|
||
X conforms to the standard, or that it will conform to the
|
||
X actual C Language Standard.
|
||
X
|
||
X When CPP is itself compiled, many features of the Draft
|
||
X Proposed Standard that are incompatible with existing
|
||
X Page 4
|
||
X cpp C Pre-Processor
|
||
X
|
||
X
|
||
X preprocessors may be disabled. See the comments in
|
||
X CPP's source for details.
|
||
X
|
||
X The latest version of the Draft Proposed Standard (as
|
||
X reflected 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
|
||
X #elif expression (#else #if)
|
||
X '\xNNN' (Hexadecimal constant)
|
||
X '\a' (Ascii BELL)
|
||
X '\v' (Ascii Vertical Tab)
|
||
X #if defined NAME 1 if defined, 0 if not
|
||
X #if defined (NAME) 1 if defined, 0 if not
|
||
X #if sizeof (basic type)
|
||
X unary +
|
||
X 123U, 123LU Unsigned ints and longs.
|
||
X 12.3L Long double numbers
|
||
X token#token Token concatenation
|
||
X #include token Expands to filename
|
||
X
|
||
X The Draft Proposed Standard has extended C, adding a
|
||
X constant string concatenation operator, where
|
||
X
|
||
X "foo" "bar"
|
||
X
|
||
X is regarded as the single string "foobar". (This does
|
||
X not affect CPP's processing but does permit a limited
|
||
X form of macro argument substitution into strings as will
|
||
X 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 Page 5
|
||
X cpp C Pre-Processor
|
||
X
|
||
X
|
||
X generates two tokens: "foo1" and "x3".
|
||
X
|
||
X If the tokens T1 and T2 are concatenated into T3, this
|
||
X 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
|
||
X string or character constant if it is the only component
|
||
X of that 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
|
||
X supports the new string concatenation operation noted
|
||
X above. As implemented here, if you write
|
||
X
|
||
X #define string(arg) "arg"
|
||
X ... string("foo") ...
|
||
X
|
||
X This implementation generates "foo", rather than the
|
||
X strictly correct ""foo"" (which will probably generate
|
||
X an error message). This is, strictly speaking, an error
|
||
X in CPP and may be removed from future releases.
|
||
X
|
||
X ERROR MESSAGES:
|
||
X
|
||
X Many. CPP prints warning or error messages if you try
|
||
X to use multiple-byte character constants
|
||
X (non-transportable) if you #undef a symbol that was not
|
||
X defined, or if your program has potentially nested
|
||
X comments.
|
||
X
|
||
X AUTHOR:
|
||
X
|
||
X Martin Minow
|
||
X
|
||
X BUGS:
|
||
X
|
||
X The #if expression processor uses signed integers only.
|
||
X I.e, #if 0xFFFFu < 0 may be TRUE.
|
||
X
|
||
END-of-cpp.mem
|
||
echo x - cpp.h
|
||
sed 's/^X//' >cpp.h << 'END-of-cpp.h'
|
||
X
|
||
X/*
|
||
X * I n t e r n a l D e f i n i t i o n s f o r C P P
|
||
X *
|
||
X * In general, definitions in this file should not be changed.
|
||
X */
|
||
X
|
||
X#ifndef TRUE
|
||
X#define TRUE 1
|
||
X#define FALSE 0
|
||
X#endif
|
||
X#ifndef EOS
|
||
X/*
|
||
X * This is predefined in Decus C
|
||
X */
|
||
X#define EOS '\0' /* End of string */
|
||
X#endif
|
||
X#define EOF_CHAR 0 /* Returned by get() on eof */
|
||
X#define NULLST ((char *) NULL) /* Pointer to nowhere (linted) */
|
||
X#define DEF_NOARGS (-1) /* #define foo vs #define foo() */
|
||
X
|
||
X/*
|
||
X * The following may need to change if the host system doesn't use ASCII.
|
||
X */
|
||
X#define DEF_MAGIC 0x1D /* Magic for #defines */
|
||
X#define TOK_SEP 0x1E /* Token concatenation delim. */
|
||
X#define COM_SEP 0x1F /* Magic comment separator */
|
||
X
|
||
X/*
|
||
X * Note -- in Ascii, the following will map macro formals onto DEL + the
|
||
X * C1 control character region (decimal 128 .. (128 + PAR_MAC)) which will
|
||
X * be ok as long as PAR_MAC is less than 33). Note that the last PAR_MAC
|
||
X * value is reserved for string substitution.
|
||
X */
|
||
X
|
||
X#define MAC_PARM 0x7F /* Macro formals start here */
|
||
X#if PAR_MAC >= 33
|
||
X assertion fails -- PAR_MAC isn't less than 33
|
||
X#endif
|
||
X#define LASTPARM (PAR_MAC - 1)
|
||
X
|
||
X/*
|
||
X * Character type codes.
|
||
X */
|
||
X
|
||
X#define INV 0 /* Invalid, must be zero */
|
||
X#define OP_EOE INV /* End of expression */
|
||
X#define DIG 1 /* Digit */
|
||
X#define LET 2 /* Identifier start */
|
||
X#define FIRST_BINOP OP_ADD
|
||
X#define OP_ADD 3
|
||
X#define OP_SUB 4
|
||
X#define OP_MUL 5
|
||
X#define OP_DIV 6
|
||
X#define OP_MOD 7
|
||
X#define OP_ASL 8
|
||
X#define OP_ASR 9
|
||
X#define OP_AND 10 /* &, not && */
|
||
X#define OP_OR 11 /* |, not || */
|
||
X#define OP_XOR 12
|
||
X#define OP_EQ 13
|
||
X#define OP_NE 14
|
||
X#define OP_LT 15
|
||
X#define OP_LE 16
|
||
X#define OP_GE 17
|
||
X#define OP_GT 18
|
||
X#define OP_ANA 19 /* && */
|
||
X#define OP_ORO 20 /* || */
|
||
X#define OP_QUE 21 /* ? */
|
||
X#define OP_COL 22 /* : */
|
||
X#define OP_CMA 23 /* , (relevant?) */
|
||
X#define LAST_BINOP OP_CMA /* Last binary operand */
|
||
X/*
|
||
X * The following are unary.
|
||
X */
|
||
X#define FIRST_UNOP OP_PLU /* First Unary operand */
|
||
X#define OP_PLU 24 /* + (draft ANSI standard) */
|
||
X#define OP_NEG 25 /* - */
|
||
X#define OP_COM 26 /* ~ */
|
||
X#define OP_NOT 27 /* ! */
|
||
X#define LAST_UNOP OP_NOT
|
||
X#define OP_LPA 28 /* ( */
|
||
X#define OP_RPA 29 /* ) */
|
||
X#define OP_END 30 /* End of expression marker */
|
||
X#define OP_MAX (OP_END + 1) /* Number of operators */
|
||
X#define OP_FAIL (OP_END + 1) /* For error returns */
|
||
X
|
||
X/*
|
||
X * The following are for lexical scanning only.
|
||
X */
|
||
X
|
||
X#define QUO 65 /* Both flavors of quotation */
|
||
X#define DOT 66 /* . might start a number */
|
||
X#define SPA 67 /* Space and tab */
|
||
X#define BSH 68 /* Just a backslash */
|
||
X#define END 69 /* EOF */
|
||
X
|
||
X/*
|
||
X * These bits are set in ifstack[]
|
||
X */
|
||
X#define WAS_COMPILING 1 /* TRUE if compile set at entry */
|
||
X#define ELSE_SEEN 2 /* TRUE when #else processed */
|
||
X#define TRUE_SEEN 4 /* TRUE when #if TRUE processed */
|
||
X
|
||
X/*
|
||
X * Define bits for the basic types and their adjectives
|
||
X */
|
||
X
|
||
X#define T_CHAR 1
|
||
X#define T_INT 2
|
||
X#define T_FLOAT 4
|
||
X#define T_DOUBLE 8
|
||
X#define T_SHORT 16
|
||
X#define T_LONG 32
|
||
X#define T_SIGNED 64
|
||
X#define T_UNSIGNED 128
|
||
X#define T_PTR 256 /* Pointer */
|
||
X#define T_FPTR 512 /* Pointer to functions */
|
||
X
|
||
X/*
|
||
X * The DEFBUF structure stores information about #defined
|
||
X * macros. Note that the defbuf->repl information is always
|
||
X * in malloc storage.
|
||
X */
|
||
X
|
||
Xtypedef struct defbuf {
|
||
X struct defbuf *link; /* Next define in chain */
|
||
X char *repl; /* -> replacement */
|
||
X int hash; /* Symbol table hash */
|
||
X int nargs; /* For define(args) */
|
||
X char name[1]; /* #define name */
|
||
X} DEFBUF;
|
||
X
|
||
X/*
|
||
X * The FILEINFO structure stores information about open files
|
||
X * and macros being expanded.
|
||
X */
|
||
X
|
||
Xtypedef struct fileinfo {
|
||
X char *bptr; /* Buffer pointer */
|
||
X int line; /* for include or macro */
|
||
X FILE *fp; /* File if non-null */
|
||
X struct fileinfo *parent; /* Link to includer */
|
||
X char *filename; /* File/macro name */
|
||
X char *progname; /* From #line statement */
|
||
X unsigned int unrecur; /* For macro recursion */
|
||
X char buffer[1]; /* current input line */
|
||
X} FILEINFO;
|
||
X
|
||
X/*
|
||
X * The SIZES structure is used to store the values for #if sizeof
|
||
X */
|
||
X
|
||
Xtypedef struct sizes {
|
||
X short bits; /* If this bit is set, */
|
||
X short size; /* this is the datum size value */
|
||
X short psize; /* this is the pointer size */
|
||
X} SIZES;
|
||
X/*
|
||
X * nomacarg is a built-in #define on Decus C.
|
||
X */
|
||
X
|
||
X#ifdef nomacarg
|
||
X#define cput output /* cput concatenates tokens */
|
||
X#else
|
||
X#if COMMENT_INVISIBLE
|
||
X#define cput(c) { if (c != TOK_SEP && c != COM_SEP) putchar(c); }
|
||
X#else
|
||
X#define cput(c) { if (c != TOK_SEP) putchar(c); }
|
||
X#endif
|
||
X#endif
|
||
X
|
||
X#ifndef nomacarg
|
||
X#define streq(s1, s2) (strcmp(s1, s2) == 0)
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * Error codes. VMS uses system definitions.
|
||
X * Decus C codes are defined in stdio.h.
|
||
X * Others are cooked to order.
|
||
X */
|
||
X
|
||
X#if HOST == SYS_VMS
|
||
X#include <ssdef.h>
|
||
X#include <stsdef.h>
|
||
X#define IO_NORMAL (SS$_NORMAL | STS$M_INHIB_MSG)
|
||
X#define IO_ERROR SS$_ABORT
|
||
X#endif
|
||
X/*
|
||
X * Note: IO_NORMAL and IO_ERROR are defined in the Decus C stdio.h file
|
||
X */
|
||
X#ifndef IO_NORMAL
|
||
X#define IO_NORMAL 0
|
||
X#endif
|
||
X#ifndef IO_ERROR
|
||
X#define IO_ERROR 1
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * Externs
|
||
X */
|
||
X
|
||
Xextern int line; /* Current line number */
|
||
Xextern int wrongline; /* Force #line to cc pass 1 */
|
||
Xextern char type[]; /* Character classifier */
|
||
Xextern char token[IDMAX + 1]; /* Current input token */
|
||
Xextern int instring; /* TRUE if scanning string */
|
||
Xextern int inmacro; /* TRUE if scanning #define */
|
||
Xextern int errors; /* Error counter */
|
||
Xextern int recursion; /* Macro depth counter */
|
||
Xextern char ifstack[BLK_NEST]; /* #if information */
|
||
X#define compiling ifstack[0]
|
||
Xextern char *ifptr; /* -> current ifstack item */
|
||
Xextern char *incdir[NINCLUDE]; /* -i directories */
|
||
Xextern char **incend; /* -> active end of incdir */
|
||
Xextern int cflag; /* -C option (keep comments) */
|
||
Xextern int eflag; /* -E option (ignore errors) */
|
||
Xextern int nflag; /* -N option (no pre-defines) */
|
||
Xextern int rec_recover; /* unwind recursive macros */
|
||
Xextern char *preset[]; /* Standard predefined symbols */
|
||
Xextern char *magic[]; /* Magic predefined symbols */
|
||
Xextern FILEINFO *infile; /* Current input file */
|
||
Xextern char work[NWORK + 1]; /* #define scratch */
|
||
Xextern char *workp; /* Free space in work */
|
||
X#if DEBUG
|
||
Xextern int debug; /* Debug level */
|
||
X#endif
|
||
Xextern int keepcomments; /* Don't remove comments if set */
|
||
Xextern SIZES size_table[]; /* For #if sizeof sizes */
|
||
Xextern char *getmem(); /* Get memory or die. */
|
||
Xextern DEFBUF *lookid(); /* Look for a #define'd thing */
|
||
Xextern DEFBUF *defendel(); /* Symbol table enter/delete */
|
||
Xextern char *savestring(); /* Stuff string in malloc mem. */
|
||
Xextern char *strcpy();
|
||
Xextern char *strcat();
|
||
Xextern char *strrchr();
|
||
Xextern char *strchr();
|
||
Xextern long time();
|
||
X/* extern char *sprintf(); /* Lint needs this */
|
||
END-of-cpp.h
|
||
echo x - cppdef.h
|
||
sed 's/^X//' >cppdef.h << 'END-of-cppdef.h'
|
||
X/*
|
||
X * S y s t e m D e p e n d e n t
|
||
X * D e f i n i t i o n s f o r C P P
|
||
X *
|
||
X * Definitions in this file may be edited to configure CPP for particular
|
||
X * host operating systems and target configurations.
|
||
X *
|
||
X * NOTE: cpp assumes it is compiled by a compiler that supports macros
|
||
X * with arguments. If this is not the case (as for Decus C), #define
|
||
X * nomacarg -- and provide function equivalents for all macros.
|
||
X *
|
||
X * cpp also assumes the host and target implement the Ascii character set.
|
||
X * If this is not the case, you will have to do some editing here and there.
|
||
X */
|
||
X
|
||
X/*
|
||
X * This redundant definition of TRUE and FALSE works around
|
||
X * a limitation of Decus C.
|
||
X */
|
||
X#ifndef TRUE
|
||
X#define TRUE 1
|
||
X#define FALSE 0
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * Define the HOST operating system. This is needed so that
|
||
X * cpp can use appropriate filename conventions.
|
||
X */
|
||
X#define SYS_UNKNOWN 0
|
||
X#define SYS_UNIX 1
|
||
X#define SYS_VMS 2
|
||
X#define SYS_RSX 3
|
||
X#define SYS_RT11 4
|
||
X#define SYS_LATTICE 5
|
||
X#define SYS_ONYX 6
|
||
X#define SYS_68000 7
|
||
X#define SYS_GCOS 8
|
||
X#define SYS_IBM 9
|
||
X#define SYS_OS 10
|
||
X#define SYS_TSS 11
|
||
X
|
||
X#ifndef HOST
|
||
X#ifdef unix
|
||
X#define HOST SYS_UNIX
|
||
X#else
|
||
X#ifdef vms
|
||
X#define HOST SYS_VMS
|
||
X#else
|
||
X#ifdef rsx
|
||
X#define HOST SYS_RSX
|
||
X#else
|
||
X#ifdef rt11
|
||
X#define HOST SYS_RT11
|
||
X#else
|
||
X#ifdef dmert
|
||
X#define HOST SYS_DMERT
|
||
X#else
|
||
X#ifdef gcos
|
||
X#define HOST SYS_GCOS
|
||
X#else
|
||
X#ifdef ibm
|
||
X#define HOST SYS_IBM
|
||
X#else
|
||
X#ifdef os
|
||
X#define HOST SYS_OS
|
||
X#else
|
||
X#ifdef tss
|
||
X#define HOST SYS_TSS
|
||
X#endif
|
||
X#endif
|
||
X#endif
|
||
X#endif
|
||
X#endif
|
||
X#endif
|
||
X#endif
|
||
X#endif
|
||
X#endif
|
||
X
|
||
X#ifndef HOST
|
||
X#define HOST SYS_UNKNOWN
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * We assume that the target is the same as the host system
|
||
X */
|
||
X#ifndef TARGET
|
||
X#define TARGET HOST
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * In order to predefine machine-dependent constants,
|
||
X * several strings are defined here:
|
||
X *
|
||
X * MACHINE defines the target cpu (by name)
|
||
X * SYSTEM defines the target operating system
|
||
X * COMPILER defines the target compiler
|
||
X *
|
||
X * The above may be #defined as "" if they are not wanted.
|
||
X * They should not be #defined as NULL.
|
||
X *
|
||
X * LINE_PREFIX defines the # output line prefix, if not "line"
|
||
X * This should be defined as "" if cpp is to replace
|
||
X * the "standard" C pre-processor.
|
||
X *
|
||
X * FILE_LOCAL marks functions which are referenced only in the
|
||
X * file they reside. Some C compilers allow these
|
||
X * to be marked "static" even though they are referenced
|
||
X * by "extern" statements elsewhere.
|
||
X *
|
||
X * OK_DOLLAR Should be set TRUE if $ is a valid alphabetic character
|
||
X * in identifiers (default), or zero if $ is invalid.
|
||
X * Default is TRUE.
|
||
X *
|
||
X * OK_CONCAT Should be set TRUE if # may be used to concatenate
|
||
X * tokens in macros (per the Ansi Draft Standard) or
|
||
X * FALSE for old-style # processing (needed if cpp is
|
||
X * to process assembler source code).
|
||
X *
|
||
X * OK_DATE Predefines the compilation date if set TRUE.
|
||
X * Not permitted by the Nov. 12, 1984 Draft Standard.
|
||
X *
|
||
X * S_CHAR etc. Define the sizeof the basic TARGET machine word types.
|
||
X * By default, sizes are set to the values for the HOST
|
||
X * computer. If this is inappropriate, see the code in
|
||
X * cpp3.c for details on what to change. Also, if you
|
||
X * have a machine where sizeof (signed int) differs from
|
||
X * sizeof (unsigned int), you will have to edit code and
|
||
X * tables in cpp3.c (and extend the -S option definition.)
|
||
X *
|
||
X * CPP_LIBRARY May be defined if you have a site-specific include directory
|
||
X * which is to be searched *before* the operating-system
|
||
X * specific directories.
|
||
X */
|
||
X
|
||
X#if TARGET == SYS_LATTICE
|
||
X/*
|
||
X * We assume the operating system is pcdos for the IBM-PC.
|
||
X * We also assume the small model (just like the PDP-11)
|
||
X */
|
||
X#define MACHINE "i8086"
|
||
X#define SYSTEM "pcdos"
|
||
X#endif
|
||
X
|
||
X#if TARGET == SYS_ONYX
|
||
X#define MACHINE "z8000"
|
||
X#define SYSTEM "unix"
|
||
X#endif
|
||
X
|
||
X#if TARGET == SYS_VMS
|
||
X#define MACHINE "vax"
|
||
X#define SYSTEM "vms"
|
||
X#define COMPILER "vax11c"
|
||
X#endif
|
||
X
|
||
X#if TARGET == SYS_RSX
|
||
X#define MACHINE "pdp11"
|
||
X#define SYSTEM "rsx"
|
||
X#define COMPILER "decus"
|
||
X#endif
|
||
X
|
||
X#if TARGET == SYS_RT11
|
||
X#define MACHINE "pdp11"
|
||
X#define SYSTEM "rt11"
|
||
X#define COMPILER "decus"
|
||
X#endif
|
||
X
|
||
X#if TARGET == SYS_68000
|
||
X/*
|
||
X * All three machine designators have been seen in various systems.
|
||
X * Warning -- compilers differ as to sizeof (int). cpp3 assumes that
|
||
X * sizeof (int) == 2
|
||
X */
|
||
X#define MACHINE "M68000", "m68000", "m68k"
|
||
X#define SYSTEM "unix"
|
||
X#endif
|
||
X
|
||
X#if TARGET == SYS_UNIX
|
||
X#define SYSTEM "unix"
|
||
X#ifdef pdp11
|
||
X#define MACHINE "pdp11"
|
||
X#endif
|
||
X#ifdef vax
|
||
X#define MACHINE "vax"
|
||
X#endif
|
||
X#ifdef u370
|
||
X#define MACHINE "u370"
|
||
X#endif
|
||
X#ifdef interdata
|
||
X#define MACHINE "interdata"
|
||
X#endif
|
||
X#ifdef u3b
|
||
X#define MACHINE "u3b"
|
||
X#endif
|
||
X#ifdef u3b5
|
||
X#define MACHINE "u3b5"
|
||
X#endif
|
||
X#ifdef u3b2
|
||
X#define MACHINE "u3b2"
|
||
X#endif
|
||
X#ifdef u3b20d
|
||
X#define MACHINE "u3b20d"
|
||
X#endif
|
||
X#endif
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * defaults
|
||
X */
|
||
X
|
||
X#ifndef MSG_PREFIX
|
||
X#define MSG_PREFIX "cpp: "
|
||
X#endif
|
||
X
|
||
X#ifndef LINE_PREFIX
|
||
X#ifdef decus
|
||
X#define LINE_PREFIX ""
|
||
X#else
|
||
X#define LINE_PREFIX "line"
|
||
X#endif
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * OLD_PREPROCESSOR forces the definition of OK_DOLLAR, OK_CONCAT,
|
||
X * COMMENT_INVISIBLE, and STRING_FORMAL to values appropriate for
|
||
X * an old-style preprocessor.
|
||
X */
|
||
X
|
||
X#ifndef OLD_PREPROCESSOR
|
||
X#define OLD_PREPROCESSOR FALSE
|
||
X#endif
|
||
X
|
||
X#if OLD_PREPROCESSOR
|
||
X#define OK_DOLLAR FALSE
|
||
X#define OK_CONCAT FALSE
|
||
X#define COMMENT_INVISIBLE TRUE
|
||
X#define STRING_FORMAL TRUE
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * RECURSION_LIMIT may be set to -1 to disable the macro recursion test.
|
||
X */
|
||
X#ifndef RECURSION_LIMIT
|
||
X#define RECURSION_LIMIT 1000
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * BITS_CHAR may be defined to set the number of bits per character.
|
||
X * it is needed only for multi-byte character constants.
|
||
X */
|
||
X#ifndef BITS_CHAR
|
||
X#define BITS_CHAR 8
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * BIG_ENDIAN is set TRUE on machines (such as the IBM 360 series)
|
||
X * where 'ab' stores 'a' in the high-bits and 'b' in the low-bits.
|
||
X * It is set FALSE on machines (such as the PDP-11 and Vax-11)
|
||
X * where 'ab' stores 'a' in the low-bits and 'b' in the high-bits.
|
||
X * (Or is it the other way around?) -- Warning: BIG_ENDIAN code is untested.
|
||
X */
|
||
X#ifndef BIG_ENDIAN
|
||
X#define BIG_ENDIAN FALSE
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * COMMENT_INVISIBLE may be defined to allow "old-style" comment
|
||
X * processing, whereby the comment becomes a zero-length token
|
||
X * delimiter. This permitted tokens to be concatenated in macro
|
||
X * expansions. This was removed from the Draft Ansi Standard.
|
||
X */
|
||
X#ifndef COMMENT_INVISIBLE
|
||
X#define COMMENT_INVISIBLE FALSE
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * STRING_FORMAL may be defined to allow recognition of macro parameters
|
||
X * anywhere in replacement strings. This was removed from the Draft Ansi
|
||
X * Standard and a limited recognition capability added.
|
||
X */
|
||
X#ifndef STRING_FORMAL
|
||
X#define STRING_FORMAL FALSE
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * OK_DOLLAR enables use of $ as a valid "letter" in identifiers.
|
||
X * This is a permitted extension to the Ansi Standard and is required
|
||
X * for e.g., VMS, RSX-11M, etc. It should be set FALSE if cpp is
|
||
X * used to preprocess assembler source on Unix systems. OLD_PREPROCESSOR
|
||
X * sets OK_DOLLAR FALSE for that reason.
|
||
X */
|
||
X#ifndef OK_DOLLAR
|
||
X#define OK_DOLLAR TRUE
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * OK_CONCAT enables (one possible implementation of) token concatenation.
|
||
X * If cpp is used to preprocess Unix assembler source, this should be
|
||
X * set FALSE as the concatenation character, #, is used by the assembler.
|
||
X */
|
||
X#ifndef OK_CONCAT
|
||
X#define OK_CONCAT TRUE
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * OK_DATE may be enabled to predefine today's date as a string
|
||
X * at the start of each compilation. This is apparently not permitted
|
||
X * by the Draft Ansi Standard.
|
||
X */
|
||
X#ifndef OK_DATE
|
||
X#define OK_DATE TRUE
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * Some common definitions.
|
||
X */
|
||
X
|
||
X#ifndef DEBUG
|
||
X#define DEBUG FALSE
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * The following definitions are used to allocate memory for
|
||
X * work buffers. In general, they should not be modified
|
||
X * by implementors.
|
||
X *
|
||
X * PAR_MAC The maximum number of #define parameters (31 per Standard)
|
||
X * Note: we need another one for strings.
|
||
X * IDMAX The longest identifier, 31 per Ansi Standard
|
||
X * NBUFF Input buffer size
|
||
X * NWORK Work buffer size -- the longest macro
|
||
X * must fit here after expansion.
|
||
X * NEXP The nesting depth of #if expressions
|
||
X * NINCLUDE The number of directories that may be specified
|
||
X * on a per-system basis, or by the -I option.
|
||
X * BLK_NEST The number of nested #if's permitted.
|
||
X */
|
||
X
|
||
X#define IDMAX 31
|
||
X#define PAR_MAC (31 + 1)
|
||
X#define NBUFF 1024
|
||
X#define NWORK 1024
|
||
X#define NEXP 128
|
||
X#define NINCLUDE 7
|
||
X#define NPARMWORK (NWORK * 2)
|
||
X#define BLK_NEST 32
|
||
X
|
||
X/*
|
||
X * Some special constants. These may need to be changed if cpp
|
||
X * is ported to a wierd machine.
|
||
X *
|
||
X * NOTE: if cpp is run on a non-ascii machine, ALERT and VT may
|
||
X * need to be changed. They are used to implement the proposed
|
||
X * ANSI standard C control characters '\a' and '\v' only.
|
||
X * DEL is used to tag macro tokens to prevent #define foo foo
|
||
X * from looping. Note that we don't try to prevent more elaborate
|
||
X * #define loops from occurring.
|
||
X */
|
||
X
|
||
X#ifndef ALERT
|
||
X#define ALERT '\007' /* '\a' is "Bell" */
|
||
X#endif
|
||
X
|
||
X#ifndef VT
|
||
X#define VT '\013' /* Vertical Tab CTRL/K */
|
||
X#endif
|
||
X
|
||
X
|
||
X#ifndef FILE_LOCAL
|
||
X#ifdef decus
|
||
X#define FILE_LOCAL static
|
||
X#else
|
||
X#ifdef vax11c
|
||
X#define FILE_LOCAL static
|
||
X#else
|
||
X#define FILE_LOCAL /* Others are global */
|
||
X#endif
|
||
X#endif
|
||
X#endif
|
||
X
|
||
END-of-cppdef.h
|
||
echo x - cpp2.c
|
||
sed 's/^X//' >cpp2.c << 'END-of-cpp2.c'
|
||
X/*
|
||
X * C P P 2 . C
|
||
X *
|
||
X * Process #control lines
|
||
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 HOST == SYS_VMS
|
||
X/*
|
||
X * Include the rms stuff. (We can't just include rms.h as it uses the
|
||
X * VaxC-specific library include syntax that Decus CPP doesn't support.
|
||
X * By including things by hand, we can CPP ourself.)
|
||
X */
|
||
X#include <nam.h>
|
||
X#include <fab.h>
|
||
X#include <rab.h>
|
||
X#include <rmsdef.h>
|
||
X#endif
|
||
X
|
||
X/*
|
||
X * Generate (by hand-inspection) a set of unique values for each control
|
||
X * operator. Note that this is not guaranteed to work for non-Ascii
|
||
X * machines. CPP won't compile if there are hash conflicts.
|
||
X */
|
||
X
|
||
X#define L_assert ('a' + ('s' << 1))
|
||
X#define L_define ('d' + ('f' << 1))
|
||
X#define L_elif ('e' + ('i' << 1))
|
||
X#define L_else ('e' + ('s' << 1))
|
||
X#define L_endif ('e' + ('d' << 1))
|
||
X#define L_ident ('i' + ('e' << 1))
|
||
X#define L_if ('i' + (EOS << 1))
|
||
X#define L_ifdef ('i' + ('d' << 1))
|
||
X#define L_ifndef ('i' + ('n' << 1))
|
||
X#define L_include ('i' + ('c' << 1))
|
||
X#define L_line ('l' + ('n' << 1))
|
||
X#define L_nogood (EOS + (EOS << 1)) /* To catch #i */
|
||
X#define L_pragma ('p' + ('a' << 1))
|
||
X#define L_sccs ('s' + ('c' << 1))
|
||
X#define L_undef ('u' + ('d' << 1))
|
||
X#if DEBUG
|
||
X#define L_debug ('d' + ('b' << 1)) /* #debug */
|
||
X#define L_nodebug ('n' + ('d' << 1)) /* #nodebug */
|
||
X#endif
|
||
X
|
||
Xint
|
||
Xcontrol(counter)
|
||
Xint counter; /* Pending newline counter */
|
||
X/*
|
||
X * Process #control lines. Simple commands are processed inline,
|
||
X * while complex commands have their own subroutines.
|
||
X *
|
||
X * The counter is used to force out a newline before #line, and
|
||
X * #pragma commands. This prevents these commands from ending up at
|
||
X * the end of the previous line if cpp is invoked with the -C option.
|
||
X */
|
||
X{
|
||
X register int c;
|
||
X register char *tp;
|
||
X register int hash;
|
||
X char *ep;
|
||
X
|
||
X c = skipws();
|
||
X if (c == '\n' || c == EOF_CHAR)
|
||
X return (counter + 1);
|
||
X if (!isdigit(c))
|
||
X scanid(c); /* Get #word to token[] */
|
||
X else {
|
||
X unget(); /* Hack -- allow #123 as a */
|
||
X strcpy(token, "line"); /* synonym for #line 123 */
|
||
X }
|
||
X hash = (token[1] == EOS) ? L_nogood : (token[0] + (token[2] << 1));
|
||
X switch (hash) {
|
||
X case L_assert: tp = "assert"; break;
|
||
X case L_define: tp = "define"; break;
|
||
X case L_elif: tp = "elif"; break;
|
||
X case L_else: tp = "else"; break;
|
||
X case L_endif: tp = "endif"; break;
|
||
X case L_ident: tp = "ident"; break;
|
||
X case L_if: tp = "if"; break;
|
||
X case L_ifdef: tp = "ifdef"; break;
|
||
X case L_ifndef: tp = "ifndef"; break;
|
||
X case L_include: tp = "include"; break;
|
||
X case L_line: tp = "line"; break;
|
||
X case L_pragma: tp = "pragma"; break;
|
||
X case L_sccs: tp = "sccs"; break;
|
||
X case L_undef: tp = "undef"; break;
|
||
X#if DEBUG
|
||
X case L_debug: tp = "debug"; break;
|
||
X case L_nodebug: tp = "nodebug"; break;
|
||
X#endif
|
||
X default: hash = L_nogood;
|
||
X case L_nogood: tp = ""; break;
|
||
X }
|
||
X if (!streq(tp, token))
|
||
X hash = L_nogood;
|
||
X /*
|
||
X * hash is set to a unique value corresponding to the
|
||
X * control keyword (or L_nogood if we think it's nonsense).
|
||
X */
|
||
X if (infile->fp == NULL)
|
||
X cwarn("Control line \"%s\" within macro expansion", token);
|
||
X if (!compiling) { /* Not compiling now */
|
||
X switch (hash) {
|
||
X case L_if: /* These can't turn */
|
||
X case L_ifdef: /* compilation on, but */
|
||
X case L_ifndef: /* we must nest #if's */
|
||
X if (++ifptr >= &ifstack[BLK_NEST])
|
||
X goto if_nest_err;
|
||
X *ifptr = 0; /* !WAS_COMPILING */
|
||
X case L_line: /* Many */
|
||
X /*
|
||
X * Are pragma's always processed?
|
||
X */
|
||
X case L_ident:
|
||
X case L_sccs:
|
||
X case L_pragma: /* options */
|
||
X case L_include: /* are uninteresting */
|
||
X case L_define: /* if we */
|
||
X case L_undef: /* aren't */
|
||
X case L_assert: /* compiling. */
|
||
Xdump_line: skipnl(); /* Ignore rest of line */
|
||
X return (counter + 1);
|
||
X }
|
||
X }
|
||
X /*
|
||
X * Make sure that #line and #pragma are output on a fresh line.
|
||
X */
|
||
X if (counter > 0 && (hash == L_line || hash == L_pragma)) {
|
||
X putchar('\n');
|
||
X counter--;
|
||
X }
|
||
X switch (hash) {
|
||
X case L_line:
|
||
X /*
|
||
X * Parse the line to update the line number and "progname"
|
||
X * field and line number for the next input line.
|
||
X * Set wrongline to force it out later.
|
||
X */
|
||
X c = skipws();
|
||
X workp = work; /* Save name in work */
|
||
X while (c != '\n' && c != EOF_CHAR) {
|
||
X save(c);
|
||
X c = get();
|
||
X }
|
||
X unget();
|
||
X save(EOS);
|
||
X /*
|
||
X * Split #line argument into <line-number> and <name>
|
||
X * We subtract 1 as we want the number of the next line.
|
||
X */
|
||
X line = atoi(work) - 1; /* Reset line number */
|
||
X for (tp = work; isdigit(*tp) || type[*tp] == SPA; tp++)
|
||
X ; /* Skip over digits */
|
||
X if (*tp != EOS) { /* Got a filename, so: */
|
||
X if (*tp == '"' && (ep = strrchr(tp + 1, '"')) != NULL) {
|
||
X tp++; /* Skip over left quote */
|
||
X *ep = EOS; /* And ignore right one */
|
||
X }
|
||
X if (infile->progname != NULL) /* Give up the old name */
|
||
X free(infile->progname); /* if it's allocated. */
|
||
X infile->progname = savestring(tp);
|
||
X }
|
||
X wrongline = TRUE; /* Force output later */
|
||
X break;
|
||
X
|
||
X case L_include:
|
||
X doinclude();
|
||
X break;
|
||
X
|
||
X case L_define:
|
||
X dodefine();
|
||
X break;
|
||
X
|
||
X case L_undef:
|
||
X doundef();
|
||
X break;
|
||
X
|
||
X case L_else:
|
||
X if (ifptr == &ifstack[0])
|
||
X goto nest_err;
|
||
X else if ((*ifptr & ELSE_SEEN) != 0)
|
||
X goto else_seen_err;
|
||
X *ifptr |= ELSE_SEEN;
|
||
X if ((*ifptr & WAS_COMPILING) != 0) {
|
||
X if (compiling || (*ifptr & TRUE_SEEN) != 0)
|
||
X compiling = FALSE;
|
||
X else {
|
||
X compiling = TRUE;
|
||
X }
|
||
X }
|
||
X break;
|
||
X
|
||
X case L_elif:
|
||
X if (ifptr == &ifstack[0])
|
||
X goto nest_err;
|
||
X else if ((*ifptr & ELSE_SEEN) != 0) {
|
||
Xelse_seen_err: cerror("#%s may not follow #else", token);
|
||
X goto dump_line;
|
||
X }
|
||
X if ((*ifptr & (WAS_COMPILING | TRUE_SEEN)) != WAS_COMPILING) {
|
||
X compiling = FALSE; /* Done compiling stuff */
|
||
X goto dump_line; /* Skip this clause */
|
||
X }
|
||
X doif(L_if);
|
||
X break;
|
||
X
|
||
X case L_if:
|
||
X case L_ifdef:
|
||
X case L_ifndef:
|
||
X if (++ifptr >= &ifstack[BLK_NEST])
|
||
Xif_nest_err: cfatal("Too many nested #%s statements", token);
|
||
X *ifptr = WAS_COMPILING;
|
||
X doif(hash);
|
||
X break;
|
||
X
|
||
X case L_endif:
|
||
X if (ifptr == &ifstack[0]) {
|
||
Xnest_err: cerror("#%s must be in an #if", token);
|
||
X goto dump_line;
|
||
X }
|
||
X if (!compiling && (*ifptr & WAS_COMPILING) != 0)
|
||
X wrongline = TRUE;
|
||
X compiling = ((*ifptr & WAS_COMPILING) != 0);
|
||
X --ifptr;
|
||
X break;
|
||
X
|
||
X case L_assert:
|
||
X if (eval() == 0)
|
||
X cerror("Preprocessor assertion failure", NULLST);
|
||
X break;
|
||
X
|
||
X case L_ident:
|
||
X case L_sccs:
|
||
X goto dump_line;
|
||
X break;
|
||
X
|
||
X case L_pragma:
|
||
X /*
|
||
X * #pragma is provided to pass "options" to later
|
||
X * passes of the compiler. cpp doesn't have any yet.
|
||
X */
|
||
X printf("#pragma ");
|
||
X while ((c = get()) != '\n' && c != EOF_CHAR)
|
||
X cput(c);
|
||
X unget();
|
||
X break;
|
||
X
|
||
X#if DEBUG
|
||
X case L_debug:
|
||
X if (debug == 0)
|
||
X dumpdef("debug set on");
|
||
X debug++;
|
||
X break;
|
||
X
|
||
X case L_nodebug:
|
||
X debug--;
|
||
X break;
|
||
X#endif
|
||
X
|
||
X default:
|
||
X /*
|
||
X * Undefined #control keyword.
|
||
X * Note: the correct behavior may be to warn and
|
||
X * pass the line to a subsequent compiler pass.
|
||
X * This would allow #asm or similar extensions.
|
||
X */
|
||
X cerror("Illegal # command \"%s\"", token);
|
||
X break;
|
||
X }
|
||
X if (hash != L_include) {
|
||
X#if OLD_PREPROCESSOR || !VERBOSE
|
||
X /*
|
||
X * Ignore the rest of the #control line so you can write
|
||
X * #if foo
|
||
X * #endif foo
|
||
X */
|
||
X goto dump_line; /* Take common exit */
|
||
X#else
|
||
X if (skipws() != '\n') {
|
||
X cwarn("Unexpected text in #control line ignored", NULLST);
|
||
X skipnl();
|
||
X }
|
||
X#endif
|
||
X }
|
||
X return (counter + 1);
|
||
X}
|
||
X
|
||
XFILE_LOCAL
|
||
Xdoif(hash)
|
||
Xint hash;
|
||
X/*
|
||
X * Process an #if, #ifdef, or #ifndef. The latter two are straightforward,
|
||
X * while #if needs a subroutine of its own to evaluate the expression.
|
||
X *
|
||
X * doif() is called only if compiling is TRUE. If false, compilation
|
||
X * is always supressed, so we don't need to evaluate anything. This
|
||
X * supresses unnecessary warnings.
|
||
X */
|
||
X{
|
||
X register int c;
|
||
X register int found;
|
||
X
|
||
X if ((c = skipws()) == '\n' || c == EOF_CHAR) {
|
||
X unget();
|
||
X goto badif;
|
||
X }
|
||
X if (hash == L_if) {
|
||
X unget();
|
||
X found = (eval() != 0); /* Evaluate expr, != 0 is TRUE */
|
||
X hash = L_ifdef; /* #if is now like #ifdef */
|
||
X }
|
||
X else {
|
||
X if (type[c] != LET) /* Next non-blank isn't letter */
|
||
X goto badif; /* ... is an error */
|
||
X found = (lookid(c) != NULL); /* Look for it in symbol table */
|
||
X }
|
||
X if (found == (hash == L_ifdef)) {
|
||
X compiling = TRUE;
|
||
X *ifptr |= TRUE_SEEN;
|
||
X }
|
||
X else {
|
||
X compiling = FALSE;
|
||
X }
|
||
X return;
|
||
X
|
||
Xbadif: cerror("#if, #ifdef, or #ifndef without an argument", NULLST);
|
||
X#if !OLD_PREPROCESSOR
|
||
X skipnl(); /* Prevent an extra */
|
||
X unget(); /* Error message */
|
||
X#endif
|
||
X return;
|
||
X}
|
||
X
|
||
XFILE_LOCAL
|
||
Xdoinclude()
|
||
X/*
|
||
X * Process the #include control line.
|
||
X * There are three variations:
|
||
X * #include "file" search somewhere relative to the
|
||
X * current source file, if not found,
|
||
X * treat as #include <file>.
|
||
X * #include <file> Search in an implementation-dependent
|
||
X * list of places.
|
||
X * #include token Expand the token, it must be one of
|
||
X * "file" or <file>, process as such.
|
||
X *
|
||
X * Note: the November 12 draft forbids '>' in the #include <file> format.
|
||
X * This restriction is unnecessary and not implemented.
|
||
X */
|
||
X{
|
||
X register int c;
|
||
X register int delim;
|
||
X#if HOST == SYS_VMS
|
||
X char def_filename[NAM$C_MAXRSS + 1];
|
||
X#endif
|
||
X
|
||
X delim = macroid(skipws());
|
||
X if (delim != '<' && delim != '"')
|
||
X goto incerr;
|
||
X if (delim == '<')
|
||
X delim = '>';
|
||
X workp = work;
|
||
X instring = TRUE; /* Accept all characters */
|
||
X while ((c = get()) != '\n' && c != delim && c != EOF_CHAR)
|
||
X save(c); /* Put it away. */
|
||
X skipnl();
|
||
X /*
|
||
X * The draft is unclear if the following should be done.
|
||
X */
|
||
X
|
||
X while (--workp >= work && (type[*workp] == SPA))
|
||
X ; /* Trim blanks from filename */
|
||
X
|
||
X/*
|
||
X * if (*workp != delim)
|
||
X * goto incerr;
|
||
X */
|
||
X *(workp + 1) = EOS; /* Terminate filename */
|
||
X instring = FALSE;
|
||
X#if HOST == SYS_VMS
|
||
X /*
|
||
X * Assume the default .h filetype.
|
||
X */
|
||
X if (!vmsparse(work, ".H", def_filename)) {
|
||
X perror(work); /* Oops. */
|
||
X goto incerr;
|
||
X }
|
||
X else if (openinclude(def_filename, (delim == '"')))
|
||
X return;
|
||
X#else
|
||
X if (openinclude(work, (delim == '"')))
|
||
X return;
|
||
X#endif
|
||
X /*
|
||
X * No sense continuing if #include file isn't there.
|
||
X */
|
||
X cfatal("Cannot open include file \"%s\"", work);
|
||
X
|
||
Xincerr: cerror("#include syntax error", NULLST);
|
||
X return;
|
||
X}
|
||
X
|
||
XFILE_LOCAL int
|
||
Xopeninclude(filename, searchlocal)
|
||
Xchar *filename; /* Input file name */
|
||
Xint searchlocal; /* TRUE if #include "file" */
|
||
X/*
|
||
X * Actually open an include file. This routine is only called from
|
||
X * doinclude() above, but was written as a separate subroutine for
|
||
X * programmer convenience. It searches the list of directories
|
||
X * and actually opens the file, linking it into the list of
|
||
X * active files. Returns TRUE if the file was opened, FALSE
|
||
X * if openinclude() fails. No error message is printed.
|
||
X */
|
||
X{
|
||
X register char **incptr;
|
||
X#if HOST == SYS_VMS
|
||
X#if NWORK < (NAM$C_MAXRSS + 1)
|
||
X << error, NWORK isn't greater than NAM$C_MAXRSS >>
|
||
X#endif
|
||
X#endif
|
||
X char tmpname[NWORK]; /* Filename work area */
|
||
X
|
||
X if (searchlocal) {
|
||
X /*
|
||
X * Look in local directory first
|
||
X */
|
||
X#if HOST == SYS_UNIX
|
||
X /*
|
||
X * Try to open filename relative to the directory of the current
|
||
X * source file (as opposed to the current directory). (ARF, SCK).
|
||
X */
|
||
X if (filename[0] != '/'
|
||
X && hasdirectory(infile->filename, tmpname))
|
||
X strcat(tmpname, filename);
|
||
X else {
|
||
X strcpy(tmpname, filename);
|
||
X }
|
||
X#else
|
||
X if (!hasdirectory(filename, tmpname)
|
||
X && hasdirectory(infile->filename, tmpname))
|
||
X strcat(tmpname, filename);
|
||
X else {
|
||
X strcpy(tmpname, filename);
|
||
X }
|
||
X#endif
|
||
X if (openfile(tmpname))
|
||
X return (TRUE);
|
||
X }
|
||
X /*
|
||
X * Look in any directories specified by -I command line
|
||
X * arguments, then in the builtin search list.
|
||
X */
|
||
X for (incptr = incdir; incptr < incend; incptr++) {
|
||
X if (strlen(*incptr) + strlen(filename) >= (NWORK - 1))
|
||
X cfatal("Filename work buffer overflow", NULLST);
|
||
X else {
|
||
X#if HOST == SYS_UNIX
|
||
X if (filename[0] == '/')
|
||
X strcpy(tmpname, filename);
|
||
X else {
|
||
X sprintf(tmpname, "%s/%s", *incptr, filename);
|
||
X }
|
||
X#else
|
||
X if (!hasdirectory(filename, tmpname))
|
||
X sprintf(tmpname, "%s%s", *incptr, filename);
|
||
X#endif
|
||
X if (openfile(tmpname))
|
||
X return (TRUE);
|
||
X }
|
||
X }
|
||
X return (FALSE);
|
||
X}
|
||
X
|
||
XFILE_LOCAL int
|
||
Xhasdirectory(source, result)
|
||
Xchar *source; /* Directory to examine */
|
||
Xchar *result; /* Put directory stuff here */
|
||
X/*
|
||
X * If a device or directory is found in the source filename string, the
|
||
X * node/device/directory part of the string is copied to result and
|
||
X * hasdirectory returns TRUE. Else, nothing is copied and it returns FALSE.
|
||
X */
|
||
X{
|
||
X#if HOST == SYS_UNIX
|
||
X register char *tp;
|
||
X
|
||
X if ((tp = strrchr(source, '/')) == NULL)
|
||
X return (FALSE);
|
||
X else {
|
||
X strncpy(result, source, tp - source + 1);
|
||
X result[tp - source + 1] = EOS;
|
||
X return (TRUE);
|
||
X }
|
||
X#else
|
||
X#if HOST == SYS_VMS
|
||
X if (vmsparse(source, NULLST, result)
|
||
X && result[0] != EOS)
|
||
X return (TRUE);
|
||
X else {
|
||
X return (FALSE);
|
||
X }
|
||
X#else
|
||
X /*
|
||
X * Random DEC operating system (RSX, RT11, RSTS/E)
|
||
X */
|
||
X register char *tp;
|
||
X
|
||
X if ((tp = strrchr(source, ']')) == NULL
|
||
X && (tp = strrchr(source, ':')) == NULL)
|
||
X return (FALSE);
|
||
X else {
|
||
X strncpy(result, source, tp - source + 1);
|
||
X result[tp - source + 1] = EOS;
|
||
X return (TRUE);
|
||
X }
|
||
X#endif
|
||
X#endif
|
||
X}
|
||
X
|
||
X#if HOST == SYS_VMS
|
||
X
|
||
X/*
|
||
X * EXP_DEV is set if a device was specified, EXP_DIR if a directory
|
||
X * is specified. (Both set indicate a file-logical, but EXP_DEV
|
||
X * would be set by itself if you are reading, say, SYS$INPUT:)
|
||
X */
|
||
X#define DEVDIR (NAM$M_EXP_DEV | NAM$M_EXP_DIR)
|
||
X
|
||
XFILE_LOCAL int
|
||
Xvmsparse(source, defstring, result)
|
||
Xchar *source;
|
||
Xchar *defstring; /* non-NULL -> default string. */
|
||
Xchar *result; /* Size is at least NAM$C_MAXRSS + 1 */
|
||
X/*
|
||
X * Parse the source string, applying the default (properly, using
|
||
X * the system parse routine), storing it in result.
|
||
X * TRUE if it parsed, FALSE on error.
|
||
X *
|
||
X * If defstring is NULL, there are no defaults and result gets
|
||
X * (just) the node::[directory] part of the string (possibly "")
|
||
X */
|
||
X{
|
||
X struct FAB fab = cc$rms_fab; /* File access block */
|
||
X struct NAM nam = cc$rms_nam; /* File name block */
|
||
X char fullname[NAM$C_MAXRSS + 1];
|
||
X register char *rp; /* Result pointer */
|
||
X
|
||
X fab.fab$l_nam = &nam; /* fab -> nam */
|
||
X fab.fab$l_fna = source; /* Source filename */
|
||
X fab.fab$b_fns = strlen(source); /* Size of source */
|
||
X fab.fab$l_dna = defstring; /* Default string */
|
||
X if (defstring != NULLST)
|
||
X fab.fab$b_dns = strlen(defstring); /* Size of default */
|
||
X nam.nam$l_esa = fullname; /* Expanded filename */
|
||
X nam.nam$b_ess = NAM$C_MAXRSS; /* Expanded name size */
|
||
X if (sys$parse(&fab) == RMS$_NORMAL) { /* Parse away */
|
||
X fullname[nam.nam$b_esl] = EOS; /* Terminate string */
|
||
X result[0] = EOS; /* Just in case */
|
||
X rp = &result[0];
|
||
X /*
|
||
X * Remove stuff added implicitly, accepting node names and
|
||
X * dev:[directory] strings (but not process-permanent files).
|
||
X */
|
||
X if ((nam.nam$l_fnb & NAM$M_PPF) == 0) {
|
||
X if ((nam.nam$l_fnb & NAM$M_NODE) != 0) {
|
||
X strncpy(result, nam.nam$l_node, nam.nam$b_node);
|
||
X rp += nam.nam$b_node;
|
||
X *rp = EOS;
|
||
X }
|
||
X if ((nam.nam$l_fnb & DEVDIR) == DEVDIR) {
|
||
X strncpy(rp, nam.nam$l_dev, nam.nam$b_dev + nam.nam$b_dir);
|
||
X rp += nam.nam$b_dev + nam.nam$b_dir;
|
||
X *rp = EOS;
|
||
X }
|
||
X }
|
||
X if (defstring != NULLST) {
|
||
X strncpy(rp, nam.nam$l_name, nam.nam$b_name + nam.nam$b_type);
|
||
X rp += nam.nam$b_name + nam.nam$b_type;
|
||
X *rp = EOS;
|
||
X if ((nam.nam$l_fnb & NAM$M_EXP_VER) != 0) {
|
||
X strncpy(rp, nam.nam$l_ver, nam.nam$b_ver);
|
||
X rp[nam.nam$b_ver] = EOS;
|
||
X }
|
||
X }
|
||
X return (TRUE);
|
||
X }
|
||
X return (FALSE);
|
||
X}
|
||
X#endif
|
||
X
|
||
END-of-cpp2.c
|
||
exit
|