Import a copy of ucpp, lightweight cpp that doesn't depend
authormatthieu <matthieu@openbsd.org>
Sat, 12 Jul 2014 16:23:43 +0000 (16:23 +0000)
committermatthieu <matthieu@openbsd.org>
Sat, 12 Jul 2014 16:23:43 +0000 (16:23 +0000)
on the 'comp'  set. Moving from xenocara with a new name.
ok deraadt@.

23 files changed:
libexec/auxcpp/CHANGELOG [new file with mode: 0644]
libexec/auxcpp/Makefile [new file with mode: 0644]
libexec/auxcpp/README [new file with mode: 0644]
libexec/auxcpp/arith.c [new file with mode: 0644]
libexec/auxcpp/arith.h [new file with mode: 0644]
libexec/auxcpp/assert.c [new file with mode: 0644]
libexec/auxcpp/atest.c [new file with mode: 0644]
libexec/auxcpp/config.h [new file with mode: 0644]
libexec/auxcpp/cpp.c [new file with mode: 0644]
libexec/auxcpp/cpp.h [new file with mode: 0644]
libexec/auxcpp/eval.c [new file with mode: 0644]
libexec/auxcpp/hash.c [new file with mode: 0644]
libexec/auxcpp/hash.h [new file with mode: 0644]
libexec/auxcpp/lexer.c [new file with mode: 0644]
libexec/auxcpp/macro.c [new file with mode: 0644]
libexec/auxcpp/mem.c [new file with mode: 0644]
libexec/auxcpp/mem.h [new file with mode: 0644]
libexec/auxcpp/nhash.c [new file with mode: 0644]
libexec/auxcpp/nhash.h [new file with mode: 0644]
libexec/auxcpp/sample.c [new file with mode: 0644]
libexec/auxcpp/tune.h [new file with mode: 0644]
libexec/auxcpp/ucpp.1 [new file with mode: 0644]
libexec/auxcpp/ucppi.h [new file with mode: 0644]

diff --git a/libexec/auxcpp/CHANGELOG b/libexec/auxcpp/CHANGELOG
new file mode 100644 (file)
index 0000000..071b468
--- /dev/null
@@ -0,0 +1,21 @@
+ucpp-1.3.2
+* Fixed Issue 8, Included files missing  a "terminating carriage
+  return character" will interrupt preprocessing in sample.c/LEXER
+  mode.
+  (http://code.google.com/p/ucpp/issues/detail?id=8)
+
+ucpp-1.3.1
+* Fixed Issue 5, "\r\n" carriage return characters are double
+  counted.
+  (http://code.google.com/p/ucpp/issues/detail?id=5)           
+* Fixed Issue 6, Included files missing a "terminating carriage
+  return character" will interrupt preprocessing in ucpp
+  (STAND_ALONE mode).
+  (http://code.google.com/p/ucpp/issues/detail?id=6)
+* Fixed Issue 7, STD_MACROS & STD_ASSERTS undefined when trying to
+  build ucpp -DSTAND_ALONE.
+  (http://code.google.com/p/ucpp/issues/detail?id=7)
+* Build ucpp & libucpp with 'make'.
+
+ucpp-1.3
+* Original import into svn at code.google.com/p/ucpp
diff --git a/libexec/auxcpp/Makefile b/libexec/auxcpp/Makefile
new file mode 100644 (file)
index 0000000..c37f0b4
--- /dev/null
@@ -0,0 +1,115 @@
+# Makefile for ucpp
+#
+# (c) Thomas Pornin 1999 - 2002
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 4. The name of the authors may not be used to endorse or promote
+#    products derived from this software without specific prior written
+#    permission.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+.POSIX:
+
+# ----- user configurable part -----
+
+# Edit the variables to suit your system.
+#
+#   use -DAUDIT to enable some internal sanity checks
+#   use -DMEM_CHECK to check the return value of malloc()
+#      (superseded by AUDIT)
+#   use -DMEM_DEBUG to enable memory leak research (warning: this
+#      slows down ucpp a bit, and greatly increases memory consumption)
+#   use -DINLINE=foobar to enable use of the 'foobar'
+#      non standard qualifier, as an equivalent to the C99 'inline'
+#      qualifier. See tune.h for details.
+#
+# Two FLAGS lines are given for each system type; chose the first one for
+# debug, the second one for a fast binary.
+
+# for a generic compiler called cc
+#CC = cc
+#FLAGS = -DAUDIT
+#FLAGS = -O -DMEM_CHECK
+
+# for Minix-86
+#CC = cc
+#LDFLAGS = -i
+#FLAGS = -m -DAUDIT
+#FLAGS = -O -m -DMEM_CHECK
+
+# for gcc
+CC = gcc
+FLAGS = -O3 -W -Wall -ansi
+#FLAGS = -g -W -Wall -ansi -DAUDIT -DMEM_DEBUG
+#FLAGS = -O3 -mcpu=pentiumpro -fomit-frame-pointer -W -Wall -ansi -DMEM_CHECK
+#FLAGS = -O -pg -W -Wall -ansi -DMEM_CHECK
+#LDFLAGS = -pg
+
+# for the Compaq C compiler on Alpha/Linux
+#CC = ccc
+#FLAGS = -w0 -g -DAUDIT
+#FLAGS = -w0 -fast -DMEM_CHECK
+
+# for the Sun Workshop C Compiler
+#CC = cc
+#FLAGS = -g -Xa -DAUDIT
+#FLAGS = -Xa -fast -DMEM_CHECK
+
+# flags for the link step
+LIBS =
+#LIBS = libefence.a
+#LIBS = -lgc_dbg
+
+STAND_ALONE = -DSTAND_ALONE
+
+ifdef STAND_ALONE
+       CSRC = mem.c nhash.c cpp.c lexer.c assert.c macro.c eval.c
+       FINAL_STEP = $(CC) $(LDFLAGS) -DUCPP_CONFIG $(STAND_ALONE) -o ucpp $(CSRC) $(LIBS)
+endif
+
+# ----- nothing should be changed below this line -----
+
+COBJ = mem.o nhash.o cpp.o lexer.o assert.o macro.o eval.o
+CFLAGS = $(FLAGS)
+
+all: ucpp
+       @ar cq libucpp.a *.o
+
+clean:
+       @rm -f *.o ucpp core *.a
+
+ucpp: $(COBJ)
+       @$(FINAL_STEP)
+
+assert.o: tune.h ucppi.h cpp.h nhash.h mem.h
+       @$(CC) $(CFLAGS) -c assert.c
+cpp.o: tune.h ucppi.h cpp.h nhash.h mem.h
+       @$(CC) $(CFLAGS) -c cpp.c
+eval.o: tune.h ucppi.h cpp.h nhash.h mem.h arith.c arith.h
+       @$(CC) $(CFLAGS) -c eval.c
+lexer.o: tune.h ucppi.h cpp.h nhash.h mem.h
+       @$(CC) $(CFLAGS) -c lexer.c
+macro.o: tune.h ucppi.h cpp.h nhash.h mem.h
+       @$(CC) $(CFLAGS) -c macro.c
+mem.o: mem.h
+       @$(CC) $(CFLAGS) -c mem.c
+nhash.o: nhash.h mem.h
+       @$(CC) $(CFLAGS) -c nhash.c
diff --git a/libexec/auxcpp/README b/libexec/auxcpp/README
new file mode 100644 (file)
index 0000000..f47ba9f
--- /dev/null
@@ -0,0 +1,877 @@
+ucpp-1.3 is a C preprocessor compliant to ISO-C99.
+
+Author: Thomas Pornin <pornin@bolet.org>
+Main site: http://pornin.nerim.net/ucpp/
+
+
+
+INTRODUCTION
+------------
+
+A C preprocessor is a part of a C compiler responsible for macro
+replacement, conditional compilation and inclusion of header files.
+It is often found as a stand-alone program on Unix systems.
+
+ucpp is such a preprocessor; it is designed to be quick and light,
+but anyway fully compliant to the ISO standard 9899:1999, also known
+as C99. ucpp can be compiled as a stand-alone program, or linked to
+some other code; in the latter case, ucpp will output tokens, one
+at a time, on demand, as an integrated lexer.
+
+ucpp operates in two modes:
+-- lexer mode: ucpp is linked to some other code and outputs a stream of
+tokens (each call to the lex() function will yield one token)
+-- non-lexer mode: ucpp preprocesses text and outputs the resulting text
+to a file descriptor; if linked to some other code, the cpp() function
+must be called repeatedly, otherwise ucpp is a stand-alone binary.
+
+
+
+INSTALLATION
+------------
+
+1. Uncompress the archive file and extract the source files.
+
+2. Edit tune.h. Here is a short explanation of compile-time options:
+
+  LOW_MEM
+     Enable memory-saving functions; this is for low-end and old systems,
+     but seems to be good for larger systems too. Keep it.
+  NO_LIBC_BUF
+  NO_UCPP_BUF
+     Two options used to disable the two bufferings inside ucpp. Define
+     both options for maximum memory savings but you will probably want
+     to keep libc buffering for decent performance. Define none on large
+     systems (modern 32 or 64-bit systems).
+  UCPP_MMAP
+     With this option, if ucpp internal buffering is active, ucpp will
+     try to mmap() the input files. This might yield a slight performance
+     improvement, but will work only on a limited set of architectures.
+  PRAGMA_TOKENIZE
+     Make ucpp generate tokenized PRAGMA tokens on #pragma and _Pragma();
+     tokenization is made this way: tokens are assembled as a null
+     terminated array of unsigned chars; if a token has a string value
+     (as defined by the STRING_TOKEN macro), the value follows the token,
+     terminated by PRAGMA_TOKEN_END (by default, a newline character cast
+     to unsigned char). Whitespace tokens are skipped. The "name" value
+     of the PRAGMA token is a pointer to that array. This setting is
+     irrelevant in non-lexer mode.
+  PRAGMA_DUMP
+     In non-lexer mode, keep #pragma in output; non-void _Pragma() are
+     translated to the equivalent #pragma. Irrelevant in lexer mode.
+  NO_PRAGMA_IN_DIRECTIVE
+     Do not evaluate _Pragma() inside #if, #include, #include_next and #line
+     directives; instead, emit an error (since the remaining _Pragma will
+     surely imply a syntax error).
+  DSHARP_TOKEN_MERGE
+     When two tokens are to be merged with the `##' operator, but fail
+     because they do not merge into a single valid token, ucpp keeps those
+     two tokens separate by adding an extra space between them in text
+     output. With this option on, that extra space is not added, which means
+     that some tokens may merge partially if the text output is preprocessed
+     again. See tune.h for details.
+  INMACRO_FLAG
+     In lexer mode, set the inmacro flag to 1 if the current token comes
+     from a macro replacement, 0 otherwise. macro_count maintains an
+     increasing counter of such replacements. CONTEXT tokens count as
+     one macro replacement each. #pragma, and _Pragma() that do not come
+     from a macro replacement, also count as one macro replacement each.
+     This setting is irrelevant in non-lexer mode.
+  STD_INCLUDE_PATH
+     Default include path in stand-alone ucpp.
+  STD_MACROS
+     Default predefined macros in stand-alone ucpp.
+  STD_ASSERT
+     Default assertions in stand-alone ucpp.
+  NATIVE_SIGNED
+  NATIVE_UNSIGNED
+  NATIVE_UNSIGNED_BITS
+  NATIVE_SIGNED_MIN
+  NATIVE_SIGNED_MAX
+  SIMUL_ARITH_SUBTYPE
+  SIMUL_SUBTYPE_BITS
+  SIMUL_NUMBITS
+  WCHAR_SIGNEDNESS
+     Those options define how #if expressions are evaluated; see the
+     cross-compilation section of this file for more info, and the
+     comments in tune.h. Extra info is found in arith.h and arith.c,
+     at the possible expense of your mental health.
+  DEFAULT_LEXER_FLAGS
+  DEFAULT_CPP_FLAGS
+     Default flags in respectively lexer and non-lexer modes.
+  POSIX_JMP
+     Define this if your architecture defines sigsetjmp() and
+     siglongjmp(); it is known to (very slightly) improve performance
+     on AIX systems.
+  MAX_CHAR_VAL
+     ucpp will consider characters whose value is equal or above
+     MAX_CHAR_VAL as outside the C source charset (so they will be
+     treated just like '@', for instance). For ASCII systems, 128
+     is fine. 256 is a safer value, but uses more (static) memory.
+     For performance reasons, use a power of two. If MAX_CHAR_VAL is
+     correctly adjusted, ucpp should be compatible with any character
+     set.
+  UNBREAKABLE_SPACE
+     If you want an extra-whitespace character, define this macro to that
+     character. For instance, define this to 160 on an ISO-8859-1 system
+     if you want the 'unbreakable space' to be considered as whitespace.
+  SEMPER_FIDELIS
+     With this option set, ucpp, when used as a lexer, will pass
+     whitespace tokens to its caller, and those tokens will have their
+     true content; this is intended for reconstruction of the source
+     line. Beware that some comments may have embedded newlines.
+  COPY_LINE_LENGTH
+     ucpp can maintain a copy of the current source line, up to that
+     length. Irrelevant to stand-alone version.
+  *_MEMG
+     Those settings modify ucpp behaviour, wrt memory allocations. With
+     higher values, ucpp will perform less malloc() calls and will run
+     faster, but it will use more memory. Reduce INPUT_BUF_MEMG and
+     OUTPUT_BUF_MEMG on low-memory systems, if you kept ucpp buffering
+     (see NO_UCPP_BUF option).
+
+3. Edit the Makefile. You should define the variables CC and FLAGS;
+   there are the following options:
+
+  -DAUDIT
+     Enable internal sanity checks; this slows down a bit ucpp. Do not
+     define unless you plan to debug ucpp.
+  -DMEM_CHECK
+     With this setting, ucpp will check for the return value of malloc()
+     and exit with a diagnostic when out of memory. MEM_CHECK is implied
+     by AUDIT.
+  -DMEM_DEBUG
+     Enable memory debug code. This will track memory leaks and several
+     occurrences of memory management errors; it will also slow down
+     things and increase memory consumption, so you probably do not
+     want to use this option.
+  -DINLINE=foobar
+     The ucpp code uses "inline" qualifier for some functions; by
+     default, that qualifier is macro-replaced with nothing. Define
+     INLINE to the correct replacement for your compiler, if supported.
+     Note that all "inline" functions in ucpp are also "static". For any
+     C99-compliant compiler, the GNU compiler (gcc), and the Compaq C
+     compiler under Linux/Alpha, no -DINLINE is needed (see tune.h for
+     details).
+
+4. Compile by typing "make". This should produce the ucpp executable
+   file. You might see some warning messages, especially with gcc:
+   gcc believes some variables might be used prior to their
+   initialization; ignore those messages.
+
+5. Install wherever you want the binary and the man page ucpp.1. I
+   have not provided an install sequence because I didn't bother.
+
+6. If you do not have the make utility, compile each file separately
+   and link them together. The exact details depend on your compiler.
+   You must define the macro STAND_ALONE when compiling cpp.c (there
+   is such a definition, commented out, in cpp.c, line 34).
+
+There is no "configure" script because:
+-- I do not like the very idea of a "configure" script.
+-- ucpp is written in ANSI-C and should be fairly portable.
+-- There is no such thing as "standard" settings for a C preprocessor.
+   The predefined system macros, standard assertions,... must be tuned
+   by the sysadmin.
+-- The primary goal of ucpp is to be included in compilers. The
+   stand-alone version is mainly a debugging tool.
+
+Please note that you need an ISO-C90 (formerly ANSI) C compiler suite
+(including the standard library) to compile ucpp. If your compiler is
+not C99 (or later), read the cross-compilation section in this README
+file.
+
+The C90 and C99 standards state that external linkage names might be
+considered equal or different based upon only their first 6 characters;
+this rule might make ucpp not compile on a conformant C implementation.
+I have yet to see such an implementation, however.
+
+If you want to use ucpp as an integrated preprocessor and lexer, see the
+section REUSE. Compiling ucpp as a library is an exercise left to the
+reader.
+
+With the LOW_MEM code enabled, ucpp can run on a Minix-i86 or Msdos
+16-bit small-memory-model machine. It will not be fully compliant
+on such an architecture to C99, since C99 states that at least one
+source code with 4095 simultaneously defined macros must be processed;
+ucpp will be limited to about 1500 macros (at most) due to memory
+restrictions. At least ucpp can preprocess its own code in these
+conditions. LOW_MEM is on by default because it seems to improve
+performance on large systems.
+
+
+
+LICENSE
+-------
+
+The copyright notice and license is at the beginning of the Makefile and
+each source file. It is basically a BSD license, without the advertising
+subclause (which BSD dropped recently anyway) and with no reference to
+Berkeley (since the code is all mine, written from scratch). Informally,
+this means that you can reuse and redistribute the code as you want,
+provided that you state in the documentation (or any substantial part of
+the software) of redistributed code that I am the original author. (If
+you press a cdrom with 200 software packages, I do not insist on having
+my name on the cover of the cdrom -- just keep a Readme file somewhere
+on the cdrom, with the copyright notice included.)
+
+As a courteous gesture, if you reuse my code, please drop me a mail.
+It raises my self-esteem.
+
+
+
+REUSE
+-----
+
+The code has been thought as part of a bigger project; it might be
+used as an integrated lexer, that will read files, process them as a
+C preprocessor, and output a stream of C tokens. To include this code
+into a project, compile with STAND_ALONE undefined.
+
+To use the preprocessor and lexer, several steps should be performed.
+See the file 'sample.c' for an example.
+
+1. call init_cpp(). This function initializes the lexer automaton.
+
+2. set the following global variables:
+       no_special_macros
+               non-zero if the special macros (__FILE__ and others)
+               should not be defined. This is a global flag since
+               it affects the redefinition of such macros (which are
+               allowed if the special macros are not defined)
+       c99_compliant
+               if non-zero, define __STDC_VERSION__ to 199901L; this
+               is the default; otherwise, do not define __STDC_VERSION__.
+               Note that ucpp will accept to undefine __STDC_VERSION__
+               with a #undef directive.
+       c99_hosted
+               if strictly positive, define __STDC_HOSTED__ to 1.
+               If zero, define __STDC_HOSTED__ to 0. If negative,
+               do not define __STDC_HOSTED__. The default is 1.
+       emit_defines and emit_assertions should be set to 0 for
+       the step 3.
+
+3. call init_tables(). This function initializes the macro table
+   and other things; it will intialize assertions if it has a non-zero
+   argument.
+
+4. call init_include_path(). This function will reset the include
+   path to the list of paths given as argument.
+
+5. set the following global variables
+       emit_dependencies
+               set to 1 if dependencies should be emitted during
+               preprocessing
+               set to 2 if dependencies should also be emitted for
+               system include files
+       emit_defines
+               set to non-zero if #define macro definitions should be
+               emitted when macros are defined
+       emit_assertions
+               set to non-zero if #define macro definitions should be
+               emitted when macros are defined
+       emit_output
+               the FILE * where the above items are sent if one of the
+               three emit_ variables is set to non zero
+       transient_characters
+               this is for some cross-compilation; see the relevant
+               part in this README file for details
+
+6. call set_init_filename() with the initial filename as argument;
+   the second argument indicates whether the filename is real or
+   conventional ("real" means "an fopen() on it will work").
+
+7. initialize your struct lexer_state:
+       call init_lexer_state()
+       call init_lexer_mode() if the preprocessor is supposed to
+          output a list of tokens, otherwise set the flags field
+          to DEFAULT_CPP_FLAGS and set the output field to the
+          FILE * where output should be sent
+       (init_lexer_mode(), if called at all, must be called after
+        init_lexer_state())
+       adjust the flags field; here is the meaning of flags:
+
+WARN_STANDARD
+       emit the standard warnings
+WARN_ANNOYING
+       emit the useless and annoying warnings
+WARN_TRIGRAPHS
+       count trigraphs encountered; it is up to the caller to emit
+       a warning if some trigraphs were indeed encountered; the count
+       is stored in the count_trigraphs field of the struct lexer_state
+WARN_TRIGRAPHS_MORE
+       emit a warning for each trigraph encountered
+WARN_PRAGMA
+       emit a warning for each non-void _Pragma encountered in non-lexer
+       mode (because these are dumped as #pragma in the output) and for each
+       #pragma too, if ucpp was compiled without PRAGMA_DUMP
+FAIL_SHARP
+       emit errors on '#' tokens beginning a line and not followed
+       by a valid cpp directive
+CCHARSET
+       emit errors when non-C characters are encountered; if this flag
+       is not set, each non-C character will be considered as a BUNCH
+       token (since C99 states that non-C characters are allowed as
+       long as they "disappear" during preprocessing [through macro
+       replacement and stringification for instance], this flag must
+       not be set, for maximum C99 compliance)
+DISCARD_COMMENTS
+       do not keep comments in output (irrelevant in lexer mode)
+CPLUSPLUS_COMMENTS
+       understand new style comments (//) (mandatory for C99)
+LINE_NUM
+       emit #line directives when entering a file, if not in lexer mode;
+       emit CONTEXT token in lexer mode for #line and new files
+GCC_LINE_NUM
+       if LINE_NUM is set, emit gcc-like directives instead of #line
+HANDLE_ASSERTIONS
+       understand assertions in #if expressions (and #assert, #unassert)
+HANDLE_PRAGMA
+       make PRAGMA tokens for #pragma; irrelevant in non-lexer mode
+       (handling of some pragmas is required in C99 but is not of
+       the competence of the preprocessor; without this flag, ucpp will
+       ignore the contents of #pragma and _Pragma directives)
+MACRO_VAARG
+       understand macros with a variable number of arguments (mandatory
+       for C99)
+UTF8_SOURCE
+       understand UTF-8 encoding: multibyte characters are considered
+       equivalent to letters as far as syntax is concerned (they can
+       be used in identifiers)
+LEXER
+       act as a lexer, outputting tokens
+TEXT_OUTPUT
+       this flag should be set to 0 if ucpp works as a lexer, 1 otherwise.
+       It is somehow redundant with the LEXER flag, but the presence of
+       those two different flags is needed in ucpp.
+KEEP_OUTPUT
+       in non-lexer mode, emit the result of preprocessing
+COPY_LINE
+       maintain a copy of the last read line in the copy_line field of
+       the struct lexer_state ; see below for how to use this buffer
+HANDLE_TRIGRAPHS
+       understand trigraphs, such as ??/ for \. This option should be
+       set by default, except for some legacy code.
+
+       There are other flags, but they are for private usage of ucpp.
+
+8. adjust the input field in the lexer_state to the FILE * from where
+   source file is read. If you use the UCPP_MMAP compile-time option,
+   and your input file is eligible to mmap(), then you can call
+   fopen_mmap_file() to open it, then set_input_file() to set ls->input
+   and some other internal options. Do not call set_input_file() unless
+   you just called fopen_mmap_file() just before on the same file.
+
+9. call add_incpath() to add an include path, define_macro() and
+   undef_macro() to add or remove macros, make_assertion() and
+   destroy_assertion() to add or remove assertions.
+
+10. call enter_file() (this is needed only in non-lexer mode, or if
+    LINE_NUM is set).
+
+
+Afterwards:
+
+-- if you are in lexer mode, call lex(); each call will make the ctok
+   field point to the next token. A non-zero return value is an error.
+   lex() skips whitespace tokens. The memory used by the string value
+   of some tokens (identifiers, numbers...) is automatically freed,
+   so copy the contents of each such token if you want to keep it
+   (tokens with a string content are identified by the STRING_TOKEN
+   macro applied to their type).
+   When lex() returned a non-zero value: if it is CPPERR_EOF, then
+   end-of-input was reached. Otherwise, it is a genuine error and
+   ls->ctok is an undefined token; skip it and call lex() again to
+   ignore the error.
+
+-- otherwise, call cpp(); each call will analyze one or more tokens
+   (one token if it did find neither a cpp directive nor a macro name).
+   A positive return value is an error.
+
+For both functions, if the return value is CPPERR_EOF (which is a
+strictly positive value), then it means that the end of file was
+reached. Call check_cpp_errors() after end of file for pending errors
+(unfinished #if constructions for instance). In non-lexer mode,
+call flush_output().
+
+In the struct lexer_state, the following fields might be read:
+       line               the current input line number
+       oline              the current output line number (in non-lexer mode)
+       flags              the flags described above
+       count_trigraphs    the number of trigraphs encountered
+       inmacro            the current token comes from a macro
+       macro_count        the current macro counter
+"flags" is an unsigned long and might be modified; the three others
+are of long type.
+
+
+To perform another preprocessing: use free_lexer_state() to release
+memory used by the buffers referenced in lexer_state, and go back to
+step 2. The different tables (macros, assertions...) should be reset to
+their respective initial contents.
+
+There is also the wipeout() function: when called, it should release
+(almost) all memory blocks allocated dynamically. After a wipeout(),
+ucpp should be back to its state at step 2 (init_cpp() initializes only
+static tables, that are never freed nor modified afterwards).
+
+
+The COPY_LINE buffer: the struct lexer_state contains two interesting
+fields, copy_line[] and cli. If the COPY_LINE flag is on, each read
+line is stored in this buffer, up to (at most) COPY_LINE_LENGTH - 1
+characters (COPY_LINE_LENGTH is defined in tune.h). The last character
+of the buffer is always a zero, and if the line was read entirely, it is
+zero terminated; the trailing newline is not included.
+
+The purpose of this buffer is error-reporting. When an error occurs
+(cpp() returns a strictly positive value, or lex() returns a non-zero
+value), if your struct lexer_state is called ls, use this code:
+
+       if (ls.cli != 0) ls.copy_line[ls.cli] = 0;
+
+This will add a trailing 0 if the line was not read entirely.
+
+
+ucpp may be configured at runtime to accept alternate characters as
+possible parts of identifiers. Typical intended usage is for the '$'
+and '@' characters. The two relevant functions are set_identifier_char()
+and unset_identifier_char(). When this call is issued:
+       set_identifier_char('$');
+then for all the remaining input, the '$' character will be considered
+as just another letter, as far as identifier tokenizing is concerned. This
+is for identifiers only; numeric constants are not modified by that setting.
+This call resets things back:
+       unset_identifier_char('$');
+Those two functions modify the static table which is initialized by
+init_cpp(). You may call init_cpp() at any time to restore the table
+to its standard state.
+
+When using this feature, take care of the following points:
+
+-- Do NOT use a character whose numeric value (as an `unsigned char'
+cast into an `int') is greater than or equal to MAX_CHAR_VAL (in tune.h).
+This would lead to unpredictable results, including an abrupt crash of
+ucpp. ucpp makes absolutely no check whatsoever on that matter: this is
+the programmer's responsibility.
+
+-- If you use a standard character such as '+' or '{', tokens which
+begin with those characters cease to exist. This can be troublesome.
+If you use set_identifier_char() on the '<' character, the handling of
+#include directives will be greatly disturbed. Therefore the use of any
+standard C character in set_identifier_char() of unset_identifier_char()
+is declared unsupported, forbidden and altogether unwise.
+
+-- Stricto sensu, when an extra character is declared as part of an
+identifier, ucpp behaviour cease to conform to C99, which mandates that
+characters such as '$' or '@' must be treated as independant tokens of
+their own. Therefore, if your purpose is to use ucpp in a conformant
+C implementation, the use of set_identifier_char() should be made at
+least a runtime option.
+
+-- When enabling a new character in the middle of a macro replacement,
+the effect of that replacement may be delayed up to the end of that
+macro (but this is a "may" !). If you wish to trigger this feature with
+a custom #pragma or _Pragma(), you should remember it (for instance,
+usine _Pragma() in a macro replacement, and then the extra character
+in the same macro replacement, is not reliable).
+
+
+
+COMPATIBILITY NOTES
+-------------------
+
+The C language has a lengthening history. Nowadays, C comes in three
+flavours:
+
+-- Traditional C, aka "K&R". This is the language first described by
+Brian Kernighan and Dennis Ritchie, and implemented in the first C
+compiler that was ever coded. There are actually several dialects of
+K&R, and all of them are considered deprecated.
+
+-- ISO 9899:1990, aka C90, aka C89, aka ANSI-C. Formalized by ANSI
+in 1989 and adopted by ISO the next year, it is the C flavour many C
+compilers understand. It is mostly backward compatible with K&R C, but
+with enhancements, clarifications and several new features.
+
+-- ISO 9899:1999, aka C99. This is an evolution on C90, almost fully
+backward compatible with C90. C99 introduces many new and useful
+features, however, including in the preprocessor.
+
+There was also a normative addendum in 1995, that added a few features
+to C90 (for instance, digraphs) that are also present in C99. It is
+sometimes refered to as "C95" or "AMD 1".
+
+
+ucpp implements the C99 standard, but can be used in a stricter mode,
+to enforce C90 compatibility (it will, however, still recognize some
+constructions that are not in plain C90).
+
+ucpp also knows about several extensions to C99:
+
+-- Assertions: this is an extension to the defined() operator, with
+   its own namespace. Assertions seem to be used in several places,
+   therefore ucpp knows about them. It is recommended to enable
+   assertions by default on Solaris systems.
+-- Unicode: the C99 norm specifies that extended characters, from
+   the ISO-10646 charset (aka "unicode") can be used in identifiers
+   with the notations \u and \U. ucpp also accepts (with the proper
+   flag) the UTF-8 encoding in the source file for such characters.
+-- #include_next directive: it works as a #include, but will look
+   for files only in the directories specified in the include path
+   after the one the current file was found. This is a GNU-ism that
+   is useful for writing transparent wrappers around header files.
+
+Assertions and unicode are activated by specific flags; the #include_next
+support is always active.
+
+The ucpp code itself should be compatible with any ISO-C90 compiler.
+The cpp.c file is rather big (~ 64kB), it might confuse old 16-bit C
+compilers; the macro.c file is somewhat large also (~ 47kB).
+
+The evaluation of #if expressions is subject to some subtleties, see the
+section "cross-compilation".
+
+The lexer code makes no assumption about the source character set, but
+the following: source characters (those which have a syntactic value in
+C; comment and string literal contents are not concerned) must have a
+strictly positive value that is strictly lower than MAX_CHAR_VAL. The
+strict positivity is already assured by the C standard, so you just need
+to adjust MAX_CHAR_VAL.
+
+ucpp has been tested succesfully on ASCII/ISO-8859-1 and EBCDIC systems.
+Beware that UTF-8 is NOT compatible with EBCDIC.
+
+Pragma handling: when used in non-lexer mode, ucpp tries to output a
+source text that, when read again, will yield the exact same stream of
+tokens. This is not completely true with regards to line numbering in
+some tricky macro replacements, but it should work correctly otherwise,
+especially with pragma directives if the compile-time option PRAGMA_DUMP
+was set: #pragma are dumped, non-void _Pragma() are converted to the
+corresponding #pragma and dumped also.
+
+ucpp does not macro-replace the contents of #pragma and _Pragma();
+If you want a macro-replaced pragma, use this:
+
+#define pragma_(x)     _Pragma(#x)
+#define pragma(x)      pragma_(x)
+
+Anyway, pragmas do not nest (an _Pragma() cannot be evaluated if it is
+inside a #pragma or another _Pragma).
+
+
+I wrote ucpp according to what is found in "The C Programming Language"
+from Brian Kernighan and Dennis Ritchie (2nd edition) and the C99
+standard; but I could have misinterpreted some points. On some tricky
+points I got help from the helpful people from the comp.std.c newsgroup.
+For assertions and #include_next, I mimicked the behaviour of GNU cpp,
+as is stated in the GNU cpp info documentation. An open question is
+related to the following code:
+
+#define undefined      !
+#define makeun(x)      un ## x
+#if makeun(defined foo)
+qux
+#else
+bar
+#endif
+
+ucpp will replace 'defined foo' with 0 first (since foo is not defined),
+then it will replace the macro makeun, and the expression will become
+'un0', which is replaced by 0 since this is a remaining identifier. The
+expression evaluates to false, and 'bar' is emitted.
+However, some other preprocessors will replace makeun first, considering
+that it is not part of a 'defined' operator application; this will
+produce the macro 'undefined', which is replaced, and the expression
+becomes '!foo'. 'foo' is replaced by 0, the expression evaluates to
+true, and 'qux' is emitted.
+
+My opinion is that the behaviour is undefined, because use of the
+'defined' operator does not match an allowed form prior to macro
+replacement (I mean, its syntax matches, but its use is reconverted
+to inexistant and therefore is not anymore matching). Other people
+think that the behaviour is well-specified, and contrary to what ucpp
+does. The only thing clear to me is that the wording of the standard
+(paragraph 6.10.1.3) is unclear.
+
+Since the ucpp behaviour makes ucpp code simpler and cleaner, and
+that it is unlikely that any real-life code would ever be disturbed
+by that interpretation of the standard, ucpp will keep its current
+behaviour until convincing evidence of my misinterpretation of the
+standard is given to me. The problem can only occur if one uses ## to
+make a 'defined' operator disappear from a #if expression (everybody
+agrees that the generation of a 'defined' operator triggers undefined
+behaviour).
+
+
+Another point about macro replacement has been discussed at length in
+several occasions. It is about the following code:
+
+#define CAT(a, b)    CAT_(a, b)
+#define CAT_(a, b)   a ## b
+#define AB(x, y)     CAT(x, y)
+CAT(A, B)(X, Y)
+
+ucpp will produce `CAT(X,Y)' as replacement for the last line, whereas
+some other preprocessors output `XY'. The answer to the question
+"which behaviour is correct" seems to be "this is not defined by the
+C standard". It is the answer that has been actually given by the C
+standardization committee in 1992, to the defect report #017, question
+23, which asked that very same question. Since the wording of the
+standard has not changed in these parts from the 1990 to the 1999
+version, the preprocessor behaviour on the above-stated code should
+still be considered as undefined.
+
+It seems, however, that there used to be a time (around 1988) when the
+committee members agreed upon a precise macro-replacement algorithm,
+which specified quite clearly the preprocessor behaviour in such
+situation. ucpp behaviour is occasionnaly claimed as "incorrect" with
+regards to that algorithm. Since that macro replacement algorithm has
+never been published, and the committee itself backed out from it in
+1992, I decided to disregard those feeble claims.
+
+It is possible, however, that at some point in the future I rewrite the
+ucpp macro replacement code, since that code is a bit messy and might be
+made to use less memory in some occasions. It is then possible that, in
+the aftermath of such a rewrite, the ucpp behaviour for the above stated
+code become tunable. Don't hold your breath, though.
+
+
+About _Pragma: the standard is not clear about when this operator is
+evaluated, and if it is allowed inside #if directives and such. For
+ucpp, I coded _Pragma as a special macro with lazy replacement: it will
+be evaluated wherever a macro could be replaced, and only at the end of
+the macro replacement (for practical purposes, _Pragma can be considered
+as a macro taking one argument, and being replaced by nothing, except
+for some tricky uses of the # and ## operators). This means that, by
+default, ucpp will evaluate _Pragma inside some directives (mainly, #if,
+#include, #include_next and #line), but it can be taught not to do so by
+defining NO_PRAGMA_IN_DIRECTIVE in tune.h.
+
+
+
+CROSS-COMPILATION
+-----------------
+
+If compiled with a C99 development suite, ucpp should be fully
+C99-compliant on the host platform (up to my own understanding of the
+standard -- remember that this software is distributed as-is, without
+any guarantee). However, if a pre-C99 compiler is used, or if the
+target machine is not the host machine (for instance when you build a
+cross-compiler), the evaluation of #if expressions is subject to some
+cross-compiling issues:
+
+
+-- character constants: when evaluating expressions, character constants
+are interpreted in the source character set context; this is allowed
+by the standard but this can lead to problems with code that expects
+this interpretation to match the one made in the C code. To ease
+cross-compilation, you can define a conversion array, and make the
+global variable transient_characters point to it. The array should
+contain 256 int; transient_characters[x] is the value of the character
+whose value is x in the source character set.
+
+This facility is provided for inclusion of ucpp inside another code;
+if you want a stand-alone ucpp with that conversion, hard-code the
+conversion table into eval.c and make transient_characters[] statically
+point to it. Alternatively, you could provide an option syntax to
+provide such a table on command-line, if you feel like it.
+
+
+-- wide character constants signedness: by default, ucpp makes wide
+characters as signed as what plain chars are on the build host. To
+force wide character constant signedness, define WCHAR_SIGNEDNESS to 0
+(for unsigned) or 1 (for signed). Beware, however, that "native" wide
+character constants, even signed, are considered positive. Non-wide
+character constants are, according to the C99 standard, of type int, and
+therefore always signed.
+
+
+-- evaluation type: C90 states that all constants in #if expressions
+are considered as either long or unsigned long, and that the evaluation
+is performed with operands of that size. In C99, the situation is
+equivalent, except that the types used are intmax_t and uintmax_t, as
+defined in <stdint.h>.
+
+ucpp can use two expression evaluators: one uses native integer types
+(one signed and one unsigned), the other evaluator emulates big integer
+numbers by representing them with two values of some unsigned type. The
+emulated type handles signed values in two's complement representation,
+and can be any width ranging from 2 bits to twice the size of the
+underlying native unsigned type used. An odd width is allowed. When
+right shifting an emulated signed negative value, it is left-padded with
+bits set to 1 (this is sign extension).
+
+When the ARITHMETIC_CHECKS macro is defined in tune.h, all occurrences
+of implementation-defined or undefined behaviour during arithmetic
+evaluation are reported as errors or warned upon. This includes all
+overflows and underflows on signed quantities, constants too large,
+and so on. Errors (which terminate immediately evaluation) are emitted
+for division by 0 (on / and % operators) and overflow (on / operator);
+otherwise, warnings are emitted and the faulty evaluation takes place.
+This prevents ucpp from crashing on typical x86 machines, while still
+allowing to use some extensions.
+
+
+
+FUTURE EVOLUTIONS
+-----------------
+
+ucpp is quite complete now. There was a longstanding project of
+"traditional" preprocessing, but I dropped it because it would not
+map cleanly on the token-based ucpp structure. Maybe I will code a
+string-based preprocessor one day; it would certainly use some of the
+code from lexer.c, eval.c, mem.c and nhash.c. However, making such a
+tool is almost irrelevant nowadays. If one wants to handle such project,
+using ucpp as code base, I would happily provide some help, if needed.
+
+
+
+CHANGES
+-------
+
+From 1.2 to 1.3:
+
+* brand new integer evaluation code, with precise evaluation and checks
+* new hash table implementation, with binary trees
+* relaxed attitude on failed `##' operators
+* bugfix on macro definition on command-line wrt nesting macros
+* support for up to 32766 macro arguments in LOW_MEM code
+* support for optional additional "identifier" characters such as '$' or '@'
+* bugfix: memory leak on void #assert
+
+From 1.1 to 1.2:
+
+* bugfix: numerous memory leaks
+* new function: wipeout(); this should release all malloc() blocks
+* bugfix: missing "newline" and trailing "context" tokens
+* improved included files name caching
+* included memory leak detection code
+
+From 1.0 to 1.1:
+
+* bugfix: missing newline when exiting from a non-newline-terminated file
+* bugfix: crash when resetting due to definition of the _Pragma pseudo-macro
+* bugfix: handling of additional "optional" whitespace with SEMPER_FIDELIS
+* improved handling of unreplaced arg macros wrt output line
+* tricky handling of utterly tricky #include
+* bugfix: spurious token `~=' eliminated
+
+From 0.9 to 1.0:
+
+* bugfix: crash after erroneous #assert
+* changed ERR_SHARP to FAIL_SHARP, EMUL_UINTMAX to SIMUL_UINTMAX
+* made "inline" default on gcc and DEC ccc (Linux/Alpha)
+* semantic of -I is now Unix-like (added directories are looked first)
+* added -J flag (to add include directories after the system ones)
+* cleaned up non-ascii issues
+* bugfix: missing brace in no-LOW_MEM code
+* bugfix: argument number check in variadic macros
+* bugfix: crash in non-lexer mode after some cases of unreplaced macro
+* bugfix: _Pragma() handling wrt # and ##
+* made evaluation of _Pragma() optional in #if, #include and #line
+* bugfix: re-dump of multiline #pragma
+* added the inmacro and macro_count flags
+* added mmap() support
+* added option to retain whitespace content in lexer mode
+
+From 0.8 to 0.9:
+
+* added check for division by 0 in #if evaluation
+* added check for non-standard line numbers
+* added check for trailing garbage in most directives
+* corrected signedness of char constants (always int, therefore always signed)
+* made LOW_MEM code, so that ucpp runs smoothly on low memory architectures
+* multiple bugfixes (using the GNU cpp testsuite)
+* added handling of _Pragma (as a macro)
+* added tokenization of pragma directives
+* added conservation of pragma directives in text output
+* produced Msdos 16-bit small memory model executable
+* produced Minix-86 executable
+
+From 0.7 to 0.8:
+
+* added some support for Amiga systems
+* fixed extra spacing in stringified tokens
+* fixed bug related to %:% and tolerated rogue sharps
+* namespace cleanup
+* bugfix for macro redefinition
+* added warning for evaluated comma operators in #if (ISO requirement)
+* -Dfoo now defines foo with content 1 (and not void content)
+* trigraphs can be disabled (for incorrect but legacy code)
+* fixed semantics for #include "file" (local directory)
+* fixed detection of protected files
+* produced a Msdos 16-bit executable
+
+From 0.6 to 0.7:
+
+* officially changed the goal to full C99 compliance
+* added the CONTEXT token and let NEWLINE tokens go
+* added report_context() for error reporting
+* enforced matching of #if/#endif (file-global nesting level = 0)
+* added support of C99 digraphs
+* added UTF-8 encoding support
+* added universal character names
+* rewrote #if expressions (sizes fixed, bignum, signed/unsigned fixed)
+* fixed incomplete evaluation of #if expressions
+* added transient_characters[]
+
+From 0.5 to 0.6:
+
+* disappearance of error_nonl()
+* added extra optional warnings for trigraphs
+* some bugfixes, especially in lexer mode
+* handled MacIntosh files correctly
+
+From 0.4 to 0.5:
+
+* nicer #pragma handling (a token can be emitted)
+* bugfix in lexer mode after #line and #error
+* sample.c   an example of code linked with ucpp
+* made #if expressions conforming to standard signed/unsigned handling
+* added the copy_line[] buffer feature
+
+From 0.3 to 0.4:
+
+* relaxed interpretation of '#include foo' when foo ends up, after macro
+  substitution, with a '<bar>' content
+* corrected the 'double-dot' bug
+* corrected two bugs related to the treatment of macro aborted calls (due
+  to lack of arguments)
+* some namespaces cleanup, to ease integration into other code
+* documented the way to include ucpp into another program
+* made newlines embedded into strings illegal (and reported as such)
+
+From 0.2 to 0.3:
+
+* added support for system predefined macros
+* made several bugfixes
+* checked C99 compliance for most of the features
+* ucpp now accepts non-C characters on standard when used stand-alone
+* removed many useless spaces in the output
+
+From 0.1 to 0.2:
+
+* added support for assertions
+* added support for macros with variable arguments
+* split the pharaonic cpp.c file into many
+* made several bugfixes
+* relaxed the behaviour with regards to the void arguments
+* made C++-like comments an option
+
+
+
+THANKS TO
+---------
+
+Volker Barthelmann, Neil Booth, Stephen Davies, Stéphane Ecolivet,
+Marc Espie, Marcus Holland-Moritz, Antoine Leca, Cyrille Lefevre,
+Dave Rivers, Loic Tortay and Laurent Wacrenier, for suggestions and
+beta-testing.
+
+Paul Eggert, Douglas A. Gwyn, Clive D.W. Feather, and the other guys from
+comp.std.c, for explanations about the standard.
+
+Dave Brolley, Jamie Lokier and Neil Booth, for discussion about tricky
+points on nesting macros.
+
+Brian Kernighan and Dennis Ritchie, for bringing C to mortal Men.
diff --git a/libexec/auxcpp/arith.c b/libexec/auxcpp/arith.c
new file mode 100644 (file)
index 0000000..bef2580
--- /dev/null
@@ -0,0 +1,1462 @@
+/*
+ * Integer arithmetic evaluation.
+ *
+ * (c) Thomas Pornin 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <limits.h>
+#include "arith.h"
+
+#define ARITH_OCTAL(x)   ((x) >= '0' && (x) <= '7')
+#define ARITH_OVAL(x)    ((x) - '0')
+#define ARITH_DECIM(x)   ((x) >= '0' && (x) <= '9')
+#define ARITH_DVAL(x)    ((x) - '0')
+#define ARITH_HEXAD(x)   (ARITH_DECIM(x) \
+                         || (x) == 'a' || (x) == 'A' \
+                         || (x) == 'b' || (x) == 'B' \
+                         || (x) == 'c' || (x) == 'C' \
+                         || (x) == 'd' || (x) == 'D' \
+                         || (x) == 'e' || (x) == 'E' \
+                         || (x) == 'f' || (x) == 'F')
+#define ARITH_HVAL(x)    (ARITH_DECIM(x) ? ARITH_DVAL(x) \
+                         : (x) == 'a' || (x) == 'A' ? 10 \
+                         : (x) == 'b' || (x) == 'B' ? 11 \
+                         : (x) == 'c' || (x) == 'C' ? 12 \
+                         : (x) == 'd' || (x) == 'D' ? 13 \
+                         : (x) == 'e' || (x) == 'E' ? 14 : 15)
+
+#ifdef NATIVE_SIGNED
+/* ====================================================================== */
+/* Arithmetics with native types */
+/* ====================================================================== */
+
+/*
+ * The following properties are imposed by the C standard:
+ *
+ * -- Arithmetics on the unsigned type should never overflow; every
+ * result is reduced modulo some power of 2. The macro NATIVE_UNSIGNED_BITS
+ * should have been defined to that specific exponent.
+ *
+ * -- The signed type should use either two's complement, one's complement
+ * or a sign bit and a magnitude. There should be an integer N such that
+ * the maximum signed value is (2^N)-1 and the minimum signed value is
+ * either -(2^N) or -((2^N)-1). -(2^N) is possible only for two's complement.
+ *
+ * -- The maximum signed value is at most equal to the maximum unsigned
+ * value.
+ *
+ * -- Trap representations can only be:
+ *    ** In two's complement, 1 as sign bit and 0 for all value bits.
+ *       This can happen only if the minimum signed value is -((2^N)-1).
+ *    ** In one's complement, all bits set to 1.
+ *    ** In mantissa + sign, sign bit to 1 and 0 for all value bits.
+ * Unsigned values have no trap representation achievable with numerical
+ * operators. Only signed values can have such representations, with
+ * operators &, |, ^, ~, << and >>. If trap representations are possible,
+ * such occurrences are reported as warnings.
+ *
+ * -- The operators +, -, * and << may overflow or underflow on signed
+ * quantities, which is potentially an error. A warning is emitted.
+ *
+ * -- The operator >> yields an implementation-defined result on
+ * signed negative quantities. Usually, the sign is extended, but this
+ * is not guaranteed. A warning is emitted.
+ *
+ * -- The operators / and % used with a second operand of 0 cannot work.
+ * An error is emitted when such a call is performed. Furthermore, in
+ * two's complemement representation, with NATIVE_SIGNED_MIN == -(2^N)
+ * for some N, the expression `NATIVE_SIGNED_MIN / (-1)' yields an
+ * unrepresentable result, which is also an error.
+ *
+ *
+ * For the value checks, we need to consider those different cases. So
+ * we calculate the following macros:
+ *   -- TWOS_COMPLEMENT: is 1 if representation is two's complement, 0
+ *      otherwise.
+ *   -- ONES_COMPLEMENT: is 1 if representation is one's complement, 0
+ *      otherwise.
+ *   -- SIGNED_IS_BIGGER: 1 if the maximum signed value is equal to the
+ *      maximum unsigned value, 0 otherwise. NATIVE_SIGNED_MAX cannot
+ *      exceed the maximum unsigned value. If SIGNED_IS_BIGGER is 0, then
+ *      the maximum unsigned value is strictly superior to twice the
+ *      value of NATIVE_SIGNED_MAX (e.g. 65535 to 32767).
+ *   -- TRAP_REPRESENTATION: 1 if a trap representation is possible, 0
+ *      otherwise. The only way trap representations are guaranteed
+ *      impossible is when TWOS_COMPLEMENT is set, and NATIVE_SIGNED_MIN
+ *      is equal to -NATIVE_SIGNED_MAX - 1.
+ *
+ * Those macros are calculated by some preprocessor directives. This
+ * supposes that the implementation conforms to C99. Rules on preprocessing
+ * were quite looser in C90, and it could be that an old compiler, used
+ * for a cross-compiling task, does not get those right. Therefore, if
+ * ARCH_DEFINED is defined prior to the inclusion of this file, those
+ * four macros are supposed to be already defined. Otherwise they are
+ * (re)defined. The macro ARCH_TRAP_DEFINED has the same meaning, but
+ * is limited to the TRAP_REPRESENTATION macro (if ARCH_TRAP_DEFINED is
+ * defined, the macro TRAP_REPRESENTATION is supposed to be already
+ * defined; the three other macros are recalculated).
+ *
+ *
+ * To sum up:
+ * -- Whenever a division operator (/ or %) is invoked and would yield
+ * an unrepresentable result, ARITH_ERROR() is invoked.
+ * -- With ARITHMETIC_CHECKS undefined, ARITH_WARNING() is never invoked.
+ * -- With ARITHMETIC_CHECKS defined:
+ *    ** If ARCH_DEFINED is defined, the including context must provide
+ *       the macros TWOS_COMPLEMENT, ONES_COMPLEMENT, SIGNED_IS_BIGGER
+ *       and TRAP_REPRESENTATION.
+ *    ** Otherwise, if ARCH_TRAP_DEFINED is defined, the including context
+ *       must provide the macro TRAP_REPRESENTATION.
+ *    The code then detects all operator invokations that would yield an
+ *    overflow, underflow, trap representation, or any implementation
+ *    defined result or undefined behaviour. The macro ARITH_WARNING() is
+ *    invoked for each detection.
+ * -- Trap representation detection code supposes that the operands are
+ * _not_ trap representation.
+ */
+
+#ifndef ARCH_DEFINED
+
+#undef TWOS_COMPLEMENT
+#undef ONES_COMPLEMENT
+#undef SIGNED_IS_BIGGER
+#ifndef ARCH_TRAP_DEFINED
+#undef TRAP_REPRESENTATION
+#endif
+
+#if (-1) & 3 == 3
+/*
+ * Two's complement.
+ */
+#define TWOS_COMPLEMENT         1
+#define ONES_COMPLEMENT         0
+#ifndef ARCH_TRAP_DEFINED
+#if NATIVE_SIGNED_MIN < -NATIVE_SIGNED_MAX
+#define TRAP_REPRESENTATION     0
+#else
+#define TRAP_REPRESENTATION     1
+#endif
+#endif
+
+#elif (-1) & 3 == 2
+/*
+ * One's complement.
+ */
+#define TWOS_COMPLEMENT         0
+#define ONES_COMPLEMENT         1
+#ifndef ARCH_TRAP_DEFINED
+#define TRAP_REPRESENTATION     1
+#endif
+
+#else
+/*
+ * Mantissa + sign.
+ */
+#define TWOS_COMPLEMENT         0
+#define ONES_COMPLEMENT         0
+#ifndef ARCH_TRAP_DEFINED
+#define TRAP_REPRESENTATION     1
+#endif
+
+#endif
+
+/*
+ * Maximum native unsigned value. The first macro is for #if directives,
+ * the second macro is for use as constant expression in C code.
+ */
+#define NATIVE_UNSIGNED_MAX     ((((1U << (NATIVE_UNSIGNED_BITS - 1)) - 1U) \
+                                << 1) + 1U)
+#define NATIVE_UNSIGNED_MAX_A   (((((arith_u)1 << (NATIVE_UNSIGNED_BITS - 1)) \
+                                - (arith_u)1) << 1) + (arith_u)1)
+
+#if NATIVE_SIGNED_MAX == NATIVE_UNSIGNED_MAX
+#define SIGNED_IS_BIGGER        1
+#else
+#define SIGNED_IS_BIGGER        0
+#endif
+
+#endif
+
+#undef NEGATIVE_IS_BIGGER
+#if NATIVE_SIGNED_MIN < -NATIVE_SIGNED_MAX
+#define NEGATIVE_IS_BIGGER      1
+#else
+#define NEGATIVE_IS_BIGGER      0
+#endif
+
+/* sanity check: we cannot have a trap representation if we have
+   two's complement with NATIVE_SIGNED_MIN < -NATIVE_SIGNED_MAX */
+#if TRAP_REPRESENTATION && NEGATIVE_IS_BIGGER
+#error Impossible to get trap representations.
+#endif
+
+/* operations on the unsigned type */
+
+ARITH_DECL_MONO_S_U(to_u) { return (arith_u)x; }
+ARITH_DECL_MONO_I_U(fromint) { return (arith_u)x; }
+ARITH_DECL_MONO_L_U(fromulong) { return (arith_u)x; }
+
+ARITH_DECL_MONO_U_I(toint)
+{
+#if NATIVE_UNSIGNED_MAX > INT_MAX
+       if (x > (arith_u)INT_MAX) return INT_MAX;
+#endif
+       return (int)x;
+}
+
+ARITH_DECL_MONO_U_L(toulong)
+{
+#if NATIVE_UNSIGNED_MAX > LONG_MAX
+       if (x > (arith_u)LONG_MAX) return LONG_MAX;
+#endif
+       return (long)x;
+}
+
+ARITH_DECL_MONO_U_U(neg) { return -x; }
+ARITH_DECL_MONO_U_U(not) { return ~x; }
+ARITH_DECL_MONO_U_I(lnot) { return !x; }
+ARITH_DECL_MONO_U_I(lval) { return x != 0; }
+
+ARITH_DECL_BI_UU_U(plus) { return x + y; }
+ARITH_DECL_BI_UU_U(minus) { return x - y; }
+ARITH_DECL_BI_UU_I(lt) { return x < y; }
+ARITH_DECL_BI_UU_I(leq) { return x <= y; }
+ARITH_DECL_BI_UU_I(gt) { return x > y; }
+ARITH_DECL_BI_UU_I(geq) { return x >= y; }
+ARITH_DECL_BI_UU_I(same) { return x == y; }
+ARITH_DECL_BI_UU_I(neq) { return x != y; }
+ARITH_DECL_BI_UU_U(and) { return x & y; }
+ARITH_DECL_BI_UU_U(xor) { return x ^ y; }
+ARITH_DECL_BI_UU_U(or) { return x | y; }
+ARITH_DECL_BI_UU_U(star) { return x * y; }
+
+ARITH_DECL_BI_UI_U(lsh)
+{
+#ifdef ARITHMETIC_CHECKS
+       if (y >= NATIVE_UNSIGNED_BITS)
+               ARITH_WARNING(ARITH_EXCEP_LSH_W);
+       else if (y < 0)
+               ARITH_WARNING(ARITH_EXCEP_LSH_C);
+#endif
+       return x << y;
+}
+
+ARITH_DECL_BI_UI_U(rsh)
+{
+#ifdef ARITHMETIC_CHECKS
+       if (y >= NATIVE_UNSIGNED_BITS)
+               ARITH_WARNING(ARITH_EXCEP_RSH_W);
+       else if (y < 0)
+               ARITH_WARNING(ARITH_EXCEP_RSH_C);
+#endif
+       return x >> y;
+}
+
+ARITH_DECL_BI_UU_U(slash)
+{
+       if (y == 0) ARITH_ERROR(ARITH_EXCEP_SLASH_D);
+       return x / y;
+}
+
+ARITH_DECL_BI_UU_U(pct)
+{
+       if (y == 0) ARITH_ERROR(ARITH_EXCEP_PCT_D);
+       return x % y;
+}
+
+/* operations on the signed type */
+
+ARITH_DECL_MONO_U_S(to_s)
+{
+#ifdef ARITHMETIC_CHECKS
+#if !SIGNED_IS_BIGGER
+       if (x > (arith_u)NATIVE_SIGNED_MAX)
+               ARITH_WARNING(ARITH_EXCEP_CONV_O);
+#endif
+#endif
+       return (arith_s)x;
+}
+
+ARITH_DECL_MONO_I_S(fromint) { return (arith_s)x; }
+ARITH_DECL_MONO_L_S(fromlong) { return (arith_s)x; }
+
+ARITH_DECL_MONO_S_I(toint)
+{
+#if NATIVE_SIGNED_MIN < INT_MIN
+       if (x < (arith_s)INT_MIN) return INT_MIN;
+#endif
+#if NATIVE_SIGNED_MAX > INT_MAX
+       if (x > (arith_s)INT_MAX) return INT_MAX;
+#endif
+       return (int)x;
+}
+
+ARITH_DECL_MONO_S_L(tolong)
+{
+#if NATIVE_SIGNED_MIN < LONG_MIN
+       if (x < (arith_s)LONG_MIN) return LONG_MIN;
+#endif
+#if NATIVE_SIGNED_MAX > LONG_MAX
+       if (x > (arith_s)LONG_MAX) return LONG_MAX;
+#endif
+       return (long)x;
+}
+
+ARITH_DECL_MONO_S_S(neg)
+{
+#ifdef ARITHMETIC_CHECKS
+#if NEGATIVE_IS_BIGGER
+       if (x == NATIVE_SIGNED_MIN)
+               ARITH_WARNING(ARITH_EXCEP_NEG_O);
+#endif
+#endif
+       return -x;
+}
+
+ARITH_DECL_MONO_S_S(not)
+{
+#ifdef ARITHMETIC_CHECKS
+#if TRAP_REPRESENTATION
+       if (
+#if TWOS_COMPLEMENT
+               (x == NATIVE_SIGNED_MAX)
+#elif ONES_COMPLEMENT
+               (x == 0)
+#else
+               (x == NATIVE_SIGNED_MAX)
+#endif
+               ) ARITH_WARNING(ARITH_EXCEP_NOT_T);
+#endif
+#endif
+       return ~x;
+}
+
+ARITH_DECL_MONO_S_I(lnot) { return !x; }
+ARITH_DECL_MONO_S_I(lval) { return x != 0; }
+
+/*
+ * Addition of signed values:
+ * -- overflows occur only when both operands are strictly positive
+ * -- underflows occur only when both operands are strictly negative
+ * -- overflow check (both operands > 0):
+ *    ** if SIGNED_IS_BIGGER == 1, overflows are kept as such in the
+ *       unsigned world (if the signed addition overflows, so does the
+ *       unsigned, and vice versa)
+ *    ** if SIGNED_IS_BIGGER == 0, no overflow can happen in the unsigned
+ *       world
+ * -- underflow check (both operands < 0):
+ *    ** if NEGATIVE_IS_BIGGER == 1 (must be two's complement)
+ *       ++ we have a guaranteed underflow if one of the operand is equal
+ *          to NATIVE_SIGNED_MIN; otherwise, -x and -y are valid integers,
+ *          and we cast them into the unsigned world
+ *       ++ if SIGNED_IS_BIGGER == 1, underflows become unsigned overflows
+ *          with a non-zero result
+ *       ++ if SIGNED_IS_BIGGER == 0, no overflow happens in the unsigned
+ *          world; we use the fact that -NATIVE_SIGNED_MIN is then
+ *          exaxctly 1 more than NATIVE_SIGNED_MAX
+ *    ** if NEGATIVE_IS_BIGGER == 0, underflow check is identical to
+ *       overflow check on (signed) -x and -y.
+ */
+ARITH_DECL_BI_SS_S(plus)
+{
+#ifdef ARITHMETIC_CHECKS
+       if (x > 0 && y > 0 && (
+#if SIGNED_IS_BIGGER
+               ((arith_u)((arith_u)x + (arith_u)y) < (arith_u)x)
+#else
+               (((arith_u)x + (arith_u)y) > (arith_u)NATIVE_SIGNED_MAX)
+#endif
+               )) ARITH_WARNING(ARITH_EXCEP_PLUS_O);
+       else if (x < 0 && y < 0 && (
+#if NEGATIVE_IS_BIGGER
+               (x == NATIVE_SIGNED_MIN || y == NATIVE_SIGNED_MIN) ||
+#if SIGNED_IS_BIGGER
+               (((arith_u)(-x) + (arith_u)(-y) != 0)
+                       && (arith_u)((arith_u)(-x) + (arith_u)(-y))
+                       < (arith_u)(-x))
+#else
+               (((arith_u)(-x) + (arith_u)(-y))
+                       > ((arith_u)1 + (arith_u)NATIVE_SIGNED_MAX))
+#endif
+#else
+#if SIGNED_IS_BIGGER
+               ((arith_u)((arith_u)(-x) + (arith_u)(-y)) < (arith_u)(-x))
+#else
+               (((arith_u)(-x) + (arith_u)(-y))
+                       > (arith_u)NATIVE_SIGNED_MAX)
+#endif
+#endif
+               )) ARITH_WARNING(ARITH_EXCEP_PLUS_U);
+#endif
+       return x + y;
+}
+
+/*
+ * Subtraction of signed values:
+ * -- overflow: only if x > 0 and y < 0
+ *    ** if NEGATIVE_IS_BIGGER == 1 (must be two's complement) and
+ *       y == NATIVE_SIGNED_MIN then overflow
+ *    ** otherwise, cast x and -y to unsigned, then add and check
+ *       for overflows
+ * -- underflow: only if x < 0 and y > 0
+ *    ** if NEGATIVE_IS_BIGGER == 1 (must be two's complement):
+ *       ++ if x == NATIVE_SIGNED_MIN then underflow
+ *       ++ cast -x and y to unsigned, then add. If SIGNED_IS_BIGGER == 0,
+ *          just check. Otherwise, check for overflow with non-zero result.
+ *    ** if NEGATIVE_IS_BIGGER == 0: cast -x and y to unsigned, then
+ *       add. Overflow check as in addition.
+ */
+ARITH_DECL_BI_SS_S(minus)
+{
+#ifdef ARITHMETIC_CHECKS
+       if (x > 0 && y < 0 && (
+#if NEGATIVE_IS_BIGGER
+       (y == NATIVE_SIGNED_MIN) ||
+#endif
+#if SIGNED_IS_BIGGER
+       ((arith_u)((arith_u)x + (arith_u)(-y)) < (arith_u)x)
+#else
+       (((arith_u)x + (arith_u)(-y)) > (arith_u)NATIVE_SIGNED_MAX)
+#endif
+       )) ARITH_WARNING(ARITH_EXCEP_MINUS_O);
+       else if (x < 0 && y > 0 && (
+#if NEGATIVE_IS_BIGGER
+       (x == NATIVE_SIGNED_MIN) ||
+#if SIGNED_IS_BIGGER
+       ((((arith_u)(-x) + (arith_u)y) != 0) &&
+               ((arith_u)((arith_u)(-x) + (arith_u)y) < (arith_u)(-x)))
+#else
+       (((arith_u)(-x) + (arith_u)y) >
+               ((arith_u)1 + (arith_u)NATIVE_SIGNED_MAX))
+#endif
+#else
+#if SIGNED_IS_BIGGER
+       ((arith_u)((arith_u)(-x) + (arith_u)y) < (arith_u)(-x))
+#else
+       (((arith_u)(-x) + (arith_u)y) > (arith_u)NATIVE_SIGNED_MAX)
+#endif
+#endif
+       )) ARITH_WARNING(ARITH_EXCEP_MINUS_U);
+#endif
+       return x - y;
+}
+
+ARITH_DECL_BI_SS_I(lt) { return x < y; }
+ARITH_DECL_BI_SS_I(leq) { return x <= y; }
+ARITH_DECL_BI_SS_I(gt) { return x > y; }
+ARITH_DECL_BI_SS_I(geq) { return x >= y; }
+ARITH_DECL_BI_SS_I(same) { return x == y; }
+ARITH_DECL_BI_SS_I(neq) { return x != y; }
+
+/*
+ * Provided neither x nor y is a trap representation:
+ * -- one's complement: impossible to get a trap representation
+ * -- two's complement and sign + mantissa: trap representation if and
+ * only if x and y are strictly negative and (-x) & (-y) == 0
+ * (in two's complement, -x is safe because overflow would occur only
+ * if x was already a trap representation).
+ */
+ARITH_DECL_BI_SS_S(and)
+{
+#ifdef ARITHMETIC_CHECKS
+#if TRAP_REPRESENTATION && !ONES_COMPLEMENT
+       if (x < 0 && y < 0 && ((-x) & (-y)) == 0)
+               ARITH_WARNING(ARITH_EXCEP_AND_T);
+#endif
+#endif
+       return x & y;
+}
+
+/*
+ * Provided neither x nor y is a trap representation:
+ * -- two's complement: trap if and only if x != NATIVE_SIGNED_MAX && ~x == y
+ * -- one's complement: trap if and only if x != 0 && ~x == y
+ * -- mantissa + sign: trap if and only if x != 0 && -x == y
+ */
+ARITH_DECL_BI_SS_S(xor)
+{
+#ifdef ARITHMETIC_CHECKS
+#if TRAP_REPRESENTATION
+       if (
+#if TWOS_COMPLEMENT
+       (x != NATIVE_SIGNED_MAX && ~x == y)
+#elif ONES_COMPLEMENT
+       (x != 0 && ~x == y)
+#else
+       (x != 0 && -x == y)
+#endif
+               ) ARITH_WARNING(ARITH_EXCEP_XOR_T);
+#endif
+#endif
+       return x ^ y;
+}
+
+/*
+ * Provided neither x nor y is a trap representation:
+ * -- two's complement: impossible to trap
+ * -- one's complement: trap if and only if x != 0 && y != 0 && (~x & ~y) == 0
+ * -- mantissa + sign: impossible to trap
+ */
+ARITH_DECL_BI_SS_S(or)
+{
+#ifdef ARITHMETIC_CHECKS
+#if TRAP_REPRESENTATION
+#if ONES_COMPLEMENT
+       if (x != 0 && y != 0 && (~x & ~y) == 0)
+               ARITH_WARNING(ARITH_EXCEP_OR_T);
+#endif
+#endif
+#endif
+       return x | y;
+}
+
+/*
+ * Left-shifting by a negative or greater than type width count is
+ * forbidden. Left-shifting a negative value is forbidden (underflow).
+ * Left-shifting a positive value can trigger an overflow. We check it
+ * by casting into the unsigned world and simulating a truncation.
+ *
+ * If SIGNED_IS_BIGGER is set, then the signed type width is 1 more
+ * than the unsigned type width (the sign bit is included in the width);
+ * otherwise, if W is the signed type width, 1U << (W-1) is equal to
+ * NATIVE_SIGNED_MAX + 1.
+ */
+ARITH_DECL_BI_SI_S(lsh)
+{
+#ifdef ARITHMETIC_CHECKS
+       if (y < 0) ARITH_WARNING(ARITH_EXCEP_LSH_C);
+       else if (
+#if SIGNED_IS_BIGGER
+               y > NATIVE_UNSIGNED_BITS
+#else
+               y >= NATIVE_UNSIGNED_BITS
+               || (y > 0 && (((arith_u)1 << (y - 1))
+                       > (arith_u)NATIVE_SIGNED_MAX))
+#endif
+               ) ARITH_WARNING(ARITH_EXCEP_LSH_W);
+       else if (x < 0) ARITH_WARNING(ARITH_EXCEP_LSH_U);
+       else if (x > 0 && ((((arith_u)x << y) & NATIVE_SIGNED_MAX) >> y)
+               != (arith_u)x) ARITH_WARNING(ARITH_EXCEP_LSH_O);
+#endif
+       return x << y;
+}
+
+/*
+ * Right-shifting is handled as left-shifting, except that the problem
+ * is somehow simpler: there is no possible overflow or underflow. Only
+ * right-shifting a negative value yields an implementation defined
+ * result (_not_ an undefined behaviour).
+ */
+ARITH_DECL_BI_SI_S(rsh)
+{
+#ifdef ARITHMETIC_CHECKS
+       if (y < 0) ARITH_WARNING(ARITH_EXCEP_RSH_C);
+       else if (
+#if SIGNED_IS_BIGGER
+               y > NATIVE_UNSIGNED_BITS
+#else
+               y >= NATIVE_UNSIGNED_BITS
+               || (y > 0 && (((arith_u)1 << (y - 1))
+                       > (arith_u)NATIVE_SIGNED_MAX))
+#endif
+               ) ARITH_WARNING(ARITH_EXCEP_RSH_W);
+       else if (x < 0) ARITH_WARNING(ARITH_EXCEP_RSH_N);
+#endif
+       return x >> y;
+}
+
+/*
+ * Overflow can happen only if both operands have the same sign.
+ * Underflow can happen only if both operands have opposite signs.
+ *
+ * Overflow checking: this is done quite inefficiently by performing
+ * a division on the result and check if it matches the initial operand.
+ */
+ARITH_DECL_BI_SS_S(star)
+{
+#ifdef ARITHMETIC_CHECKS
+       if (x == 0 || y == 0) return 0;
+       if (x > 0 && y > 0) {
+               if ((((arith_u)x * (arith_u)y) & (arith_u)NATIVE_SIGNED_MAX)
+                       / (arith_u)y != (arith_u)x)
+                       ARITH_WARNING(ARITH_EXCEP_STAR_O);
+       } else if (x < 0 && y < 0) {
+               if (
+#if NEGATIVE_IS_BIGGER
+                       (x == NATIVE_SIGNED_MIN || y == NATIVE_SIGNED_MIN) ||
+#endif
+                       (((arith_u)(-x) * (arith_u)(-y))
+                       & (arith_u)NATIVE_SIGNED_MAX) / (arith_u)(-y)
+                       != (arith_u)(-x))
+                       ARITH_WARNING(ARITH_EXCEP_STAR_O);
+       } else if (x > 0 && y < 0) {
+               if ((arith_u)x > (arith_u)1 && (
+#if NEGATIVE_IS_BIGGER
+               y == NATIVE_SIGNED_MIN ||
+#endif
+               (((arith_u)x * (arith_u)(-y)) & (arith_u)NATIVE_SIGNED_MAX)
+               / (arith_u)(-y) != (arith_u)x))
+               ARITH_WARNING(ARITH_EXCEP_STAR_U);
+       } else {
+               if ((arith_u)y > (arith_u)1 && (
+#if NEGATIVE_IS_BIGGER
+               x == NATIVE_SIGNED_MIN ||
+#endif
+               (((arith_u)y * (arith_u)(-x)) & (arith_u)NATIVE_SIGNED_MAX)
+               / (arith_u)(-x) != (arith_u)y))
+               ARITH_WARNING(ARITH_EXCEP_STAR_U);
+       }
+#endif
+       return x * y;
+}
+
+/*
+ * Division by 0 is an error. The only other possible problem is an
+ * overflow of the result. Such an overflow can only happen in two's
+ * complement representation, when NEGATIVE_IS_BIGGER is set, and
+ * one attempts to divide NATIVE_SIGNED_MIN by -1: the result is then
+ * -NATIVE_SIGNED_MIN, which is not representable by the type. This is
+ * considered as an error, not a warning, because it actually triggers
+ * an exception on modern Pentium-based PC.
+ */
+ARITH_DECL_BI_SS_S(slash)
+{
+       if (y == 0) ARITH_ERROR(ARITH_EXCEP_SLASH_D);
+#if NEGATIVE_IS_BIGGER
+       else if (x == NATIVE_SIGNED_MIN && y == (arith_s)(-1))
+               ARITH_ERROR(ARITH_EXCEP_SLASH_O);
+#endif
+       return x / y;
+}
+
+/*
+ * Only division by 0 needs to be checked.
+ */
+ARITH_DECL_BI_SS_S(pct)
+{
+       if (y == 0) ARITH_ERROR(ARITH_EXCEP_PCT_D);
+       return x % y;
+}
+
+ARITH_DECL_MONO_ST_US(octconst)
+{
+       arith_u z = 0;
+
+       for (; ARITH_OCTAL(*c); c ++) {
+               arith_u w = ARITH_OVAL(*c);
+               if (z > (NATIVE_UNSIGNED_MAX_A / 8))
+                       ARITH_ERROR(ARITH_EXCEP_CONST_O);
+               z *= 8;
+#if 0
+/* obsolete */
+/* NATIVE_UNSIGNED_MAX_A is 2^N - 1, 0 <= w <= 7 and 8 divides z */
+               if (z > (NATIVE_UNSIGNED_MAX_A - w))
+                       ARITH_ERROR(ARITH_EXCEP_CONST_O);
+#endif
+               z += w;
+       }
+       *ru = z;
+#if SIGNED_IS_BIGGER
+       *rs = z;
+       *sp = 1;
+#else
+       if (z > NATIVE_SIGNED_MAX) {
+               *sp = 0;
+       } else {
+               *rs = z;
+               *sp = 1;
+       }
+#endif
+       return c;
+}
+
+ARITH_DECL_MONO_ST_US(decconst)
+{
+       arith_u z = 0;
+
+       for (; ARITH_DECIM(*c); c ++) {
+               arith_u w = ARITH_DVAL(*c);
+               if (z > (NATIVE_UNSIGNED_MAX_A / 10))
+                       ARITH_ERROR(ARITH_EXCEP_CONST_O);
+               z *= 10;
+               if (z > (NATIVE_UNSIGNED_MAX_A - w))
+                       ARITH_ERROR(ARITH_EXCEP_CONST_O);
+               z += w;
+       }
+       *ru = z;
+#if SIGNED_IS_BIGGER
+       *rs = z;
+       *sp = 1;
+#else
+       if (z > NATIVE_SIGNED_MAX) {
+               *sp = 0;
+       } else {
+               *rs = z;
+               *sp = 1;
+       }
+#endif
+       return c;
+}
+
+ARITH_DECL_MONO_ST_US(hexconst)
+{
+       arith_u z = 0;
+
+       for (; ARITH_HEXAD(*c); c ++) {
+               arith_u w = ARITH_HVAL(*c);
+               if (z > (NATIVE_UNSIGNED_MAX_A / 16))
+                       ARITH_ERROR(ARITH_EXCEP_CONST_O);
+               z *= 16;
+#if 0
+/* obsolete */
+/* NATIVE_UNSIGNED_MAX_A is 2^N - 1, 0 <= w <= 15 and 16 divides z */
+               if (z > (NATIVE_UNSIGNED_MAX_A - w))
+                       ARITH_ERROR(ARITH_EXCEP_CONST_O);
+#endif
+               z += w;
+       }
+       *ru = z;
+#if SIGNED_IS_BIGGER
+       *rs = z;
+       *sp = 1;
+#else
+       if (z > NATIVE_SIGNED_MAX) {
+               *sp = 0;
+       } else {
+               *rs = z;
+               *sp = 1;
+       }
+#endif
+       return c;
+}
+
+#else
+/* ====================================================================== */
+/* Arithmetics with a simple simulated type */
+/* ====================================================================== */
+
+/*
+ * We simulate a type with the following characteristics:
+ * -- the signed type width is equal to the unsigned type width (which
+ * means that there is one less value bit in the signed type);
+ * -- the signed type uses two's complement representation;
+ * -- there is no trap representation;
+ * -- overflows and underflows are truncated (but a warning is emitted
+ * if ARITHMETIC_CHECKS is defined);
+ * -- overflow on integer division is still an error;
+ * -- right-shifting of a negative value extends the sign;
+ * -- the shift count value is first cast to unsigned, then reduced modulo
+ * the type size.
+ *
+ * These characteristics follow what is usually found on modern
+ * architectures.
+ *
+ * The maximum emulated type size is twice the size of the unsigned native
+ * type which is used to emulate the type.
+ */
+
+#undef SIMUL_ONE_TMP
+#undef SIMUL_MSW_TMP1
+#undef SIMUL_MSW_MASK
+#undef SIMUL_LSW_TMP1
+#undef SIMUL_LSW_MASK
+
+#define SIMUL_ONE_TMP     ((SIMUL_ARITH_SUBTYPE)1)
+#define SIMUL_MSW_TMP1    (SIMUL_ONE_TMP << (SIMUL_MSW_WIDTH - 1))
+#define SIMUL_MSW_MASK    (SIMUL_MSW_TMP1 | (SIMUL_MSW_TMP1 - SIMUL_ONE_TMP))
+#define SIMUL_LSW_TMP1    (SIMUL_ONE_TMP << (SIMUL_LSW_WIDTH - 1))
+#define SIMUL_LSW_MASK    (SIMUL_LSW_TMP1 | (SIMUL_LSW_TMP1 - SIMUL_ONE_TMP))
+
+#undef TMSW
+#undef TLSW
+
+#define TMSW(x)           ((x) & SIMUL_MSW_MASK)
+#define TLSW(x)           ((x) & SIMUL_LSW_MASK)
+
+#undef SIMUL_ZERO
+#undef SIMUL_ONE
+
+#define SIMUL_ZERO        arith_strc(ARITH_TYPENAME, _zero)
+#define SIMUL_ONE         arith_strc(ARITH_TYPENAME, _one)
+
+static arith_u SIMUL_ZERO = { 0, 0 };
+static arith_u SIMUL_ONE = { 0, 1 };
+
+/*
+ * We use the fact that both the signed and unsigned type are the same
+ * structure. The difference between the signed and the unsigned type
+ * is a type information, and, as such, is considered compile-time and
+ * not maintained in the value structure itself. This is a job for
+ * the programmer / compiler.
+ */
+ARITH_DECL_MONO_S_U(to_u) { return x; }
+
+ARITH_DECL_MONO_I_U(fromint)
+{
+       arith_u z;
+
+       if (x < 0) return arith_op_u(neg)(arith_op_u(fromint)(-x));
+       /*
+        * This code works because types smaller than int are promoted
+        * by the C compiler before evaluating the >> operator.
+        */
+       z.msw = TMSW(((SIMUL_ARITH_SUBTYPE)x >> (SIMUL_LSW_WIDTH - 1)) >> 1);
+       z.lsw = TLSW((SIMUL_ARITH_SUBTYPE)x);
+       return z;
+}
+
+ARITH_DECL_MONO_L_U(fromulong)
+{
+       arith_u z;
+
+#if (ULONG_MAX >> (SIMUL_LSW_WIDTH - 1)) >> 1 == 0
+       z.msw = 0;
+       z.lsw = x;
+#else
+       z.msw = TMSW(x >> SIMUL_LSW_WIDTH);
+       z.lsw = TLSW((SIMUL_ARITH_SUBTYPE)x);
+#endif
+       return z;
+}
+
+ARITH_DECL_MONO_U_I(toint)
+{
+#if ((INT_MAX >> (SIMUL_LSW_WIDTH - 1)) >> 1) == 0
+       if (x.msw != 0 || x.lsw > (SIMUL_ARITH_SUBTYPE)INT_MAX)
+               return INT_MAX;
+       return (int)x.lsw;
+#else
+#if (INT_MAX >> (SIMUL_SUBTYPE_BITS - 1)) == 0
+       if (x.msw > (SIMUL_ARITH_SUBTYPE)(INT_MAX >> SIMUL_LSW_WIDTH))
+               return INT_MAX;
+#endif
+       return ((int)x.msw << SIMUL_LSW_WIDTH) | (int)x.lsw;
+#endif
+}
+
+ARITH_DECL_MONO_U_L(toulong)
+{
+#if ((ULONG_MAX >> (SIMUL_LSW_WIDTH - 1)) >> 1) == 0
+       if (x.msw != 0 || x.lsw > (SIMUL_ARITH_SUBTYPE)ULONG_MAX)
+               return ULONG_MAX;
+       return (unsigned long)x.lsw;
+#else
+#if (ULONG_MAX >> (SIMUL_SUBTYPE_BITS - 1)) == 0
+       if (x.msw > (SIMUL_ARITH_SUBTYPE)(ULONG_MAX >> SIMUL_LSW_WIDTH))
+               return ULONG_MAX;
+#endif
+       return ((unsigned long)x.msw << SIMUL_LSW_WIDTH) | (unsigned long)x.lsw;
+#endif
+}
+
+ARITH_DECL_MONO_U_U(neg)
+{
+       x = arith_op_u(not)(x);
+       return arith_op_u(plus)(x, SIMUL_ONE);
+}
+
+ARITH_DECL_MONO_U_U(not)
+{
+       x.msw = TMSW(~x.msw);
+       x.lsw = TLSW(~x.lsw);
+       return x;
+}
+
+ARITH_DECL_MONO_U_I(lnot)
+{
+       return x.msw == 0 && x.lsw == 0;
+}
+
+ARITH_DECL_MONO_U_I(lval)
+{
+       return x.msw != 0 || x.lsw != 0;
+}
+
+ARITH_DECL_BI_UU_U(plus)
+{
+       x.lsw = TLSW(x.lsw + y.lsw);
+       x.msw = TMSW(x.msw + y.msw);
+       if (x.lsw < y.lsw) x.msw = TMSW(x.msw + 1);
+       return x;
+}
+
+ARITH_DECL_BI_UU_U(minus)
+{
+       return arith_op_u(plus)(x, arith_op_u(neg)(y));
+}
+
+ARITH_DECL_BI_UI_U(lsh)
+{
+       if (y == 0) return x;
+#ifdef ARITHMETIC_CHECKS
+       if (y < 0) ARITH_WARNING(ARITH_EXCEP_LSH_C);
+       else if (y >= SIMUL_NUMBITS) ARITH_WARNING(ARITH_EXCEP_LSH_W);
+#endif
+       y = (unsigned)y % SIMUL_NUMBITS;
+       if (y >= SIMUL_LSW_WIDTH) {
+               /*
+                * We use here the fact that the LSW size is always
+                * equal to or greater than the MSW size.
+                */
+               x.msw = TMSW(x.lsw << (y - SIMUL_LSW_WIDTH));
+               x.lsw = 0;
+               return x;
+       }
+       x.msw = TMSW((x.msw << y) | (x.lsw >> (SIMUL_LSW_WIDTH - y)));
+       x.lsw = TLSW(x.lsw << y);
+       return x;
+}
+
+ARITH_DECL_BI_UI_U(rsh)
+{
+#ifdef ARITHMETIC_CHECKS
+       if (y < 0) ARITH_WARNING(ARITH_EXCEP_RSH_C);
+       else if (y >= SIMUL_NUMBITS) ARITH_WARNING(ARITH_EXCEP_RSH_W);
+#endif
+       y = (unsigned)y % SIMUL_NUMBITS;
+       if (y >= SIMUL_LSW_WIDTH) {
+               x.lsw = x.msw >> (y - SIMUL_LSW_WIDTH);
+               x.msw = 0;
+               return x;
+       }
+       x.lsw = TLSW((x.lsw >> y) | (x.msw << (SIMUL_LSW_WIDTH - y)));
+       x.msw >>= y;
+       return x;
+}
+
+ARITH_DECL_BI_UU_I(lt)
+{
+       return x.msw < y.msw || (x.msw == y.msw && x.lsw < y.lsw);
+}
+
+ARITH_DECL_BI_UU_I(leq)
+{
+       return x.msw < y.msw || (x.msw == y.msw && x.lsw <= y.lsw);
+}
+
+ARITH_DECL_BI_UU_I(gt)
+{
+       return arith_op_u(lt)(y, x);
+}
+
+ARITH_DECL_BI_UU_I(geq)
+{
+       return arith_op_u(leq)(y, x);
+}
+
+ARITH_DECL_BI_UU_I(same)
+{
+       return x.msw == y.msw && x.lsw == y.lsw;
+}
+
+ARITH_DECL_BI_UU_I(neq)
+{
+       return !arith_op_u(same)(x, y);
+}
+
+ARITH_DECL_BI_UU_U(and)
+{
+       x.msw &= y.msw;
+       x.lsw &= y.lsw;
+       return x;
+}
+
+ARITH_DECL_BI_UU_U(xor)
+{
+       x.msw ^= y.msw;
+       x.lsw ^= y.lsw;
+       return x;
+}
+
+ARITH_DECL_BI_UU_U(or)
+{
+       x.msw |= y.msw;
+       x.lsw |= y.lsw;
+       return x;
+}
+
+#undef SIMUL_LSW_ODDLEN
+#undef SIMUL_LSW_HALFLEN
+#undef SIMUL_LSW_HALFMASK
+
+#define SIMUL_LSW_ODDLEN    (SIMUL_LSW_WIDTH & 1)
+#define SIMUL_LSW_HALFLEN   (SIMUL_LSW_WIDTH / 2)
+#define SIMUL_LSW_HALFMASK  (~(~(SIMUL_ARITH_SUBTYPE)0 << SIMUL_LSW_HALFLEN))
+
+ARITH_DECL_BI_UU_U(star)
+{
+       arith_u z;
+       SIMUL_ARITH_SUBTYPE a = x.lsw, b = y.lsw, t00, t01, t10, t11, c = 0, t;
+#if SIMUL_LSW_ODDLEN
+       SIMUL_ARITH_SUBTYPE bms = b & (SIMUL_ONE_TMP << (SIMUL_LSW_WIDTH - 1));
+
+       b &= ~(SIMUL_ONE_TMP << (SIMUL_LSW_WIDTH - 1));
+#endif
+
+       t00 = (a & SIMUL_LSW_HALFMASK) * (b & SIMUL_LSW_HALFMASK);
+       t01 = (a & SIMUL_LSW_HALFMASK) * (b >> SIMUL_LSW_HALFLEN);
+       t10 = (a >> SIMUL_LSW_HALFLEN) * (b & SIMUL_LSW_HALFMASK);
+       t11 = (a >> SIMUL_LSW_HALFLEN) * (b >> SIMUL_LSW_HALFLEN);
+       t = z.lsw = t00;
+       z.lsw = TLSW(z.lsw + (t01 << SIMUL_LSW_HALFLEN));
+       if (t > z.lsw) c ++;
+       t = z.lsw;
+       z.lsw = TLSW(z.lsw + (t10 << SIMUL_LSW_HALFLEN));
+       if (t > z.lsw) c ++;
+#if SIMUL_LSW_ODDLEN
+       t = z.lsw;
+       z.lsw = TLSW(z.lsw + (t11 << (2 * SIMUL_LSW_HALFLEN)));
+       if (t > z.lsw) c ++;
+       if (bms && (a & SIMUL_ONE_TMP)) {
+               t = z.lsw;
+               z.lsw = TLSW(z.lsw + b);
+               if (t > z.lsw) c ++;
+       }
+#endif
+       z.msw = TMSW(x.lsw * y.msw + x.msw * y.lsw + c
+               + (t01 >> (SIMUL_LSW_WIDTH - SIMUL_LSW_HALFLEN))
+               + (t10 >> (SIMUL_LSW_WIDTH - SIMUL_LSW_HALFLEN))
+               + (t11 >> (SIMUL_LSW_WIDTH - (2 * SIMUL_LSW_HALFLEN))));
+       return z;
+}
+
+/*
+ * This function calculates the unsigned integer division, yielding
+ * both quotient and remainder. The divider (y) MUST be non-zero.
+ */
+static void arith_op_u(udiv)(arith_u x, arith_u y, arith_u *q, arith_u *r)
+{
+       int i, j;
+       arith_u a;
+
+       *q = SIMUL_ZERO;
+       for (i = SIMUL_NUMBITS - 1; i >= 0; i --) {
+               if (i >= (int)SIMUL_LSW_WIDTH
+                       && (y.msw & (SIMUL_ONE_TMP << (i - SIMUL_LSW_WIDTH))))
+                       break;
+               if (i < (int)SIMUL_LSW_WIDTH && (y.lsw & (SIMUL_ONE_TMP << i)))
+                       break;
+       }
+       a = arith_op_u(lsh)(y, SIMUL_NUMBITS - 1 - i);
+       for (j = SIMUL_NUMBITS - 1 - i; j >= SIMUL_LSW_WIDTH; j --) {
+               if (arith_op_u(leq)(a, x)) {
+                       x = arith_op_u(minus)(x, a);
+                       q->msw |= SIMUL_ONE_TMP << (j - SIMUL_LSW_WIDTH);
+               }
+               a = arith_op_u(rsh)(a, 1);
+       }
+       for (; j >= 0; j --) {
+               if (arith_op_u(leq)(a, x)) {
+                       x = arith_op_u(minus)(x, a);
+                       q->lsw |= SIMUL_ONE_TMP << j;
+               }
+               a = arith_op_u(rsh)(a, 1);
+       }
+       *r = x;
+}
+
+ARITH_DECL_BI_UU_U(slash)
+{
+       arith_u q, r;
+
+       if (arith_op_u(same)(y, SIMUL_ZERO))
+               ARITH_ERROR(ARITH_EXCEP_SLASH_D);
+       arith_op_u(udiv)(x, y, &q, &r);
+       return q;
+}
+
+ARITH_DECL_BI_UU_U(pct)
+{
+       arith_u q, r;
+
+       if (arith_op_u(same)(y, SIMUL_ZERO))
+               ARITH_ERROR(ARITH_EXCEP_PCT_D);
+       arith_op_u(udiv)(x, y, &q, &r);
+       return r;
+}
+
+#undef SIMUL_TRAP
+#undef SIMUL_TRAPL
+#define SIMUL_TRAP   (SIMUL_ONE_TMP << (SIMUL_MSW_WIDTH - 1))
+#define SIMUL_TRAPL  (SIMUL_ONE_TMP << (SIMUL_LSW_WIDTH - 1))
+
+ARITH_DECL_MONO_U_S(to_s)
+{
+#ifdef ARITHMETIC_CHECKS
+       if (x.msw & SIMUL_TRAP) ARITH_WARNING(ARITH_EXCEP_CONV_O);
+#endif
+       return x;
+}
+
+ARITH_DECL_MONO_I_S(fromint) { return arith_op_u(fromint)(x); }
+ARITH_DECL_MONO_L_S(fromlong)
+{
+       if (x < 0) return arith_op_u(neg)(
+               arith_op_u(fromulong)((unsigned long)(-x)));
+       return arith_op_u(fromulong)((unsigned long)x);
+}
+
+ARITH_DECL_MONO_S_I(toint)
+{
+       if (x.msw & SIMUL_TRAP) return -arith_op_u(toint)(arith_op_u(neg)(x));
+       return arith_op_u(toint)(x);
+}
+
+ARITH_DECL_MONO_S_L(tolong)
+{
+       if (x.msw & SIMUL_TRAP)
+               return -(long)arith_op_u(toulong)(arith_op_u(neg)(x));
+       return (long)arith_op_u(toulong)(x);
+}
+
+ARITH_DECL_MONO_S_S(neg)
+{
+#ifdef ARITHMETIC_CHECKS
+       if (x.lsw == 0 && x.msw == SIMUL_TRAP)
+               ARITH_WARNING(ARITH_EXCEP_NEG_O);
+#endif
+       return arith_op_u(neg)(x);
+}
+
+ARITH_DECL_MONO_S_S(not) { return arith_op_u(not)(x); }
+ARITH_DECL_MONO_S_I(lnot) { return arith_op_u(lnot)(x); }
+ARITH_DECL_MONO_S_I(lval) { return arith_op_u(lval)(x); }
+
+ARITH_DECL_BI_SS_S(plus)
+{
+       arith_u z = arith_op_u(plus)(x, y);
+
+#ifdef ARITHMETIC_CHECKS
+       if (x.msw & y.msw & ~z.msw & SIMUL_TRAP)
+               ARITH_WARNING(ARITH_EXCEP_PLUS_U);
+       else if (~x.msw & ~y.msw & z.msw & SIMUL_TRAP)
+               ARITH_WARNING(ARITH_EXCEP_PLUS_O);
+#endif
+       return z;
+}
+
+ARITH_DECL_BI_SS_S(minus)
+{
+       arith_s z = arith_op_u(minus)(x, y);
+
+#ifdef ARITHMETIC_CHECKS
+       if (x.msw & ~y.msw & ~z.msw & SIMUL_TRAP)
+               ARITH_WARNING(ARITH_EXCEP_MINUS_U);
+       else if (~x.msw & y.msw & z.msw & SIMUL_TRAP)
+               ARITH_WARNING(ARITH_EXCEP_MINUS_O);
+#endif
+       return z;
+}
+
+/*
+ * Since signed and unsigned widths are equal for the simulated type,
+ * we can use the unsigned left shift function, which performs the
+ * the checks on the type width.
+ */
+ARITH_DECL_BI_SI_S(lsh)
+{
+       arith_s z = arith_op_u(lsh)(x, y);
+
+#ifdef ARITHMETIC_CHECKS
+       if (x.msw & SIMUL_TRAP) ARITH_WARNING(ARITH_EXCEP_LSH_U);
+       else {
+               /*
+                * To check for possible overflow, we right shift the
+                * result. We need to make the shift count proper so that
+                * we do not emit a double-warning. Besides, the left shift
+                * could have been untruncated but yet affet the sign bit,
+                * so we must test this explicitly.
+                */
+               arith_s w = arith_op_u(rsh)(z, (unsigned)y % SIMUL_NUMBITS);
+
+               if ((z.msw & SIMUL_TRAP) || w.msw != x.msw || w.lsw != x.lsw)
+                       ARITH_WARNING(ARITH_EXCEP_LSH_O);
+       }
+#endif
+       return z;
+}
+
+/*
+ * We define that right shifting a negative value, besides being worth a
+ * warning, duplicates the sign bit. This is the most useful and most
+ * usually encountered behaviour, and the standard allows it.
+ */
+ARITH_DECL_BI_SI_S(rsh)
+{
+       int xn = (x.msw & SIMUL_TRAP) != 0;
+       arith_s z = arith_op_u(rsh)(x, y);
+       int gy = (unsigned)y % SIMUL_NUMBITS;
+
+#ifdef ARITHMETIC_CHECKS
+       if (xn) ARITH_WARNING(ARITH_EXCEP_RSH_N);
+#endif
+       if (xn && gy > 0) {
+               if (gy <= SIMUL_MSW_WIDTH) {
+                       z.msw |= TMSW(~(SIMUL_MSW_MASK >> gy));
+               } else {
+                       z.msw = SIMUL_MSW_MASK;
+                       z.lsw |= TLSW(~(SIMUL_LSW_MASK
+                               >> (gy - SIMUL_MSW_WIDTH)));
+               }
+       }
+       return z;
+}
+
+ARITH_DECL_BI_SS_I(lt)
+{
+       int xn = (x.msw & SIMUL_TRAP) != 0;
+       int yn = (y.msw & SIMUL_TRAP) != 0;
+
+       if (xn == yn) {
+               return x.msw < y.msw || (x.msw == y.msw && x.lsw < y.lsw);
+       } else {
+               return xn;
+       }
+}
+
+ARITH_DECL_BI_SS_I(leq)
+{
+       int xn = (x.msw & SIMUL_TRAP) != 0;
+       int yn = (y.msw & SIMUL_TRAP) != 0;
+
+       if (xn == yn) {
+               return x.msw < y.msw || (x.msw == y.msw && x.lsw <= y.lsw);
+       } else {
+               return xn;
+       }
+}
+
+ARITH_DECL_BI_SS_I(gt)
+{
+       return arith_op_s(lt)(y, x);
+}
+
+ARITH_DECL_BI_SS_I(geq)
+{
+       return arith_op_s(leq)(y, x);
+}
+
+ARITH_DECL_BI_SS_I(same)
+{
+       return x.msw == y.msw && x.lsw == y.lsw;
+}
+
+ARITH_DECL_BI_SS_I(neq)
+{
+       return !arith_op_s(same)(x, y);
+}
+
+ARITH_DECL_BI_SS_S(and)
+{
+       return arith_op_u(and)(x, y);
+}
+
+ARITH_DECL_BI_SS_S(xor)
+{
+       return arith_op_u(xor)(x, y);
+}
+
+ARITH_DECL_BI_SS_S(or)
+{
+       return arith_op_u(or)(x, y);
+}
+
+/*
+ * This function calculates the signed integer division, yielding
+ * both quotient and remainder. The divider (y) MUST be non-zero.
+ */
+static void arith_op_s(sdiv)(arith_s x, arith_s y, arith_s *q, arith_s *r)
+{
+       arith_u a = x, b = y, c, d;
+       int xn = 0, yn = 0;
+
+       if (x.msw & SIMUL_TRAP) { a = arith_op_u(neg)(x); xn = 1; }
+       if (y.msw & SIMUL_TRAP) { b = arith_op_u(neg)(y); yn = 1; }
+       arith_op_u(udiv)(a, b, &c, &d);
+       if (xn != yn) *q = arith_op_u(neg)(c); else *q = c;
+       if (xn != yn) *r = arith_op_u(neg)(d); else *r = d;
+}
+
+/*
+ * Overflow/underflow check is done the following way: obvious cases
+ * are checked (both upper words non-null, both upper words null...)
+ * and border-line occurrences are verified with an unsigned division
+ * (which is quite computationaly expensive).
+ */
+ARITH_DECL_BI_SS_S(star)
+{
+#ifdef ARITHMETIC_CHECKS
+       arith_s z = arith_op_u(star)(x, y);
+       int warn = 0;
+
+       if (x.msw > 0) {
+               if (y.msw > 0
+#if SIMUL_LSW_ODDLEN
+                       || (y.lsw & SIMUL_TRAPL)
+#endif
+               ) warn = 1;
+       }
+#if SIMUL_LSW_ODDLEN
+       else if (y.msw > 0 && (x.lsw & SIMUL_TRAPL)) warn = 1;
+#endif
+       if (!warn && (x.msw > 0 || y.msw > 0
+#if SIMUL_LSW_ODDLEN
+               || ((x.lsw | y.lsw) & SIMUL_TRAPL)
+#endif
+       )) {
+               if (x.msw == SIMUL_MSW_MASK && x.lsw == SIMUL_LSW_MASK) {
+                       if (y.msw == SIMUL_TRAP && y.lsw == 0) warn = 1;
+               } else if (!(x.msw == 0 && x.lsw == 0)
+                       && !arith_op_s(same)(arith_op_s(slash)(z, x), y)) {
+               } warn = 1;
+       }
+       if (warn) ARITH_WARNING(((x.msw ^ y.msw) & SIMUL_TRAP)
+               ? ARITH_EXCEP_STAR_U : ARITH_EXCEP_STAR_O);
+       return z;
+#else
+       return arith_op_u(star)(x, y);
+#endif
+}
+
+ARITH_DECL_BI_SS_S(slash)
+{
+       arith_s q, r;
+
+       if (arith_op_s(same)(y, SIMUL_ZERO))
+               ARITH_ERROR(ARITH_EXCEP_SLASH_D);
+       else if (x.msw == SIMUL_TRAP && x.lsw == 0
+               && y.msw == SIMUL_MSW_MASK && y.lsw == SIMUL_LSW_MASK)
+               ARITH_ERROR(ARITH_EXCEP_SLASH_O);
+       arith_op_s(sdiv)(x, y, &q, &r);
+       return q;
+}
+
+ARITH_DECL_BI_SS_S(pct)
+{
+       arith_s q, r;
+
+       if (arith_op_s(same)(y, SIMUL_ZERO))
+               ARITH_ERROR(ARITH_EXCEP_PCT_D);
+       arith_op_s(sdiv)(x, y, &q, &r);
+       return r;
+}
+
+ARITH_DECL_MONO_ST_US(octconst)
+{
+       arith_u z = { 0, 0 };
+
+       for (; ARITH_OCTAL(*c); c ++) {
+               unsigned w = ARITH_OVAL(*c);
+               if (z.msw > (SIMUL_MSW_MASK / 8))
+                       ARITH_ERROR(ARITH_EXCEP_CONST_O);
+               z = arith_op_u(lsh)(z, 3);
+               z.lsw |= w;
+       }
+       *ru = z;
+       if (z.msw & SIMUL_TRAP) {
+               *sp = 0;
+       } else {
+               *rs = z;
+               *sp = 1;
+       }
+       return c;
+}
+
+ARITH_DECL_MONO_ST_US(decconst)
+{
+#define ARITH_ALPHA_TRAP    (1U << (SIMUL_MSW_WIDTH - 1))
+#define ARITH_ALPHA_MASK    (ARITH_ALPHA_TRAP | (ARITH_ALPHA_TRAP - 1))
+#define ARITH_ALPHA     ((ARITH_ALPHA_MASK - 10 * (ARITH_ALPHA_TRAP / 5)) + 1)
+#define ARITH_ALPHA_A   ((SIMUL_MSW_MASK - 10 * (SIMUL_TRAP / 5)) + 1)
+
+       arith_u z = { 0, 0 };
+
+       for (; ARITH_DECIM(*c); c ++) {
+               unsigned w = ARITH_DVAL(*c);
+               SIMUL_ARITH_SUBTYPE t;
+
+               if (z.msw > (SIMUL_MSW_MASK / 10)
+                       || (z.msw == (SIMUL_MSW_MASK / 10) &&
+/* ARITH_ALPHA is between 1 and 9, inclusive. */
+#if ARITH_ALPHA == 5
+                       z.lsw >= SIMUL_TRAPL
+#else
+                       z.lsw > ((SIMUL_TRAPL / 5) * ARITH_ALPHA_A
+                       + ((SIMUL_TRAPL % 5) * ARITH_ALPHA_A) / 5)
+#endif
+                       )) ARITH_ERROR(ARITH_EXCEP_CONST_O);
+               z = arith_op_u(plus)(arith_op_u(lsh)(z, 3),
+                       arith_op_u(lsh)(z, 1));
+               t = TLSW(z.lsw + w);
+               if (t < z.lsw) z.msw ++;
+               z.lsw = t;
+       }
+       *ru = z;
+       if (z.msw & SIMUL_TRAP) {
+               *sp = 0;
+       } else {
+               *rs = z;
+               *sp = 1;
+       }
+       return c;
+
+#undef ARITH_ALPHA_A
+#undef ARITH_ALPHA
+#undef ARITH_ALPHA_TRAP
+#undef ARITH_ALPHA_MASK
+}
+
+ARITH_DECL_MONO_ST_US(hexconst)
+{
+       arith_u z = { 0, 0 };
+
+       for (; ARITH_HEXAD(*c); c ++) {
+               unsigned w = ARITH_HVAL(*c);
+               if (z.msw > (SIMUL_MSW_MASK / 16))
+                       ARITH_ERROR(ARITH_EXCEP_CONST_O);
+               z = arith_op_u(lsh)(z, 4);
+               z.lsw |= w;
+       }
+       *ru = z;
+       if (z.msw & SIMUL_TRAP) {
+               *sp = 0;
+       } else {
+               *rs = z;
+               *sp = 1;
+       }
+       return c;
+}
+
+#endif
+
+#undef ARITH_HVAL
+#undef ARITH_HEXAD
+#undef ARITH_DVAL
+#undef ARITH_DECIM
+#undef ARITH_OVAL
+#undef ARITH_OCTAL
diff --git a/libexec/auxcpp/arith.h b/libexec/auxcpp/arith.h
new file mode 100644 (file)
index 0000000..ae64e5c
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Integer arithmetic evaluation, header file.
+ *
+ * (c) Thomas Pornin 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This arithmetic evaluator uses two files: this header file (arith.h)
+ * and the source file (arith.c). To use this code, the source file should
+ * be included from another .c file which defines some macros (see below).
+ * Then the functions defined in the arith.c file become available to the
+ * including source file. If those functions are defined with external
+ * linkage (that is, `ARITH_FUNCTION_HEADER' does not contain `static'),
+ * it is possible for other source files to use the arithmetic functions
+ * by including the arith.h header only. The source file which includes
+ * arith.c should *not* include arith.h.
+ *
+ * If the #include is for arith.h, the following macros should be
+ * defined:
+ *
+ * -- If the evaluator is supposed to use a native type:
+ *   NATIVE_SIGNED            the native signed integer type
+ *   NATIVE_UNSIGNED          the native unsigned integer type
+ *
+ * -- If the evaluator is supposed to use an emulated type:
+ *   SIMUL_ARITH_SUBTYPE      the native unsigned type used for the simulation
+ *   SIMUL_SUBTYPE_BITS       the native unsigned type size
+ *   SIMUL_NUMBITS            the emulated type size
+ *
+ * -- For both cases:
+ *   ARITH_TYPENAME           the central arithmetic type name
+ *   ARITH_FUNCTION_HEADER    the qualifiers to add to function definitions
+ *
+ * The presence (respectively absence) of the NATIVE_SIGNED macro triggers
+ * the use of the native type evaluator (respectively simulated type
+ * evaluator).
+ *
+ * If the #include is for arith.c, the macros for arith.h should be defined,
+ * and the following should be defined as well:
+ *
+ * -- If the evaluator is supposed to use a native type:
+ *   NATIVE_UNSIGNED_BITS     the native unsigned type size
+ *   NATIVE_SIGNED_MIN        the native signed minimum value
+ *   NATIVE_SIGNED_MAX        the native signed maximum value
+ * (the last two macros must evaluate to signed constant expressions)
+ *
+ * -- For both cases:
+ *   ARITH_WARNING(type)      code to perform on warning
+ *   ARITH_ERROR(type)        code to perform on error
+ *
+ * The macro ARITH_WARNING() and ARITH_ERROR() are invoked with a
+ * numerical argument which is one of the enumeration constants
+ * defined below (ARITH_EXCEP_*) that identifies the specific problem.
+ *
+ * If the #include is for arith.c, the macro ARITHMETIC_CHECKS may be
+ * defined. When this macro is defined, checks are performed so that all
+ * operation which would lead to undefined or implementation-defined
+ * behaviour are first reported through ARITH_WARNING(). Code is smaller
+ * and faster without these checks, of course. Regardless of the status
+ * of that macro, divisions by 0 and overflows on signed division are
+ * reported as errors through ARITH_ERROR().
+ *
+ */
+
+#ifndef ARITH_H__
+#define ARITH_H__
+
+enum {
+       /* Warnings */
+       ARITH_EXCEP_CONV_O,    /* overflow on conversion */
+       ARITH_EXCEP_NEG_O,     /* overflow on unary minus */
+       ARITH_EXCEP_NOT_T,     /* trap representation on bitwise inversion */
+       ARITH_EXCEP_PLUS_O,    /* overflow on addition */
+       ARITH_EXCEP_PLUS_U,    /* underflow on addition */
+       ARITH_EXCEP_MINUS_O,   /* overflow on subtraction */
+       ARITH_EXCEP_MINUS_U,   /* underflow on subtraction */
+       ARITH_EXCEP_AND_T,     /* trap representation on bitwise and */
+       ARITH_EXCEP_XOR_T,     /* trap representation on bitwise xor */
+       ARITH_EXCEP_OR_T,      /* trap representation on bitwise or */
+       ARITH_EXCEP_LSH_W,     /* left shift by type width or more */
+       ARITH_EXCEP_LSH_C,     /* left shift by negative count */
+       ARITH_EXCEP_LSH_O,     /* overflow on left shift */
+       ARITH_EXCEP_LSH_U,     /* underflow on left shift */
+       ARITH_EXCEP_RSH_W,     /* right shift by type width or more */
+       ARITH_EXCEP_RSH_C,     /* right shift by negative count */
+       ARITH_EXCEP_RSH_N,     /* right shift of negative value */
+       ARITH_EXCEP_STAR_O,    /* overflow on multiplication */
+       ARITH_EXCEP_STAR_U,    /* underflow on multiplication */
+
+       /* Errors */
+       ARITH_EXCEP_SLASH_D,   /* division by 0 */
+       ARITH_EXCEP_SLASH_O,   /* overflow on division */
+       ARITH_EXCEP_PCT_D,     /* division by 0 on modulus operator */
+       ARITH_EXCEP_CONST_O    /* constant too large */
+};
+
+#define arith_strc_(x, y)      x ## y
+#define arith_strc(x, y)       arith_strc_(x, y)
+
+#define arith_u                arith_strc(u_, ARITH_TYPENAME)
+#define arith_s                arith_strc(s_, ARITH_TYPENAME)
+#define arith_op_u(op) arith_strc(ARITH_TYPENAME, arith_strc(_u_, op))
+#define arith_op_s(op) arith_strc(ARITH_TYPENAME, arith_strc(_s_, op))
+
+#define ARITH_DECL_MONO_U_U(op)    ARITH_FUNCTION_HEADER arith_u \
+                                   arith_op_u(op)(arith_u x)
+#define ARITH_DECL_MONO_U_S(op)    ARITH_FUNCTION_HEADER arith_s \
+                                   arith_op_u(op)(arith_u x)
+#define ARITH_DECL_MONO_U_I(op)    ARITH_FUNCTION_HEADER int \
+                                   arith_op_u(op)(arith_u x)
+#define ARITH_DECL_MONO_U_L(op)    ARITH_FUNCTION_HEADER unsigned long \
+                                   arith_op_u(op)(arith_u x)
+#define ARITH_DECL_MONO_S_U(op)    ARITH_FUNCTION_HEADER arith_u \
+                                   arith_op_s(op)(arith_s x)
+#define ARITH_DECL_MONO_S_S(op)    ARITH_FUNCTION_HEADER arith_s \
+                                   arith_op_s(op)(arith_s x)
+#define ARITH_DECL_MONO_S_I(op)    ARITH_FUNCTION_HEADER int \
+                                   arith_op_s(op)(arith_s x)
+#define ARITH_DECL_MONO_S_L(op)    ARITH_FUNCTION_HEADER long \
+                                   arith_op_s(op)(arith_s x)
+#define ARITH_DECL_MONO_I_U(op)    ARITH_FUNCTION_HEADER arith_u \
+                                   arith_op_u(op)(int x)
+#define ARITH_DECL_MONO_L_U(op)    ARITH_FUNCTION_HEADER arith_u \
+                                   arith_op_u(op)(unsigned long x)
+#define ARITH_DECL_MONO_I_S(op)    ARITH_FUNCTION_HEADER arith_s \
+                                   arith_op_s(op)(int x)
+#define ARITH_DECL_MONO_L_S(op)    ARITH_FUNCTION_HEADER arith_s \
+                                   arith_op_s(op)(long x)
+#define ARITH_DECL_MONO_ST_US(op)  ARITH_FUNCTION_HEADER char *arith_op_u(op) \
+                                   (char *c, arith_u *ru, arith_s *rs, int *sp)
+
+#define ARITH_DECL_BI_UU_U(op)     ARITH_FUNCTION_HEADER arith_u \
+                                   arith_op_u(op)(arith_u x, arith_u y)
+#define ARITH_DECL_BI_UI_U(op)     ARITH_FUNCTION_HEADER arith_u \
+                                   arith_op_u(op)(arith_u x, int y)
+#define ARITH_DECL_BI_UU_I(op)     ARITH_FUNCTION_HEADER int \
+                                   arith_op_u(op)(arith_u x, arith_u y)
+#define ARITH_DECL_BI_SS_S(op)     ARITH_FUNCTION_HEADER arith_s \
+                                   arith_op_s(op)(arith_s x, arith_s y)
+#define ARITH_DECL_BI_SI_S(op)     ARITH_FUNCTION_HEADER arith_s \
+                                   arith_op_s(op)(arith_s x, int y)
+#define ARITH_DECL_BI_SS_I(op)     ARITH_FUNCTION_HEADER int \
+                                   arith_op_s(op)(arith_s x, arith_s y)
+
+#endif
+
+#ifdef NATIVE_SIGNED
+
+typedef NATIVE_SIGNED arith_s;
+typedef NATIVE_UNSIGNED arith_u;
+
+#else
+
+#if SIMUL_NUMBITS > (2 * SIMUL_SUBTYPE_BITS)
+#error Native subtype too small for arithmetic simulation.
+#endif
+
+#define SIMUL_MSW_WIDTH   (SIMUL_NUMBITS / 2)
+#define SIMUL_LSW_WIDTH   ((SIMUL_NUMBITS + 1) / 2)
+
+typedef struct {
+       SIMUL_ARITH_SUBTYPE msw, lsw;
+} arith_u, arith_s;
+
+#endif
+
+/* functions with the unsigned type */
+
+ARITH_DECL_MONO_S_U(to_u);
+ARITH_DECL_MONO_I_U(fromint);
+ARITH_DECL_MONO_L_U(fromulong);
+ARITH_DECL_MONO_U_I(toint);
+ARITH_DECL_MONO_U_L(toulong);
+
+ARITH_DECL_MONO_U_U(neg);
+ARITH_DECL_MONO_U_U(not);
+ARITH_DECL_MONO_U_I(lnot);
+ARITH_DECL_MONO_U_I(lval);
+
+ARITH_DECL_BI_UU_U(plus);
+ARITH_DECL_BI_UU_U(minus);
+ARITH_DECL_BI_UI_U(lsh);
+ARITH_DECL_BI_UI_U(rsh);
+ARITH_DECL_BI_UU_I(lt);
+ARITH_DECL_BI_UU_I(leq);
+ARITH_DECL_BI_UU_I(gt);
+ARITH_DECL_BI_UU_I(geq);
+ARITH_DECL_BI_UU_I(same);
+ARITH_DECL_BI_UU_I(neq);
+ARITH_DECL_BI_UU_U(and);
+ARITH_DECL_BI_UU_U(xor);
+ARITH_DECL_BI_UU_U(or);
+ARITH_DECL_BI_UU_U(star);
+ARITH_DECL_BI_UU_U(slash);
+ARITH_DECL_BI_UU_U(pct);
+
+/* functions with the signed type */
+
+ARITH_DECL_MONO_U_S(to_s);
+ARITH_DECL_MONO_I_S(fromint);
+ARITH_DECL_MONO_L_S(fromlong);
+ARITH_DECL_MONO_S_I(toint);
+ARITH_DECL_MONO_S_L(tolong);
+
+ARITH_DECL_MONO_S_S(neg);
+ARITH_DECL_MONO_S_S(not);
+ARITH_DECL_MONO_S_I(lnot);
+ARITH_DECL_MONO_S_I(lval);
+
+ARITH_DECL_BI_SS_S(plus);
+ARITH_DECL_BI_SS_S(minus);
+ARITH_DECL_BI_SI_S(lsh);
+ARITH_DECL_BI_SI_S(rsh);
+ARITH_DECL_BI_SS_I(lt);
+ARITH_DECL_BI_SS_I(leq);
+ARITH_DECL_BI_SS_I(gt);
+ARITH_DECL_BI_SS_I(geq);
+ARITH_DECL_BI_SS_I(same);
+ARITH_DECL_BI_SS_I(neq);
+ARITH_DECL_BI_SS_S(and);
+ARITH_DECL_BI_SS_S(xor);
+ARITH_DECL_BI_SS_S(or);
+ARITH_DECL_BI_SS_S(star);
+ARITH_DECL_BI_SS_S(slash);
+ARITH_DECL_BI_SS_S(pct);
+
+/* conversions from string */
+ARITH_DECL_MONO_ST_US(octconst);
+ARITH_DECL_MONO_ST_US(hexconst);
+ARITH_DECL_MONO_ST_US(decconst);
diff --git a/libexec/auxcpp/assert.c b/libexec/auxcpp/assert.c
new file mode 100644 (file)
index 0000000..579d47e
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * (c) Thomas Pornin 1999 - 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "tune.h"
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <limits.h>
+#include <time.h>
+#include "ucppi.h"
+#include "mem.h"
+#include "nhash.h"
+
+/*
+ * Assertion support. Each assertion is indexed by its predicate, and
+ * the list of 'questions' which yield a true answer.
+ */
+
+static HTT assertions;
+static int assertions_init_done = 0;
+
+static struct assert *new_assertion(void)
+{
+       struct assert *a = getmem(sizeof(struct assert));
+
+       a->nbval = 0;
+       return a;
+}
+
+static void del_token_fifo(struct token_fifo *tf)
+{
+       size_t i;
+
+       for (i = 0; i < tf->nt; i ++)
+               if (S_TOKEN(tf->t[i].type)) freemem(tf->t[i].name);
+       if (tf->nt) freemem(tf->t);
+}
+
+static void del_assertion(void *va)
+{
+       struct assert *a = va;
+       size_t i;
+
+       for (i = 0; i < a->nbval; i ++) del_token_fifo(a->val + i);
+       if (a->nbval) freemem(a->val);
+       freemem(a);
+}
+
+/*
+ * print the contents of a token list
+ */
+static void print_token_fifo(struct token_fifo *tf)
+{
+       size_t i;
+
+       for (i = 0; i < tf->nt; i ++)
+               if (ttMWS(tf->t[i].type)) fputc(' ', emit_output);
+               else fputs(token_name(tf->t + i), emit_output);
+}
+
+/*
+ * print all assertions related to a given name
+ */
+static void print_assert(void *va)
+{
+       struct assert *a = va;
+       size_t i;
+
+       for (i = 0; i < a->nbval; i ++) {
+               fprintf(emit_output, "#assert %s(", HASH_ITEM_NAME(a));
+               print_token_fifo(a->val + i);
+               fprintf(emit_output, ")\n");
+       }
+}
+
+/*
+ * compare two token_fifo, return 0 if they are identical, 1 otherwise.
+ * All whitespace tokens are considered identical, but sequences of
+ * whitespace are not shrinked.
+ */
+int cmp_token_list(struct token_fifo *f1, struct token_fifo *f2)
+{
+       size_t i;
+
+       if (f1->nt != f2->nt) return 1;
+       for (i = 0; i < f1->nt; i ++) {
+               if (ttMWS(f1->t[i].type) && ttMWS(f2->t[i].type)) continue;
+               if (f1->t[i].type != f2->t[i].type) return 1;
+               if (f1->t[i].type == MACROARG
+                       && f1->t[i].line != f2->t[i].line) return 1;
+               if (S_TOKEN(f1->t[i].type)
+                       && strcmp(f1->t[i].name, f2->t[i].name)) return 1;
+       }
+       return 0;
+}
+
+/*
+ * for #assert
+ * Assertions are not part of the ISO-C89 standard, but they are sometimes
+ * encountered, for instance in Solaris standard include files.
+ */
+int handle_assert(struct lexer_state *ls)
+{
+       int ina = 0, ltww;
+       struct token t;
+       struct token_fifo *atl = 0;
+       struct assert *a;
+       char *aname;
+       int ret = -1;
+       long l = ls->line;
+       int nnp;
+       size_t i;
+
+       while (!next_token(ls)) {
+               if (ls->ctok->type == NEWLINE) break;
+               if (ttMWS(ls->ctok->type)) continue;
+               if (ls->ctok->type == NAME) {
+                       if (!(a = HTT_get(&assertions, ls->ctok->name))) {
+                               a = new_assertion();
+                               aname = sdup(ls->ctok->name);
+                               ina = 1;
+                       }
+                       goto handle_assert_next;
+               }
+               error(l, "illegal assertion name for #assert");
+               goto handle_assert_warp_ign;
+       }
+       goto handle_assert_trunc;
+
+handle_assert_next:
+       while (!next_token(ls)) {
+               if (ls->ctok->type == NEWLINE) break;
+               if (ttMWS(ls->ctok->type)) continue;
+               if (ls->ctok->type != LPAR) {
+                       error(l, "syntax error in #assert");
+                       goto handle_assert_warp_ign;
+               }
+               goto handle_assert_next2;
+       }
+       goto handle_assert_trunc;
+
+handle_assert_next2:
+       atl = getmem(sizeof(struct token_fifo));
+       atl->art = atl->nt = 0;
+       for (nnp = 1, ltww = 1; nnp && !next_token(ls);) {
+               if (ls->ctok->type == NEWLINE) break;
+               if (ltww && ttMWS(ls->ctok->type)) continue;
+               ltww = ttMWS(ls->ctok->type);
+               if (ls->ctok->type == LPAR) nnp ++;
+               else if (ls->ctok->type == RPAR) {
+                       if (!(-- nnp)) goto handle_assert_next3;
+               }
+               t.type = ls->ctok->type;
+               if (S_TOKEN(t.type)) t.name = sdup(ls->ctok->name);
+               aol(atl->t, atl->nt, t, TOKEN_LIST_MEMG);
+       }
+       goto handle_assert_trunc;
+
+handle_assert_next3:
+       while (!next_token(ls) && ls->ctok->type != NEWLINE) {
+               if (!ttWHI(ls->ctok->type) && (ls->flags & WARN_STANDARD)) {
+                       warning(l, "trailing garbage in #assert");
+               }
+       }
+       if (atl->nt && ttMWS(atl->t[atl->nt - 1].type) && (-- atl->nt) == 0)
+               freemem(atl->t);
+       if (atl->nt == 0) {
+               error(l, "void assertion in #assert");
+               goto handle_assert_error;
+       }
+       for (i = 0; i < a->nbval && cmp_token_list(atl, a->val + i); i ++);
+       if (i != a->nbval) {
+               /* we already have it */
+               ret = 0;
+               goto handle_assert_error;
+       }
+
+       /* This is a new assertion. Let's keep it. */
+       aol(a->val, a->nbval, *atl, TOKEN_LIST_MEMG);
+       if (ina) {
+               HTT_put(&assertions, a, aname);
+               freemem(aname);
+       }
+       if (emit_assertions) {
+               fprintf(emit_output, "#assert %s(", HASH_ITEM_NAME(a));
+               print_token_fifo(atl);
+               fputs(")\n", emit_output);
+       }
+       freemem(atl);
+       return 0;
+
+handle_assert_trunc:
+       error(l, "unfinished #assert");
+handle_assert_error:
+       if (atl) {
+               del_token_fifo(atl);
+               freemem(atl);
+       }
+       if (ina) {
+               freemem(aname);
+               freemem(a);
+       }
+       return ret;
+handle_assert_warp_ign:
+       while (!next_token(ls) && ls->ctok->type != NEWLINE);
+       if (ina) {
+               freemem(aname);
+               freemem(a);
+       }
+       return ret;
+}
+
+/*
+ * for #unassert
+ */
+int handle_unassert(struct lexer_state *ls)
+{
+       int ltww;
+       struct token t;
+       struct token_fifo atl;
+       struct assert *a;
+       int ret = -1;
+       long l = ls->line;
+       int nnp;
+       size_t i;
+
+       atl.art = atl.nt = 0;
+       while (!next_token(ls)) {
+               if (ls->ctok->type == NEWLINE) break;
+               if (ttMWS(ls->ctok->type)) continue;
+               if (ls->ctok->type == NAME) {
+                       if (!(a = HTT_get(&assertions, ls->ctok->name))) {
+                               ret = 0;
+                               goto handle_unassert_warp;
+                       }
+                       goto handle_unassert_next;
+               }
+               error(l, "illegal assertion name for #unassert");
+               goto handle_unassert_warp;
+       }
+       goto handle_unassert_trunc;
+
+handle_unassert_next:
+       while (!next_token(ls)) {
+               if (ls->ctok->type == NEWLINE) break;
+               if (ttMWS(ls->ctok->type)) continue;
+               if (ls->ctok->type != LPAR) {
+                       error(l, "syntax error in #unassert");
+                       goto handle_unassert_warp;
+               }
+               goto handle_unassert_next2;
+       }
+       if (emit_assertions)
+               fprintf(emit_output, "#unassert %s\n", HASH_ITEM_NAME(a));
+       HTT_del(&assertions, HASH_ITEM_NAME(a));
+       return 0;
+
+handle_unassert_next2:
+       for (nnp = 1, ltww = 1; nnp && !next_token(ls);) {
+               if (ls->ctok->type == NEWLINE) break;
+               if (ltww && ttMWS(ls->ctok->type)) continue;
+               ltww = ttMWS(ls->ctok->type);
+               if (ls->ctok->type == LPAR) nnp ++;
+               else if (ls->ctok->type == RPAR) {
+                       if (!(-- nnp)) goto handle_unassert_next3;
+               }
+               t.type = ls->ctok->type;
+               if (S_TOKEN(t.type)) t.name = sdup(ls->ctok->name);
+               aol(atl.t, atl.nt, t, TOKEN_LIST_MEMG);
+       }
+       goto handle_unassert_trunc;
+
+handle_unassert_next3:
+       while (!next_token(ls) && ls->ctok->type != NEWLINE) {
+               if (!ttWHI(ls->ctok->type) && (ls->flags & WARN_STANDARD)) {
+                       warning(l, "trailing garbage in #unassert");
+               }
+       }
+       if (atl.nt && ttMWS(atl.t[atl.nt - 1].type) && (-- atl.nt) == 0)
+               freemem(atl.t);
+       if (atl.nt == 0) {
+               error(l, "void assertion in #unassert");
+               return ret;
+       }
+       for (i = 0; i < a->nbval && cmp_token_list(&atl, a->val + i); i ++);
+       if (i != a->nbval) {
+               /* we have it, undefine it */
+               del_token_fifo(a->val + i);
+               if (i < (a->nbval - 1))
+                       mmvwo(a->val + i, a->val + i + 1, (a->nbval - i - 1)
+                               * sizeof(struct token_fifo));
+               if ((-- a->nbval) == 0) freemem(a->val);
+               if (emit_assertions) {
+                       fprintf(emit_output, "#unassert %s(",
+                               HASH_ITEM_NAME(a));
+                       print_token_fifo(&atl);
+                       fputs(")\n", emit_output);
+               }
+       }
+       ret = 0;
+       goto handle_unassert_finish;
+
+handle_unassert_trunc:
+       error(l, "unfinished #unassert");
+handle_unassert_finish:
+       if (atl.nt) del_token_fifo(&atl);
+       return ret;
+handle_unassert_warp:
+       while (!next_token(ls) && ls->ctok->type != NEWLINE);
+       return ret;
+}
+
+/*
+ * Add the given assertion (as string).
+ */
+int make_assertion(char *aval)
+{
+       struct lexer_state lls;
+       size_t n = strlen(aval) + 1;
+       char *c = sdup(aval);
+       int ret;
+
+       *(c + n - 1) = '\n';
+       init_buf_lexer_state(&lls, 0);
+       lls.flags = DEFAULT_LEXER_FLAGS;
+       lls.input = 0;
+       lls.input_string = (unsigned char *)c;
+       lls.pbuf = 0;
+       lls.ebuf = n;
+       lls.line = -1;
+       ret = handle_assert(&lls);
+       freemem(c);
+       free_lexer_state(&lls);
+       return ret;
+}
+
+/*
+ * Remove the given assertion (as string).
+ */
+int destroy_assertion(char *aval)
+{
+       struct lexer_state lls;
+       size_t n = strlen(aval) + 1;
+       char *c = sdup(aval);
+       int ret;
+
+       *(c + n - 1) = '\n';
+       init_buf_lexer_state(&lls, 0);
+       lls.flags = DEFAULT_LEXER_FLAGS;
+       lls.input = 0;
+       lls.input_string = (unsigned char *)c;
+       lls.pbuf = 0;
+       lls.ebuf = n;
+       lls.line = -1;
+       ret = handle_unassert(&lls);
+       freemem(c);
+       free_lexer_state(&lls);
+       return ret;
+}
+
+/*
+ * erase the assertion table
+ */
+void wipe_assertions(void)
+{
+       if (assertions_init_done) HTT_kill(&assertions);
+       assertions_init_done = 0;
+}
+
+/*
+ * initialize the assertion table
+ */
+void init_assertions(void)
+{
+       wipe_assertions();
+       HTT_init(&assertions, del_assertion);
+       assertions_init_done = 1;
+}
+
+/*
+ * retrieve an assertion from the hash table
+ */
+struct assert *get_assertion(char *name)
+{
+       return HTT_get(&assertions, name);
+}
+
+/*
+ * print already defined assertions
+ */
+void print_assertions(void)
+{
+       HTT_scan(&assertions, print_assert);
+}
diff --git a/libexec/auxcpp/atest.c b/libexec/auxcpp/atest.c
new file mode 100644 (file)
index 0000000..7137d93
--- /dev/null
@@ -0,0 +1,236 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <setjmp.h>
+
+#if defined TEST_NATIVE
+
+#define NATIVE_SIGNED             int
+#define NATIVE_UNSIGNED           unsigned
+
+#define NATIVE_UNSIGNED_BITS      32
+#define NATIVE_SIGNED_MIN         LONG_MIN
+#define NATIVE_SIGNED_MAX         LONG_MAX
+
+#elif defined TEST_SIMUL
+
+#define SIMUL_ARITH_SUBTYPE       unsigned short
+#define SIMUL_SUBTYPE_BITS        16
+#define SIMUL_NUMBITS             31
+
+#else
+
+#error ====== Either TEST_NATIVE or TEST_SIMUL must be defined.
+
+#endif
+
+#define ARITH_TYPENAME            zoinx
+#define ARITH_FUNCTION_HEADER     static inline
+
+#define ARITH_WARNING(type)       z_warn(type)
+#define ARITH_ERROR(type)         z_error(type)
+
+void z_warn(int type);
+void z_error(int type);
+
+#include "arith.c"
+
+#if defined TEST_NATIVE
+
+static inline u_zoinx unsigned_to_uz(unsigned x)
+{
+       return (u_zoinx)x;
+}
+
+static inline s_zoinx int_to_sz(int x)
+{
+       return (s_zoinx)x;
+}
+
+static inline void print_uz(u_zoinx x)
+{
+       printf("%u", x);
+}
+
+static inline void print_sz(s_zoinx x)
+{
+       printf("%d", x);
+}
+
+#else
+
+static inline u_zoinx unsigned_to_uz(unsigned x)
+{
+       u_zoinx v;
+       v.msw = (x >> 16) & 0x7FFFU;
+       v.lsw = x & 0xFFFFU;
+       return v;
+}
+
+static inline s_zoinx int_to_sz(int x)
+{
+       return unsigned_to_uz((unsigned)x);
+}
+
+static inline void print_uz(u_zoinx x)
+{
+       printf("%u", ((unsigned)(x.msw) << 16) + (unsigned)(x.lsw));
+}
+
+static inline void print_sz(s_zoinx x)
+{
+       if (x.msw & 0x4000U) {
+               putchar('-');
+               x = zoinx_u_neg(x);
+       }
+       print_uz(x);
+}
+
+#endif
+
+static inline void print_int(int x)
+{
+       printf("%d", x);
+}
+
+static jmp_buf jbuf;
+
+void z_warn(int type)
+{
+       switch (type) {
+       case ARITH_EXCEP_CONV_O:
+               fputs("[overflow on conversion] ", stdout); break;
+       case ARITH_EXCEP_NEG_O:
+               fputs("[overflow on unary minus] ", stdout); break;
+       case ARITH_EXCEP_NOT_T:
+               fputs("[trap representation on bitwise inversion] ", stdout);
+               break;
+       case ARITH_EXCEP_PLUS_O:
+               fputs("[overflow on addition] ", stdout); break;
+       case ARITH_EXCEP_PLUS_U:
+               fputs("[underflow on addition] ", stdout); break;
+       case ARITH_EXCEP_MINUS_O:
+               fputs("[overflow on subtraction] ", stdout); break;
+       case ARITH_EXCEP_MINUS_U:
+               fputs("[underflow on subtraction] ", stdout); break;
+       case ARITH_EXCEP_AND_T:
+               fputs("[trap representation on bitwise and] ", stdout); break;
+       case ARITH_EXCEP_XOR_T:
+               fputs("[trap representation on bitwise xor] ", stdout); break;
+       case ARITH_EXCEP_OR_T:
+               fputs("[trap representation on bitwise or] ", stdout); break;
+       case ARITH_EXCEP_LSH_W:
+               fputs("[left shift by type width or more] ", stdout); break;
+       case ARITH_EXCEP_LSH_C:
+               fputs("[left shift by negative count] ", stdout); break;
+       case ARITH_EXCEP_LSH_O:
+               fputs("[overflow on left shift] ", stdout); break;
+       case ARITH_EXCEP_LSH_U:
+               fputs("[underflow on left shift] ", stdout); break;
+       case ARITH_EXCEP_RSH_W:
+               fputs("[right shift by type width or more] ", stdout); break;
+       case ARITH_EXCEP_RSH_C:
+               fputs("[right shift by negative count] ", stdout); break;
+       case ARITH_EXCEP_RSH_N:
+               fputs("[right shift of negative value] ", stdout); break;
+       case ARITH_EXCEP_STAR_O:
+               fputs("[overflow on multiplication] ", stdout); break;
+       case ARITH_EXCEP_STAR_U:
+               fputs("[underflow on multiplication] ", stdout); break;
+       default:
+               fprintf(stdout, "UNKNOWN WARNING TYPE: %d\n", type);
+               exit(EXIT_FAILURE);
+       }
+}
+
+void z_error(int type)
+{
+       switch (type) {
+       case ARITH_EXCEP_SLASH_D:
+               fputs("division by 0\n", stdout);
+               break;
+       case ARITH_EXCEP_SLASH_O:
+               fputs("overflow on division\n", stdout);
+               break;
+       case ARITH_EXCEP_PCT_D:
+               fputs("division by 0 on modulus operator\n", stdout);
+               break;
+       default:
+               fprintf(stdout, "UNKNOWN ERROR TYPE: %d\n", type);
+               exit(EXIT_FAILURE);
+       }
+       longjmp(jbuf, 1);
+}
+
+int main(void)
+{
+
+#define OPTRY_GEN(op, x, y, convx, convy, printz)   do { \
+               printf("%s %s %s -> ", #x, #op, #y); \
+               if (!setjmp(jbuf)) { \
+                       printz(zoinx_ ## op (convx(x), convy(y))); \
+                       putchar('\n'); \
+               } \
+       } while (0)
+
+#define IDENT(x)  x
+
+#define OPTRY_UU_U(op, x, y) \
+       OPTRY_GEN(op, x, y, unsigned_to_uz, unsigned_to_uz, print_uz)
+
+#define OPTRY_UI_U(op, x, y) \
+       OPTRY_GEN(op, x, y, unsigned_to_uz, IDENT, print_uz)
+
+#define OPTRY_UU_I(op, x, y) \
+       OPTRY_GEN(op, x, y, unsigned_to_uz, unsigned_to_uz, print_int)
+
+#define OPTRY_SS_S(op, x, y) \
+       OPTRY_GEN(op, x, y, int_to_sz, int_to_sz, print_sz)
+
+#define OPTRY_SI_S(op, x, y) \
+       OPTRY_GEN(op, x, y, int_to_sz, IDENT, print_sz)
+
+#define OPTRY_SS_I(op, x, y) \
+       OPTRY_GEN(op, x, y, int_to_sz, int_to_sz, print_int)
+
+       OPTRY_UU_U(u_plus, 3, 4);
+       OPTRY_UU_U(u_plus, 1549587182, 1790478233);
+       OPTRY_UU_U(u_minus, 1549587182, 1790478233);
+       OPTRY_UU_U(u_minus, 1790478233, 1549587182);
+       OPTRY_UU_U(u_star, 432429875, 347785487);
+       OPTRY_UU_U(u_slash, 432429875, 34487);
+       OPTRY_UU_U(u_pct, 432429875, 34487);
+       OPTRY_UI_U(u_lsh, 1783, 19);
+       OPTRY_UI_U(u_lsh, 1783, 20);
+       OPTRY_UI_U(u_lsh, 1783, 21);
+       OPTRY_UI_U(u_rsh, 475902857, 7);
+       OPTRY_UI_U(u_rsh, 475902857, 17);
+       OPTRY_UI_U(u_rsh, 475902857, 38);
+
+       OPTRY_SS_S(s_plus, 3, 4);
+       OPTRY_SS_S(s_plus, 1549587182, 1790478233);
+       OPTRY_SS_S(s_plus, -1549587182, -1790478233);
+       OPTRY_SS_S(s_minus, 1549587182, 1790478233);
+       OPTRY_SS_S(s_minus, 1790478233, 1549587182);
+       OPTRY_SS_S(s_minus, -1790478233, -1549587182);
+       OPTRY_SS_S(s_minus, -1790478233, 1549587182);
+       OPTRY_SS_S(s_star, 432429875, 347785487);
+       OPTRY_SS_S(s_star, 432429875, -347785487);
+       OPTRY_SS_S(s_slash, 432429875, 34487);
+       OPTRY_SS_S(s_slash, -432429875, 34487);
+       OPTRY_SS_S(s_slash, 432429875, -34487);
+       OPTRY_SS_S(s_slash, -432429875, -34487);
+       OPTRY_SS_S(s_slash, 432429875, 0);
+       OPTRY_SS_S(s_slash, -2147483647 - 1, -1);
+       OPTRY_SS_S(s_pct, 432429875, 34487);
+       OPTRY_SS_S(s_pct, 432429875, 0);
+       OPTRY_SI_S(s_lsh, -1, 10);
+       OPTRY_SI_S(s_lsh, 1783, 19);
+       OPTRY_SI_S(s_lsh, 1783, 20);
+       OPTRY_SI_S(s_lsh, 1783, 21);
+       OPTRY_SI_S(s_rsh, -1024, 8);
+       OPTRY_SI_S(s_rsh, 475902857, 7);
+       OPTRY_SI_S(s_rsh, 475902857, 17);
+
+       return 0;
+}
diff --git a/libexec/auxcpp/config.h b/libexec/auxcpp/config.h
new file mode 100644 (file)
index 0000000..a1bbe99
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * (c) Thomas Pornin 1999 - 2002
+ * (c) Louis P. Santillan 2011
+ * This file is derived from tune.h
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* ====================================================================== */
+/*
+ * The LOW_MEM macro triggers the use of macro storage which uses less
+ * memory. It actually also improves performance on large, modern machines
+ * (due to less cache pressure). This option implies no limitation (except
+ * on the number of arguments a macro may, which is then limited to 32766)
+ * so it is on by default. Non-LOW_MEM code is considered deprecated.
+ */
+#define LOW_MEM
+
+/* ====================================================================== */
+/*
+ * Define AMIGA for systems using "drive letters" at the beginning of
+ * some paths; define MSDOS on systems with drive letters and using
+ * backslashes to seperate directory components.
+ */
+/* #define AMIGA */
+/* #define MSDOS */
+
+/* ====================================================================== */
+/*
+ * Define this if your compiler does not know the strftime() function;
+ * TurboC 2.01 under Msdos does not know strftime().
+ */
+/* #define NOSTRFTIME */
+
+/* ====================================================================== */
+/*
+ * Buffering: there are two levels of buffering on input and output streams:
+ * the standard libc buffering (manageable with setbuf() and setvbuf())
+ * and some buffering provided by ucpp itself. The ucpp buffering uses
+ * two buffers, of size respectively INPUT_BUF_MEMG and OUTPUT_BUF_MEMG
+ * (as defined below).
+ * You can disable one or both of these bufferings by defining the macros
+ * NO_LIBC_BUF and NO_UCPP_BUF.
+ */
+/* #define NO_LIBC_BUF */
+/* #define NO_UCPP_BUF */
+
+/*
+ * On Unix stations, the system call mmap() might be used on input files.
+ * This option is a subclause of ucpp internal buffering. On one station,
+ * a 10% speed improvement was observed. Do not define this unless the
+ * host architecture has the following characteristics:
+ *  -- Posix / Single Unix compliance
+ *  -- Text files correspond one to one with memory representation
+ * If a file is not seekable or not mmapable, ucpp will revert to the
+ * standard fread() solution.
+ *
+ * This feature is still considered beta quality. On some systems where
+ * files can be bigger than memory address space (mainly, 32-bit systems
+ * with files bigger than 4 GB), this option makes ucpp fail to operate
+ * on those extremely large files.
+ */
+#define UCPP_MMAP
+
+/*
+ * Performance issues:
+ * -- On memory-starved systems, such as Minix-i86, do not use ucpp
+ * buffering; keep only libc buffering.
+ * -- If you do not use libc buffering, activate the UCPP_MMAP option.
+ * Note that the UCPP_MMAP option is ignored if ucpp buffering is not
+ * activated.
+ *
+ * On an Athlon 1200 running FreeBSD 4.7, the best performances are
+ * achieved when libc buffering is activated and/or UCPP_MMAP is on.
+ */
+
+/* ====================================================================== */
+/*
+ * Define this if you want ucpp to generate tokenized PRAGMA tokens;
+ * otherwise, it will generate raw string contents. This setting is
+ * irrelevant to the stand-alone version of ucpp.
+ */
+#define PRAGMA_TOKENIZE
+
+/*
+ * Define this to the special character that marks the end of tokens with
+ * a string value inside a tokenized PRAGMA token. The #pragma and _Pragma()
+ * directives which use this character will be a bit more difficult to
+ * decode (but ucpp will not mind). 0 cannot be used. '\n' is fine because
+ * it cannot appear inside a #pragma or _Pragma(), since newlines cannot be
+ * embedded inside tokens, neither directly nor by macro substitution and
+ * stringization. Besides, '\n' is portable.
+ */
+#define PRAGMA_TOKEN_END       ((unsigned char)'\n')
+
+/*
+ * Define this if you want ucpp to include encountered #pragma directives
+ * in its output in non-lexer mode; _Pragma() are translated to equivalent
+ * #pragma directives.
+ */
+#define PRAGMA_DUMP
+
+/*
+ * According to my interpretation of the C99 standard, _Pragma() are
+ * evaluated wherever macro expansion could take place. However, Neil Booth,
+ * whose mother language is English (contrary to me) and who is well aware
+ * of the C99 standard (and especially the C preprocessor) told me that
+ * it was unclear whether _Pragma() are evaluated inside directives such
+ * as #if, #include and #line. If you want to disable the evaluation of
+ * _Pragma() inside such directives, define the following macro.
+ */
+/* #define NO_PRAGMA_IN_DIRECTIVE */
+
+/*
+ * The C99 standard mandates that the operator `##' must yield a single,
+ * valid token, lest undefined behaviour befall upon thy head. Hence,
+ * for instance, `+ ## +=' is forbidden, because `++=' is not a valid
+ * token (although it is a valid list of two tokens, `++' and `=').
+ * However, ucpp only emits a warning for such sin, and unmerges the
+ * tokens (thus emitting `+' then `+=' for that example). When ucpp
+ * produces text output, those two tokens will be separated by a space
+ * character so that the basic rule of text output is preserved: when
+ * parsed again, text output yields the exact same stream of tokens.
+ * That extra space is virtual: it does not count as a true whitespace
+ * token for stringization.
+ *
+ * However, it might be desirable, for some uses other than preprocessing
+ * C source code, not to emit that extra space at all. To make ucpp behave
+ * that way, define the DSHARP_TOKEN_MERGE macro. Please note that this
+ * can trigger spurious token merging. For instance, with that macro
+ * activated, `+ ## +=' will be output as `++=' which, if preprocessed
+ * again, will read as `++' followed by `='.
+ *
+ * All this is irrelevant to lexer mode; and trying to merge incompatible
+ * tokens is a shooting offence, anyway.
+ */
+/* #define DSHARP_TOKEN_MERGE */
+
+/* ====================================================================== */
+/*
+ * Define INMACRO_FLAG to include two flags to the structure lexer_state,
+ * that tell whether tokens come from a macro-replacement, and count those
+ * macro-replacements.
+ */
+/* #define INMACRO_FLAG */
+
+/* ====================================================================== */
+/*
+ * Paths where files are looked for by default, when #include is used.
+ * Typical path is /usr/local/include and /usr/include, in that order.
+ * If you want to set up no path, define the macro to 0.
+ *
+ * For Linux, get gcc includes too, or you will miss things like stddef.h.
+ * The exact path varies much, depending on the distribution.
+ */
+#define STD_INCLUDE_PATH       "/usr/local/include", "/usr/include"
+
+/* ====================================================================== */
+/*
+ * Arithmetic code for evaluation of #if expressions. Evaluation
+ * uses either a native machine type, or an emulated two's complement
+ * type. Division by 0 and overflow on division are considered as errors
+ * and reported as such. If ARITHMETIC_CHECKS is defined, all other
+ * operations that imply undefined or implementation-defined behaviour
+ * are reported as warnings but otherwise performed nonetheless.
+ *
+ * For native type evaluation, the following macros should be defined:
+ *   NATIVE_SIGNED           the native signed type
+ *   NATIVE_UNSIGNED         the native corresponding unsigned type
+ *   NATIVE_UNSIGNED_BITS    the native unsigned type width, in bits
+ *   NATIVE_SIGNED_MIN       the native signed type minimum value
+ *   NATIVE_SIGNED_MAX       the native signed type maximum value
+ *
+ * The code in the arith.c file performs some tricky detection
+ * operations on the native type representation and possible existence
+ * of a trap representation. These operations assume a C99-compliant
+ * compiler; on a C90-only compiler, the operations are valid but may
+ * yield incorrect results. You may force those settings with some
+ * more macros: see the comments in arith.c (look for "ARCH_DEFINED").
+ * Remember that this is mostly a non-issue, unless you are building
+ * ucpp with a pre-C99 cross-compiler and either the host or target
+ * architecture uses a non-two's complement representation of signed
+ * integers. Such a combination is pretty rare nowadays, so the best
+ * you can do is forgetting completely this paragraph and live in peace.
+ *
+ *
+ * If you do not have a handy native type (for instance, you compile ucpp
+ * with a C90 compiler which lacks the "long long" type, or you compile
+ * ucpp for a cross-compiler which should support an evaluation integer
+ * type of a size that is not available on the host machine), you may use
+ * a simulated type. The type uses two's complement representation and
+ * may have any width from 2 bits to twice the underlying native type
+ * width, inclusive (odd widths are allowed). To use an emulated type,
+ * make sure that NATIVE_SIGNED is not defined, and define the following
+ * macros:
+ *   SIMUL_ARITH_SUBTYPE     the native underlying type to use
+ *   SIMUL_SUBTYPE_BITS      the native underlying type width
+ *   SIMUL_NUMBITS           the emulated type width
+ *
+ * Undefined and implementation-defined behaviours are warned upon, if
+ * ARITHMETIC_CHECKS is defined. Results are truncated to the type
+ * width; shift count for the << and >> operators is reduced modulo the
+ * emulatd type width; right shifting of a signed negative value performs
+ * sign extension (the result is left-padded with bits set to 1).
+ */
+
+/*
+ * For native type evaluation with a 64-bit "long long" type.
+ */
+#define NATIVE_SIGNED           long long
+#define NATIVE_UNSIGNED         unsigned long long
+#define NATIVE_UNSIGNED_BITS    64
+#define NATIVE_SIGNED_MIN       (-9223372036854775807LL - 1)
+#define NATIVE_SIGNED_MAX       9223372036854775807LL
+
+/*
+ * For emulation of a 64-bit type using a native 32-bit "unsigned long"
+ * type.
+#undef NATIVE_SIGNED
+#define SIMUL_ARITH_SUBTYPE     unsigned long
+#define SIMUL_SUBTYPE_BITS      32
+#define SIMUL_NUMBITS           64
+ */
+
+/*
+ * Comment out the following line if you want to deactivate arithmetic
+ * checks (warnings upon undefined and implementation-defined
+ * behaviour). Arithmetic checks slow down a bit arithmetic operations,
+ * especially multiplications, but this should not be an issue with
+ * typical C source code.
+ */
+#define ARITHMETIC_CHECKS
+
+/* ====================================================================== */
+/*
+ * To force signedness of wide character constants, define WCHAR_SIGNEDNESS
+ * to 0 for unsigned, 1 for signed. By default, wide character constants
+ * are signed if the native `char' type is signed, and unsigned otherwise.
+#define WCHAR_SIGNEDNESS       0
+ */
+
+/*
+ * Standard assertions. They should include one cpu() assertion, one machine()
+ * assertion (identical to cpu()), and one or more system() assertions.
+ *
+ * for Linux/PC:      cpu(i386),  machine(i386),  system(unix), system(linux)
+ * for Linux/Alpha:   cpu(alpha), machine(alpha), system(unix), system(linux)
+ * for Sparc/Solaris: cpu(sparc), machine(sparc), system(unix), system(solaris)
+ *
+ * These are only suggestions. On Solaris, machine() should be defined
+ * for i386 or sparc (standard system header use such an assertion). For
+ * cross-compilation, define assertions related to the target architecture.
+ *
+ * If you want no standard assertion, define STD_ASSERT to 0.
+ */
+#define STD_ASSERT 0
+/*
+#define STD_ASSERT     "cpu(i386)", "machine(i386)", "system(unix)", \
+                       "system(freebsd)"
+*/
+
+/* ====================================================================== */
+/*
+ * System predefined macros. Nothing really mandatory, but some programs
+ * might rely on those.
+ * Each string must be either "name" or "name=token-list". If you want
+ * no predefined macro, define STD_MACROS to 0.
+ */
+#define STD_MACROS 0
+/*
+#define STD_MACROS     "__FreeBSD=4", "__unix", "__i386", \
+                       "__FreeBSD__=4", "__unix__", "__i386__"
+*/
+
+/* ====================================================================== */
+/*
+ * Default flags; HANDLE_ASSERTIONS is required for Solaris system headers.
+ * See cpp.h for the definition of these flags.
+ */
+#define DEFAULT_CPP_FLAGS      (DISCARD_COMMENTS | WARN_STANDARD \
+                               | WARN_PRAGMA | FAIL_SHARP | MACRO_VAARG \
+                               | CPLUSPLUS_COMMENTS | LINE_NUM | TEXT_OUTPUT \
+                               | KEEP_OUTPUT | HANDLE_TRIGRAPHS \
+                               | HANDLE_ASSERTIONS)
+#define DEFAULT_LEXER_FLAGS    (DISCARD_COMMENTS | WARN_STANDARD | FAIL_SHARP \
+                               | MACRO_VAARG | CPLUSPLUS_COMMENTS | LEXER \
+                               | HANDLE_TRIGRAPHS | HANDLE_ASSERTIONS)
+
+/* ====================================================================== */
+/*
+ * Define this to use sigsetjmp()/siglongjmp() instead of setjmp()/longjmp().
+ * This is non-ANSI, but it improves performance on some POSIX system.
+ * On typical C source code, such improvement is completely negligeable.
+ */
+/* #define POSIX_JMP */
+
+/* ====================================================================== */
+/*
+ * Maximum value (plus one) of a character handled by the lexer; 128 is
+ * alright for ASCII native source code, but 256 is needed for EBCDIC.
+ * 256 is safe in both cases; you will have big problems if you set
+ * this value to INT_MAX or above. On Minix-i86 or Msdos (small memory
+ * model), define MAX_CHAR_VAL to 128.
+ *
+ * Set MAX_CHAR_VAL to a power of two to increase lexing speed. Beware
+ * that lexer.c defines a static array of size MSTATE * MAX_CHAR_VAL
+ * values of type int (MSTATE is defined in lexer.c and is about 40).
+ */
+#define MAX_CHAR_VAL   128
+
+/*
+ * If you want some extra character to be considered as whitespace,
+ * define this macro to that space. On ISO-8859-1 machines, 160 is
+ * the code for the unbreakable space.
+ */
+/* #define UNBREAKABLE_SPACE   160 */
+
+/*
+ * If you want whitespace tokens contents to be recorded (making them
+ * tokens with a string content), define this. The macro STRING_TOKEN
+ * will be adjusted accordingly.
+ * Without this option, whitespace tokens are not even returned by the
+ * lex() function. This is irrelevant for the non-lexer mode (almost --
+ * it might slow down a bit ucpp, and with this option, comments will be
+ * kept inside #pragma directives).
+ */
+/* #define SEMPER_FIDELIS */
+
+/* End of options overridable by UCPP_CONFIG and config.h */
diff --git a/libexec/auxcpp/cpp.c b/libexec/auxcpp/cpp.c
new file mode 100644 (file)
index 0000000..7cdc358
--- /dev/null
@@ -0,0 +1,2565 @@
+/*
+ * C and T preprocessor, and integrated lexer
+ * (c) Thomas Pornin 1999 - 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#define VERS_MAJ       1
+#define VERS_MIN       3
+/* uncomment the following if you cannot set it with a compiler flag */
+/* #define STAND_ALONE */
+
+#include "tune.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <stddef.h>
+#include <limits.h>
+#include <time.h>
+#include "ucppi.h"
+#include "mem.h"
+#include "nhash.h"
+#ifdef UCPP_MMAP
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#endif
+
+/*
+ * The standard path where includes are looked for.
+ */
+#ifdef STAND_ALONE
+static char *include_path_std[] = { STD_INCLUDE_PATH, 0 };
+#endif
+static char **include_path;
+static size_t include_path_nb = 0;
+
+int no_special_macros = 0;
+int emit_dependencies = 0, emit_defines = 0, emit_assertions = 0;
+FILE *emit_output;
+
+#ifdef STAND_ALONE
+static char *system_macros_def[] = { STD_MACROS, 0 };
+static char *system_assertions_def[] = { STD_ASSERT, 0 };
+#endif
+
+char *current_filename = 0, *current_long_filename = 0;
+static int current_incdir = -1;
+
+#ifndef NO_UCPP_ERROR_FUNCTIONS
+/*
+ * "ouch" is the name for an internal ucpp error. If AUDIT is not defined,
+ * no code calling this function will be generated; a "ouch" may still be
+ * emitted by getmem() (in mem.c) if MEM_CHECK is defined, but this "ouch"
+ * does not use this function.
+ */
+void ucpp_ouch(char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       fprintf(stderr, "%s: ouch, ", current_filename);
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n");
+       va_end(ap);
+       die();
+}
+
+/*
+ * report an error, with current_filename, line, and printf-like syntax
+ */
+void ucpp_error(long line, char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       if (line > 0)
+               fprintf(stderr, "%s: line %ld: ", current_filename, line);
+       else if (line == 0) fprintf(stderr, "%s: ", current_filename);
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n");
+       if (line >= 0) {
+               struct stack_context *sc = report_context();
+               size_t i;
+
+               for (i = 0; sc[i].line >= 0; i ++)
+                       fprintf(stderr, "\tincluded from %s:%ld\n",
+                               sc[i].long_name ? sc[i].long_name : sc[i].name,
+                               sc[i].line);
+               freemem(sc);
+       }
+       va_end(ap);
+}
+
+/*
+ * like error(), with the mention "warning"
+ */
+void ucpp_warning(long line, char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       if (line > 0)
+               fprintf(stderr, "%s: warning: line %ld: ",
+                       current_filename, line);
+       else if (line == 0)
+               fprintf(stderr, "%s: warning: ", current_filename);
+       else fprintf(stderr, "warning: ");
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n");
+       if (line >= 0) {
+               struct stack_context *sc = report_context();
+               size_t i;
+
+               for (i = 0; sc[i].line >= 0; i ++)
+                       fprintf(stderr, "\tincluded from %s:%ld\n",
+                               sc[i].long_name ? sc[i].long_name : sc[i].name,
+                               sc[i].line);
+               freemem(sc);
+       }
+       va_end(ap);
+}
+#endif /* NO_UCPP_ERROR_FUNCTIONS */
+
+/*
+ * Some memory allocations are manually garbage-collected; essentially,
+ * strings duplicated in the process of macro replacement. Each such
+ * string is referenced in the garbage_fifo, which is cleared when all
+ * nested macros have been resolved.
+ */
+
+struct garbage_fifo {
+       char **garbage;
+       size_t ngarb, memgarb;
+};
+
+/*
+ * throw_away() marks a string to be collected later
+ */
+void throw_away(struct garbage_fifo *gf, char *n)
+{
+       wan(gf->garbage, gf->ngarb, n, gf->memgarb);
+}
+
+/*
+ * free marked strings
+ */
+void garbage_collect(struct garbage_fifo *gf)
+{
+       size_t i;
+
+       for (i = 0; i < gf->ngarb; i ++) freemem(gf->garbage[i]);
+       gf->ngarb = 0;
+}
+
+static void init_garbage_fifo(struct garbage_fifo *gf)
+{
+       gf->garbage = getmem((gf->memgarb = GARBAGE_LIST_MEMG)
+               * sizeof(char *));
+       gf->ngarb = 0;
+}
+
+static void free_garbage_fifo(struct garbage_fifo *gf)
+{
+       garbage_collect(gf);
+       freemem(gf->garbage);
+       freemem(gf);
+}
+
+/*
+ * order is important: it must match the token-constants declared as an
+ * enum in the header file.
+ */
+char *operators_name[] = {
+       " ", "\n", " ",
+       "0000", "name", "bunch", "pragma", "context",
+       "\"dummy string\"", "'dummy char'",
+       "/", "/=", "-", "--", "-=", "->", "+", "++", "+=", "<", "<=", "<<",
+       "<<=", ">", ">=", ">>", ">>=", "=", "==",
+#ifdef CAST_OP
+       "=>",
+#endif
+       "~", "!=", "&", "&&", "&=", "|", "||", "|=", "%", "%=", "*", "*=",
+       "^", "^=", "!",
+       "{", "}", "[", "]", "(", ")", ",", "?", ";",
+       ":", ".", "...", "#", "##", " ", "ouch", "<:", ":>", "<%", "%>",
+       "%:", "%:%:"
+};
+
+/* the ascii representation of a token */
+#ifdef SEMPER_FIDELIS
+#define tname(x)       (ttWHI((x).type) ? " " : S_TOKEN((x).type) \
+                       ? (x).name : operators_name[(x).type])
+#else
+#define tname(x)       (S_TOKEN((x).type) ? (x).name \
+                       : operators_name[(x).type])
+#endif
+
+char *token_name(struct token *t)
+{
+       return tname(*t);
+}
+
+/*
+ * To speed up deeply nested and repeated inclusions, we:
+ * -- use a hash table to remember where we found each file
+ * -- remember when the file is protected by a #ifndef/#define/#endif
+ *    construction; we can then avoid including several times a file
+ *    when this is not necessary.
+ * -- remember in which directory, in the include path, the file was found.
+ */
+struct found_file {
+       hash_item_header head;    /* first field */
+       char *name;
+       char *protect;
+};
+
+/*
+ * For files from system include path.
+ */
+struct found_file_sys {
+       hash_item_header head;    /* first field */
+       struct found_file *rff;
+       int incdir;
+};
+
+static HTT found_files, found_files_sys;
+static int found_files_init_done = 0, found_files_sys_init_done = 0;
+
+static struct found_file *new_found_file(void)
+{
+       struct found_file *ff = getmem(sizeof(struct found_file));
+
+       ff->name = 0;
+       ff->protect = 0;
+       return ff;
+}
+
+static void del_found_file(void *m)
+{
+       struct found_file *ff = (struct found_file *)m;
+
+       if (ff->name) freemem(ff->name);
+       if (ff->protect) freemem(ff->protect);
+       freemem(ff);
+}
+
+static struct found_file_sys *new_found_file_sys(void)
+{
+       struct found_file_sys *ffs = getmem(sizeof(struct found_file_sys));
+
+       ffs->rff = 0;
+       ffs->incdir = -1;
+       return ffs;
+}
+
+static void del_found_file_sys(void *m)
+{
+       struct found_file_sys *ffs = (struct found_file_sys *)m;
+
+       freemem(ffs);
+}
+
+/*
+ * To keep up with the #ifndef/#define/#endif protection mechanism
+ * detection.
+ */
+struct protect protect_detect;
+static struct protect *protect_detect_stack = 0;
+
+void set_init_filename(char *x, int real_file)
+{
+       if (current_filename) freemem(current_filename);
+       current_filename = sdup(x);
+       current_long_filename = 0;
+       current_incdir = -1;
+       if (real_file) {
+               protect_detect.macro = 0;
+               protect_detect.state = 1;
+               protect_detect.ff = new_found_file();
+               protect_detect.ff->name = sdup(x);
+               HTT_put(&found_files, protect_detect.ff, x);
+       } else {
+               protect_detect.state = 0;
+       }
+}
+
+static void init_found_files(void)
+{
+       if (found_files_init_done) HTT_kill(&found_files);
+       HTT_init(&found_files, del_found_file);
+       found_files_init_done = 1;
+       if (found_files_sys_init_done) HTT_kill(&found_files_sys);
+       HTT_init(&found_files_sys, del_found_file_sys);
+       found_files_sys_init_done = 1;
+}
+
+/*
+ * Set the lexer state at the beginning of a file.
+ */
+static void reinit_lexer_state(struct lexer_state *ls, int wb)
+{
+#ifndef NO_UCPP_BUF
+       ls->input_buf = wb ? getmem(INPUT_BUF_MEMG) : 0;
+#ifdef UCPP_MMAP
+       ls->from_mmap = 0;
+#endif
+#endif
+       ls->input = 0;
+       ls->ebuf = ls->pbuf = 0;
+       ls->nlka = 0;
+       ls->macfile = 0;
+       ls->discard = 1;
+       ls->last = 0;           /* we suppose '\n' is not 0 */
+       ls->line = 1;
+       ls->ltwnl = 1;
+       ls->oline = 1;
+       ls->pending_token = 0;
+       ls->cli = 0;
+       ls->copy_line[COPY_LINE_LENGTH - 1] = 0;
+       ls->ifnest = 0;
+       ls->condf[0] = ls->condf[1] = 0;
+}
+
+/*
+ * Initialize the struct lexer_state, with optional input and output buffers.
+ */
+void init_buf_lexer_state(struct lexer_state *ls, int wb)
+{
+       reinit_lexer_state(ls, wb);
+#ifndef NO_UCPP_BUF
+       ls->output_buf = wb ? getmem(OUTPUT_BUF_MEMG) : 0;
+#endif
+       ls->sbuf = 0;
+       ls->output_fifo = 0;
+
+       ls->ctok = getmem(sizeof(struct token));
+       ls->ctok->name = getmem(ls->tknl = TOKEN_NAME_MEMG);
+       ls->pending_token = 0;
+
+       ls->flags = 0;
+       ls->count_trigraphs = 0;
+       ls->gf = getmem(sizeof(struct garbage_fifo));
+       init_garbage_fifo(ls->gf);
+       ls->condcomp = 1;
+       ls->condnest = 0;
+#ifdef INMACRO_FLAG
+       ls->inmacro = 0;
+       ls->macro_count = 0;
+#endif
+}
+
+/*
+ * Initialize the (complex) struct lexer_state.
+ */
+void init_lexer_state(struct lexer_state *ls)
+{
+       init_buf_lexer_state(ls, 1);
+       ls->input = 0;
+}
+
+/*
+ * Restore what is needed from a lexer_state. This is used for #include.
+ */
+static void restore_lexer_state(struct lexer_state *ls,
+       struct lexer_state *lsbak)
+{
+#ifndef NO_UCPP_BUF
+       freemem(ls->input_buf);
+       ls->input_buf = lsbak->input_buf;
+#ifdef UCPP_MMAP
+       ls->from_mmap = lsbak->from_mmap;
+       ls->input_buf_sav = lsbak->input_buf_sav;
+#endif
+#endif
+       ls->input = lsbak->input;
+       ls->ebuf = lsbak->ebuf;
+       ls->pbuf = lsbak->pbuf;
+       ls->nlka = lsbak->nlka;
+       ls->discard = lsbak->discard;
+       ls->line = lsbak->line;
+       ls->oline = lsbak->oline;
+       ls->ifnest = lsbak->ifnest;
+       ls->condf[0] = lsbak->condf[0];
+       ls->condf[1] = lsbak->condf[1];
+}
+
+/*
+ * close input file operations on a struct lexer_state
+ */
+static void close_input(struct lexer_state *ls)
+{
+#ifdef UCPP_MMAP
+       if (ls->from_mmap) {
+               munmap((void *)ls->input_buf, ls->ebuf);
+               ls->from_mmap = 0;
+               ls->input_buf = ls->input_buf_sav;
+       }
+#endif
+       if (ls->input) {
+               fclose(ls->input);
+               ls->input = 0;
+       }
+}
+
+/*
+ * file_context (and the two functions push_ and pop_) are used to save
+ * all that is needed when including a file.
+ */
+static struct file_context {
+       struct lexer_state ls;
+       char *name, *long_name;
+       int incdir;
+} *ls_stack;
+static size_t ls_depth = 0;
+
+static void push_file_context(struct lexer_state *ls)
+{
+       struct file_context fc;
+
+       fc.name = current_filename;
+       fc.long_name = current_long_filename;
+       fc.incdir = current_incdir;
+       mmv(&(fc.ls), ls, sizeof(struct lexer_state));
+       aol(ls_stack, ls_depth, fc, LS_STACK_MEMG);
+       ls_depth --;
+       aol(protect_detect_stack, ls_depth, protect_detect, LS_STACK_MEMG);
+       protect_detect.macro = 0;
+}
+
+static void pop_file_context(struct lexer_state *ls)
+{
+#ifdef AUDIT
+       if (ls_depth <= 0) ouch("prepare to meet thy creator");
+#endif
+       close_input(ls);
+       restore_lexer_state(ls, &(ls_stack[-- ls_depth].ls));
+       if (protect_detect.macro) freemem(protect_detect.macro);
+       protect_detect = protect_detect_stack[ls_depth];
+       if (current_filename) freemem(current_filename);
+       current_filename = ls_stack[ls_depth].name;
+       current_long_filename = ls_stack[ls_depth].long_name;
+       current_incdir = ls_stack[ls_depth].incdir;
+       if (ls_depth == 0) {
+               freemem(ls_stack);
+               freemem(protect_detect_stack);
+       }
+}
+
+/*
+ * report_context() returns the list of successive includers of the
+ * current file, ending with a dummy entry with a negative line number.
+ * The caller is responsible for freeing the returned pointer.
+ */
+struct stack_context *report_context(void)
+{
+       struct stack_context *sc;
+       size_t i;
+
+       sc = getmem((ls_depth + 1) * sizeof(struct stack_context));
+       for (i = 0; i < ls_depth; i ++) {
+               sc[i].name = ls_stack[ls_depth - i - 1].name;
+               sc[i].long_name = ls_stack[ls_depth - i - 1].long_name;
+               sc[i].line = ls_stack[ls_depth - i - 1].ls.line - 1;
+       }
+       sc[ls_depth].line = -1;
+       return sc;
+}
+
+/*
+ * init_lexer_mode() is used to end initialization of a struct lexer_state
+ * if it must be used for a lexer
+ */
+void init_lexer_mode(struct lexer_state *ls)
+{
+       ls->flags = DEFAULT_LEXER_FLAGS;
+       ls->output_fifo = getmem(sizeof(struct token_fifo));
+       ls->output_fifo->art = ls->output_fifo->nt = 0;
+       ls->toplevel_of = ls->output_fifo;
+       ls->save_ctok = ls->ctok;
+}
+
+/*
+ * release memory used by a struct lexer_state; this implies closing
+ * any input stream held by this structure.
+ */
+void free_lexer_state(struct lexer_state *ls)
+{
+       close_input(ls);
+#ifndef NO_UCPP_BUF
+       if (ls->input_buf) {
+               freemem(ls->input_buf);
+               ls->input_buf = 0;
+       }
+       if (ls->output_buf) {
+               freemem(ls->output_buf);
+               ls->output_buf = 0;
+       }
+#endif
+       if (ls->ctok && (!ls->output_fifo || ls->output_fifo->nt == 0)) {
+               freemem(ls->ctok->name);
+               freemem(ls->ctok);
+               ls->ctok = 0;
+       }
+       if (ls->gf) {
+               free_garbage_fifo(ls->gf);
+               ls->gf = 0;
+       }
+       if (ls->output_fifo) {
+               freemem(ls->output_fifo);
+               ls->output_fifo = 0;
+       }
+}
+
+/*
+ * Print line information.
+ */
+static void print_line_info(struct lexer_state *ls, unsigned long flags)
+{
+       char *fn = current_long_filename ?
+               current_long_filename : current_filename;
+       char *b, *d;
+
+       b = getmem(50 + strlen(fn));
+       if (flags & GCC_LINE_NUM) {
+               sprintf(b, "# %ld \"%s\"\n", ls->line, fn);
+       } else {
+               sprintf(b, "#line %ld \"%s\"\n", ls->line, fn);
+       }
+       for (d = b; *d; d ++) put_char(ls, (unsigned char)(*d));
+       freemem(b);
+}
+
+/*
+ * Enter a file; this implies the possible emission of a #line directive.
+ * The flags used are passed as second parameter instead of being
+ * extracted from the struct lexer_state.
+ *
+ * As a command-line option, gcc-like directives (with only a '#',
+ * without 'line') may be produced.
+ *
+ * enter_file() returns 1 if a (CONTEXT) token was produced, 0 otherwise.
+ */
+int enter_file(struct lexer_state *ls, unsigned long flags)
+{
+       char *fn = current_long_filename ?
+               current_long_filename : current_filename;
+
+       if (!(flags & LINE_NUM)) return 0;
+       if ((flags & LEXER) && !(flags & TEXT_OUTPUT)) {
+               struct token t;
+
+               t.type = CONTEXT;
+               t.line = ls->line;
+               t.name = fn;
+               print_token(ls, &t, 0);
+               return 1;
+       }
+       print_line_info(ls, flags);
+       ls->oline --;   /* emitted #line troubled oline */
+       return 0;
+}
+
+#ifdef UCPP_MMAP
+/*
+ * We open() the file, then fdopen() it and fseek() to its end. If the
+ * fseek() worked, we try to mmap() the file, up to the point where we
+ * arrived.
+ * On an architecture where end-of-lines are multibytes and translated
+ * into single '\n', bad things could happen. We strongly hope that, if
+ * we could fseek() to the end but could not mmap(), then we can get back.
+ */
+static void *find_file_map;
+static size_t map_length;
+
+FILE *fopen_mmap_file(char *name)
+{
+       FILE *f;
+       int fd;
+       long l;
+
+       find_file_map = 0;
+       fd = open(name, O_RDONLY, 0);
+       if (fd < 0) return 0;
+       l = lseek(fd, 0, SEEK_END);
+       f = fdopen(fd, "r");
+       if (!f) {
+               close(fd);
+               return 0;
+       }
+       if (l < 0) return f;    /* not seekable */
+       map_length = l;
+       if ((find_file_map = mmap(0, map_length, PROT_READ,
+               MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
+               /* we could not mmap() the file; get back */
+               find_file_map = 0;
+               if (fseek(f, 0, SEEK_SET)) {
+                       /* bwaah... can't get back. This file is cursed. */
+                       fclose(f);
+                       return 0;
+               }
+       }
+       return f;
+}
+
+void set_input_file(struct lexer_state *ls, FILE *f)
+{
+       ls->input = f;
+       if (find_file_map) {
+               ls->from_mmap = 1;
+               ls->input_buf_sav = ls->input_buf;
+               ls->input_buf = find_file_map;
+               ls->pbuf = 0;
+               ls->ebuf = map_length;
+       } else {
+               ls->from_mmap = 0;
+       }
+}
+#endif
+
+/*
+ * Find a file by looking through the include path.
+ * return value: a FILE * on the file, opened in "r" mode, or 0.
+ *
+ * find_file_error will contain:
+ *   FF_ERROR      on error (file not found or impossible to read)
+ *   FF_PROTECT    file is protected and therefore useless to read
+ *   FF_KNOWN      file is already known
+ *   FF_UNKNOWN    file was not already known
+ */
+static int find_file_error;
+
+enum { FF_ERROR, FF_PROTECT, FF_KNOWN, FF_UNKNOWN };
+
+static FILE *find_file(char *name, int localdir)
+{
+       FILE *f;
+       int i, incdir = -1;
+       size_t nl = strlen(name);
+       char *s = 0;
+       struct found_file *ff = 0, *nff;
+       int lf = 0;
+       int nffa = 0;
+
+       find_file_error = FF_ERROR;
+       protect_detect.state = -1;
+       protect_detect.macro = 0;
+       if (localdir) {
+               int i;
+               char *rfn = current_long_filename ? current_long_filename
+                       : current_filename;
+
+               for (i = strlen(rfn) - 1; i >= 0; i --)
+#ifdef MSDOS
+                       if (rfn[i] == '\\') break;
+#else
+                       if (rfn[i] == '/') break;
+#endif
+#if defined MSDOS
+               if (i >= 0 && *name != '\\' && (nl < 2 || name[1] != ':'))
+#elif defined AMIGA
+               if (i >= 0 && *name != '/' && (nl < 2 || name[1] != ':'))
+#else
+               if (i >= 0 && *name != '/')
+#endif
+               {
+                       /*
+                        * current file is somewhere else, and the provided
+                        * file name is not absolute, so we must adjust the
+                        * base for looking for the file; besides,
+                        * found_files and found_files_loc are irrelevant
+                        * for this search.
+                        */
+                       s = getmem(i + 2 + nl);
+                       mmv(s, rfn, i);
+#ifdef MSDOS
+                       s[i] = '\\';
+#else
+                       s[i] = '/';
+#endif
+                       mmv(s + i + 1, name, nl);
+                       s[i + 1 + nl] = 0;
+                       ff = HTT_get(&found_files, s);
+               } else ff = HTT_get(&found_files, name);
+       }
+       if (!ff) {
+               struct found_file_sys *ffs = HTT_get(&found_files_sys, name);
+
+               if (ffs) {
+                       ff = ffs->rff;
+                       incdir = ffs->incdir;
+               }
+       }
+       /*
+        * At that point: if the file was found in the cache, ff points to
+        * the cached descriptive structure; its name is s if s is not 0,
+        * name otherwise.
+        */
+       if (ff) goto found_file_cache;
+
+       /*
+        * This is the first time we find the file, or it was not protected.
+        */
+       protect_detect.ff = new_found_file();
+       nffa = 1;
+       if (localdir &&
+#ifdef UCPP_MMAP
+               (f = fopen_mmap_file(s ? s : name))
+#else
+               (f = fopen(s ? s : name, "r"))
+#endif
+               ) {
+               lf = 1;
+               goto found_file;
+       }
+       /*
+        * If s contains a name, that name is now irrelevant: it was a
+        * filename for a search in the current directory, and the file
+        * was not found.
+        */
+       if (s) {
+               freemem(s);
+               s = 0;
+       }
+       for (i = 0; (size_t)i < include_path_nb; i ++) {
+               size_t ni = strlen(include_path[i]);
+
+               s = getmem(ni + nl + 2);
+               mmv(s, include_path[i], ni);
+#ifdef AMIGA
+       /* contributed by Volker Barthelmann */
+               if (ni == 1 && *s == '.') {
+                       *s = 0;
+                       ni = 0;
+               }
+               if (ni > 0 && s[ni - 1] != ':' && s[ni - 1] != '/') {
+                       s[ni] = '/';
+                       mmv(s + ni + 1, name, nl + 1);
+               } else {
+                       mmv(s + ni, name, nl + 1);
+               }
+#else
+               s[ni] = '/';
+               mmv(s + ni + 1, name, nl + 1);
+#endif
+#ifdef MSDOS
+               /* on msdos systems, replace all / by \ */
+               {
+                       char *c;
+
+                       for (c = s; *c; c ++) if (*c == '/') *c = '\\';
+               }
+#endif
+               incdir = i;
+               if ((ff = HTT_get(&found_files, s)) != 0) {
+                       /*
+                        * The file is known, but not as a system include
+                        * file under the name provided.
+                        */
+                       struct found_file_sys *ffs = new_found_file_sys();
+
+                       ffs->rff = ff;
+                       ffs->incdir = incdir;
+                       HTT_put(&found_files_sys, ffs, name);
+                       freemem(s);
+                       s = 0;
+                       if (nffa) {
+                               del_found_file(protect_detect.ff);
+                               protect_detect.ff = 0;
+                               nffa = 0;
+                       }
+                       goto found_file_cache;
+               }
+#ifdef UCPP_MMAP
+               f = fopen_mmap_file(s);
+#else
+               f = fopen(s, "r");
+#endif
+               if (f) goto found_file;
+               freemem(s);
+               s = 0;
+       }
+zero_out:
+       if (s) freemem(s);
+       if (nffa) {
+               del_found_file(protect_detect.ff);
+               protect_detect.ff = 0;
+               nffa = 0;
+       }
+       return 0;
+
+       /*
+        * This part is invoked when the file was found in the
+        * cache.
+        */
+found_file_cache:
+       if (ff->protect) {
+               if (get_macro(ff->protect)) {
+                       /* file is protected, do not include it */
+                       find_file_error = FF_PROTECT;
+                       goto zero_out;
+               }
+               /* file is protected but the guardian macro is
+                  not available; disable guardian detection. */
+               protect_detect.state = 0;
+       }
+       protect_detect.ff = ff;
+#ifdef UCPP_MMAP
+       f = fopen_mmap_file(HASH_ITEM_NAME(ff));
+#else
+       f = fopen(HASH_ITEM_NAME(ff), "r");
+#endif
+       if (!f) goto zero_out;
+       find_file_error = FF_KNOWN;
+       goto found_file_2;
+
+       /*
+        * This part is invoked when we found a new file, which was not
+        * yet referenced. If lf == 1, then the file was found directly,
+        * otherwise it was found in some system include directory.
+        * A new found_file structure has been allocated and is in
+        * protect_detect.ff
+        */
+found_file:
+       if (f && ((emit_dependencies == 1 && lf && current_incdir == -1)
+               || emit_dependencies == 2)) {
+               fprintf(emit_output, " %s", s ? s : name);
+       }
+       nff = protect_detect.ff;
+       nff->name = sdup(name);
+#ifdef AUDIT
+       if (
+#endif
+       HTT_put(&found_files, nff, s ? s : name)
+#ifdef AUDIT
+       ) ouch("filename collided with a wraith")
+#endif
+       ;
+       if (!lf) {
+               struct found_file_sys *ffs = new_found_file_sys();
+
+               ffs->rff = nff;
+               ffs->incdir = incdir;
+               HTT_put(&found_files_sys, ffs, name);
+       }
+       if (s) freemem(s);
+       s = 0;
+       find_file_error = FF_UNKNOWN;
+       ff = nff;
+
+found_file_2:
+       if (s) freemem(s);
+       current_long_filename = HASH_ITEM_NAME(ff);
+#ifdef NO_LIBC_BUF
+       setbuf(f, 0);
+#endif
+       current_incdir = incdir;
+       return f;
+}
+
+/*
+ * Find the named file by looking through the end of the include path.
+ * This is for #include_next directives.
+ * #include_next <foo> and #include_next "foo" are considered identical,
+ * for all practical purposes.
+ */
+static FILE *find_file_next(char *name)
+{
+       int i;
+       size_t nl = strlen(name);
+       FILE *f;
+       struct found_file *ff;
+
+       find_file_error = FF_ERROR;
+       protect_detect.state = -1;
+       protect_detect.macro = 0;
+       for (i = current_incdir + 1; (size_t)i < include_path_nb; i ++) {
+               char *s;
+               size_t ni = strlen(include_path[i]);
+
+               s = getmem(ni + nl + 2);
+               mmv(s, include_path[i], ni);
+               s[ni] = '/';
+               mmv(s + ni + 1, name, nl + 1);
+#ifdef MSDOS
+               /* on msdos systems, replace all / by \ */
+               {
+                       char *c;
+
+                       for (c = s; *c; c ++) if (*c == '/') *c = '\\';
+               }
+#endif
+               ff = HTT_get(&found_files, s);
+               if (ff) {
+                       /* file was found in the cache */
+                       if (ff->protect) {
+                               if (get_macro(ff->protect)) {
+                                       find_file_error = FF_PROTECT;
+                                       freemem(s);
+                                       return 0;
+                               }
+                               /* file is protected but the guardian macro is
+                                  not available; disable guardian detection. */
+                               protect_detect.state = 0;
+                       }
+                       protect_detect.ff = ff;
+#ifdef UCPP_MMAP
+                       f = fopen_mmap_file(HASH_ITEM_NAME(ff));
+#else
+                       f = fopen(HASH_ITEM_NAME(ff), "r");
+#endif
+                       if (!f) {
+                               /* file is referenced but yet unavailable. */
+                               freemem(s);
+                               return 0;
+                       }
+                       find_file_error = FF_KNOWN;
+                       freemem(s);
+                       s = HASH_ITEM_NAME(ff);
+               } else {
+#ifdef UCPP_MMAP
+                       f = fopen_mmap_file(s);
+#else
+                       f = fopen(s, "r");
+#endif
+                       if (f) {
+                               if (emit_dependencies == 2) {
+                                       fprintf(emit_output, " %s", s);
+                               }
+                               ff = protect_detect.ff = new_found_file();
+                               ff->name = sdup(s);
+#ifdef AUDIT
+                               if (
+#endif
+                               HTT_put(&found_files, ff, s)
+#ifdef AUDIT
+                               ) ouch("filename collided with a wraith")
+#endif
+                               ;
+                               find_file_error = FF_UNKNOWN;
+                               freemem(s);
+                               s = HASH_ITEM_NAME(ff);
+                       }
+               }
+               if (f) {
+                       current_long_filename = s;
+                       current_incdir = i;
+                       return f;
+               }
+               freemem(s);
+       }
+       return 0;
+}
+
+/*
+ * The #if directive. This function parse the expression, performs macro
+ * expansion (and handles the "defined" operator), and call eval_expr.
+ * return value: 1 if the expression is true, 0 if it is false, -1 on error.
+ */
+static int handle_if(struct lexer_state *ls)
+{
+       struct token_fifo tf, tf1, tf2, tf3, *save_tf;
+       long l = ls->line;
+       unsigned long z;
+       int ret = 0, ltww = 1;
+
+       /* first, get the whole line */
+       tf.art = tf.nt = 0;
+       while (!next_token(ls) && ls->ctok->type != NEWLINE) {
+               struct token t;
+
+               if (ltww && ttMWS(ls->ctok->type)) continue;
+               ltww = ttMWS(ls->ctok->type);
+               t.type = ls->ctok->type;
+               t.line = l;
+               if (S_TOKEN(ls->ctok->type)) {
+                       t.name = sdup(ls->ctok->name);
+                       throw_away(ls->gf, t.name);
+               }
+               aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG);
+       }
+       if (ltww && tf.nt) if ((-- tf.nt) == 0) freemem(tf.t);
+       if (tf.nt == 0) {
+               error(l, "void condition for a #if/#elif");
+               return -1;
+       }
+       /* handle the "defined" operator */
+       tf1.art = tf1.nt = 0;
+       while (tf.art < tf.nt) {
+               struct token *ct, rt;
+               struct macro *m;
+               size_t nidx, eidx;
+
+               ct = tf.t + (tf.art ++);
+               if (ct->type == NAME && !strcmp(ct->name, "defined")) {
+                       if (tf.art >= tf.nt) goto store_token;
+                       nidx = tf.art;
+                       if (ttMWS(tf.t[nidx].type))
+                               if (++ nidx >= tf.nt) goto store_token;
+                       if (tf.t[nidx].type == NAME) {
+                               eidx = nidx;
+                               goto check_macro;
+                       }
+                       if (tf.t[nidx].type != LPAR) goto store_token;
+                       if (++ nidx >= tf.nt) goto store_token;
+                       if (ttMWS(tf.t[nidx].type))
+                               if (++ nidx >= tf.nt) goto store_token;
+                       if (tf.t[nidx].type != NAME) goto store_token;
+                       eidx = nidx + 1;
+                       if (eidx >= tf.nt) goto store_token;
+                       if (ttMWS(tf.t[eidx].type))
+                               if (++ eidx >= tf.nt) goto store_token;
+                       if (tf.t[eidx].type != RPAR) goto store_token;
+                       goto check_macro;
+               }
+       store_token:
+               aol(tf1.t, tf1.nt, *ct, TOKEN_LIST_MEMG);
+               continue;
+
+       check_macro:
+               m = get_macro(tf.t[nidx].name);
+               rt.type = NUMBER;
+               rt.name = m ? "1L" : "0L";
+               aol(tf1.t, tf1.nt, rt, TOKEN_LIST_MEMG);
+               tf.art = eidx + 1;
+       }
+       freemem(tf.t);
+       if (tf1.nt == 0) {
+               error(l, "void condition (after expansion) for a #if/#elif");
+               return -1;
+       }
+
+       /* perform all macro substitutions */
+       tf2.art = tf2.nt = 0;
+       save_tf = ls->output_fifo;
+       ls->output_fifo = &tf2;
+       while (tf1.art < tf1.nt) {
+               struct token *ct;
+
+               ct = tf1.t + (tf1.art ++);
+               if (ct->type == NAME) {
+                       struct macro *m = get_macro(ct->name);
+
+                       if (m) {
+                               if (substitute_macro(ls, m, &tf1, 0,
+#ifdef NO_PRAGMA_IN_DIRECTIVE
+                                       1,
+#else
+                                       0,
+#endif
+                                       ct->line)) {
+                                       ls->output_fifo = save_tf;
+                                       goto error1;
+                               }
+                               continue;
+                       }
+               } else if ((ct->type == SHARP || ct->type == DIG_SHARP)
+                       && (ls->flags & HANDLE_ASSERTIONS)) {
+                       /* we have an assertion; parse it */
+                       int nnp, ltww = 1;
+                       size_t i = tf1.art;
+                       struct token_fifo atl;
+                       char *aname;
+                       struct assert *a;
+                       int av = 0;
+                       struct token rt;
+
+                       atl.art = atl.nt = 0;
+                       while (i < tf1.nt && ttMWS(tf1.t[i].type)) i ++;
+                       if (i >= tf1.nt) goto assert_error;
+                       if (tf1.t[i].type != NAME) goto assert_error;
+                       aname = tf1.t[i ++].name;
+                       while (i < tf1.nt && ttMWS(tf1.t[i].type)) i ++;
+                       if (i >= tf1.nt) goto assert_generic;
+                       if (tf1.t[i].type != LPAR) goto assert_generic;
+                       i ++;
+                       for (nnp = 1; nnp && i < tf1.nt; i ++) {
+                               if (ltww && ttMWS(tf1.t[i].type)) continue;
+                               if (tf1.t[i].type == LPAR) nnp ++;
+                               else if (tf1.t[i].type == RPAR
+                                       && (-- nnp) == 0) {
+                                       tf1.art = i + 1;
+                                       break;
+                               }
+                               ltww = ttMWS(tf1.t[i].type);
+                               aol(atl.t, atl.nt, tf1.t[i], TOKEN_LIST_MEMG);
+                       }
+                       if (nnp) goto assert_error;
+                       if (ltww && atl.nt && (-- atl.nt) == 0) freemem(atl.t);
+                       if (atl.nt == 0) goto assert_error;
+
+                       /* the assertion is in aname and atl; check it */
+                       a = get_assertion(aname);
+                       if (a) for (i = 0; i < a->nbval; i ++)
+                               if (!cmp_token_list(&atl, a->val + i)) {
+                                       av = 1;
+                                       break;
+                               }
+                       rt.type = NUMBER;
+                       rt.name = av ? "1" : "0";
+                       aol(tf2.t, tf2.nt, rt, TOKEN_LIST_MEMG);
+                       if (atl.nt) freemem(atl.t);
+                       continue;
+
+               assert_generic:
+                       tf1.art = i;
+                       rt.type = NUMBER;
+                       rt.name = get_assertion(aname) ? "1" : "0";
+                       aol(tf2.t, tf2.nt, rt, TOKEN_LIST_MEMG);
+                       continue;
+
+               assert_error:
+                       error(l, "syntax error for assertion in #if");
+                       ls->output_fifo = save_tf;
+                       goto error1;
+               }
+               aol(tf2.t, tf2.nt, *ct, TOKEN_LIST_MEMG);
+       }
+       ls->output_fifo = save_tf;
+       freemem(tf1.t);
+       if (tf2.nt == 0) {
+               error(l, "void condition (after expansion) for a #if/#elif");
+               return -1;
+       }
+
+       /*
+        * suppress whitespace and replace rogue identifiers by 0
+        */
+       tf3.art = tf3.nt = 0;
+       while (tf2.art < tf2.nt) {
+               struct token *ct = tf2.t + (tf2.art ++);
+
+               if (ttMWS(ct->type)) continue;
+               if (ct->type == NAME) {
+                       /*
+                        * a rogue identifier; we replace it with "0".
+                        */
+                       struct token rt;
+
+                       rt.type = NUMBER;
+                       rt.name = "0";
+                       aol(tf3.t, tf3.nt, rt, TOKEN_LIST_MEMG);
+                       continue;
+               }
+               aol(tf3.t, tf3.nt, *ct, TOKEN_LIST_MEMG);
+       }
+       freemem(tf2.t);
+
+       if (tf3.nt == 0) {
+               error(l, "void condition (after expansion) for a #if/#elif");
+               return -1;
+       }
+       eval_line = l;
+       z = eval_expr(&tf3, &ret, (ls->flags & WARN_STANDARD) != 0);
+       freemem(tf3.t);
+       if (ret) return -1;
+       return (z != 0);
+
+error1:
+       if (tf1.nt) freemem(tf1.t);
+       if (tf2.nt) freemem(tf2.t);
+       return -1;
+}
+
+/*
+ * A #include was found; parse the end of line, replace macros if
+ * necessary.
+ *
+ * If nex is set to non-zero, the directive is considered as a #include_next
+ * (extension to C99, mimicked from GNU)
+ */
+static int handle_include(struct lexer_state *ls, unsigned long flags, int nex)
+{
+       int c, string_fname = 0;
+       char *fname;
+       unsigned char *fname2;
+       size_t fname_ptr = 0;
+       long l = ls->line;
+       int x, y;
+       FILE *f;
+       struct token_fifo tf, tf2, *save_tf;
+       size_t nl;
+       int tgd;
+       struct lexer_state alt_ls;
+
+#define left_angle(t)  ((t) == LT || (t) == LEQ || (t) == LSH \
+                       || (t) == ASLSH || (t) == DIG_LBRK || (t) == LBRA)
+#define right_angle(t) ((t) == GT || (t) == RSH || (t) == ARROW \
+                       || (t) == DIG_RBRK || (t) == DIG_RBRA)
+
+       while ((c = grap_char(ls)) >= 0 && c != '\n') {
+               if (space_char(c)) {
+                       discard_char(ls);
+                       continue;
+               }
+               if (c == '<') {
+                       discard_char(ls);
+                       while ((c = grap_char(ls)) >= 0) {
+                               discard_char(ls);
+                               if (c == '\n') goto include_last_chance;
+                               if (c == '>') break;
+                               aol(fname, fname_ptr, (char)c, FNAME_MEMG);
+                       }
+                       aol(fname, fname_ptr, (char)0, FNAME_MEMG);
+                       string_fname = 0;
+                       goto do_include;
+               } else if (c == '"') {
+                       discard_char(ls);
+                       while ((c = grap_char(ls)) >= 0) {
+                               discard_char(ls);
+                               if (c == '\n') {
+                               /* macro replacements won't save that one */
+                                       if (fname_ptr) freemem(fname);
+                                       goto include_error;
+                               }
+                               if (c == '"') break;
+                               aol(fname, fname_ptr, (char)c, FNAME_MEMG);
+                       }
+                       aol(fname, fname_ptr, (char)0, FNAME_MEMG);
+                       string_fname = 1;
+                       goto do_include;
+               }
+               goto include_macro;
+       }
+
+include_last_chance:
+       /*
+        * We found a '<' but not the trailing '>'; so we tokenize the
+        * line, and try to act upon it. The standard lets us free in that
+        * matter, and no sane programmer would use such a construct, but
+        * it is no reason not to support it.
+        */
+       if (fname_ptr == 0) goto include_error;
+       fname2 = getmem(fname_ptr + 1);
+       mmv(fname2 + 1, fname, fname_ptr);
+       fname2[0] = '<';
+       /*
+        * We merely copy the lexer_state structure; this should be ok,
+        * since we do want to share the memory structure (garbage_fifo),
+        * and do not touch any other context-full thing.
+        */
+       alt_ls = *ls;
+       alt_ls.input = 0;
+       alt_ls.input_string = fname2;
+       alt_ls.pbuf = 0;
+       alt_ls.ebuf = fname_ptr + 1;
+       tf.art = tf.nt = 0;
+       while (!next_token(&alt_ls)) {
+               if (!ttMWS(alt_ls.ctok->type)) {
+                       struct token t;
+
+                       t.type = alt_ls.ctok->type;
+                       t.line = l;
+                       if (S_TOKEN(alt_ls.ctok->type)) {
+                               t.name = sdup(alt_ls.ctok->name);
+                               throw_away(alt_ls.gf, t.name);
+                       }
+                       aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG);
+               }
+       }
+       freemem(fname2);
+       if (alt_ls.pbuf < alt_ls.ebuf) goto include_error;
+               /* tokenizing failed */
+       goto include_macro2;
+       
+include_error:
+       error(l, "invalid '#include'");
+       return 1;
+
+include_macro:
+       tf.art = tf.nt = 0;
+       while (!next_token(ls) && ls->ctok->type != NEWLINE) {
+               if (!ttMWS(ls->ctok->type)) {
+                       struct token t;
+
+                       t.type = ls->ctok->type;
+                       t.line = l;
+                       if (S_TOKEN(ls->ctok->type)) {
+                               t.name = sdup(ls->ctok->name);
+                               throw_away(ls->gf, t.name);
+                       }
+                       aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG);
+               }
+       }
+include_macro2:
+       tf2.art = tf2.nt = 0;
+       save_tf = ls->output_fifo;
+       ls->output_fifo = &tf2;
+       while (tf.art < tf.nt) {
+               struct token *ct;
+
+               ct = tf.t + (tf.art ++);
+               if (ct->type == NAME) {
+                       struct macro *m = get_macro(ct->name);
+                       if (m) {
+                               if (substitute_macro(ls, m, &tf, 0,
+#ifdef NO_PRAGMA_IN_DIRECTIVE
+                                       1,
+#else
+                                       0,
+#endif
+                                       ct->line)) {
+                                       ls->output_fifo = save_tf;
+                                       return -1;
+                               }
+                               continue;
+                       }
+               }
+               aol(tf2.t, tf2.nt, *ct, TOKEN_LIST_MEMG);
+       }
+       freemem(tf.t);
+       ls->output_fifo = save_tf;
+       for (x = 0; (size_t)x < tf2.nt && ttWHI(tf2.t[x].type); x ++);
+       for (y = tf2.nt - 1; y >= 0 && ttWHI(tf2.t[y].type); y --);
+       if ((size_t)x >= tf2.nt) goto include_macro_err;
+       if (tf2.t[x].type == STRING) {
+               if (y != x) goto include_macro_err;
+               if (tf2.t[x].name[0] == 'L') {
+                       if (ls->flags & WARN_STANDARD)
+                               warning(l, "wide string for #include");
+                       fname = sdup(tf2.t[x].name);
+                       nl = strlen(fname);
+                       *(fname + nl - 1) = 0;
+                       mmvwo(fname, fname + 2, nl - 2);
+               } else {
+                       fname = sdup(tf2.t[x].name);
+                       nl = strlen(fname);
+                       *(fname + nl - 1) = 0;
+                       mmvwo(fname, fname + 1, nl - 1);
+               }
+               string_fname = 1;
+       } else if (left_angle(tf2.t[x].type) && right_angle(tf2.t[y].type)) {
+               int i, j;
+
+               if (ls->flags & WARN_ANNOYING) warning(l, "reconstruction "
+                       "of <foo> in #include");
+               for (j = 0, i = x; i <= y; i ++) if (!ttWHI(tf2.t[i].type))
+                       j += strlen(tname(tf2.t[i]));
+               fname = getmem(j + 1);
+               for (j = 0, i = x; i <= y; i ++) {
+                       if (ttWHI(tf2.t[i].type)) continue;
+                       strcpy(fname + j, tname(tf2.t[i]));
+                       j += strlen(tname(tf2.t[i]));
+               }
+               *(fname + j - 1) = 0;
+               mmvwo(fname, fname + 1, j);
+               string_fname = 0;
+       } else goto include_macro_err;
+       freemem(tf2.t);
+       goto do_include_next;
+
+include_macro_err:
+       error(l, "macro expansion did not produce a valid filename "
+               "for #include");
+       if (tf2.nt) freemem(tf2.t);
+       return 1;
+
+do_include:
+       tgd = 1;
+       while (!next_token(ls)) {
+               if (tgd && !ttWHI(ls->ctok->type)
+                       && (ls->flags & WARN_STANDARD)) {
+                       warning(l, "trailing garbage in #include");
+                       tgd = 0;
+               }
+               if (ls->ctok->type == NEWLINE) break;
+       }
+
+       /* the increment of ls->line is intended so that the line
+          numbering is reported correctly in report_context() even if
+          the #include is at the end of the file with no trailing newline */
+       if (ls->ctok->type != NEWLINE) ls->line ++;
+do_include_next:
+       if (!(ls->flags & LEXER) && (ls->flags & KEEP_OUTPUT))
+               put_char(ls, '\n');
+       push_file_context(ls);
+       reinit_lexer_state(ls, 1);
+#ifdef MSDOS
+       /* on msdos systems, replace all / by \ */
+       {
+               char *d;
+
+               for (d = fname; *d; d ++) if (*d == '/') *d = '\\';
+       }
+#endif
+       f = nex ? find_file_next(fname) : find_file(fname, string_fname);
+       if (!f) {
+               current_filename = 0;
+               pop_file_context(ls);
+               if (find_file_error == FF_ERROR) {
+                       error(l, "file '%s' not found", fname);
+                       freemem(fname);
+                       return 1;
+               }
+               /* file was found, but it is useless to include it again */
+               freemem(fname);
+               return 0;
+       }
+#ifdef UCPP_MMAP
+       set_input_file(ls, f);
+#else
+       ls->input = f;
+#endif
+       current_filename = fname;
+       enter_file(ls, flags);
+       return 0;
+
+#undef left_angle
+#undef right_angle
+}
+
+/*
+ * for #line directives
+ */
+static int handle_line(struct lexer_state *ls, unsigned long flags)
+{
+       char *fname;
+       long l = ls->line;
+       struct token_fifo tf, tf2, *save_tf;
+       size_t nl, j;
+       unsigned long z;
+
+       tf.art = tf.nt = 0;
+       while (!next_token(ls) && ls->ctok->type != NEWLINE) {
+               if (!ttMWS(ls->ctok->type)) {
+                       struct token t;
+
+                       t.type = ls->ctok->type;
+                       t.line = l;
+                       if (S_TOKEN(ls->ctok->type)) {
+                               t.name = sdup(ls->ctok->name);
+                               throw_away(ls->gf, t.name);
+                       }
+                       aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG);
+               }
+       }
+       tf2.art = tf2.nt = 0;
+       save_tf = ls->output_fifo;
+       ls->output_fifo = &tf2;
+       while (tf.art < tf.nt) {
+               struct token *ct;
+
+               ct = tf.t + (tf.art ++);
+               if (ct->type == NAME) {
+                       struct macro *m = get_macro(ct->name);
+                       if (m) {
+                               if (substitute_macro(ls, m, &tf, 0,
+#ifdef NO_PRAGMA_IN_DIRECTIVE
+                                       1,
+#else
+                                       0,
+#endif
+                                       ct->line)) {
+                                       ls->output_fifo = save_tf;
+                                       return -1;
+                               }
+                               continue;
+                       }
+               }
+               aol(tf2.t, tf2.nt, *ct, TOKEN_LIST_MEMG);
+       }
+       freemem(tf.t);
+       for (tf2.art = 0; tf2.art < tf2.nt && ttWHI(tf2.t[tf2.art].type);
+               tf2.art ++);
+       ls->output_fifo = save_tf;
+       if (tf2.art == tf2.nt || (tf2.t[tf2.art].type != NUMBER
+               && tf2.t[tf2.art].type != CHAR)) {
+               error(l, "not a valid number for #line");
+               goto line_macro_err;
+       }
+       for (j = 0; tf2.t[tf2.art].name[j]; j ++)
+               if (tf2.t[tf2.art].name[j] < '0'
+                       || tf2.t[tf2.art].name[j] > '9')
+                       if (ls->flags & WARN_STANDARD)
+                               warning(l, "non-standard line number in #line");
+       if (catch(eval_exception)) goto line_macro_err;
+       z = strtoconst(tf2.t[tf2.art].name);
+       if (j > 10 || z > 2147483647U) {
+               error(l, "out-of-bound line number for #line");
+               goto line_macro_err;
+       }
+       ls->oline = ls->line = z;
+       if ((++ tf2.art) < tf2.nt) {
+               size_t i;
+
+               for (i = tf2.art; i < tf2.nt && ttMWS(tf2.t[i].type); i ++);
+               if (i < tf2.nt) {
+                       if (tf2.t[i].type != STRING) {
+                               error(l, "not a valid filename for #line");
+                               goto line_macro_err;
+                       }
+                       if (tf2.t[i].name[0] == 'L') {
+                               if (ls->flags & WARN_STANDARD) {
+                                       warning(l, "wide string for #line");
+                               }
+                               fname = sdup(tf2.t[i].name);
+                               nl = strlen(fname);
+                               *(fname + nl - 1) = 0;
+                               mmvwo(fname, fname + 2, nl - 2);
+                       } else {
+                               fname = sdup(tf2.t[i].name);
+                               nl = strlen(fname);
+                               *(fname + nl - 1) = 0;
+                               mmvwo(fname, fname + 1, nl - 1);
+                       }
+                       if (current_filename) freemem(current_filename);
+                       current_filename = fname;
+               }
+               for (i ++; i < tf2.nt && ttMWS(tf2.t[i].type); i ++);
+               if (i < tf2.nt && (ls->flags & WARN_STANDARD)) {
+                       warning(l, "trailing garbage in #line");
+               }
+       }
+       freemem(tf2.t);
+       enter_file(ls, flags);
+       return 0;
+
+line_macro_err:
+       if (tf2.nt) freemem(tf2.t);
+       return 1;
+}
+
+/*
+ * a #error directive: we emit the message without any modification
+ * (except the usual backslash+newline and trigraphs)
+ */
+static void handle_error(struct lexer_state *ls)
+{
+       int c;
+       size_t p = 0, lp = 128;
+       long l = ls->line;
+       unsigned char *buf = getmem(lp);
+
+       while ((c = grap_char(ls)) >= 0 && c != '\n') {
+               discard_char(ls);
+               wan(buf, p, (unsigned char)c, lp);
+       }
+       wan(buf, p, 0, lp);
+       error(l, "#error%s", buf);
+       freemem(buf);
+}
+
+/*
+ * convert digraph tokens to their standard equivalent.
+ */
+static int undig(int type)
+{
+       static int ud[6] = { LBRK, RBRK, LBRA, RBRA, SHARP, DSHARP };
+
+       return ud[type - DIG_LBRK];
+}
+
+#ifdef PRAGMA_TOKENIZE
+/*
+ * Make a compressed representation of a token list; the contents of
+ * the token_fifo are freed. Values equal to 0 are replaced by
+ * PRAGMA_TOKEN_END (by default, (unsigned char)'\n') and the compressed
+ * string is padded by a 0 (so that it may be * handled like a string).
+ * Digraph tokens are replaced by their non-digraph equivalents.
+ */
+struct comp_token_fifo compress_token_list(struct token_fifo *tf)
+{
+       struct comp_token_fifo ct;
+       size_t l;
+
+       for (l = 0, tf->art = 0; tf->art < tf->nt; tf->art ++) {
+               l ++;
+               if (S_TOKEN(tf->t[tf->art].type))
+                       l += strlen(tf->t[tf->art].name) + 1;
+       }
+       ct.t = getmem((ct.length = l) + 1);
+       for (l = 0, tf->art = 0; tf->art < tf->nt; tf->art ++) {
+               int tt = tf->t[tf->art].type;
+
+               if (tt == 0) tt = PRAGMA_TOKEN_END;
+               if (tt > DIGRAPH_TOKENS && tt < DIGRAPH_TOKENS_END)
+                       tt = undig(tt);
+               ct.t[l ++] = tt;
+               if (S_TOKEN(tt)) {
+                       char *tn = tf->t[tf->art].name;
+                       size_t sl = strlen(tn);
+
+                       mmv(ct.t + l, tn, sl);
+                       l += sl;
+                       ct.t[l ++] = PRAGMA_TOKEN_END;
+                       freemem(tn);
+               }
+       }
+       ct.t[l] = 0;
+       if (tf->nt) freemem(tf->t);
+       ct.rp = 0;
+       return ct;
+}
+#endif
+
+/*
+ * A #pragma directive: we make a PRAGMA token containing the rest of
+ * the line.
+ *
+ * We strongly hope that we are called only in LEXER mode.
+ */
+static void handle_pragma(struct lexer_state *ls)
+{
+       unsigned char *buf;
+       struct token t;
+       long l = ls->line;
+
+#ifdef PRAGMA_TOKENIZE
+       struct token_fifo tf;
+
+       tf.art = tf.nt = 0;
+       while (!next_token(ls) && ls->ctok->type != NEWLINE)
+               if (!ttMWS(ls->ctok->type)) break;
+       if (ls->ctok->type != NEWLINE) {
+               do {
+                       struct token t;
+
+                       t.type = ls->ctok->type;
+                       if (ttMWS(t.type)) continue;
+                       if (S_TOKEN(t.type)) t.name = sdup(ls->ctok->name);
+                       aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG);
+               } while (!next_token(ls) && ls->ctok->type != NEWLINE);
+       }
+       if (tf.nt == 0) {
+               /* void pragma are silently ignored */
+               return;
+       }
+       buf = (compress_token_list(&tf)).t;
+#else
+       int c, x = 1, y = 32;
+
+       while ((c = grap_char(ls)) >= 0 && c != '\n') {
+               discard_char(ls);
+               if (!space_char(c)) break;
+       }
+       /* void #pragma are ignored */
+       if (c == '\n') return;
+       buf = getmem(y);
+       buf[0] = c;
+       while ((c = grap_char(ls)) >= 0 && c != '\n') {
+               discard_char(ls);
+               wan(buf, x, c, y);
+       }
+       for (x --; x >= 0 && space_char(buf[x]); x --);
+       x ++;
+       wan(buf, x, 0, y);
+#endif
+       t.type = PRAGMA;
+       t.line = l;
+       t.name = (char *)buf;
+       aol(ls->output_fifo->t, ls->output_fifo->nt, t, TOKEN_LIST_MEMG);
+       throw_away(ls->gf, (char *)buf);
+}
+
+/*
+ * We saw a # at the beginning of a line (or preceeded only by whitespace).
+ * We check the directive name and act accordingly.
+ */
+static int handle_cpp(struct lexer_state *ls, int sharp_type)
+{
+#define condfset(x)    do { \
+               ls->condf[(x) / 32] |= 1UL << ((x) % 32); \
+       } while (0)
+#define condfclr(x)    do { \
+               ls->condf[(x) / 32] &= ~(1UL << ((x) % 32)); \
+       } while (0)
+#define condfval(x)    ((ls->condf[(x) / 32] & (1UL << ((x) % 32))) != 0)
+
+       long l = ls->line;
+       unsigned long save_flags = ls->flags;
+       int ret = 0;
+
+       save_flags = ls->flags;
+       ls->flags |= LEXER;
+       while (!next_token(ls)) {
+               int t = ls->ctok->type;
+
+               switch (t) {
+               case COMMENT:
+                       if (ls->flags & WARN_ANNOYING) {
+                               warning(l, "comment in the middle of "
+                                       "a cpp directive");
+                       }
+                       /* fall through */
+               case NONE:
+                       continue;
+               case NEWLINE:
+                       /* null directive */
+                       if (ls->flags & WARN_ANNOYING) {
+                               /* truly an annoying warning; null directives
+                                  are rare but may increase readability of
+                                  some source files, and they are legal */
+                               warning(l, "null cpp directive");
+                       }
+                       if (!(ls->flags & LEXER)) put_char(ls, '\n');
+                       goto handle_exit2;
+               case NAME:
+                       break;
+               default:
+                       if (ls->flags & FAIL_SHARP) {
+                                /* LPS 20050602 - ignores '#!' if on the first line */
+                                if( ( l == 1 ) &&
+                                    ( ls->condcomp ) )
+                                {
+                                       ret = 1;
+                                }
+                                else
+                                /* LPS 20050602 */
+                               if (ls->condcomp) {
+                                       error(l, "rogue '#'");
+                                       ret = 1;
+                               } else {
+                                       if (ls->flags & WARN_STANDARD) {
+                                               warning(l, "rogue '#' in code "
+                                                       "compiled out");
+                                               ret = 0;
+                                       }
+                               }
+                               ls->flags = save_flags;
+                               goto handle_warp_ign;
+                       } else {
+                               struct token u;
+
+                               u.type = sharp_type;
+                               u.line = l;
+                               ls->flags = save_flags;
+                               print_token(ls, &u, 0);
+                               print_token(ls, ls->ctok, 0);
+                               if (ls->flags & WARN_ANNOYING) {
+                                       warning(l, "rogue '#' dumped");
+                               }
+                               goto handle_exit3;
+                       }
+               }
+               if (ls->condcomp) {
+                       if (!strcmp(ls->ctok->name, "define")) {
+                               ret = handle_define(ls);
+                               goto handle_exit;
+                       } else if (!strcmp(ls->ctok->name, "undef")) {
+                               ret = handle_undef(ls);
+                               goto handle_exit;
+                       } else if (!strcmp(ls->ctok->name, "if")) {
+                               if ((++ ls->ifnest) > 63) goto too_many_if;
+                               condfclr(ls->ifnest - 1);
+                               ret = handle_if(ls);
+                               if (ret > 0) ret = 0;
+                               else if (ret == 0) {
+                                       ls->condcomp = 0;
+                                       ls->condmet = 0;
+                                       ls->condnest = ls->ifnest - 1;
+                               }
+                               else ret = 1;
+                               goto handle_exit;
+                       } else if (!strcmp(ls->ctok->name, "ifdef")) {
+                               if ((++ ls->ifnest) > 63) goto too_many_if;
+                               condfclr(ls->ifnest - 1);
+                               ret = handle_ifdef(ls);
+                               if (ret > 0) ret = 0;
+                               else if (ret == 0) {
+                                       ls->condcomp = 0;
+                                       ls->condmet = 0;
+                                       ls->condnest = ls->ifnest - 1;
+                               }
+                               else ret = 1;
+                               goto handle_exit;
+                       } else if (!strcmp(ls->ctok->name, "ifndef")) {
+                               if ((++ ls->ifnest) > 63) goto too_many_if;
+                               condfclr(ls->ifnest - 1);
+                               ret = handle_ifndef(ls);
+                               if (ret > 0) ret = 0;
+                               else if (ret == 0) {
+                                       ls->condcomp = 0;
+                                       ls->condmet = 0;
+                                       ls->condnest = ls->ifnest - 1;
+                               }
+                               else ret = 1;
+                               goto handle_exit;
+                       } else if (!strcmp(ls->ctok->name, "else")) {
+                               if (ls->ifnest == 0
+                                       || condfval(ls->ifnest - 1)) {
+                                       error(l, "rogue #else");
+                                       ret = 1;
+                                       goto handle_warp;
+                               }
+                               condfset(ls->ifnest - 1);
+                               if (ls->ifnest == 1) protect_detect.state = 0;
+                               ls->condcomp = 0;
+                               ls->condmet = 1;
+                               ls->condnest = ls->ifnest - 1;
+                               goto handle_warp;
+                       } else if (!strcmp(ls->ctok->name, "elif")) {
+                               if (ls->ifnest == 0
+                                       || condfval(ls->ifnest - 1)) {
+                                       error(l, "rogue #elif");
+                                       ret = 1;
+                                       goto handle_warp_ign;
+                               }
+                               if (ls->ifnest == 1) protect_detect.state = 0;
+                               ls->condcomp = 0;
+                               ls->condmet = 1;
+                               ls->condnest = ls->ifnest - 1;
+                               goto handle_warp_ign;
+                       } else if (!strcmp(ls->ctok->name, "endif")) {
+                               if (ls->ifnest == 0) {
+                                       error(l, "unmatched #endif");
+                                       ret = 1;
+                                       goto handle_warp;
+                               }
+                               if ((-- ls->ifnest) == 0
+                                       && protect_detect.state == 2) {
+                                       protect_detect.state = 3;
+                               }
+                               goto handle_warp;
+                       } else if (!strcmp(ls->ctok->name, "include")) {
+                               ret = handle_include(ls, save_flags, 0);
+                               goto handle_exit3;
+                       } else if (!strcmp(ls->ctok->name, "include_next")) {
+                               ret = handle_include(ls, save_flags, 1);
+                               goto handle_exit3;
+                       } else if (!strcmp(ls->ctok->name, "pragma")) {
+                               if (!(save_flags & LEXER)) {
+#ifdef PRAGMA_DUMP
+                                       /* dump #pragma in output */
+                                       struct token u;
+
+                                       u.type = sharp_type;
+                                       u.line = l;
+                                       ls->flags = save_flags;
+                                       print_token(ls, &u, 0);
+                                       print_token(ls, ls->ctok, 0);
+                                       while (ls->flags |= LEXER,
+                                               !next_token(ls)) {
+                                               long save_line;
+
+                                               ls->flags &= ~LEXER;
+                                               save_line = ls->line;
+                                               ls->line = l;
+                                               print_token(ls, ls->ctok, 0);
+                                               ls->line = save_line;
+                                               if (ls->ctok->type == NEWLINE)
+                                                       break;
+                                       }
+                                       goto handle_exit3;
+#else
+                                       if (ls->flags & WARN_PRAGMA)
+                                               warning(l, "#pragma ignored "
+                                                       "and not dumped");
+                                       goto handle_warp_ign;
+#endif
+                               }
+                               if (!(ls->flags & HANDLE_PRAGMA))
+                                       goto handle_warp_ign;
+                               handle_pragma(ls);
+                               goto handle_exit;
+                       } else if (!strcmp(ls->ctok->name, "error")) {
+                               ret = 1;
+                               handle_error(ls);
+                               goto handle_exit;
+                       } else if (!strcmp(ls->ctok->name, "line")) {
+                               ret = handle_line(ls, save_flags);
+                               goto handle_exit;
+                       } else if ((ls->flags & HANDLE_ASSERTIONS)
+                               && !strcmp(ls->ctok->name, "assert")) {
+                               ret = handle_assert(ls);
+                               goto handle_exit;
+                       } else if ((ls->flags & HANDLE_ASSERTIONS)
+                               && !strcmp(ls->ctok->name, "unassert")) {
+                               ret = handle_unassert(ls);
+                               goto handle_exit;
+                       }
+               } else {
+                       if (!strcmp(ls->ctok->name, "else")) {
+                               if (condfval(ls->ifnest - 1)
+                                       && (ls->flags & WARN_STANDARD)) {
+                                       warning(l, "rogue #else in code "
+                                               "compiled out");
+                               }
+                               if (ls->condnest == ls->ifnest - 1) {
+                                       if (!ls->condmet) ls->condcomp = 1;
+                               }
+                               condfset(ls->ifnest - 1);
+                               if (ls->ifnest == 1) protect_detect.state = 0;
+                               goto handle_warp;
+                       } else if (!strcmp(ls->ctok->name, "elif")) {
+                               if (condfval(ls->ifnest - 1)
+                                       && (ls->flags & WARN_STANDARD)) {
+                                       warning(l, "rogue #elif in code "
+                                               "compiled out");
+                               }
+                               if (ls->condnest != ls->ifnest - 1
+                                       || ls->condmet)
+                                       goto handle_warp_ign;
+                               if (ls->ifnest == 1) protect_detect.state = 0;
+                               ret = handle_if(ls);
+                               if (ret > 0) {
+                                       ls->condcomp = 1;
+                                       ls->condmet = 1;
+                                       ret = 0;
+                               } else if (ret < 0) ret = 1;
+                               goto handle_exit;
+                       } else if (!strcmp(ls->ctok->name, "endif")) {
+                               if ((-- ls->ifnest) == ls->condnest) {
+                                       if (ls->ifnest == 0 &&
+                                               protect_detect.state == 2)
+                                               protect_detect.state = 3;
+                                       ls->condcomp = 1;
+                               }
+                               goto handle_warp;
+                       } else if (!strcmp(ls->ctok->name, "if")
+                               || !strcmp(ls->ctok->name, "ifdef")
+                               || !strcmp(ls->ctok->name, "ifndef")) {
+                               if ((++ ls->ifnest) > 63) goto too_many_if;
+                               condfclr(ls->ifnest - 1);
+                       }
+                       goto handle_warp_ign;
+               }
+               /*
+                * Unrecognized directive. We emit either an error or
+                * an annoying warning, depending on a command-line switch.
+                */
+               if (ls->flags & FAIL_SHARP) {
+                       error(l, "unknown cpp directive '#%s'",
+                               ls->ctok->name);
+                       goto handle_warp_ign;
+               } else {
+                       struct token u;
+
+                       u.type = sharp_type;
+                       u.line = l;
+                       ls->flags = save_flags;
+                       print_token(ls, &u, 0);
+                       print_token(ls, ls->ctok, 0);
+                       if (ls->flags & WARN_ANNOYING) {
+                               warning(l, "rogue '#' dumped");
+                       }
+               }
+       }
+       return 1;
+
+handle_warp_ign:
+       while (!next_token(ls)) if (ls->ctok->type == NEWLINE) break;
+       goto handle_exit;
+handle_warp:
+       while (!next_token(ls)) {
+               if (!ttWHI(ls->ctok->type) && (ls->flags & WARN_STANDARD)) {
+                       warning(l, "trailing garbage in "
+                               "preprocessing directive");
+               }
+               if (ls->ctok->type == NEWLINE) break;
+       }
+handle_exit:
+       if (!(ls->flags & LEXER)) put_char(ls, '\n');
+handle_exit3:
+       if (protect_detect.state == 1) {
+               protect_detect.state = 0;
+       } else if (protect_detect.state == -1) {
+               /* just after the #include */
+               protect_detect.state = 1;
+       }
+handle_exit2:
+       ls->flags = save_flags;
+       return ret;
+too_many_if:
+       error(l, "too many levels of conditional inclusion (max 63)");
+       ret = 1;
+       goto handle_warp;
+#undef condfset
+#undef condfclr
+#undef condfval
+}
+
+/*
+ * This is the main entry function. It maintains count of #, and call the
+ * appropriate functions when it encounters a cpp directive or a macro
+ * name.
+ * return value: positive on error; CPPERR_EOF means "end of input reached"
+ */
+int cpp(struct lexer_state *ls)
+{
+       int r = 0;
+
+       while (next_token(ls)) {
+               if (protect_detect.state == 3) {
+                       /*
+                        * At that point, protect_detect.ff->protect might
+                        * be non-zero, if the file has been recursively
+                        * included, and a guardian detected.
+                        */
+                       if (!protect_detect.ff->protect) {
+                               /* Cool ! A new guardian has been detected. */
+                               protect_detect.ff->protect =
+                                       protect_detect.macro;
+                       } else if (protect_detect.macro) {
+                               /* We found a guardian but an old one. */
+                               freemem(protect_detect.macro);
+                       }
+                       protect_detect.macro = 0;
+               }
+               if (ls->ifnest) {
+                       error(ls->line, "unterminated #if construction "
+                               "(depth %ld)", ls->ifnest);
+                       r = CPPERR_NEST;
+               }
+               if (ls_depth == 0) return CPPERR_EOF;
+               close_input(ls);
+               if (!(ls->flags & LEXER) && !ls->ltwnl) {
+                       put_char(ls, '\n');
+                       ls->ltwnl = 1;
+               }
+               pop_file_context(ls);
+               ls->oline ++;
+               if (enter_file(ls, ls->flags)) {
+                       ls->ctok->type = NEWLINE;
+                       ls->ltwnl = 1;
+                       break;
+               }
+       }
+       if (!(ls->ltwnl && (ls->ctok->type == SHARP
+               || ls->ctok->type == DIG_SHARP))
+               && protect_detect.state == 1 && !ttWHI(ls->ctok->type)) {
+               /* the first non-whitespace token encountered is not
+                  a sharp introducing a cpp directive */
+               protect_detect.state = 0;
+       }
+       if (protect_detect.state == 3 && !ttWHI(ls->ctok->type)) {
+               /* a non-whitespace token encountered after the #endif */
+               protect_detect.state = 0;
+       }
+       if (ls->condcomp) {
+               if (ls->ltwnl && (ls->ctok->type == SHARP
+                       || ls->ctok->type == DIG_SHARP)) {
+                       int x = handle_cpp(ls, ls->ctok->type);
+
+                       ls->ltwnl = 1;
+                       return r ? r : x;
+               }
+               if (ls->ctok->type == NAME) {
+                       struct macro *m;
+
+                       if ((m = get_macro(ls->ctok->name)) != 0) {
+                               int x;
+
+                               x = substitute_macro(ls, m, 0, 1, 0,
+                                       ls->ctok->line);
+                               if (!(ls->flags & LEXER))
+                                       garbage_collect(ls->gf);
+                               return r ? r : x;
+                       }
+                       if (!(ls->flags & LEXER))
+                               print_token(ls, ls->ctok, 0);
+               }
+       } else {
+               if (ls->ltwnl && (ls->ctok->type == SHARP
+                       || ls->ctok->type == DIG_SHARP)) {
+                       int x = handle_cpp(ls, ls->ctok->type);
+
+                       ls->ltwnl = 1;
+                       return r ? r : x;
+               }
+       }
+       if (ls->ctok->type == NEWLINE) ls->ltwnl = 1;
+       else if (!ttWHI(ls->ctok->type)) ls->ltwnl = 0;
+       return r ? r : -1;
+}
+
+#ifndef STAND_ALONE
+/*
+ * llex() and lex() are the lexing functions, when the preprocessor is
+ * linked to another code. llex() should be called only by lex().
+ */
+static int llex(struct lexer_state *ls)
+{
+       struct token_fifo *tf = ls->output_fifo;
+       int r;
+
+       if (tf->nt != 0) {
+               if (tf->art < tf->nt) {
+#ifdef INMACRO_FLAG
+                       if (!ls->inmacro) {
+                               ls->inmacro = 1;
+                               ls->macro_count ++;
+                       }
+#endif
+                       ls->ctok = tf->t + (tf->art ++);
+                       if (ls->ctok->type > DIGRAPH_TOKENS
+                               && ls->ctok->type < DIGRAPH_TOKENS_END) {
+                               ls->ctok->type = undig(ls->ctok->type);
+                       }
+                       return 0;
+               } else {
+#ifdef INMACRO_FLAG
+                       ls->inmacro = 0;
+#endif
+                       freemem(tf->t);
+                       tf->art = tf->nt = 0;
+                       garbage_collect(ls->gf);
+                       ls->ctok = ls->save_ctok;
+               }
+       }
+       r = cpp(ls);
+       if (ls->ctok->type > DIGRAPH_TOKENS
+               && ls->ctok->type < LAST_MEANINGFUL_TOKEN) {
+               ls->ctok->type = undig(ls->ctok->type);
+       }
+       if (r > 0) return r;
+       if (r < 0) return 0;
+       return llex(ls);
+}
+
+/*
+ * lex() reads the next token from the processed stream and stores it
+ * into ls->ctok.
+ * return value: non zero on error (including CPPERR_EOF, which is not
+ * quite an error)
+ */
+int lex(struct lexer_state *ls)
+{
+       int r;
+
+       do {
+               r = llex(ls);
+#ifdef SEMPER_FIDELIS
+       } while (!r && !ls->condcomp);
+#else
+       } while (!r && (!ls->condcomp || (ttWHI(ls->ctok->type) &&
+               (!(ls->flags & LINE_NUM) || ls->ctok->type != NEWLINE))));
+#endif
+       return r;
+}
+#endif
+
+/*
+ * check_cpp_errors() must be called when the end of input is reached;
+ * it checks pending errors due to truncated constructs (actually none,
+ * this is reserved for future evolutions).
+ */
+int check_cpp_errors(struct lexer_state *ls)
+{
+       if (ls->flags & KEEP_OUTPUT) {
+               put_char(ls, '\n');
+       }
+       if (emit_dependencies) fputc('\n', emit_output);
+#ifndef NO_UCPP_BUF
+       if (!(ls->flags & LEXER)) {
+               flush_output(ls);
+       }
+#endif
+       if ((ls->flags & WARN_TRIGRAPHS) && ls->count_trigraphs)
+               warning(0, "%ld trigraph(s) encountered", ls->count_trigraphs);
+       return 0;
+}
+
+/*
+ * init_cpp() initializes static tables inside ucpp. It needs not be
+ * called more than once.
+ */
+void init_cpp(void)
+{
+       init_cppm();
+}
+
+/*
+ * (re)init the global tables.
+ * If standard_assertions is non 0, init the assertions table.
+ */
+void init_tables(int with_assertions)
+{
+       time_t t;
+       struct tm *ct;
+
+       init_buf_lexer_state(&dsharp_lexer, 0);
+#ifdef PRAGMA_TOKENIZE
+       init_buf_lexer_state(&tokenize_lexer, 0);
+#endif
+       time(&t);
+       ct = localtime(&t);
+#ifdef NOSTRFTIME
+       /* we have a quite old compiler, that does not know the
+          (standard since 1990) strftime() function. */
+       {
+               char *c = asctime(ct);
+
+               compile_time[0] = '"';
+               mmv(compile_time + 1, c + 11, 8);
+               compile_time[9] = '"';
+               compile_time[10] = 0;
+               compile_date[0] = '"';
+               mmv(compile_date + 1, c + 4, 7);
+               mmv(compile_date + 8, c + 20, 4);
+               compile_date[12] = '"';
+               compile_date[13] = 0;
+       }
+#else
+       strftime(compile_time, 12, "\"%H:%M:%S\"", ct);
+       strftime(compile_date, 24, "\"%b %d %Y\"", ct);
+#endif
+       init_macros();
+       if (with_assertions) init_assertions();
+       init_found_files();
+}
+
+/*
+ * Resets the include path.
+ */
+void init_include_path(char *incpath[])
+{
+       if (include_path_nb) {
+               size_t i;
+
+               for (i = 0; i < include_path_nb; i ++)
+                       freemem(include_path[i]);
+               freemem(include_path);
+               include_path_nb = 0;
+       }
+       if (incpath) {
+               int i;
+
+               for (i = 0; incpath[i]; i ++)
+                       aol(include_path, include_path_nb,
+                               sdup(incpath[i]), INCPATH_MEMG);
+       }
+}
+
+/*
+ * add_incpath() adds "path" to the standard include path.
+ */
+void add_incpath(char *path)
+{
+       aol(include_path, include_path_nb, sdup(path), INCPATH_MEMG);
+}
+
+/*
+ * This function cleans the memory. It should release all allocated
+ * memory structures and may be called even if the current pre-processing
+ * is not finished or reported an error.
+ */
+void wipeout()
+{
+       struct lexer_state ls;
+
+       if (include_path_nb > 0) {
+               size_t i;
+
+               for (i = 0; i < include_path_nb; i ++)
+                       freemem(include_path[i]);
+               freemem(include_path);
+               include_path = 0;
+               include_path_nb = 0;
+       }
+       if (current_filename) freemem(current_filename);
+       current_filename = 0;
+       current_long_filename = 0;
+       current_incdir = -1;
+       protect_detect.state = 0;
+       if (protect_detect.macro) freemem(protect_detect.macro);
+       protect_detect.macro = 0;
+       protect_detect.ff = 0;
+       init_lexer_state(&ls);
+       while (ls_depth > 0) pop_file_context(&ls);
+       free_lexer_state(&ls);
+       free_lexer_state(&dsharp_lexer);
+#ifdef PRAGMA_TOKENIZE
+       free_lexer_state(&tokenize_lexer);
+#endif
+       if (found_files_init_done) HTT_kill(&found_files);
+       found_files_init_done = 0;
+       if (found_files_sys_init_done) HTT_kill(&found_files_sys);
+       found_files_sys_init_done = 0;
+       wipe_macros();
+       wipe_assertions();
+}
+
+#ifdef STAND_ALONE
+/*
+ * print some help
+ */
+static void usage(char *command_name)
+{
+       fprintf(stderr,
+       "Usage: %s [options] [file]\n"
+       "language options:\n"
+       "  -C              keep comments in output\n"
+       "  -s              keep '#' when no cpp directive is recognized\n"
+       "  -l              do not emit line numbers\n"
+       "  -lg             emit gcc-like line numbers\n"
+       "  -CC             disable C++-like comments\n"
+       "  -a, -na, -a0    handle (or not) assertions\n"
+       "  -V              disable macros with extra arguments\n"
+       "  -u              understand UTF-8 in source\n"
+       "  -X              enable -a, -u and -Y\n"
+       "  -c90            mimic C90 behaviour\n"
+       "  -t              disable trigraph support\n"
+       "warning options:\n"
+       "  -wt             emit a final warning when trigaphs are encountered\n"
+       "  -wtt            emit warnings for each trigaph encountered\n"
+       "  -wa             emit warnings that are usually useless\n"
+       "  -w0             disable standard warnings\n"
+       "directory options:\n"
+       "  -I directory    add 'directory' before the standard include path\n"
+       "  -J directory    add 'directory' after the standard include path\n"
+       "  -zI             do not use the standard include path\n"
+       "  -M              emit Makefile-like dependencies instead of normal "
+                       "output\n"
+       "  -Ma             emit also dependancies for system files\n"
+       "  -o file         store output in file\n"
+       "macro and assertion options:\n"
+       "  -Dmacro         predefine 'macro'\n"
+       "  -Dmacro=def     predefine 'macro' with 'def' content\n"
+       "  -Umacro         undefine 'macro'\n"
+       "  -Afoo(bar)      assert foo(bar)\n"
+       "  -Bfoo(bar)      unassert foo(bar)\n"
+       "  -Y              predefine system-dependant macros\n"
+       "  -Z              do not predefine special macros\n"
+       "  -d              emit defined macros\n"
+       "  -e              emit assertions\n"
+       "misc options:\n"
+       "  -v              print version number and settings\n"
+       "  -h              show this help\n",
+       command_name);
+}
+
+/*
+ * print version and compile-time settings
+ */
+static void version(void)
+{
+       size_t i;
+
+       fprintf(stderr, "ucpp version %d.%d\n", VERS_MAJ, VERS_MIN);
+       fprintf(stderr, "search path:\n");
+       for (i = 0; i < include_path_nb; i ++)
+               fprintf(stderr, "  %s\n", include_path[i]);
+}
+
+/*
+ * parse_opt() initializes many things according to the command-line
+ * options.
+ * Return values:
+ * 0  on success
+ * 1  on semantic error (redefinition of a special macro, for instance)
+ * 2  on syntaxic error (unknown options for instance)
+ */
+static int parse_opt(int argc, char *argv[], struct lexer_state *ls)
+{
+       int i, ret = 0;
+       char *filename = 0;
+       int with_std_incpath = 1;
+       int print_version = 0, print_defs = 0, print_asserts = 0;
+       int system_macros = 0, standard_assertions = 1;
+
+       init_lexer_state(ls);
+       ls->flags = DEFAULT_CPP_FLAGS;
+       emit_output = ls->output = stdout;
+       for (i = 1; i < argc; i ++) if (argv[i][0] == '-') {
+               if (!strcmp(argv[i], "-h")) {
+                       return 2;
+               } else if (!strcmp(argv[i], "-C")) {
+                       ls->flags &= ~DISCARD_COMMENTS;
+               } else if (!strcmp(argv[i], "-CC")) {
+                       ls->flags &= ~CPLUSPLUS_COMMENTS;
+               } else if (!strcmp(argv[i], "-a")) {
+                       ls->flags |= HANDLE_ASSERTIONS;
+               } else if (!strcmp(argv[i], "-na")) {
+                       ls->flags |= HANDLE_ASSERTIONS;
+                       standard_assertions = 0;
+               } else if (!strcmp(argv[i], "-a0")) {
+                       ls->flags &= ~HANDLE_ASSERTIONS;
+               } else if (!strcmp(argv[i], "-V")) {
+                       ls->flags &= ~MACRO_VAARG;
+               } else if (!strcmp(argv[i], "-u")) {
+                       ls->flags |= UTF8_SOURCE;
+               } else if (!strcmp(argv[i], "-X")) {
+                       ls->flags |= HANDLE_ASSERTIONS;
+                       ls->flags |= UTF8_SOURCE;
+                       system_macros = 1;
+               } else if (!strcmp(argv[i], "-c90")) {
+                       ls->flags &= ~MACRO_VAARG;
+                       ls->flags &= ~CPLUSPLUS_COMMENTS;
+                       c99_compliant = 0;
+                       c99_hosted = -1;
+               } else if (!strcmp(argv[i], "-t")) {
+                       ls->flags &= ~HANDLE_TRIGRAPHS;
+               } else if (!strcmp(argv[i], "-wt")) {
+                       ls->flags |= WARN_TRIGRAPHS;
+               } else if (!strcmp(argv[i], "-wtt")) {
+                       ls->flags |= WARN_TRIGRAPHS_MORE;
+               } else if (!strcmp(argv[i], "-wa")) {
+                       ls->flags |= WARN_ANNOYING;
+               } else if (!strcmp(argv[i], "-w0")) {
+                       ls->flags &= ~WARN_STANDARD;
+                       ls->flags &= ~WARN_PRAGMA;
+               } else if (!strcmp(argv[i], "-s")) {
+                       ls->flags &= ~FAIL_SHARP;
+               } else if (!strcmp(argv[i], "-l")) {
+                       ls->flags &= ~LINE_NUM;
+               } else if (!strcmp(argv[i], "-lg")) {
+                       ls->flags |= GCC_LINE_NUM;
+               } else if (!strcmp(argv[i], "-M")) {
+                       ls->flags &= ~KEEP_OUTPUT;
+                       emit_dependencies = 1;
+               } else if (!strcmp(argv[i], "-Ma")) {
+                       ls->flags &= ~KEEP_OUTPUT;
+                       emit_dependencies = 2;
+               } else if (!strcmp(argv[i], "-Y")) {
+                       system_macros = 1;
+               } else if (!strcmp(argv[i], "-Z")) {
+                       no_special_macros = 1;
+               } else if (!strcmp(argv[i], "-d")) {
+                       ls->flags &= ~KEEP_OUTPUT;
+                       print_defs = 1;
+               } else if (!strcmp(argv[i], "-e")) {
+                       ls->flags &= ~KEEP_OUTPUT;
+                       print_asserts = 1;
+               } else if (!strcmp(argv[i], "-zI")) {
+                       with_std_incpath = 0;
+               } else if (!strcmp(argv[i], "-I") || !strcmp(argv[i], "-J")) {
+                       i ++;
+               } else if (!strcmp(argv[i], "-o")) {
+                       if ((++ i) >= argc) {
+                               error(-1, "missing filename after -o");
+                               return 2;
+                       }
+                       if (argv[i][0] == '-' && argv[i][1] == 0) {
+                               emit_output = ls->output = stdout;
+                       } else {
+                               ls->output = fopen(argv[i], "w");
+                               if (!ls->output) {
+                                       error(-1, "failed to open for "
+                                               "writing: %s", argv[i]);
+                                       return 2;
+                               }
+                               emit_output = ls->output;
+                       }
+               } else if (!strcmp(argv[i], "-v")) {
+                       print_version = 1;
+               } else if (argv[i][1] != 'I' && argv[i][1] != 'J'
+                       && argv[i][1] != 'D' && argv[i][1] != 'U'
+                       && argv[i][1] != 'A' && argv[i][1] != 'B')
+                       warning(-1, "unknown option '%s'", argv[i]);
+       } else {
+               if (filename != 0) {
+                       error(-1, "spurious filename '%s'", argv[i]);
+                       return 2;
+               }
+               filename = argv[i];
+       }
+       init_tables(ls->flags & HANDLE_ASSERTIONS);
+       init_include_path(0);
+       if (filename) {
+#ifdef UCPP_MMAP
+               FILE *f = fopen_mmap_file(filename);
+
+               ls->input = 0;
+               if (f) set_input_file(ls, f);
+#else
+               ls->input = fopen(filename, "r");
+#endif
+               if (!ls->input) {
+                       error(-1, "file '%s' not found", filename);
+                       return 1;
+               }
+#ifdef NO_LIBC_BUF
+               setbuf(ls->input, 0);
+#endif
+               set_init_filename(filename, 1);
+       } else {
+               ls->input = stdin;
+               set_init_filename("<stdin>", 0);
+       }
+       for (i = 1; i < argc; i ++)
+               if (argv[i][0] == '-' && argv[i][1] == 'I')
+                       add_incpath(argv[i][2] ? argv[i] + 2 : argv[i + 1]);
+       if (system_macros) for (i = 0; system_macros_def[i]; i ++)
+               ret = ret || define_macro(ls, system_macros_def[i]);
+       for (i = 1; i < argc; i ++)
+               if (argv[i][0] == '-' && argv[i][1] == 'D')
+                       ret = ret || define_macro(ls, argv[i] + 2);
+       for (i = 1; i < argc; i ++)
+               if (argv[i][0] == '-' && argv[i][1] == 'U')
+                       ret = ret || undef_macro(ls, argv[i] + 2);
+       if (ls->flags & HANDLE_ASSERTIONS) {
+               if (standard_assertions)
+                       for (i = 0; system_assertions_def[i]; i ++)
+                               make_assertion(system_assertions_def[i]);
+               for (i = 1; i < argc; i ++)
+                       if (argv[i][0] == '-' && argv[i][1] == 'A')
+                               ret = ret || make_assertion(argv[i] + 2);
+               for (i = 1; i < argc; i ++)
+                       if (argv[i][0] == '-' && argv[i][1] == 'B')
+                               ret = ret || destroy_assertion(argv[i] + 2);
+       } else {
+               for (i = 1; i < argc; i ++)
+                       if (argv[i][0] == '-'
+                               && (argv[i][1] == 'A' || argv[i][1] == 'B'))
+                               warning(-1, "assertions disabled");
+       }
+       if (with_std_incpath) {
+               for (i = 0; include_path_std[i]; i ++)
+                       add_incpath(include_path_std[i]);
+       }
+       for (i = 1; i < argc; i ++)
+               if (argv[i][0] == '-' && argv[i][1] == 'J')
+                       add_incpath(argv[i][2] ? argv[i] + 2 : argv[i + 1]);
+
+       if (print_version) {
+               version();
+               return 1;
+       }
+       if (print_defs) {
+               print_defines();
+               emit_defines = 1;
+       }
+       if (print_asserts && (ls->flags & HANDLE_ASSERTIONS)) {
+               print_assertions();
+               emit_assertions = 1;
+       }
+       return ret;
+}
+
+int main(int argc, char *argv[])
+{
+       struct lexer_state ls;
+       int r, fr = 0;
+
+       init_cpp();
+       if ((r = parse_opt(argc, argv, &ls)) != 0) {
+               if (r == 2) usage(argv[0]);
+               return EXIT_FAILURE;
+       }
+       enter_file(&ls, ls.flags);
+       while ((r = cpp(&ls)) < CPPERR_EOF) fr = fr || (r > 0);
+       fr = fr || check_cpp_errors(&ls);
+       free_lexer_state(&ls);
+       wipeout();
+#ifdef MEM_DEBUG
+       report_leaks();
+#endif
+       return fr ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+#endif
diff --git a/libexec/auxcpp/cpp.h b/libexec/auxcpp/cpp.h
new file mode 100644 (file)
index 0000000..2bb707a
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * (c) Thomas Pornin 1999 - 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef UCPP__CPP__
+#define UCPP__CPP__
+
+/*
+ * Uncomment the following if you want ucpp to use externally provided
+ * error-reporting functions (ucpp_warning(), ucpp_error() and ucpp_ouch())
+ */
+/* #define NO_UCPP_ERROR_FUNCTIONS */
+
+/*
+ * Tokens (do not change the order unless checking operators_name[] in cpp.c)
+ *
+ * It is important that the token NONE is 0
+ * Check the STRING_TOKEN macro
+ */
+#define CPPERR 512
+enum {
+       NONE,           /* whitespace */
+       NEWLINE,        /* newline */
+       COMMENT,        /* comment */
+       NUMBER,         /* number constant */
+       NAME,           /* identifier */
+       BUNCH,          /* non-C characters */
+       PRAGMA,         /* a #pragma directive */
+       CONTEXT,        /* new file or #line */
+       STRING,         /* constant "xxx" */
+       CHAR,           /* constant 'xxx' */
+       SLASH,          /*      /       */
+       ASSLASH,        /*      /=      */
+       MINUS,          /*      -       */
+       MMINUS,         /*      --      */
+       ASMINUS,        /*      -=      */
+       ARROW,          /*      ->      */
+       PLUS,           /*      +       */
+       PPLUS,          /*      ++      */
+       ASPLUS,         /*      +=      */
+       LT,             /*      <       */
+       LEQ,            /*      <=      */
+       LSH,            /*      <<      */
+       ASLSH,          /*      <<=     */
+       GT,             /*      >       */
+       GEQ,            /*      >=      */
+       RSH,            /*      >>      */
+       ASRSH,          /*      >>=     */
+       ASGN,           /*      =       */
+       SAME,           /*      ==      */
+#ifdef CAST_OP
+       CAST,           /*      =>      */
+#endif
+       NOT,            /*      ~       */
+       NEQ,            /*      !=      */
+       AND,            /*      &       */
+       LAND,           /*      &&      */
+       ASAND,          /*      &=      */
+       OR,             /*      |       */
+       LOR,            /*      ||      */
+       ASOR,           /*      |=      */
+       PCT,            /*      %       */
+       ASPCT,          /*      %=      */
+       STAR,           /*      *       */
+       ASSTAR,         /*      *=      */
+       CIRC,           /*      ^       */
+       ASCIRC,         /*      ^=      */
+       LNOT,           /*      !       */
+       LBRA,           /*      {       */
+       RBRA,           /*      }       */
+       LBRK,           /*      [       */
+       RBRK,           /*      ]       */
+       LPAR,           /*      (       */
+       RPAR,           /*      )       */
+       COMMA,          /*      ,       */
+       QUEST,          /*      ?       */
+       SEMIC,          /*      ;       */
+       COLON,          /*      :       */
+       DOT,            /*      .       */
+       MDOTS,          /*      ...     */
+       SHARP,          /*      #       */
+       DSHARP,         /*      ##      */
+
+       OPT_NONE,       /* optional space to separate tokens in text output */
+
+       DIGRAPH_TOKENS,                 /* there begin digraph tokens */
+
+       /* for DIG_*, do not change order, unless checking undig() in cpp.c */
+       DIG_LBRK,       /*      <:      */
+       DIG_RBRK,       /*      :>      */
+       DIG_LBRA,       /*      <%      */
+       DIG_RBRA,       /*      %>      */
+       DIG_SHARP,      /*      %:      */
+       DIG_DSHARP,     /*      %:%:    */
+
+       DIGRAPH_TOKENS_END,             /* digraph tokens end here */
+
+       LAST_MEANINGFUL_TOKEN,          /* reserved words will go there */
+
+       MACROARG,       /* special token for representing macro arguments */
+
+       UPLUS = CPPERR, /* unary + */
+       UMINUS          /* unary - */
+};
+
+#include "tune.h"
+#include <stdio.h>
+#include <setjmp.h>
+
+struct token {
+       int type;
+       long line;
+       char *name;
+};
+
+struct token_fifo {
+       struct token *t;
+       size_t nt, art;
+};
+
+struct lexer_state {
+       /* input control */
+       FILE *input;
+#ifndef NO_UCPP_BUF
+       unsigned char *input_buf;
+#ifdef UCPP_MMAP
+       int from_mmap;
+       unsigned char *input_buf_sav;
+#endif
+#endif
+       unsigned char *input_string;
+       size_t ebuf;
+       size_t pbuf;
+       int lka[2];
+       int nlka;
+       int macfile;
+       int last;
+       int discard;
+       unsigned long utf8;
+       unsigned char copy_line[COPY_LINE_LENGTH];
+       int cli;
+
+       /* output control */
+       FILE *output;
+       struct token_fifo *output_fifo, *toplevel_of;
+#ifndef NO_UCPP_BUF
+       unsigned char *output_buf;
+#endif
+       size_t sbuf;
+
+       /* token control */
+       struct token *ctok;
+       struct token *save_ctok;
+       size_t tknl;
+       int ltwnl;
+       int pending_token;
+#ifdef INMACRO_FLAG
+       int inmacro;
+       long macro_count;
+#endif
+
+       /* lexer options */
+       long line;
+       long oline;
+       unsigned long flags;
+       long count_trigraphs;
+       struct garbage_fifo *gf;
+       int ifnest;
+       int condnest;
+       int condcomp;
+       int condmet;
+       unsigned long condf[2];
+};
+
+/*
+ * Flags for struct lexer_state
+ */
+/* warning flags */
+#define WARN_STANDARD       0x000001UL /* emit standard warnings */
+#define WARN_ANNOYING       0x000002UL /* emit annoying warnings */
+#define WARN_TRIGRAPHS      0x000004UL /* warn when trigraphs are used */
+#define WARN_TRIGRAPHS_MORE  0x000008UL        /* extra-warn for trigraphs */
+#define WARN_PRAGMA         0x000010UL /* warn for pragmas in non-lexer mode */
+
+/* error flags */
+#define FAIL_SHARP          0x000020UL /* emit errors on rogue '#' */
+#define CCHARSET            0x000040UL /* emit errors on non-C characters */
+
+/* emission flags */
+#define DISCARD_COMMENTS     0x000080UL        /* discard comments from text output */
+#define CPLUSPLUS_COMMENTS   0x000100UL        /* understand C++-like comments */
+#define LINE_NUM            0x000200UL /* emit #line directives in output */
+#define GCC_LINE_NUM        0x000400UL /* same as #line, with gcc-syntax */
+
+/* language flags */
+#define HANDLE_ASSERTIONS    0x000800UL        /* understand assertions */
+#define HANDLE_PRAGMA       0x001000UL /* emit PRAGMA tokens in lexer mode */
+#define MACRO_VAARG         0x002000UL /* understand macros with '...' */
+#define UTF8_SOURCE         0x004000UL /* identifiers are in UTF8 encoding */
+#define HANDLE_TRIGRAPHS     0x008000UL        /* handle trigraphs */
+
+/* global ucpp behaviour */
+#define LEXER               0x010000UL /* behave as a lexer */
+#define KEEP_OUTPUT         0x020000UL /* emit the result of preprocessing */
+#define COPY_LINE           0x040000UL /* make a copy of the parsed line */
+
+/* internal flags */
+#define READ_AGAIN          0x080000UL /* emit again the last token */
+#define TEXT_OUTPUT         0x100000UL /* output text */
+
+/*
+ * Public function prototypes
+ */
+
+#ifndef NO_UCPP_BUF
+void flush_output(struct lexer_state *);
+#endif
+
+void init_assertions(void);
+int make_assertion(char *);
+int destroy_assertion(char *);
+void print_assertions(void);
+
+void init_macros(void);
+int define_macro(struct lexer_state *, char *);
+int undef_macro(struct lexer_state *, char *);
+void print_defines(void);
+
+void set_init_filename(char *, int);
+void init_cpp(void);
+void init_include_path(char *[]);
+void init_lexer_state(struct lexer_state *);
+void init_lexer_mode(struct lexer_state *);
+void free_lexer_state(struct lexer_state *);
+void wipeout(void);
+int lex(struct lexer_state *);
+int check_cpp_errors(struct lexer_state *);
+void add_incpath(char *);
+void init_tables(int);
+int enter_file(struct lexer_state *, unsigned long);
+int cpp(struct lexer_state *);
+void set_identifier_char(int c);
+void unset_identifier_char(int c);
+
+#ifdef UCPP_MMAP
+FILE *fopen_mmap_file(char *);
+void set_input_file(struct lexer_state *, FILE *);
+#endif
+
+struct stack_context {
+       char *long_name, *name;
+       long line;
+};
+struct stack_context *report_context(void);
+
+extern int no_special_macros, system_macros,
+       emit_dependencies, emit_defines, emit_assertions;
+extern int c99_compliant, c99_hosted;
+extern FILE *emit_output;
+extern char *current_filename, *current_long_filename;
+extern char *operators_name[];
+extern struct protect {
+       char *macro;
+       int state;
+       struct found_file *ff;
+} protect_detect;
+
+void ucpp_ouch(char *, ...);
+void ucpp_error(long, char *, ...);
+void ucpp_warning(long, char *, ...);
+
+extern int *transient_characters;
+
+/*
+ * Errors from CPPERR_EOF and above are not real erros, only show-stoppers.
+ * Errors below CPPERR_EOF are real ones.
+ */
+#define CPPERR_NEST     900
+#define CPPERR_EOF     1000
+
+/*
+ * This macro tells whether the name field of a given token type is
+ * relevant, or not. Irrelevant name field means that it might point
+ * to outerspace.
+ */
+#ifdef SEMPER_FIDELIS
+#define STRING_TOKEN(x)    ((x) == NONE || ((x) >= COMMENT && (x) <= CHAR))
+#else
+#define STRING_TOKEN(x)    ((x) >= NUMBER && (x) <= CHAR)
+#endif
+
+#endif
diff --git a/libexec/auxcpp/eval.c b/libexec/auxcpp/eval.c
new file mode 100644 (file)
index 0000000..8297197
--- /dev/null
@@ -0,0 +1,699 @@
+/*
+ * (c) Thomas Pornin 1999 - 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "tune.h"
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <limits.h>
+#include "ucppi.h"
+#include "mem.h"
+
+JMP_BUF eval_exception;
+long eval_line;
+static int emit_eval_warnings;
+
+/*
+ * If you want to hardcode a conversion table, define a static array
+ * of 256 int, and make transient_characters point to it.
+ */
+int *transient_characters = 0;
+
+#define OCTAL(x)       ((x) >= '0' && (x) <= '7')
+#define DECIM(x)       ((x) >= '0' && (x) <= '9')
+#define HEXAD(x)       (DECIM(x) \
+                       || (x) == 'a' || (x) == 'b' || (x) == 'c' \
+                       || (x) == 'd' || (x) == 'e' || (x) == 'f' \
+                       || (x) == 'A' || (x) == 'B' || (x) == 'C' \
+                       || (x) == 'D' || (x) == 'E' || (x) == 'F')
+#define OVAL(x)               ((int)((x) - '0'))
+#define DVAL(x)               ((int)((x) - '0'))
+#define HVAL(x)               (DECIM(x) ? DVAL(x) \
+                       : (x) == 'a' || (x) == 'A' ? 10 \
+                       : (x) == 'b' || (x) == 'B' ? 11 \
+                       : (x) == 'c' || (x) == 'C' ? 12 \
+                       : (x) == 'd' || (x) == 'D' ? 13 \
+                       : (x) == 'e' || (x) == 'E' ? 14 : 15)
+
+#define ARITH_TYPENAME             big
+#define ARITH_FUNCTION_HEADER      static inline
+
+#define ARITH_ERROR(type)          z_error(type)
+static void z_error(int type);
+
+#ifdef ARITHMETIC_CHECKS
+#define ARITH_WARNING(type)        z_warn(type)
+static void z_warn(int type);
+#endif
+
+#include "arith.c"
+
+static void z_error(int type)
+{
+       switch (type) {
+       case ARITH_EXCEP_SLASH_D:
+               error(eval_line, "division by 0");
+               break;
+       case ARITH_EXCEP_SLASH_O:
+               error(eval_line, "overflow on division");
+               break;
+       case ARITH_EXCEP_PCT_D:
+               error(eval_line, "division by 0 on modulus operator");
+               break;
+       case ARITH_EXCEP_CONST_O:
+               error(eval_line, "constant too large for destination type");
+               break;
+#ifdef AUDIT
+       default:
+               ouch("erroneous integer error: %d", type);
+#endif
+       }
+       throw(eval_exception);
+}
+
+#ifdef ARITHMETIC_CHECKS
+static void z_warn(int type)
+{
+       switch (type) {
+       case ARITH_EXCEP_CONV_O:
+               warning(eval_line, "overflow on integer conversion");
+               break;
+       case ARITH_EXCEP_NEG_O:
+               warning(eval_line, "overflow on unary minus");
+               break;
+       case ARITH_EXCEP_NOT_T:
+               warning(eval_line,
+                       "bitwise inversion yields trap representation");
+               break;
+       case ARITH_EXCEP_PLUS_O:
+               warning(eval_line, "overflow on addition");
+               break;
+       case ARITH_EXCEP_PLUS_U:
+               warning(eval_line, "underflow on addition");
+               break;
+       case ARITH_EXCEP_MINUS_O:
+               warning(eval_line, "overflow on subtraction");
+               break;
+       case ARITH_EXCEP_MINUS_U:
+               warning(eval_line, "underflow on subtraction");
+               break;
+       case ARITH_EXCEP_AND_T:
+               warning(eval_line,
+                       "bitwise AND yields trap representation");
+               break;
+       case ARITH_EXCEP_XOR_T:
+               warning(eval_line,
+                       "bitwise XOR yields trap representation");
+               break;
+       case ARITH_EXCEP_OR_T:
+               warning(eval_line,
+                       "bitwise OR yields trap representation");
+               break;
+       case ARITH_EXCEP_LSH_W:
+               warning(eval_line, "left shift count greater than "
+                       "or equal to type width");
+               break;
+       case ARITH_EXCEP_LSH_C:
+               warning(eval_line, "left shift count negative");
+               break;
+       case ARITH_EXCEP_LSH_O:
+               warning(eval_line, "overflow on left shift");
+               break;
+       case ARITH_EXCEP_RSH_W:
+               warning(eval_line, "right shift count greater than "
+                       "or equal to type width");
+               break;
+       case ARITH_EXCEP_RSH_C:
+               warning(eval_line, "right shift count negative");
+               break;
+       case ARITH_EXCEP_RSH_N:
+               warning(eval_line, "right shift of negative value");
+               break;
+       case ARITH_EXCEP_STAR_O:
+               warning(eval_line, "overflow on multiplication");
+               break;
+       case ARITH_EXCEP_STAR_U:
+               warning(eval_line, "underflow on multiplication");
+               break;
+#ifdef AUDIT
+       default:
+               ouch("erroneous integer warning: %d", type);
+#endif
+       }
+}
+#endif
+
+typedef struct {
+       int sign;
+       union {
+               u_big uv;
+               s_big sv;
+       } u;
+} ppval;
+
+static int boolval(ppval x)
+{
+       return x.sign ? big_s_lval(x.u.sv) : big_u_lval(x.u.uv);
+}
+
+#if !defined(WCHAR_SIGNEDNESS)
+#  if CHAR_MIN == 0
+#    define WCHAR_SIGNEDNESS   0
+#  else
+#    define WCHAR_SIGNEDNESS   1
+#  endif
+#endif
+
+/*
+ * Check the suffix, return 1 if it is signed, 0 otherwise. 1 is
+ * returned for a void suffix. Legal suffixes are:
+ * unsigned: u U ul uL Ul UL lu Lu lU LU ull uLL Ull ULL llu LLu llU LLU
+ * signed: l L ll LL
+ */
+static int pp_suffix(char *d, char *refc)
+{
+       if (!*d) return 1;
+       if (*d == 'u' || *d == 'U') {
+               if (!*(++ d)) return 0;
+               if (*d == 'l' || *d == 'L') {
+                       char *e = d + 1;
+
+                       if (*e && *e != *d) goto suffix_error;
+                       if (!*e || !*(e + 1)) return 0;
+                       goto suffix_error;
+               }
+               goto suffix_error;
+       }
+       if (*d == 'l' || *d == 'L') {
+               if (!*(++ d)) return 1;
+               if (*d == *(d - 1)) {
+                       d ++;
+                       if (!*d) return 1;
+               }
+               if (*d == 'u' || *d == 'U') {
+                       d ++;
+                       if (!*d) return 0;
+               }
+               goto suffix_error;
+       }
+suffix_error:
+       error(eval_line, "invalid integer constant '%s'", refc);
+       throw(eval_exception);
+       return 666;
+}
+
+static unsigned long pp_char(char *c, char *refc)
+{
+       unsigned long r = 0;
+
+       c ++;
+       if (*c == '\\') {
+               int i;
+
+               c ++;
+               switch (*c) {
+               case 'n': r = '\n'; c ++; break;
+               case 't': r = '\t'; c ++; break;
+               case 'v': r = '\v'; c ++; break;
+               case 'b': r = '\b'; c ++; break;
+               case 'r': r = '\r'; c ++; break;
+               case 'f': r = '\f'; c ++; break;
+               case 'a': r = '\a'; c ++; break;
+               case '\\': r = '\\'; c ++; break;
+               case '\?': r = '\?'; c ++; break;
+               case '\'': r = '\''; c ++; break;
+               case '\"': r = '\"'; c ++; break;
+               case 'u':
+                       for (i = 0, c ++; i < 4 && HEXAD(*c); i ++, c ++) {
+                               r = (r * 16) + HVAL(*c);
+                       }
+                       if (i != 4) {
+                               error(eval_line, "malformed UCN in %s", refc);
+                               throw(eval_exception);
+                       }
+                       break;
+               case 'U':
+                       for (i = 0, c ++; i < 8 && HEXAD(*c); i ++, c ++) {
+                               r = (r * 16) + HVAL(*c);
+                       }
+                       if (i != 8) {
+                               error(eval_line, "malformed UCN in %s", refc);
+                               throw(eval_exception);
+                       }
+                       break;
+               case 'x':
+                       for (c ++; HEXAD(*c); c ++) r = (r * 16) + HVAL(*c);
+                       break;
+               default:
+                       if (OCTAL(*c)) {
+                               r = OVAL(*(c ++));
+                               if (OCTAL(*c)) r = (r * 8) + OVAL(*(c ++));
+                               if (OCTAL(*c)) r = (r * 8) + OVAL(*(c ++));
+                       } else {
+                               error(eval_line, "invalid escape sequence "
+                                       "'\\%c'", *c);
+                               throw(eval_exception);
+                       }
+               }
+       } else if (*c == '\'') {
+               error(eval_line, "empty character constant");
+               throw(eval_exception);
+       } else {
+               r = *((unsigned char *)(c ++));
+       }
+
+       if (transient_characters && r < 256) {
+               r = transient_characters[(size_t)r];
+       }
+
+       if (*c != '\'' && emit_eval_warnings) {
+               warning(eval_line, "multicharacter constant");
+       }
+       return r;
+}
+
+static ppval pp_strtoconst(char *refc)
+{
+       ppval q;
+       char *c = refc, *d;
+       u_big ru;
+       s_big rs;
+       int sp, dec;
+
+       if (*c == '\'' || *c == 'L') {
+               q.sign = (*c == 'L') ? WCHAR_SIGNEDNESS : 1;
+               if (*c == 'L' && *(++ c) != '\'') {
+                       error(eval_line,
+                               "invalid wide character constant: %s", refc);
+                       throw(eval_exception);
+               }
+               if (q.sign) {
+                       q.u.sv = big_s_fromlong(pp_char(c, refc));
+               } else {
+                       q.u.uv = big_u_fromulong(pp_char(c, refc));
+               }
+               return q;
+       }
+       if (*c == '0') {
+               /* octal or hexadecimal */
+               dec = 0;
+               c ++;
+               if (*c == 'x' || *c == 'X') {
+                       c ++;
+                       d = big_u_hexconst(c, &ru, &rs, &sp);
+               } else {
+                       d = big_u_octconst(c, &ru, &rs, &sp);
+               }
+       } else {
+               dec = 1;
+               d = big_u_decconst(c, &ru, &rs, &sp);
+       }
+       q.sign = pp_suffix(d, refc);
+       if (q.sign) {
+               if (!sp) {
+                       if (dec) {
+                               error(eval_line, "constant too large "
+                                       "for destination type");
+                               throw(eval_exception);
+                       } else {
+                               warning(eval_line, "constant is so large "
+                                       "that it is unsigned");
+                       }
+                       q.u.uv = ru;
+                       q.sign = 0;
+               } else {
+                       q.u.sv = rs;
+               }
+       } else {
+               q.u.uv = ru;
+       }
+       return q;
+}
+
+/*
+ * Used by #line directives -- anything beyond what can be put in an
+ * unsigned long, is considered absurd.
+ */
+unsigned long strtoconst(char *c)
+{
+       ppval q = pp_strtoconst(c);
+
+       if (q.sign) q.u.uv = big_s_to_u(q.u.sv);
+       return big_u_toulong(q.u.uv);
+}
+
+#define OP_UN(x)       ((x) == LNOT || (x) == NOT || (x) == UPLUS \
+                       || (x) == UMINUS)
+
+static ppval eval_opun(int op, ppval v)
+{
+       if (op == LNOT) {
+               v.sign = 1;
+               v.u.sv = big_s_fromint(big_s_lnot(v.u.sv));
+               return v;
+       }
+       if (v.sign) {
+               switch (op) {
+               case NOT: v.u.sv = big_s_not(v.u.sv); break;
+               case UPLUS: break;
+               case UMINUS: v.u.sv = big_s_neg(v.u.sv); break;
+               }
+       } else {
+               switch (op) {
+               case NOT: v.u.uv = big_u_not(v.u.uv); break;
+               case UPLUS: break;
+               case UMINUS: v.u.uv = big_u_neg(v.u.uv); break;
+               }
+       }
+       return v;
+}
+
+#define OP_BIN(x)      ((x) == STAR || (x) == SLASH || (x) == PCT \
+                       || (x) == PLUS || (x) == MINUS || (x) == LSH \
+                       || (x) == RSH || (x) == LT || (x) == LEQ \
+                       || (x) == GT || (x) == GEQ || (x) == SAME \
+                       || (x) == NEQ || (x) == AND || (x) == CIRC \
+                       || (x) == OR || (x) == LAND || (x) == LOR \
+                       || (x) == COMMA)
+
+static ppval eval_opbin(int op, ppval v1, ppval v2)
+{
+       ppval r;
+       int iv2 = 0;
+
+       switch (op) {
+       case STAR:      case SLASH:     case PCT:
+       case PLUS:      case MINUS:     case AND:
+       case CIRC:      case OR:
+               /* promote operands, adjust signedness of result */
+               if (!v1.sign || !v2.sign) {
+                       if (v1.sign) {
+                               v1.u.uv = big_s_to_u(v1.u.sv);
+                               v1.sign = 0;
+                       } else if (v2.sign) {
+                               v2.u.uv = big_s_to_u(v2.u.sv);
+                               v2.sign = 0;
+                       }
+                       r.sign = 0;
+               } else {
+                       r.sign = 1;
+               }
+               break;
+       case LT:        case LEQ:       case GT:
+       case GEQ:       case SAME:      case NEQ:
+               /* promote operands */
+               if (!v1.sign || !v2.sign) {
+                       if (v1.sign) {
+                               v1.u.uv = big_s_to_u(v1.u.sv);
+                               v1.sign = 0;
+                       } else if (v2.sign) {
+                               v2.u.uv = big_s_to_u(v2.u.sv);
+                               v2.sign = 0;
+                       }
+               }
+               /* fall through */
+       case LAND:
+       case LOR:
+               /* result is signed anyway */
+               r.sign = 1;
+               break;
+       case LSH:
+       case RSH:
+               /* result is as signed as left operand; convert right
+                  operand to int */
+               r.sign = v1.sign;
+               if (v2.sign) {
+                       iv2 = big_s_toint(v2.u.sv);
+               } else {
+                       iv2 = big_u_toint(v2.u.uv);
+               }
+               break;
+       case COMMA:
+               if (emit_eval_warnings) {
+                       warning(eval_line, "ISO C forbids evaluated comma "
+                               "operators in #if expressions");
+               }
+               r.sign = v2.sign;
+               break;
+#ifdef AUDIT
+       default: ouch("a good operator is a dead operator");
+#endif
+       }
+
+#define SBINOP(x)      if (r.sign) r.u.sv = big_s_ ## x (v1.u.sv, v2.u.sv); \
+                       else r.u.uv = big_u_ ## x (v1.u.uv, v2.u.uv);
+
+#define NSSBINOP(x)    if (v1.sign) r.u.sv = big_s_fromint(big_s_ ## x \
+                       (v1.u.sv, v2.u.sv)); else r.u.sv = big_s_fromint( \
+                       big_u_ ## x (v1.u.uv, v2.u.uv));
+
+#define LBINOP(x)      if (v1.sign) r.u.sv = big_s_fromint( \
+                       big_s_lval(v1.u.sv) x big_s_lval(v2.u.sv)); \
+                       else r.u.sv = big_s_fromint( \
+                       big_u_lval(v1.u.uv) x big_u_lval(v2.u.uv));
+
+#define ABINOP(x)      if (r.sign) r.u.sv = big_s_ ## x (v1.u.sv, iv2); \
+                       else r.u.uv = big_u_ ## x (v1.u.uv, iv2);
+
+       switch (op) {
+       case STAR: SBINOP(star); break;
+       case SLASH: SBINOP(slash); break;
+       case PCT: SBINOP(pct); break;
+       case PLUS: SBINOP(plus); break;
+       case MINUS: SBINOP(minus); break;
+       case LSH: ABINOP(lsh); break;
+       case RSH: ABINOP(rsh); break;
+       case LT: NSSBINOP(lt); break;
+       case LEQ: NSSBINOP(leq); break;
+       case GT: NSSBINOP(gt); break;
+       case GEQ: NSSBINOP(geq); break;
+       case SAME: NSSBINOP(same); break;
+       case NEQ: NSSBINOP(neq); break;
+       case AND: SBINOP(and); break;
+       case CIRC: SBINOP(xor); break;
+       case OR: SBINOP(or); break;
+       case LAND: LBINOP(&&); break;
+       case LOR: LBINOP(||); break;
+       case COMMA: r = v2; break;
+       }
+       return r;
+}
+
+#define ttOP(x)                (OP_UN(x) || OP_BIN(x) || (x) == QUEST || (x) == COLON)
+
+static int op_prec(int op)
+{
+       switch (op) {
+       case LNOT:
+       case NOT:
+       case UPLUS:
+       case UMINUS:
+               return 13;
+       case STAR:
+       case SLASH:
+       case PCT:
+               return 12;
+       case PLUS:
+       case MINUS:
+               return 11;
+       case LSH:
+       case RSH:
+               return 10;
+       case LT:
+       case LEQ:
+       case GT:
+       case GEQ:
+               return 9;
+       case SAME:
+       case NEQ:
+               return 8;
+       case AND:
+               return 7;
+       case CIRC:
+               return 6;
+       case OR:
+               return 5;
+       case LAND:
+               return 4;
+       case LOR:
+               return 3;
+       case QUEST:
+               return 2;
+       case COMMA:
+               return 1;
+       }
+#ifdef AUDIT
+       ouch("an unknown species should have a higher precedence");
+#endif
+       return 666;
+}
+
+/*
+ * Perform the hard work of evaluation.
+ *
+ * This function works because:
+ * -- all unary operators are right to left associative, and with
+ *    identical precedence
+ * -- all binary operators are left to right associative
+ * -- there is only one non-unary and non-binary operator: the quest-colon
+ *
+ * If do_eval is 0, the evaluation of operators is not done. This is
+ * for sequence point operators (&&, || and ?:).
+ */
+static ppval eval_shrd(struct token_fifo *tf, int minprec, int do_eval)
+{
+       ppval top;
+       struct token *ct;
+
+       top.sign = 1;
+       if (tf->art == tf->nt) goto trunc_err;
+       ct = tf->t + (tf->art ++);
+       if (ct->type == LPAR) {
+               top = eval_shrd(tf, 0, do_eval);
+               if (tf->art == tf->nt) goto trunc_err;
+               ct = tf->t + (tf->art ++);
+               if (ct->type != RPAR) {
+                       error(eval_line, "a right parenthesis was expected");
+                       throw(eval_exception);
+               }
+       } else if (ct->type == NUMBER || ct->type == CHAR) {
+               top = pp_strtoconst(ct->name);
+       } else if (OP_UN(ct->type)) {
+               top = eval_opun(ct->type, eval_shrd(tf,
+                       op_prec(ct->type), do_eval));
+               goto eval_loop;
+       } else if (ttOP(ct->type)) goto rogue_op_err;
+       else {
+               goto invalid_token_err;
+       }
+
+eval_loop:
+       if (tf->art == tf->nt) {
+               return top;
+       }
+       ct = tf->t + (tf->art ++);
+       if (OP_BIN(ct->type)) {
+               int bp = op_prec(ct->type);
+
+               if (bp > minprec) {
+                       ppval tr;
+
+                       if ((ct->type == LOR && boolval(top))
+                               || (ct->type == LAND && !boolval(top))) {
+                               tr = eval_shrd(tf, bp, 0);
+                               if (do_eval) {
+                                       top.sign = 1;
+                                       if (ct->type == LOR)
+                                               top.u.sv = big_s_fromint(1);
+                                       if (ct->type == LAND)
+                                               top.u.sv = big_s_fromint(0);
+                               }
+                       } else {
+                               tr = eval_shrd(tf, bp, do_eval);
+                               if (do_eval)
+                                       top = eval_opbin(ct->type, top, tr);
+                       }
+                       goto eval_loop;
+               }
+       } else if (ct->type == QUEST) {
+               int bp = op_prec(QUEST);
+               ppval r1, r2;
+
+               if (bp >= minprec) {
+                       int qv = boolval(top);
+
+                       r1 = eval_shrd(tf, bp, qv ? do_eval : 0);
+                       if (tf->art == tf->nt) goto trunc_err;
+                       ct = tf->t + (tf->art ++);
+                       if (ct->type != COLON) {
+                               error(eval_line, "a colon was expected");
+                               throw(eval_exception);
+                       }
+                       r2 = eval_shrd(tf, bp, qv ? 0 : do_eval);
+                       if (do_eval) {
+                               if (qv) top = r1; else top = r2;
+                       }
+                       goto eval_loop;
+               }
+       }
+       tf->art --;
+       return top;
+
+trunc_err:
+       error(eval_line, "truncated constant integral expression");
+       throw(eval_exception);
+rogue_op_err:
+       error(eval_line, "rogue operator '%s' in constant integral "
+               "expression", operators_name[ct->type]);
+       throw(eval_exception);
+invalid_token_err:
+       error(eval_line, "invalid token in constant integral expression");
+       throw(eval_exception);
+}
+
+#define UNARY(x)       ((x) != NUMBER && (x) != NAME && (x) != CHAR \
+                       && (x) != RPAR)
+
+/*
+ * Evaluate the integer expression contained in the given token_fifo.
+ * Evaluation is made by precedence of operators, as described in the
+ * Dragon Book. The unary + and - are distinguished from their binary
+ * counterparts using the Fortran way: a + or a - is considered unary
+ * if it does not follow a constant, an identifier or a right parenthesis.
+ */
+unsigned long eval_expr(struct token_fifo *tf, int *ret, int ew)
+{
+       size_t sart;
+       ppval r;
+
+       emit_eval_warnings = ew;
+       if (catch(eval_exception)) goto eval_err;
+       /* first, distinguish unary + and - from binary + and - */
+       for (sart = tf->art; tf->art < tf->nt; tf->art ++) {
+               if (tf->t[tf->art].type == PLUS) {
+                       if (sart == tf->art || UNARY(tf->t[tf->art - 1].type))
+                               tf->t[tf->art].type = UPLUS;
+               } else if (tf->t[tf->art].type == MINUS) {
+                       if (sart == tf->art || UNARY(tf->t[tf->art - 1].type))
+                               tf->t[tf->art].type = UMINUS;
+               }
+       }
+       tf->art = sart;
+       r = eval_shrd(tf, 0, 1);
+       if (tf->art < tf->nt) {
+               error(eval_line, "trailing garbage in constant integral "
+                       "expression");
+               goto eval_err;
+       }
+       *ret = 0;
+       return boolval(r);
+eval_err:
+       *ret = 1;
+       return 0;
+}
diff --git a/libexec/auxcpp/hash.c b/libexec/auxcpp/hash.c
new file mode 100644 (file)
index 0000000..60bd212
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Generic hash table routines.
+ * (c) Thomas Pornin 1998, 1999, 2000
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <string.h>
+#include "hash.h"
+#include "mem.h"
+#include "tune.h"
+
+/*
+ * hash_string() is a sample hash function for strings
+ */
+int hash_string(char *s)
+{
+#ifdef FAST_HASH
+       unsigned h = 0, g;
+
+       while (*s) {
+               h = (h << 4) + *(unsigned char *)(s ++);
+               if ((g = h & 0xF000U) != 0) h ^= (g >> 12);
+               h &= ~g;
+       }
+       return (h ^ (h >> 9)) & 127U;
+#else
+       unsigned char h = 0;
+
+       for (; *s; s ++) h ^= (unsigned char)(*s);
+       return ((int)h);
+#endif
+}
+
+/*
+ * struct hash_item is the basic data type to internally handle hash tables
+ */
+struct hash_item {
+       void *data;
+       struct hash_item *next;
+};
+
+/*
+ * This function adds an entry to the struct hash_item list
+ */
+static struct hash_item *add_entry(struct hash_item *blist, void *data)
+{
+       struct hash_item *t = getmem(sizeof(struct hash_item));
+
+       t->data = data;
+       t->next = blist;
+       return t;
+}
+
+/*
+ * This function finds a struct hash_item in a list, using the
+ * comparison function provided as cmpdata (*cmpdata() returns
+ * non-zero if the two parameters are to be considered identical).
+ *
+ * It returns 0 if the item is not found.
+ */
+static struct hash_item *get_entry(struct hash_item *blist, void *data,
+       int (*cmpdata)(void *, void *))
+{
+       while (blist) {
+               if ((*cmpdata)(data, blist->data)) return blist;
+               blist = blist->next;
+       }
+       return 0;
+}
+
+/*
+ * This function acts like get_entry but deletes the found item, using
+ * the provided function deldata(); it returns 0 if the given data was
+ * not found.
+ */
+static struct hash_item *del_entry(struct hash_item *blist, void *data,
+       int (*cmpdata)(void *, void *), void (*deldata)(void *))
+{
+       struct hash_item *prev = 0, *save = blist;
+
+       while (blist) {
+               if ((*cmpdata)(data, blist->data)) {
+                       if (deldata) (*deldata)(blist->data);
+                       if (prev) prev->next = blist->next;
+                       if (save == blist) save = blist->next;
+                       freemem(blist);
+                       return save;
+               }
+               prev = blist;
+               blist = blist->next;
+       }
+       return 0;
+}
+
+/*
+ * This function creates a new hashtable, with the hashing and comparison
+ * functions given as parameters
+ */
+struct HT *newHT(int n, int (*cmpdata)(void *, void *), int (*hash)(void *),
+       void (*deldata)(void *))
+{
+       struct HT *t = getmem(sizeof(struct HT));
+       int i;
+
+       t->lists = getmem(n * sizeof(struct hash_item *));
+       for (i = 0; i < n; i ++) t->lists[i] = 0;
+       t->nb_lists = n;
+       t->cmpdata = cmpdata;
+       t->hash = hash;
+       t->deldata = deldata;
+       return t;
+}
+
+/*
+ * This function adds a new entry in the hashtable ht; it returns 0
+ * on success, or a pointer to the already present item otherwise.
+ */
+void *putHT(struct HT *ht, void *data)
+{
+       int h;
+       struct hash_item *d;
+
+       h = ((*(ht->hash))(data));
+#ifndef FAST_HASH
+       h %= ht->nb_lists;
+#endif
+       if ((d = get_entry(ht->lists[h], data, ht->cmpdata)))
+               return d->data;
+       ht->lists[h] = add_entry(ht->lists[h], data);
+       return 0;
+}
+
+/*
+ * This function adds a new entry in the hashtable ht, even if an equal
+ * entry is already there. Exercise caution !
+ * The new entry will "hide" the old one, which means that the new will be
+ * found upon lookup/delete, not the old one.
+ */
+void *forceputHT(struct HT *ht, void *data)
+{
+       int h;
+
+       h = ((*(ht->hash))(data));
+#ifndef FAST_HASH
+       h %= ht->nb_lists;
+#endif
+       ht->lists[h] = add_entry(ht->lists[h], data);
+       return 0;
+}
+
+/*
+ * This function finds the entry corresponding to *data in the
+ * hashtable ht (using the comparison function given as argument
+ * to newHT)
+ */
+void *getHT(struct HT *ht, void *data)
+{
+       int h;
+       struct hash_item *t;
+
+       h = ((*(ht->hash))(data));
+#ifndef FAST_HASH
+       h %= ht->nb_lists;
+#endif
+       if ((t = get_entry(ht->lists[h], data, ht->cmpdata)) == 0)
+               return 0;
+       return (t->data);
+}
+
+/*
+ * This function finds and delete the entry corresponding to *data
+ * in the hashtable ht (using the comparison function given as
+ * argument to newHT).
+ */
+
+int delHT(struct HT *ht, void *data)
+{
+       int h;
+
+       h = ((*(ht->hash))(data));
+#ifndef FAST_HASH
+       h %= ht->nb_lists;
+#endif
+       ht->lists[h] = del_entry(ht->lists[h], data, ht->cmpdata, ht->deldata);
+       return 1;
+}
+
+/*
+ * This function completely eradicates from memory a given hash table,
+ * releasing all objects
+ */
+void killHT(struct HT *ht)
+{
+       int i;
+       struct hash_item *t, *n;
+       void (*dd)(void *) = ht->deldata;
+
+       for (i = 0; i < ht->nb_lists; i ++) for (t = ht->lists[i]; t;) {
+               n = t->next;
+               if (dd) (*dd)(t->data);
+               freemem(t);
+               t = n;
+       }
+       freemem(ht->lists);
+       freemem(ht);
+}
+
+/*
+ * This function stores a backup of the hash table, for context stacking.
+ */
+void saveHT(struct HT *ht, void **buffer)
+{
+       struct hash_item **b = (struct hash_item **)buffer;
+
+       mmv(b, ht->lists, ht->nb_lists * sizeof(struct hash_item *));
+}
+
+/*
+ * This function restores the saved state of the hash table.
+ * Do NOT use if some of the entries that were present before the backup
+ * have been removed (even temporarily).
+ */
+void restoreHT(struct HT *ht, void **buffer)
+{
+       struct hash_item **b = (struct hash_item **)buffer;
+       int i;
+
+       for (i = 0; i < ht->nb_lists; i ++) {
+               struct hash_item *t = ht->lists[i], *n;
+
+               while (t != b[i]) {
+                       n = t->next;
+                       (*(ht->deldata))(t->data);
+                       freemem(t);
+                       t = n;
+               }
+               ht->lists[i] = b[i];
+       }
+}
+
+/*
+ * This function is evil. It inserts a new item in a saved hash table,
+ * tweaking the save buffer and the hash table in order to keep things
+ * stable. There are no checks.
+ */
+void tweakHT(struct HT *ht, void **buffer, void *data)
+{
+       int h;
+       struct hash_item *d, *e;
+
+       h = ((*(ht->hash))(data));
+#ifndef FAST_HASH
+       h %= ht->nb_lists;
+#endif
+       for (d = ht->lists[h]; d != buffer[h]; d = d->next);
+       d = add_entry(buffer[h], data);
+       if (buffer[h] == ht->lists[h]) {
+               buffer[h] = ht->lists[h] = d;
+               return;
+       }
+       for (e = ht->lists[h]; e->next != buffer[h]; e = e->next);
+       e->next = d;
+       buffer[h] = d;
+}
+
+/*
+ * This function scans the whole table and calls the given function on
+ * each entry.
+ */
+void scanHT(struct HT *ht, void (*action)(void *))
+{
+       int i;
+
+       for (i = 0; i < ht->nb_lists; i ++) {
+               struct hash_item *t = ht->lists[i];
+
+               while (t) {
+                       (*action)(t->data);
+                       t = t->next;
+               }
+       }
+}
+
+/*
+ * The two following fonctions are generic for storing structures
+ * uniquely identified by their name, which must be the first
+ * field of the structure.
+ */
+int hash_struct(void *m)
+{
+       char *n = *(char **)m;
+
+#ifdef FAST_HASH
+       return hash_string(n);
+#else
+       return hash_string(n) & 127;
+#endif
+}
+
+int cmp_struct(void *m1, void *m2)
+{
+       char *n1 = *(char **)m1, *n2 = *(char **)m2;
+
+       return !strcmp(n1, n2);
+}
diff --git a/libexec/auxcpp/hash.h b/libexec/auxcpp/hash.h
new file mode 100644 (file)
index 0000000..3adbb77
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * (c) Thomas Pornin 1998, 1999, 2000
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef UCPP__HASH__
+#define UCPP__HASH__
+
+struct hash_item;
+
+struct HT {
+       struct hash_item **lists;
+       int nb_lists;
+       int (*cmpdata)(void *, void *);
+       int (*hash)(void *);
+       void (*deldata)(void *);
+};
+
+int hash_string(char *);
+struct HT *newHT(int, int (*)(void *, void *), int (*)(void *),
+       void (*)(void *));
+void *putHT(struct HT *, void *);
+void *forceputHT(struct HT *, void *);
+void *getHT(struct HT *, void *);
+int delHT(struct HT *, void *);
+void killHT(struct HT *);
+void saveHT(struct HT *, void **);
+void restoreHT(struct HT *, void **);
+void tweakHT(struct HT *, void **, void *);
+void scanHT(struct HT *, void (*)(void *));
+int hash_struct(void *);
+int cmp_struct(void *, void *);
+
+#endif
diff --git a/libexec/auxcpp/lexer.c b/libexec/auxcpp/lexer.c
new file mode 100644 (file)
index 0000000..3812567
--- /dev/null
@@ -0,0 +1,1020 @@
+/*
+ * (c) Thomas Pornin 1999 - 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "tune.h"
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <limits.h>
+#include "ucppi.h"
+#include "mem.h"
+#ifdef UCPP_MMAP
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#endif
+
+/*
+ * Character classes for description of the automaton.
+ * The characters used for representing classes should not appear
+ * explicitely in an automaton rule.
+ */
+#define SPC    ' '     /* whitespace characters */
+#define ALP    'Z'     /* A-Z, a-z, _ */
+#define NUM    '9'     /* 0-9 */
+#define ANY    'Y'     /* any character */
+#define VCH    'F'     /* void character (for end of input) */
+
+/*
+ * flags and macros to test those flags
+ * STO: the currently read string is a complete token
+ * PUT: the currently read character must be added to the string
+ * FRZ: the currently read character must be kept and read again
+ */
+#define MOD_MK         255
+#define noMOD(x)       ((x) & 255)
+#define STO(x)         ((x) | 256)
+#define ttSTO(x)       ((x) & 256)
+#define FRZ(x)         ((x) | 512)
+#define ttFRZ(x)       ((x) & 512)
+#define PUT(x)         ((x) | 1024)
+#define ttPUT(x)       ((x) & 1024)
+
+/* order is important */
+enum {
+       S_START, S_SPACE, S_BANG, S_STRING, S_STRING2, S_COLON,
+       S_SHARP, S_PCT, S_PCT2, S_PCT3, S_AMPER, S_CHAR, S_CHAR2, S_STAR,
+       S_PLUS, S_MINUS, S_DOT, S_DOT2, S_SLASH, S_NUMBER, S_NUMBER2, S_LT,
+       S_LT2, S_EQ, S_GT, S_GT2, S_CIRC, S_PIPE, S_BACKSLASH,
+       S_COMMENT, S_COMMENT2, S_COMMENT3, S_COMMENT4, S_COMMENT5,
+       S_NAME, S_NAME_BS, S_LCHAR,
+       MSTATE,
+       S_ILL, S_DDOT, S_DDSHARP, S_BS, S_ROGUE_BS, S_BEHEAD, S_DECAY,
+       S_TRUNC, S_TRUNCC, S_OUCH
+};
+
+#define CMT(x)         ((x) >= S_COMMENT && (x) <= S_COMMENT5)
+
+#define CMCR   2
+
+/*
+ * This is the description of the automaton. It is not used "as is"
+ * but copied at execution time into a table.
+ *
+ * To my utmost displeasure, there are a few hacks in read_token()
+ * (which uses the transformed automaton) about the special handling
+ * of slashes, sharps, and the letter L.
+ */
+static struct machine_state {
+       int state;
+       unsigned char input[CMCR];
+       int new_state;
+} cppms[] = {
+       /* S_START is the generic beginning state */
+       { S_START,      { ANY },        S_ILL                   },
+#ifdef SEMPER_FIDELIS
+       { S_START,      { SPC },        PUT(S_SPACE)            },
+#else
+       { S_START,      { SPC },        S_SPACE                 },
+#endif
+       { S_START,      { '\n' },       STO(NEWLINE)            },
+       { S_START,      { '!' },        S_BANG                  },
+       { S_START,      { '"' },        PUT(S_STRING)           },
+       { S_START,      { '#' },        S_SHARP                 },
+       { S_START,      { '%' },        S_PCT                   },
+       { S_START,      { '&' },        S_AMPER                 },
+       { S_START,      { '\'' },       PUT(S_CHAR)             },
+       { S_START,      { '(' },        STO(LPAR)               },
+       { S_START,      { ')' },        STO(RPAR)               },
+       { S_START,      { '*' },        S_STAR                  },
+       { S_START,      { '+' },        S_PLUS                  },
+       { S_START,      { ',' },        STO(COMMA)              },
+       { S_START,      { '-' },        S_MINUS                 },
+       { S_START,      { '.' },        PUT(S_DOT)              },
+#ifdef SEMPER_FIDELIS
+       { S_START,      { '/' },        PUT(S_SLASH)            },
+#else
+       { S_START,      { '/' },        S_SLASH                 },
+#endif
+       { S_START,      { NUM },        PUT(S_NUMBER)           },
+       { S_START,      { ':' },        S_COLON                 },
+       { S_START,      { ';' },        STO(SEMIC)              },
+       { S_START,      { '<' },        S_LT                    },
+       { S_START,      { '=' },        S_EQ                    },
+       { S_START,      { '>' },        S_GT                    },
+       { S_START,      { '?' },        STO(QUEST)              },
+       { S_START,      { ALP },        PUT(S_NAME)             },
+       { S_START,      { 'L' },        PUT(S_LCHAR)            },
+       { S_START,      { '[' },        STO(LBRK)               },
+       { S_START,      { ']' },        STO(RBRK)               },
+       { S_START,      { '^' },        S_CIRC                  },
+       { S_START,      { '{' },        STO(LBRA)               },
+       { S_START,      { '|' },        S_PIPE                  },
+       { S_START,      { '}' },        STO(RBRA)               },
+       { S_START,      { '~' },        STO(NOT)                },
+       { S_START,      { '\\' },       S_BACKSLASH             },
+
+       /* after a space */
+       { S_SPACE,      { ANY },        FRZ(STO(NONE))          },
+#ifdef SEMPER_FIDELIS
+       { S_SPACE,      { SPC },        PUT(S_SPACE)            },
+#else
+       { S_SPACE,      { SPC },        S_SPACE                 },
+#endif
+
+       /* after a ! */
+       { S_BANG,       { ANY },        FRZ(STO(LNOT))          },
+       { S_BANG,       { '=' },        STO(NEQ)                },
+
+       /* after a " */
+       { S_STRING,     { ANY },        PUT(S_STRING)           },
+       { S_STRING,     { VCH },        FRZ(S_TRUNC)            },
+       { S_STRING,     { '\n' },       FRZ(S_BEHEAD)           },
+       { S_STRING,     { '\\' },       PUT(S_STRING2)          },
+       { S_STRING,     { '"' },        PUT(STO(STRING))        },
+
+       { S_STRING2,    { ANY },        PUT(S_STRING)           },
+       { S_STRING2,    { VCH },        FRZ(S_TRUNC)            },
+
+       /* after a # */
+       { S_SHARP,      { ANY },        FRZ(STO(SHARP))         },
+       { S_SHARP,      { '#' },        STO(DSHARP)             },
+
+       /* after a : */
+       { S_COLON,      { ANY },        FRZ(STO(COLON))         },
+       { S_COLON,      { '>' },        STO(DIG_RBRK)           },
+
+       /* after a % */
+       { S_PCT,        { ANY },        FRZ(STO(PCT))           },
+       { S_PCT,        { '=' },        STO(ASPCT)              },
+       { S_PCT,        { '>' },        STO(DIG_RBRA)           },
+       { S_PCT,        { ':' },        S_PCT2                  },
+
+       /* after a %: */
+       { S_PCT2,       { ANY },        FRZ(STO(DIG_SHARP))     },
+       { S_PCT2,       { '%' },        S_PCT3                  },
+
+       /* after a %:% */
+       { S_PCT3,       { ANY },        FRZ(S_DDSHARP)          },
+       { S_PCT3,       { ':' },        STO(DIG_DSHARP)         },
+
+       /* after a & */
+       { S_AMPER,      { ANY },        FRZ(STO(AND))           },
+       { S_AMPER,      { '=' },        STO(ASAND)              },
+       { S_AMPER,      { '&' },        STO(LAND)               },
+
+       /* after a ' */
+       { S_CHAR,       { ANY },        PUT(S_CHAR)             },
+       { S_CHAR,       { VCH },        FRZ(S_TRUNC)            },
+       { S_CHAR,       { '\'' },       PUT(STO(CHAR))          },
+       { S_CHAR,       { '\\' },       PUT(S_CHAR2)            },
+
+       /* after a \ in a character constant
+          useful only for '\'' */
+       { S_CHAR2,      { ANY },        PUT(S_CHAR)             },
+       { S_CHAR2,      { VCH },        FRZ(S_TRUNC)            },
+
+       /* after a * */
+       { S_STAR,       { ANY },        FRZ(STO(STAR))          },
+       { S_STAR,       { '=' },        STO(ASSTAR)             },
+
+       /* after a + */
+       { S_PLUS,       { ANY },        FRZ(STO(PLUS))          },
+       { S_PLUS,       { '+' },        STO(PPLUS)              },
+       { S_PLUS,       { '=' },        STO(ASPLUS)             },
+
+       /* after a - */
+       { S_MINUS,      { ANY },        FRZ(STO(MINUS))         },
+       { S_MINUS,      { '-' },        STO(MMINUS)             },
+       { S_MINUS,      { '=' },        STO(ASMINUS)            },
+       { S_MINUS,      { '>' },        STO(ARROW)              },
+
+       /* after a . */
+       { S_DOT,        { ANY },        FRZ(STO(DOT))           },
+       { S_DOT,        { NUM },        PUT(S_NUMBER)           },
+       { S_DOT,        { '.' },        S_DOT2                  },
+
+       /* after .. */
+       { S_DOT2,       { ANY },        FRZ(S_DDOT)             },
+       { S_DOT2,       { '.' },        STO(MDOTS)              },
+
+       /* after a / */
+       { S_SLASH,      { ANY },        FRZ(STO(SLASH))         },
+       { S_SLASH,      { '=' },        STO(ASSLASH)            },
+#ifdef SEMPER_FIDELIS
+       { S_SLASH,      { '*' },        PUT(S_COMMENT)          },
+       { S_SLASH,      { '/' },        PUT(S_COMMENT5)         },
+#else
+       { S_SLASH,      { '*' },        S_COMMENT               },
+       { S_SLASH,      { '/' },        S_COMMENT5              },
+#endif
+       /*
+        * There is a little hack in read_token() to disable
+        * this last rule, if C++ (C99) comments are not enabled.
+        */
+
+       /* after a number */
+       { S_NUMBER,     { ANY },        FRZ(STO(NUMBER))        },
+       { S_NUMBER,     { ALP, NUM },   PUT(S_NUMBER)           },
+       { S_NUMBER,     { '.' },        PUT(S_NUMBER)           },
+       { S_NUMBER,     { 'E', 'e' },   PUT(S_NUMBER2)          },
+       { S_NUMBER,     { 'P', 'p' },   PUT(S_NUMBER2)          },
+
+       { S_NUMBER2,    { ANY },        FRZ(STO(NUMBER))        },
+       { S_NUMBER2,    { ALP, NUM },   PUT(S_NUMBER)           },
+       { S_NUMBER2,    { '+', '-' },   PUT(S_NUMBER)           },
+
+       /* after a < */
+       { S_LT,         { ANY },        FRZ(STO(LT))            },
+       { S_LT,         { '=' },        STO(LEQ)                },
+       { S_LT,         { '<' },        S_LT2                   },
+       { S_LT,         { ':' },        STO(DIG_LBRK)           },
+       { S_LT,         { '%' },        STO(DIG_LBRA)           },
+
+       { S_LT2,        { ANY },        FRZ(STO(LSH))           },
+       { S_LT2,        { '=' },        STO(ASLSH)              },
+
+       /* after a > */
+       { S_GT,         { ANY },        FRZ(STO(GT))            },
+       { S_GT,         { '=' },        STO(GEQ)                },
+       { S_GT,         { '>' },        S_GT2                   },
+
+       { S_GT2,        { ANY },        FRZ(STO(RSH))           },
+       { S_GT2,        { '=' },        STO(ASRSH)              },
+
+       /* after a = */
+       { S_EQ,         { ANY },        FRZ(STO(ASGN))          },
+       { S_EQ,         { '=' },        STO(SAME)               },
+#ifdef CAST_OP
+       { S_EQ,         { '>' },        STO(CAST)               },
+#endif
+
+       /* after a \ */
+       { S_BACKSLASH,  { ANY },        FRZ(S_BS)               },
+       { S_BACKSLASH,  { 'U', 'u' },   FRZ(S_NAME_BS)          },
+
+       /* after a letter */
+       { S_NAME,       { ANY },        FRZ(STO(NAME))          },
+       { S_NAME,       { ALP, NUM },   PUT(S_NAME)             },
+       { S_NAME,       { '\\' },       S_NAME_BS               },
+
+       /* after a \ in an identifier */
+       { S_NAME_BS,    { ANY },        FRZ(S_ROGUE_BS)         },
+       { S_NAME_BS,    { 'u', 'U' },   PUT(S_NAME)             },
+
+       /* after a L */
+       { S_LCHAR,      { ANY },        FRZ(S_NAME)             },
+       { S_LCHAR,      { '"' },        PUT(S_STRING)           },
+       { S_LCHAR,      { '\'' },       PUT(S_CHAR)             },
+
+       /* after a ^ */
+       { S_CIRC,       { ANY },        FRZ(STO(CIRC))          },
+       { S_CIRC,       { '=' },        STO(ASCIRC)             },
+
+       /* after a | */
+       { S_PIPE,       { ANY },        FRZ(STO(OR))            },
+       { S_PIPE,       { '=' },        STO(ASOR)               },
+       { S_PIPE,       { '|' },        STO(LOR)                },
+
+       /* after a / and * */
+#ifdef SEMPER_FIDELIS
+       { S_COMMENT,    { ANY },        PUT(S_COMMENT)          },
+       { S_COMMENT,    { VCH },        FRZ(S_TRUNCC)           },
+       { S_COMMENT,    { '*' },        PUT(S_COMMENT2)         },
+
+       { S_COMMENT2,   { ANY },        FRZ(S_COMMENT)          },
+       { S_COMMENT2,   { VCH },        FRZ(S_TRUNCC)           },
+       { S_COMMENT2,   { '*' },        PUT(S_COMMENT2)         },
+       { S_COMMENT2,   { '/' },        STO(PUT(COMMENT))       },
+
+       { S_COMMENT5,   { ANY },        PUT(S_COMMENT5)         },
+       { S_COMMENT5,   { VCH },        FRZ(S_DECAY)            },
+       { S_COMMENT5,   { '\n' },       FRZ(STO(COMMENT))       },
+#else
+       { S_COMMENT,    { ANY },        S_COMMENT               },
+       { S_COMMENT,    { VCH },        FRZ(S_TRUNCC)           },
+       { S_COMMENT,    { '*' },        S_COMMENT2              },
+
+       { S_COMMENT2,   { ANY },        FRZ(S_COMMENT)          },
+       { S_COMMENT2,   { VCH },        FRZ(S_TRUNCC)           },
+       { S_COMMENT2,   { '*' },        S_COMMENT2              },
+       { S_COMMENT2,   { '/' },        STO(COMMENT)            },
+
+       { S_COMMENT5,   { ANY },        S_COMMENT5              },
+       { S_COMMENT5,   { VCH },        FRZ(S_DECAY)            },
+       { S_COMMENT5,   { '\n' },       FRZ(STO(COMMENT))       },
+#endif
+
+       /* dummy end of machine description */
+       { 0,            { 0 },          0                       }
+};
+
+/*
+ * cppm is the table used to store the automaton: if we are in state s
+ * and we read character c, we apply the action cppm[s][c] (jumping to
+ * another state, or emitting a token).
+ * cppm_vch is the table for the special virtual character "end of input"
+ */
+static int cppm[MSTATE][MAX_CHAR_VAL];
+static int cppm_vch[MSTATE];
+
+/*
+ * init_cppm() fills cppm[][] with the information stored in cppms[].
+ * It must be called before beginning the lexing process.
+ */
+void init_cppm(void)
+{
+       int i, j, k, c;
+       static unsigned char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+       static unsigned char lower[] = "abcdefghijklmnopqrstuvwxyz";
+       unsigned char *cp;
+
+       for (i = 0; i < MSTATE; i ++) {
+               for (j = 0; j < MAX_CHAR_VAL; j ++) cppm[i][j] = S_OUCH;
+               cppm_vch[i] = S_OUCH;
+       }
+       for (i = 0; cppms[i].input[0]; i ++) for (k = 0; k < CMCR; k ++) {
+               int s = cppms[i].state;
+               int ns = cppms[i].new_state;
+
+               switch (c = cppms[i].input[k]) {
+               case 0:
+                       break;
+               case SPC:
+                       /* see space_char() also */
+                       cppm[s][' '] = ns;
+                       cppm[s]['\t'] = ns;
+                       cppm[s]['\v'] = ns;
+                       cppm[s]['\f'] = ns;
+#ifdef UNBREAKABLE_SPACE
+                       if (MAX_CHAR_VAL > UNBREAKABLE_SPACE)
+                               cppm[s][UNBREAKABLE_SPACE] = ns;
+#endif
+                       break;
+               case ALP:
+                       for (cp = upper; *cp; cp ++) cppm[s][(int)*cp] = ns;
+                       for (cp = lower; *cp; cp ++) cppm[s][(int)*cp] = ns;
+                       cppm[s]['_'] = ns;
+                       break;
+               case NUM:
+                       for (j = '0'; j <= '9'; j ++) cppm[s][j] = ns;
+                       break;
+               case ANY:
+                       for (j = 0; j < MAX_CHAR_VAL; j ++) cppm[s][j] = ns;
+                       cppm_vch[s] = ns;
+                       break;
+               case VCH:
+                       cppm_vch[s] = ns;
+                       break;
+               default:
+                       cppm[s][c] = ns;
+                       break;
+               }
+       }
+}
+
+/*
+ * Make some character as equivalent to a letter for identifiers.
+ */
+void set_identifier_char(int c)
+{
+       cppm[S_START][c] = PUT(S_NAME);
+       cppm[S_NAME][c] = PUT(S_NAME);
+}
+
+/*
+ * Remove the "identifier" status from a character.
+ */
+void unset_identifier_char(int c)
+{
+       cppm[S_START][c] = S_ILL;
+       cppm[S_NAME][c] = FRZ(STO(NAME));
+}
+
+int space_char(int c)
+{
+       if (c == ' ' || c == '\t' || c == '\v' || c == '\f'
+#ifdef UNBREAKABLE_SPACE
+               || c == UNBREAKABLE_SPACE
+#endif
+               ) return 1;
+       return 0;
+}
+
+#ifndef NO_UCPP_BUF
+/*
+ * our output buffer is full, flush it
+ */
+void flush_output(struct lexer_state *ls)
+{
+       size_t x = ls->sbuf, y = 0, z;
+
+       if (ls->sbuf == 0) return;
+       do {
+               z = fwrite(ls->output_buf + y, 1, x, ls->output);
+               x -= z;
+               y += z;
+       } while (z && x > 0);
+       if (!y) {
+               error(ls->line, "could not flush output (disk full ?)");
+               die();
+       }
+       ls->sbuf = 0;
+}
+#endif
+
+/*
+ * Output one character; flush the buffer if needed.
+ * This function should not be called, except by put_char().
+ */
+static inline void write_char(struct lexer_state *ls, unsigned char c)
+{
+#ifndef NO_UCPP_BUF
+       ls->output_buf[ls->sbuf ++] = c;
+       if (ls->sbuf == OUTPUT_BUF_MEMG) flush_output(ls);
+#else
+       if (putc((int)c, ls->output) == EOF) {
+               error(ls->line, "output write error (disk full ?)");
+               die();
+       }
+#endif
+       if (c == '\n') {
+               ls->oline ++;
+       }
+}
+
+/*
+ * schedule a character for output
+ */
+void put_char(struct lexer_state *ls, unsigned char c)
+{
+       if (ls->flags & KEEP_OUTPUT) write_char(ls, c);
+}
+
+/*
+ * get next raw input character
+ */
+static inline int read_char(struct lexer_state *ls)
+{
+       unsigned char c;
+
+       if (!ls->input) {
+               return ((ls->pbuf ++) < ls->ebuf) ?
+                       ls->input_string[ls->pbuf - 1] : -1;
+       }
+       while (1) {
+#ifndef NO_UCPP_BUF
+               if (ls->pbuf == ls->ebuf) {
+#ifdef UCPP_MMAP
+                       if (ls->from_mmap) {
+                               munmap((void *)ls->input_buf, ls->ebuf);
+                               ls->from_mmap = 0;
+                               ls->input_buf = ls->input_buf_sav;
+                       }
+#endif
+                       ls->ebuf = fread(ls->input_buf, 1,
+                               INPUT_BUF_MEMG, ls->input);
+                       ls->pbuf = 0;
+               }
+               if (ls->ebuf == 0) return -1;
+               c = ls->input_buf[ls->pbuf ++];
+#else
+               int x = getc(ls->input);
+
+               if (x == EOF) return -1;
+               c = x;
+#endif
+               if (ls->flags & COPY_LINE) {
+                       if (c == '\n') {
+                               ls->copy_line[ls->cli] = 0;
+                               ls->cli = 0;
+                       } else if (ls->cli < (COPY_LINE_LENGTH - 1)) {
+                               ls->copy_line[ls->cli ++] = c;
+                       }
+               }
+               if (ls->macfile && c == '\n') {
+                       ls->macfile = 0;
+                       continue;
+               }
+               ls->macfile = 0;
+               if (c == '\r') {
+                       /*
+                        * We found a '\r'; we handle it as a newline
+                        * and ignore the next newline. This should work
+                        * with all combinations of Msdos, MacIntosh and
+                        * Unix files on these three platforms. On other
+                        * platforms, native file formats are always
+                        * supported.
+                        */
+                       ls->macfile = 1;
+                       c = '\n';
+               }
+               break;
+       }
+       return c;
+}
+
+/*
+ * next_fifo_char(), char_lka1() and char_lka2() give a two character
+ * look-ahead on the input stream; this is needed for trigraphs
+ */
+static inline int next_fifo_char(struct lexer_state *ls)
+{
+       int c;
+
+       if (ls->nlka != 0) {
+               c = ls->lka[0];
+               ls->lka[0] = ls->lka[1];
+               ls->nlka --;
+       } else c = read_char(ls);
+       return c;
+}
+
+static inline int char_lka1(struct lexer_state *ls)
+{
+       if (ls->nlka == 0) {
+               ls->lka[0] = read_char(ls);
+               ls->nlka ++;
+       }
+       return ls->lka[0];
+}
+
+static inline int char_lka2(struct lexer_state *ls)
+{
+#ifdef AUDIT
+       if (ls->nlka == 0) ouch("always in motion future is");
+#endif
+       if (ls->nlka == 1) {
+               ls->lka[1] = read_char(ls);
+               ls->nlka ++;
+       }
+       return ls->lka[1];
+}
+
+static struct trigraph {
+       int old, new;
+} trig[9] = {
+       { '=', '#' },
+       { '/', '\\' },
+       { '\'', '^' },
+       { '(', '[' },
+       { ')', ']' },
+       { '!', '|' },
+       { '<', '{' },
+       { '>', '}' },
+       { '-', '~' }
+};
+
+/*
+ * Returns the next character, after treatment of trigraphs and terminating
+ * backslashes. Return value is -1 if there is no more input.
+ */
+static inline int next_char(struct lexer_state *ls)
+{
+       int c;
+
+       if (!ls->discard) return ls->last;
+       ls->discard = 0;
+       do {
+               c = next_fifo_char(ls);
+               /* check trigraphs */
+               if (c == '?' && char_lka1(ls) == '?'
+                       && (ls->flags & HANDLE_TRIGRAPHS)) {
+                       int i, d;
+
+                       d = char_lka2(ls);
+                       for (i = 0; i < 9; i ++) if (d == trig[i].old) {
+                               if (ls->flags & WARN_TRIGRAPHS) {
+                                       ls->count_trigraphs ++;
+                               }
+                               if (ls->flags & WARN_TRIGRAPHS_MORE) {
+                                       warning(ls->line, "trigraph ?""?%c "
+                                               "encountered", d);
+                               }
+                               next_fifo_char(ls);
+                               next_fifo_char(ls);
+                               c = trig[i].new;
+                               break;
+                       }
+               }
+               if (c == '\\' && char_lka1(ls) == '\n') {
+                       ls->line ++;
+                       next_fifo_char(ls);
+               } else if (c == '\r' && char_lka1(ls) == '\n') {
+                       ls->line ++;
+                       next_fifo_char(ls);
+                       c = '\n';
+                       return c;
+               } else {
+                       ls->last = c;
+                       return c;
+               }
+       } while (1);
+}
+
+/*
+ * wrapper for next_char(), to be called from outside
+ * (used by #error, #include directives)
+ */
+int grap_char(struct lexer_state *ls)
+{
+       return next_char(ls);
+}
+
+/*
+ * Discard the current character, so that the next call to next_char()
+ * will step into the input stream.
+ */
+void discard_char(struct lexer_state *ls)
+{
+#ifdef AUDIT
+       if (ls->discard) ouch("overcollecting garbage");
+#endif
+       ls->discard = 1;
+       ls->utf8 = 0;
+       if (ls->last == '\n') ls->line ++;
+}
+
+/*
+ * Convert an UTF-8 encoded character to a Universal Character Name
+ * using \u (or \U when appropriate).
+ */
+static int utf8_to_string(unsigned char buf[], unsigned long utf8)
+{
+       unsigned long val = 0;
+       static char hex[16] = "0123456789abcdef";
+
+       if (utf8 & 0x80UL) {
+               unsigned long x1, x2, x3, x4;
+
+               x1 = (utf8 >> 24) & 0x7fUL;
+               x2 = (utf8 >> 16) & 0x7fUL;
+               x3 = (utf8 >> 8) & 0x7fUL;
+               x4 = (utf8) & 0x3fUL;
+               x1 &= 0x07UL;
+               if (x2 & 0x40UL) x2 &= 0x0fUL;
+               if (x3 & 0x40UL) x3 &= 0x1fUL;
+               val = x4 | (x3 << 6) | (x2 << 12) | (x1 << 16);
+       } else val = utf8;
+       if (val < 128) {
+               buf[0] = val;
+               buf[1] = 0;
+               return 1;
+       } else if (val < 0xffffUL) {
+               buf[0] = '\\';
+               buf[1] = 'u';
+               buf[2] = hex[(size_t)(val >> 12)];
+               buf[3] = hex[(size_t)((val >> 8) & 0xfU)];
+               buf[4] = hex[(size_t)((val >> 4) & 0xfU)];
+               buf[5] = hex[(size_t)(val & 0xfU)];
+               buf[6] = 0;
+               return 6;
+       }
+       buf[0] = '\\';
+       buf[1] = 'U';
+       buf[2] = '0';
+       buf[3] = '0';
+       buf[4] = hex[(size_t)(val >> 20)];
+       buf[5] = hex[(size_t)((val >> 16) & 0xfU)];
+       buf[6] = hex[(size_t)((val >> 12) & 0xfU)];
+       buf[7] = hex[(size_t)((val >> 8) & 0xfU)];
+       buf[8] = hex[(size_t)((val >> 4) & 0xfU)];
+       buf[9] = hex[(size_t)(val & 0xfU)];
+       buf[10] = 0;
+       return 10;
+}
+
+/*
+ * Scan the identifier and put it in canonical form:
+ *  -- tranform \U0000xxxx into \uxxxx
+ *  -- inside \u and \U, make letters low case
+ *  -- report (some) incorrect use of UCN
+ */
+static void canonize_id(struct lexer_state *ls, char *id)
+{
+       char *c, *d;
+
+       for (c = d = id; *c;) {
+               if (*c == '\\') {
+                       int i;
+
+                       if (!*(c + 1)) goto canon_error;
+                       if (*(c + 1) == 'U') {
+                               for (i = 0; i < 8 && *(c + i + 2); i ++);
+                               if (i != 8) goto canon_error;
+                               *(d ++) = '\\';
+                               c += 2;
+                               for (i = 0; i < 4 && *(c + i) == '0'; i ++);
+                               if (i == 4) {
+                                       *(d ++) = 'u';
+                                       c += 4;
+                               } else {
+                                       *(d ++) = 'U';
+                                       i = 8;
+                               }
+                               for (; i > 0; i --) {
+                                       switch (*c) {
+                                       case 'A': *(d ++) = 'a'; break;
+                                       case 'B': *(d ++) = 'b'; break;
+                                       case 'C': *(d ++) = 'c'; break;
+                                       case 'D': *(d ++) = 'd'; break;
+                                       case 'E': *(d ++) = 'e'; break;
+                                       case 'F': *(d ++) = 'f'; break;
+                                       default: *(d ++) = *c; break;
+                                       }
+                                       c ++;
+                               }
+                       } else if (*(c + 1) == 'u') {
+                               for (i = 0; i < 4 && *(c + i + 2); i ++);
+                               if (i != 4) goto canon_error;
+                               *(d ++) = '\\';
+                               *(d ++) = 'u';
+                               c += 2;
+                               for (; i > 0; i --) {
+                                       switch (*c) {
+                                       case 'A': *(d ++) = 'a'; break;
+                                       case 'B': *(d ++) = 'b'; break;
+                                       case 'C': *(d ++) = 'c'; break;
+                                       case 'D': *(d ++) = 'd'; break;
+                                       case 'E': *(d ++) = 'e'; break;
+                                       case 'F': *(d ++) = 'f'; break;
+                                       default: *(d ++) = *c; break;
+                                       }
+                                       c ++;
+                               }
+                       } else goto canon_error;
+                       continue;
+               }
+               *(d ++) = *(c ++);
+       }
+       *d = 0;
+       return;
+
+canon_error:
+       for (; *c; *(d ++) = *(c ++));
+       if (ls->flags & WARN_STANDARD) {
+               warning(ls->line, "malformed identifier with UCN: '%s'", id);
+       }
+       *d = 0;
+}
+
+/*
+ * Run the automaton, in order to get the next token.
+ * This function should not be called, except by next_token()
+ *
+ * return value: 1 on error, 2 on end-of-file, 0 otherwise.
+ */
+static inline int read_token(struct lexer_state *ls)
+{
+       int cstat = S_START, nstat;
+       size_t ltok = 0;
+       int c, outc = 0, ucn_in_id = 0;
+       int shift_state;
+       unsigned long utf8;
+       long l = ls->line;
+
+       ls->ctok->line = l;
+       if (ls->pending_token) {
+               if ((ls->ctok->type = ls->pending_token) == BUNCH) {
+                       ls->ctok->name[0] = '\\';
+                       ls->ctok->name[1] = 0;
+               }
+               ls->pending_token = 0;
+               return 0;
+       }
+       if (ls->flags & UTF8_SOURCE) {
+               utf8 = ls->utf8;
+               shift_state = 0;
+       }
+       if (!(ls->flags & LEXER) && (ls->flags & KEEP_OUTPUT))
+               for (; ls->line > ls->oline;) put_char(ls, '\n');
+       do {
+               c = next_char(ls);
+               if (c < 0) {
+                       if ((ls->flags & UTF8_SOURCE) && shift_state) {
+                               if (ls->flags & WARN_STANDARD)
+                                       warning(ls->line, "truncated UTF-8 "
+                                               "character");
+                               shift_state = 0;
+                               utf8 = 0;
+                       }
+                       if (cstat == S_START) return 2;
+                       nstat = cppm_vch[cstat];
+               } else {
+                       if (ls->flags & UTF8_SOURCE) {
+                               if (shift_state) {
+                                       if ((c & 0xc0) != 0x80) {
+                                               if (ls->flags & WARN_STANDARD)
+                                                       warning(ls->line,
+                                                               "truncated "
+                                                               "UTF-8 "
+                                                               "character");
+                                               shift_state = 0;
+                                               utf8 = 0;
+                                               c = '_';
+                                       } else {
+                                               utf8 = (utf8 << 8) | c;
+                                               if (-- shift_state) {
+                                                       ls->discard = 1;
+                                                       continue;
+                                               }
+                                               c = '_';
+                                       }
+                               } else if ((c & 0xc0) == 0xc0) {
+                                       if ((c & 0x30) == 0x30) {
+                                               shift_state = 3;
+                                       } else if (c & 0x20) {
+                                               shift_state = 2;
+                                       } else {
+                                               shift_state = 1;
+                                       }
+                                       utf8 = c;
+                                       ls->discard = 1;
+                                       continue;
+                               } else utf8 = 0;
+                       }
+                       nstat = cppm[cstat][c < MAX_CHAR_VAL ? c : 0];
+               }
+#ifdef AUDIT
+               if (nstat == S_OUCH) {
+                       ouch("bad move...");
+               }
+#endif
+               /*
+                * disable C++-like comments
+                */
+               if (nstat == S_COMMENT5 && !(ls->flags & CPLUSPLUS_COMMENTS))
+                       nstat = FRZ(STO(SLASH));
+
+               if (noMOD(nstat) >= MSTATE && !ttSTO(nstat))
+                       switch (noMOD(nstat)) {
+               case S_ILL:
+                       if (ls->flags & CCHARSET) {
+                               error(ls->line, "illegal character '%c'", c);
+                               return 1;
+                       }
+                       nstat = PUT(STO(BUNCH));
+                       break;
+               case S_BS:
+                       ls->ctok->name[0] = '\\';
+                       ltok ++;
+                       nstat = FRZ(STO(BUNCH));
+                       if (!(ls->flags & LEXER)) put_char(ls, '\\');
+                       break;
+               case S_ROGUE_BS:
+                       ls->pending_token = BUNCH;
+                       nstat = FRZ(STO(NAME));
+                       break;
+               case S_DDOT:
+                       ls->pending_token = DOT;
+                       nstat = FRZ(STO(DOT));
+                       break;
+               case S_DDSHARP:
+                       ls->pending_token = PCT;
+                       nstat = FRZ(STO(DIG_SHARP));
+                       break;
+               case S_BEHEAD:
+                       error(l, "unfinished string at end of line");
+                       return 1;
+               case S_DECAY:
+                       warning(l, "unterminated // comment");
+                       nstat = FRZ(STO(COMMENT));
+                       break;
+               case S_TRUNC:
+                       error(l, "truncated token");
+                       return 1;
+               case S_TRUNCC:
+                       error(l, "truncated comment");
+                       return 1;
+#ifdef AUDIT
+               case S_OUCH:
+                       ouch("machine went out of control");
+                       break;
+#endif
+               }
+               if (!ttFRZ(nstat)) {
+                       discard_char(ls);
+                       if (!(ls->flags & LEXER) && ls->condcomp) {
+                               int z = ttSTO(nstat) ? S_ILL : noMOD(nstat);
+
+                               if (cstat == S_NAME || z == S_NAME
+                                       || ((CMT(cstat) || CMT(z))
+                                       && (ls->flags & DISCARD_COMMENTS))) {
+                                       outc = 0;
+                               } else if (z == S_LCHAR || z == S_SLASH
+                                       || (z == S_SHARP && ls->ltwnl)
+                                       || (z == S_PCT && ls->ltwnl)
+                                       || (z == S_BACKSLASH)) {
+                                       outc = c;
+                               } else if (z == S_PCT2 && ls->ltwnl) {
+                                       outc = -1;
+                               } else if (z == S_PCT3 && ls->ltwnl) {
+                                       /* we have %:% but this still might
+                                          not be a %:%: */
+                                       outc = -2;
+                               } else {
+                                       if (outc < 0) {
+                                               put_char(ls, '%');
+                                               put_char(ls, ':');
+                                               if (outc == -2)
+                                                       put_char(ls, '%');
+                                               outc = 0;
+                                       } else if (outc) {
+                                               put_char(ls, outc);
+                                               outc = 0;
+                                       }
+                                       put_char(ls, c);
+                               }
+                       }
+               } else if (outc == '/' && !(ls->flags & LEXER)
+                       && ls->condcomp) {
+                       /* this is a hack: we need to dump a pending slash */
+                       put_char(ls, outc);
+                       outc = 0;
+               }
+               if (ttPUT(nstat)) {
+                       if (cstat == S_NAME_BS) {
+                               ucn_in_id = 1;
+                               wan(ls->ctok->name, ltok, '\\', ls->tknl);
+                       }
+                       if ((ls->flags & UTF8_SOURCE) && utf8) {
+                               unsigned char buf[11];
+                               int i, j;
+
+                               for (i = 0, j = utf8_to_string(buf, utf8);
+                                       i < j; i ++)
+                                       wan(ls->ctok->name, ltok, buf[i],
+                                               ls->tknl);
+                               /* if (j > 1) ucn_in_id = 1; */
+                       } else wan(ls->ctok->name, ltok,
+                               (unsigned char)c, ls->tknl);
+               }
+               if (ttSTO(nstat)) {
+                       if (S_TOKEN(noMOD(nstat))) {
+                               wan(ls->ctok->name, ltok,
+                                       (unsigned char)0, ls->tknl);
+                       }
+                       ls->ctok->type = noMOD(nstat);
+                       break;
+               }
+               cstat = noMOD(nstat);
+       } while (1);
+       if (!(ls->flags & LEXER) && (ls->flags & DISCARD_COMMENTS)
+                       && ls->ctok->type == COMMENT) put_char(ls, ' ');
+       if (ucn_in_id && ls->ctok->type == NAME)
+               canonize_id(ls, ls->ctok->name);
+       return 0;
+}
+
+/*
+ * fills ls->ctok with the next token
+ */
+int next_token(struct lexer_state *ls)
+{
+       if (ls->flags & READ_AGAIN) {
+               ls->flags &= ~READ_AGAIN;
+               if (!(ls->flags & LEXER)) {
+                       char *c = S_TOKEN(ls->ctok->type) ?
+                               ls->ctok->name : token_name(ls->ctok);
+                       if (ls->ctok->type == OPT_NONE) {
+                               ls->ctok->type = NONE;
+#ifdef SEMPER_FIDELIS
+                               ls->ctok->name[0] = ' ';
+                               ls->ctok->name[1] = 0;
+#endif
+                               put_char(ls, ' ');
+                       } else if (ls->ctok->type != NAME &&
+                               !(ls->ltwnl && (ls->ctok->type == SHARP
+                                       || ls->ctok->type == DIG_SHARP)))
+                               for (; *c; c ++) put_char(ls, *c);
+               }
+               return 0;
+       }
+       return read_token(ls);
+}
diff --git a/libexec/auxcpp/macro.c b/libexec/auxcpp/macro.c
new file mode 100644 (file)
index 0000000..5b9540c
--- /dev/null
@@ -0,0 +1,1921 @@
+/*
+ * (c) Thomas Pornin 1999 - 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "tune.h"
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <limits.h>
+#include "ucppi.h"
+#include "mem.h"
+#include "nhash.h"
+
+/*
+ * we store macros in a hash table, and retrieve them using their name
+ * as identifier.
+ */
+static HTT macros;
+static int macros_init_done = 0;
+
+static void del_macro(void *m)
+{
+       struct macro *n = m;
+       size_t i;
+
+       for (i = 0; (int)i < n->narg; i ++) freemem(n->arg[i]);
+       if (n->narg > 0) freemem(n->arg);
+#ifdef LOW_MEM
+       if (n->cval.length) freemem(n->cval.t);
+#else
+       if (n->val.nt) {
+               for (i = 0; i < n->val.nt; i ++)
+                       if (S_TOKEN(n->val.t[i].type))
+                               freemem(n->val.t[i].name);
+               freemem(n->val.t);
+       }
+#endif
+       freemem(n);
+}
+
+static inline struct macro *new_macro(void)
+{
+       struct macro *m = getmem(sizeof(struct macro));
+
+       m->narg = -1;
+       m->nest = 0;
+#ifdef LOW_MEM
+       m->cval.length = 0;
+#else
+       m->val.nt = m->val.art = 0;
+#endif
+       m->vaarg = 0;
+       return m;
+}
+
+/*
+ * for special macros, and the "defined" operator
+ */
+enum {
+       MAC_NONE, MAC_DEFINED,
+       MAC_LINE, MAC_FILE, MAC_DATE, MAC_TIME, MAC_STDC, MAC_PRAGMA
+};
+#define MAC_SPECIAL    MAC_LINE
+
+/*
+ * returns 1 for "defined"
+ * returns x > 1 for a special macro such as __FILE__
+ * returns 0 otherwise
+ */
+static inline int check_special_macro(char *name)
+{
+       if (!strcmp(name, "defined")) return MAC_DEFINED;
+       if (*name != '_') return MAC_NONE;
+       if (*(name + 1) == 'P') {
+               if (!strcmp(name, "_Pragma")) return MAC_PRAGMA;
+               return MAC_NONE;
+       } else if (*(name + 1) != '_') return MAC_NONE;
+       if (no_special_macros) return MAC_NONE;
+       if (!strcmp(name, "__LINE__")) return MAC_LINE;
+       else if (!strcmp(name, "__FILE__")) return MAC_FILE;
+       else if (!strcmp(name, "__DATE__")) return MAC_DATE;
+       else if (!strcmp(name, "__TIME__")) return MAC_TIME;
+       else if (!strcmp(name, "__STDC__")) return MAC_STDC;
+       return MAC_NONE;
+}
+
+int c99_compliant = 1;
+int c99_hosted = 1;
+
+/*
+ * add the special macros to the macro table
+ */
+static void add_special_macros(void)
+{
+       struct macro *m;
+
+       HTT_put(&macros, new_macro(), "__LINE__");
+       HTT_put(&macros, new_macro(), "__FILE__");
+       HTT_put(&macros, new_macro(), "__DATE__");
+       HTT_put(&macros, new_macro(), "__TIME__");
+       HTT_put(&macros, new_macro(), "__STDC__");
+       m = new_macro(); m->narg = 1;
+       m->arg = getmem(sizeof(char *)); m->arg[0] = sdup("foo");
+       HTT_put(&macros, m, "_Pragma");
+       if (c99_compliant) {
+#ifndef LOW_MEM
+               struct token t;
+#endif
+
+               m = new_macro();
+#ifdef LOW_MEM
+               m->cval.t = getmem(9);
+               m->cval.t[0] = NUMBER;
+               mmv(m->cval.t + 1, "199901L", 8);
+               m->cval.length = 9;
+#else
+               t.type = NUMBER;
+               t.line = 0;
+               t.name = sdup("199901L");
+               aol(m->val.t, m->val.nt, t, TOKEN_LIST_MEMG);
+#endif
+               HTT_put(&macros, m, "__STDC_VERSION__");
+       }
+       if (c99_hosted) {
+#ifndef LOW_MEM
+               struct token t;
+#endif
+
+               m = new_macro();
+#ifdef LOW_MEM
+               m->cval.t = getmem(3);
+               m->cval.t[0] = NUMBER;
+               mmv(m->cval.t + 1, "1", 2);
+               m->cval.length = 3;
+#else
+               t.type = NUMBER;
+               t.line = 0;
+               t.name = sdup("1");
+               aol(m->val.t, m->val.nt, t, TOKEN_LIST_MEMG);
+#endif
+               HTT_put(&macros, m, "__STDC_HOSTED__");
+       }
+}
+
+#ifdef LOW_MEM
+/*
+ * We store macro arguments as a single-byte token MACROARG, followed
+ * by the argument number as a one or two-byte value. If the argument
+ * number is between 0 and 127 (inclusive), it is stored as such in
+ * a single byte. Otherwise, it is supposed to be a 14-bit number, with
+ * the 7 upper bits stored in the first byte (with the high bit set to 1)
+ * and the 7 lower bits in the second byte.
+ */
+#endif
+
+/*
+ * print the content of a macro, in #define form
+ */
+static void print_macro(void *vm)
+{
+       struct macro *m = vm;
+       char *mname = HASH_ITEM_NAME(m);
+       int x = check_special_macro(mname);
+       size_t i;
+
+       if (x != MAC_NONE) {
+               fprintf(emit_output, "/* #define %s */ /* special */\n",
+                       mname);
+               return;
+       }
+       fprintf(emit_output, "#define %s", mname);
+       if (m->narg >= 0) {
+               fprintf(emit_output, "(");
+               for (i = 0; i < (size_t)(m->narg); i ++) {
+                       fprintf(emit_output, i ? ", %s" : "%s", m->arg[i]);
+               }
+               if (m->vaarg) {
+                       fputs(m->narg ? ", ..." : "...", emit_output);
+               }
+               fprintf(emit_output, ")");
+       }
+#ifdef LOW_MEM
+       if (m->cval.length == 0) {
+               fputc('\n', emit_output);
+               return;
+       }
+       fputc(' ', emit_output);
+       for (i = 0; i < m->cval.length;) {
+               int tt = m->cval.t[i ++];
+
+               if (tt == MACROARG) {
+                       unsigned anum = m->cval.t[i];
+
+                       if (anum >= 128) anum = ((anum & 127U) << 8)
+                               | m->cval.t[++ i];
+                       if (anum == (unsigned)m->narg)
+                               fputs("__VA_ARGS__", emit_output);
+                       else
+                               fputs(m->arg[anum], emit_output);
+                       i ++;
+               }
+               else if (S_TOKEN(tt)) {
+                       fputs((char *)(m->cval.t + i), emit_output);
+                       i += 1 + strlen((char *)(m->cval.t + i));
+               } else fputs(operators_name[tt], emit_output);
+       }
+#else
+       if (m->val.nt == 0) {
+               fputc('\n', emit_output);
+               return;
+       }
+       fputc(' ', emit_output);
+       for (i = 0; i < m->val.nt; i ++) {
+               if (m->val.t[i].type == MACROARG) {
+                       if (m->val.t[i].line == m->narg)
+                               fputs("__VA_ARGS__", emit_output);
+                       else
+                               fputs(m->arg[(size_t)(m->val.t[i].line)],
+                                       emit_output);
+               } else fputs(token_name(m->val.t + i), emit_output);
+       }
+#endif
+       fputc('\n', emit_output);
+}
+
+/*
+ * Send a token to the output (a token_fifo in lexer mode, the output
+ * buffer in stand alone mode).
+ */
+void print_token(struct lexer_state *ls, struct token *t, long uz_line)
+{
+       char *x = t->name;
+
+       if (uz_line && t->line < 0) t->line = uz_line;
+       if (ls->flags & LEXER) {
+               struct token at;
+
+               at = *t;
+               if (S_TOKEN(t->type)) {
+                       at.name = sdup(at.name);
+                       throw_away(ls->gf, at.name);
+               }
+               aol(ls->output_fifo->t, ls->output_fifo->nt, at,
+                       TOKEN_LIST_MEMG);
+               return;
+       }
+       if (ls->flags & KEEP_OUTPUT) {
+               for (; ls->oline < ls->line;) put_char(ls, '\n');
+       }
+       if (!S_TOKEN(t->type)) x = operators_name[t->type];
+       for (; *x; x ++) put_char(ls, *x);
+}
+
+/*
+ * Send a token to the output at a given line (this is for text output
+ * and unreplaced macros due to lack of arguments).
+ */
+static void print_token_nailed(struct lexer_state *ls, struct token *t,
+       long nail_line)
+{
+       char *x = t->name;
+
+       if (ls->flags & LEXER) {
+               print_token(ls, t, 0);
+               return;
+       }
+       if (ls->flags & KEEP_OUTPUT) {
+               for (; ls->oline < nail_line;) put_char(ls, '\n');
+       }
+       if (!S_TOKEN(t->type)) x = operators_name[t->type];
+       for (; *x; x ++) put_char(ls, *x);
+}
+
+/*
+ * send a reduced whitespace token to the output
+ */
+#define print_space(ls)        do { \
+               struct token lt; \
+               lt.type = OPT_NONE; \
+               lt.line = (ls)->line; \
+               print_token((ls), &lt, 0); \
+       } while (0)
+
+/*
+ * We found a #define directive; parse the end of the line, perform
+ * sanity checks, store the new macro into the "macros" hash table.
+ *
+ * In case of a redefinition of a macro: we enforce the rule that a
+ * macro should be redefined identically, including the spelling of
+ * parameters. We emit an error on offending code; dura lex, sed lex.
+ * After all, it is easy to avoid such problems, with a #undef directive.
+ */
+int handle_define(struct lexer_state *ls)
+{
+       struct macro *m = 0, *n;
+#ifdef LOW_MEM
+       struct token_fifo mv;
+#endif
+       int ltwws = 1, redef = 0;
+       char *mname = 0;
+       int narg;
+       size_t nt;
+       long l = ls->line;
+       
+#ifdef LOW_MEM
+       mv.art = mv.nt = 0;
+#endif
+       /* find the next non-white token on the line, this should be
+          the macro name */
+       while (!next_token(ls) && ls->ctok->type != NEWLINE) {
+               if (ttMWS(ls->ctok->type)) continue;
+               if (ls->ctok->type == NAME) mname = sdup(ls->ctok->name);
+               break;
+       }
+       if (mname == 0) {
+               error(l, "missing macro name");
+               return 1;
+       }
+       if (check_special_macro(mname)) {
+               error(l, "trying to redefine the special macro %s", mname);
+               goto warp_error;
+       }
+       /*
+        * If a macro with this name was already defined: the K&R
+        * states that the new macro should be identical to the old one
+        * (with some arcane rule of equivalence of whitespace); otherwise,
+        * redefining the macro is an error. Most preprocessors would
+        * only emit a warning (or nothing at all) on an unidentical
+        * redefinition.
+        *
+        * Since it is easy to avoid this error (with a #undef directive),
+        * we choose to enforce the rule and emit an error.
+        */
+       if ((n = HTT_get(&macros, mname)) != 0) {
+               /* redefinition of a macro: we must check that we define
+                  it identical */
+               redef = 1;
+#ifdef LOW_MEM
+               n->cval.rp = 0;
+#endif
+               freemem(mname);
+               mname = 0;
+       }
+       if (!redef) {
+               m = new_macro();
+               m->narg = -1;
+#ifdef LOW_MEM
+#define mval   mv
+#else
+#define mval   (m->val)
+#endif
+       }
+       if (next_token(ls)) goto define_end;
+       /*
+        * Check if the token immediately following the macro name is
+        * a left parenthesis; if so, then this is a macro with arguments.
+        * Collect their names and try to match the next parenthesis.
+        */
+       if (ls->ctok->type == LPAR) {
+               int i, j;
+               int need_comma = 0, saw_mdots = 0;
+
+               narg = 0;
+               while (!next_token(ls)) {
+                       if (ls->ctok->type == NEWLINE) {
+                               error(l, "truncated macro definition");
+                               goto define_error;
+                       }
+                       if (ls->ctok->type == COMMA) {
+                               if (saw_mdots) {
+                                       error(l, "'...' must end the macro "
+                                               "argument list");
+                                       goto warp_error;
+                               }
+                               if (!need_comma) {
+                                       error(l, "void macro argument");
+                                       goto warp_error;
+                               }
+                               need_comma = 0;
+                               continue;
+                       } else if (ls->ctok->type == NAME) {
+                               if (saw_mdots) {
+                                       error(l, "'...' must end the macro "
+                                               "argument list");
+                                       goto warp_error;
+                               }
+                               if (need_comma) {
+                                       error(l, "missing comma in "
+                                               "macro argument list");
+                                       goto warp_error;
+                               }
+                               if (!redef) {
+                                       aol(m->arg, narg,
+                                               sdup(ls->ctok->name), 8);
+                                       /* we must keep track of m->narg
+                                          so that cleanup in case of
+                                          error works. */
+                                       m->narg = narg;
+                                       if (narg == 128
+                                               && (ls->flags & WARN_STANDARD))
+                                               warning(l, "more arguments to "
+                                                       "macro than the ISO "
+                                                       "limit (127)");
+#ifdef LOW_MEM
+                                       if (narg == 32767) {
+                                               error(l, "too many arguments "
+                                                       "in macro definition "
+                                                       "(max 32766)");
+                                               goto warp_error;
+                                       }
+#endif
+                               } else {
+                                       /* this is a redefinition of the
+                                          macro; check equality between
+                                          old and new definitions */
+                                       if (narg >= n->narg) goto redef_error;
+                                       if (strcmp(ls->ctok->name,
+                                               n->arg[narg ++]))
+                                               goto redef_error;
+                               }
+                               need_comma = 1;
+                               continue;
+                       } else if ((ls->flags & MACRO_VAARG)
+                               && ls->ctok->type == MDOTS) {
+                               if (need_comma) {
+                                       error(l, "missing comma before '...'");
+                                       goto warp_error;
+                               }
+                               if (redef && !n->vaarg) goto redef_error;
+                               if (!redef) m->vaarg = 1;
+                               saw_mdots = 1;
+                               need_comma = 1;
+                               continue;
+                       } else if (ls->ctok->type == RPAR) {
+                               if (narg > 0 && !need_comma) {
+                                       error(l, "void macro argument");
+                                       goto warp_error;
+                               }
+                               if (redef && n->vaarg && !saw_mdots)
+                                       goto redef_error;
+                               break;
+                       } else if (ttMWS(ls->ctok->type)) {
+                               continue;
+                       }
+                       error(l, "invalid macro argument");
+                       goto warp_error;
+               }
+               if (!redef) {
+                       for (i = 1; i < narg; i ++) for (j = 0; j < i; j ++)
+                               if (!strcmp(m->arg[i], m->arg[j])) {
+                                       error(l, "duplicate macro "
+                                               "argument");
+                                       goto warp_error;
+                               }
+               }
+               if (!redef) m->narg = narg;
+       } else {
+               if (!ttWHI(ls->ctok->type) && (ls->flags & WARN_STANDARD))
+                       warning(ls->line, "identifier not followed by "
+                               "whitespace in #define");
+               ls->flags |= READ_AGAIN;
+               narg = 0;
+       }
+       if (redef) nt = 0;
+
+       /* now, we have the arguments. Let's get the macro contents. */
+       while (!next_token(ls) && ls->ctok->type != NEWLINE) {
+               struct token t;
+
+               t.type = ls->ctok->type;
+               if (ltwws && ttMWS(t.type)) continue;
+               t.line = 0;
+               if (t.type == NAME) {
+                       int i;
+
+                       if ((ls->flags & MACRO_VAARG)
+                               && !strcmp(ls->ctok->name, "__VA_ARGS__")) {
+                               if (redef) {
+                                       if (!n->vaarg) goto redef_error;
+                               } else if (!m->vaarg) {
+                                       error(l, "'__VA_ARGS__' is forbidden "
+                                               "in macros with a fixed "
+                                               "number of arguments");
+                                       goto warp_error;
+                               }
+                               t.type = MACROARG;
+                               t.line = redef ? n->narg : m->narg;
+                       }
+                       for (i = 0; i < narg; i ++)
+                               if (!strcmp(redef ? n->arg[i] : m->arg[i],
+                                       ls->ctok->name)) {
+                                       t.type = MACROARG;
+                                       /* this is a hack: we store the
+                                          argument number in the line field */
+                                       t.line = i;
+                                       break;
+                               }
+               }
+               if (!redef && S_TOKEN(t.type)) t.name = sdup(ls->ctok->name);
+               if (ttMWS(t.type)) {
+                       if (ltwws) continue;
+#ifdef SEMPER_FIDELIS
+                       t.type = OPT_NONE;
+#else
+                       t.type = NONE;
+#endif
+                       ltwws = 1;
+               } else ltwws = 0;
+               if (!redef) {
+                       /* we ensure that each macro token has a correct
+                          line number */
+                       if (t.type != MACROARG) t.line = 1;
+                       aol(mval.t, mval.nt, t, TOKEN_LIST_MEMG);
+               } else {
+#ifdef LOW_MEM
+                       int tt;
+
+                       if (n->cval.rp >= n->cval.length) {
+#ifdef SEMPER_FIDELIS
+                               if (t.type != OPT_NONE) goto redef_error;
+#else
+                               if (t.type != NONE) goto redef_error;
+#endif
+                       } else if (t.type != n->cval.t[n->cval.rp]) {
+                               goto redef_error;
+                       } else if (t.type == MACROARG) {
+                               unsigned anum = n->cval.t[n->cval.rp + 1];
+
+                               if (anum >= 128U) anum = ((anum & 127U) << 8)
+                                       | m->cval.t[n->cval.rp + 2];
+                               if (anum != (unsigned)t.line) goto redef_error;
+                       } else if (S_TOKEN(t.type) && strcmp(ls->ctok->name,
+                                  (char *)(n->cval.t + n->cval.rp + 1))) {
+                               goto redef_error;
+                       }
+                       tt = n->cval.t[n->cval.rp ++];
+                       if (S_TOKEN(tt)) n->cval.rp += 1
+                               + strlen((char *)(n->cval.t + n->cval.rp));
+                       else if (tt == MACROARG) {
+                               if (n->cval.t[++ n->cval.rp] >= 128)
+                                       n->cval.rp ++;
+                       }
+#else
+                       if (nt >= n->val.nt) {
+#ifdef SEMPER_FIDELIS
+                               if (t.type != OPT_NONE) goto redef_error;
+#else
+                               if (t.type != NONE) goto redef_error;
+#endif
+                       } else if (t.type != n->val.t[nt].type
+                               || (t.type == MACROARG
+                                   && t.line != n->val.t[nt].line)
+                               || (S_TOKEN(t.type) && strcmp(ls->ctok->name,
+                                  n->val.t[nt].name))) {
+                               goto redef_error;
+                       }
+#endif
+                       nt ++;
+               }
+       }
+
+       if (redef) {
+#ifdef LOW_MEM
+               if (n->cval.rp < n->cval.length) goto redef_error_2;
+#else
+               if (nt < n->val.nt) goto redef_error_2;
+#endif
+               return 0;
+       }
+
+       /* now we have the complete macro; perform some checks about
+          the operators # and ##, and, if everything is ok,
+          store the macro into the hash table */
+define_end:
+#ifdef SEMPER_FIDELIS
+       if (mval.nt && mval.t[mval.nt - 1].type == OPT_NONE) {
+#else
+       if (mval.nt && mval.t[mval.nt - 1].type == NONE) {
+#endif
+               mval.nt --;
+               if (mval.nt == 0) freemem(mval.t);
+       }
+       if (mval.nt != 0) {
+               size_t i;
+
+               /* some checks about the macro */
+               if (mval.t[0].type == DSHARP
+                       || mval.t[0].type == DIG_DSHARP
+                       || mval.t[mval.nt - 1].type == DSHARP
+                       || mval.t[mval.nt - 1].type == DIG_DSHARP) {
+                       error(l, "operator '##' may neither begin "
+                               "nor end a macro");
+                       goto define_error;
+               }
+               if (m->narg >= 0) for (i = 0; i < mval.nt; i ++)
+                       if ((mval.t[i].type == SHARP
+                               || mval.t[i].type == DIG_SHARP) &&
+                               (i == (mval.nt - 1)
+                               || (ttMWS(mval.t[i + 1].type) &&
+                                   (i == mval.nt - 2
+                                    || mval.t[i + 2].type != MACROARG))
+                               || (!ttMWS(mval.t[i + 1].type)
+                                    && mval.t[i + 1].type != MACROARG))) {
+                               error(l, "operator '#' not followed "
+                                       "by a macro argument");
+                               goto define_error;
+                       }
+       }
+#ifdef LOW_MEM
+       {
+               size_t i, l;
+
+               for (i = 0, l = 0; i < mval.nt; i ++) {
+                       l ++;
+                       if (S_TOKEN(mval.t[i].type))
+                               l += 1 + strlen(mval.t[i].name);
+                       else if (mval.t[i].type == MACROARG) {
+                               l ++;
+                               if (mval.t[i].line >= 128) l ++;
+                       }
+               }
+               m->cval.length = l;
+               if (l) m->cval.t = getmem(l);
+               for (i = 0, l = 0; i < mval.nt; i ++) {
+                       m->cval.t[l ++] = mval.t[i].type;
+                       if (S_TOKEN(mval.t[i].type)) {
+                               size_t x = 1 + strlen(mval.t[i].name);
+
+                               mmv(m->cval.t + l, mval.t[i].name, x);
+                               l += x;
+                               freemem(mval.t[i].name);
+                       }
+                       else if (mval.t[i].type == MACROARG) {
+                               unsigned anum = mval.t[i].line;
+
+                               if (anum >= 128) {
+                                       m->cval.t[l ++] = 128 | (anum >> 8);
+                                       m->cval.t[l ++] = anum & 0xFF;
+                               } else {
+                                       m->cval.t[l ++] = anum;
+                               }
+                       }
+               }
+               if (mval.nt) freemem(mval.t);
+       }
+#endif
+       HTT_put(&macros, m, mname);
+       freemem(mname);
+       if (emit_defines) print_macro(m);
+       return 0;
+
+redef_error:
+       while (ls->ctok->type != NEWLINE && !next_token(ls));
+redef_error_2:
+       error(l, "macro '%s' redefined unidentically", HASH_ITEM_NAME(n));
+       return 1;
+warp_error:
+       while (ls->ctok->type != NEWLINE && !next_token(ls));
+define_error:
+       if (m) del_macro(m);
+       if (mname) freemem(mname);
+#ifdef LOW_MEM
+       if (mv.nt) {
+               size_t i;
+
+               for (i = 0; i < mv.nt; i ++)
+                       if (S_TOKEN(mv.t[i].type)) freemem(mv.t[i].name);
+               freemem(mv.t);
+       }
+#endif
+       return 1;
+#undef mval
+}
+
+/*
+ * Get the arguments for a macro. This code is tricky because there can
+ * be multiple sources for these arguments, if we are in the middle of
+ * a macro replacement; arguments are macro-replaced before inclusion
+ * into the macro replacement.
+ *
+ * return value:
+ * 1   no argument (last token read from next_token())
+ * 2    no argument (last token read from tfi)
+ * 3    no argument (nothing read)
+ * 4   error
+ *
+ * Void arguments are allowed in C99.
+ */
+static int collect_arguments(struct lexer_state *ls, struct token_fifo *tfi,
+       int penury, struct token_fifo *atl, int narg, int vaarg, int *wr)
+{
+       int ltwws = 1, npar = 0, i;
+       struct token *ct = 0;
+       int read_from_fifo = 0;
+       long begin_line = ls->line;
+
+#define unravel(ls)    (read_from_fifo = 0, !((tfi && tfi->art < tfi->nt \
+       && (read_from_fifo = 1) != 0 && (ct = tfi->t + (tfi->art ++))) \
+       || ((!tfi || penury) && !next_token(ls) && (ct = (ls)->ctok))))
+
+       /*
+        * collect_arguments() is assumed to setup correctly atl
+        * (this is not elegant, but it works)
+        */
+       for (i = 0; i < narg; i ++) atl[i].art = atl[i].nt = 0;
+       if (vaarg) atl[narg].art = atl[narg].nt = 0;
+       *wr = 0;
+       while (!unravel(ls)) {
+               if (!read_from_fifo && ct->type == NEWLINE) ls->ltwnl = 1;
+               if (ttWHI(ct->type)) {
+                       *wr = 1;
+                       continue;
+               }
+               if (ct->type == LPAR) {
+                       npar = 1;
+               }
+               break;
+       }
+       if (!npar) {
+               if (ct == ls->ctok) return 1;
+               if (read_from_fifo) return 2;
+               return 3;
+       }
+       if (!read_from_fifo && ct == ls->ctok) ls->ltwnl = 0;
+       i = 0;
+       if ((narg + vaarg) == 0) {
+               while(!unravel(ls)) {
+                       if (ttWHI(ct->type)) continue;
+                       if (ct->type == RPAR) goto harvested;
+                       npar = 1;
+                       goto too_many_args;
+               }
+       }
+       while (!unravel(ls)) {
+               struct token t;
+
+               if (ct->type == LPAR) npar ++;
+               else if (ct->type == RPAR && (-- npar) == 0) {
+                       if (atl[i].nt != 0
+                               && ttMWS(atl[i].t[atl[i].nt - 1].type))
+                                       atl[i].nt --;
+                       i ++;
+                       /*
+                        * C99 standard states that at least one argument
+                        * should be present for the ... part; to relax
+                        * this behaviour, change 'narg + vaarg' to 'narg'.
+                        */
+                       if (i < (narg + vaarg)) {
+                               error(begin_line, "not enough arguments "
+                                       "to macro");
+                               return 4;
+                       }
+                       if (i > narg) {
+                               if (!(ls->flags & MACRO_VAARG) || !vaarg)
+                                       goto too_many_args;
+                       }
+                       goto harvested;
+               } else if (ct->type == COMMA && npar <= 1 && i < narg) {
+                       if (atl[i].nt != 0
+                               && ttMWS(atl[i].t[atl[i].nt - 1].type))
+                                       atl[i].nt --;
+                       if (++ i == narg) {
+                               if (!(ls->flags & MACRO_VAARG) || !vaarg)
+                                       goto too_many_args;
+                       }
+                       if (i > 30000) goto too_many_args;
+                       ltwws = 1;
+                       continue;
+               } else if (ltwws && ttWHI(ct->type)) continue;
+
+               t.type = ct->type;
+               if (!read_from_fifo) t.line = ls->line; else t.line = ct->line;
+               /*
+                * Stringification applies only to macro arguments;
+                * so we handle here OPT_NONE.
+                * OPT_NONE is kept, but does not count as whitespace,
+                * and merges with other whitespace to give a fully
+                * qualified NONE token. Two OPT_NONE tokens merge.
+                * Initial and final OPT_NONE are discarded (initial
+                * is already done, as OPT_NONE is matched by ttWHI).
+                */
+               if (ttWHI(t.type)) {
+                       if (t.type != OPT_NONE) {
+                               t.type = NONE;
+#ifdef SEMPER_FIDELIS
+                               t.name = sdup(" ");
+                               throw_away(ls->gf, t.name);
+#endif
+                               ltwws = 1;
+                       }
+                       if (atl[i].nt > 0
+                               && atl[i].t[atl[i].nt - 1].type == OPT_NONE)
+                                       atl[i].nt --;
+               } else { 
+                       ltwws = 0;
+                       if (S_TOKEN(t.type)) {
+                               t.name = ct->name;
+                               if (ct == (ls)->ctok) {
+                                       t.name = sdup(t.name);
+                                       throw_away(ls->gf, t.name);
+                               }
+                       }
+               }
+               aol(atl[i].t, atl[i].nt, t, TOKEN_LIST_MEMG);
+       }
+       error(begin_line, "unfinished macro call");
+       return 4;
+too_many_args:
+       error(begin_line, "too many arguments to macro");
+       while (npar && !unravel(ls)) {
+               if (ct->type == LPAR) npar ++;
+               else if (ct->type == RPAR) npar --;
+       }
+       return 4;
+harvested:
+       if (i > 127 && (ls->flags & WARN_STANDARD))
+               warning(begin_line, "macro call with %d arguments (ISO "
+                       "specifies 127 max)", i);
+       return 0;
+#undef unravel
+}
+
+/*
+ * concat_token() is called when the ## operator is used. It uses
+ * the struct lexer_state dsharp_lexer to parse the result of the
+ * concatenation.
+ *
+ * Law enforcement: if the whole string does not produce a valid
+ * single token, an error (non-zero result) is returned.
+ */
+struct lexer_state dsharp_lexer;
+
+static inline int concat_token(struct token *t1, struct token *t2)
+{
+       char *n1 = token_name(t1), *n2 = token_name(t2);
+       size_t l1 = strlen(n1), l2 = strlen(n2);
+       unsigned char *x = getmem(l1 + l2 + 1);
+       int r;
+
+       mmv(x, n1, l1);
+       mmv(x + l1, n2, l2);
+       x[l1 + l2] = 0;
+       dsharp_lexer.input = 0;
+       dsharp_lexer.input_string = x;
+       dsharp_lexer.pbuf = 0;
+       dsharp_lexer.ebuf = l1 + l2;
+       dsharp_lexer.discard = 1;
+       dsharp_lexer.flags = DEFAULT_LEXER_FLAGS;
+       dsharp_lexer.pending_token = 0;
+       r = next_token(&dsharp_lexer);
+       freemem(x);
+       return (r == 1 || dsharp_lexer.pbuf < (l1 + l2)
+               || dsharp_lexer.pending_token
+               || (dsharp_lexer.pbuf == (l1 + l2) && !dsharp_lexer.discard));
+}
+
+#ifdef PRAGMA_TOKENIZE
+/*
+ * tokenize_string() takes a string as input, and split it into tokens,
+ * reassembling the tokens into a single compressed string generated by
+ * compress_token_list(); this function is used for _Pragma processing.
+ */
+struct lexer_state tokenize_lexer;
+
+static char *tokenize_string(struct lexer_state *ls, char *buf)
+{
+       struct token_fifo tf;
+       size_t bl = strlen(buf);
+       int r;
+
+       tokenize_lexer.input = 0;
+       tokenize_lexer.input_string = (unsigned char *)buf;
+       tokenize_lexer.pbuf = 0;
+       tokenize_lexer.ebuf = bl;
+       tokenize_lexer.discard = 1;
+       tokenize_lexer.flags = ls->flags | LEXER;
+       tokenize_lexer.pending_token = 0;
+       tf.art = tf.nt = 0;
+       while (!(r = next_token(&tokenize_lexer))) {
+               struct token t, *ct = tokenize_lexer.ctok;
+
+               if (ttWHI(ct->type)) continue;
+               t = *ct;
+               if (S_TOKEN(t.type)) t.name = sdup(t.name);
+               aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG);
+       }
+       if (tokenize_lexer.pbuf < bl) goto tokenize_error;
+       return (char *)((compress_token_list(&tf)).t);
+
+tokenize_error:
+       if (tf.nt) {
+               for (tf.art = 0; tf.art < tf.nt; tf.art ++)
+                       if (S_TOKEN(tf.t[tf.art].type))
+                               freemem(tf.t[tf.art].name);
+               freemem(tf.t);
+       }
+       return 0;
+}
+#endif
+
+/*
+ * stringify_string() has a self-explanatory name. It is called when
+ * the # operator is used in a macro and a string constant must be
+ * stringified.
+ */
+static inline char *stringify_string(char *x)
+{
+       size_t l;
+       int i, inside_str = 0, inside_cc = 0, must_quote, has_quoted = 0;
+       char *y, *d;
+
+       for (i = 0; i < 2; i ++) {
+               if (i) d[0] = '"';
+               for (l = 1, y = x; *y; y ++, l ++) {
+                       must_quote = 0;
+                       if (inside_cc) {
+                               if (*y == '\\') {
+                                       must_quote = 1;
+                                       has_quoted = 1;
+                               } else if (!has_quoted && *y == '\'')
+                                       inside_cc = 0;
+                       } else if (inside_str) {
+                               if (*y == '"' || *y == '\\') must_quote = 1;
+                               if (*y == '\\') has_quoted = 1;
+                               else if (!has_quoted && *y == '"')
+                                       inside_str = 0;
+                       } else if (*y == '"') {
+                               inside_str = 1;
+                               must_quote = 1;
+                       } else if (*y == '\'') {
+                               inside_cc = 1;
+                       }
+                       if (must_quote) {
+                               if (i) d[l] = '\\';
+                               l ++;
+                       }
+                       if (i) d[l] = *y;
+               }
+               if (!i) d = getmem(l + 2);
+               if (i) {
+                       d[l] = '"';
+                       d[l + 1] = 0;
+               }
+       }
+       return d;
+}
+
+/*
+ * stringify() produces a constant string, result of the # operator
+ * on a list of tokens.
+ */
+static char *stringify(struct token_fifo *tf)
+{
+       size_t tlen;
+       size_t i;
+       char *x, *y;
+
+       for (tlen = 0, i = 0; i < tf->nt; i ++)
+               if (tf->t[i].type < CPPERR && tf->t[i].type != OPT_NONE)
+                       tlen += strlen(token_name(tf->t + i));
+       if (tlen == 0) return sdup("\"\"");
+       x = getmem(tlen + 1);
+       for (tlen = 0, i = 0; i < tf->nt; i ++) {
+               if (tf->t[i].type >= CPPERR || tf->t[i].type == OPT_NONE)
+                       continue;
+               strcpy(x + tlen, token_name(tf->t + i));
+               tlen += strlen(token_name(tf->t + i));
+       }
+       /* no need to add a trailing 0: strcpy() did that (and the string
+          is not empty) */
+       y = stringify_string(x);
+       freemem(x);
+       return y;
+}
+
+/*
+ * Two strings evaluated at initialization time, to handle the __TIME__
+ * and __DATE__ special macros.
+ *
+ * C99 specifies that these macros should remain constant throughout
+ * the whole preprocessing.
+ */
+char compile_time[12], compile_date[24];
+
+/*
+ * substitute_macro() performs the macro substitution. It is called when
+ * an identifier recognized as a macro name has been found; this function
+ * tries to collect the arguments (if needed), applies # and ## operators
+ * and perform recursive and nested macro expansions.
+ *
+ * In the substitution of a macro, we remove all newlines that were in the
+ * arguments. This might confuse error reporting (which could report
+ * erroneous line numbers) or have worse effect is the preprocessor is
+ * used for another language pickier than C. Since the interface between
+ * the preprocessor and the compiler is not fully specified, I believe
+ * that this is no violation of the standard. Comments welcome.
+ *
+ * We take tokens from tfi. If tfi has no more tokens to give: we may
+ * take some tokens from ls to complete a call (fetch arguments) if
+ * and only if penury is non zero.
+ */
+int substitute_macro(struct lexer_state *ls, struct macro *m,
+       struct token_fifo *tfi, int penury, int reject_nested, long l)
+{
+       char *mname = HASH_ITEM_NAME(m);
+       struct token_fifo *atl, etl;
+       struct token t, *ct;
+       int i, save_nest = m->nest;
+       size_t save_art, save_tfi, etl_limit;
+       int ltwds, ntwds, ltwws;
+       int pragma_op = 0;
+
+       /*
+        * Reject the replacement, if we are already inside the macro.
+        */
+       if (m->nest > reject_nested) {
+               t.type = NAME;
+               t.line = ls->line;
+               t.name = mname;
+               print_token(ls, &t, 0);
+               return 0;
+       }
+
+       /*
+        * put a separation from preceeding tokens
+        */
+       print_space(ls);
+
+       /*
+        * Check if the macro is a special one.
+        */
+       if ((i = check_special_macro(mname)) >= MAC_SPECIAL) {
+               /* we have a special macro */
+               switch (i) {
+                       char buf[30], *bbuf, *cfn;
+
+               case MAC_LINE:
+                       t.type = NUMBER;
+                       t.line = l;
+                       sprintf(buf, "%ld", l);
+                       t.name = buf;
+                       print_space(ls);
+                       print_token(ls, &t, 0);
+                       break;
+               case MAC_FILE:
+                       t.type = STRING;
+                       t.line = l;
+                       cfn = current_long_filename ?
+                               current_long_filename : current_filename;
+                       bbuf = getmem(2 * strlen(cfn) + 3);
+                       {
+                               char *c, *d;
+                               int lcwb = 0;
+
+                               bbuf[0] = '"';
+                               for (c = cfn, d = bbuf + 1; *c; c ++) {
+                                       if (*c == '\\') {
+                                               if (lcwb) continue;
+                                               *(d ++) = '\\';
+                                               lcwb = 1;
+                                       } else lcwb = 0;
+                                       *(d ++) = *c;
+                               }
+                               *(d ++) = '"';
+                               *(d ++) = 0;
+                       }
+                       t.name = bbuf;
+                       print_space(ls);
+                       print_token(ls, &t, 0);
+                       freemem(bbuf);
+                       break;
+               case MAC_DATE:
+                       t.type = STRING;
+                       t.line = l;
+                       t.name = compile_date;
+                       print_space(ls);
+                       print_token(ls, &t, 0);
+                       break;
+               case MAC_TIME:
+                       t.type = STRING;
+                       t.line = l;
+                       t.name = compile_time;
+                       print_space(ls);
+                       print_token(ls, &t, 0);
+                       break;
+               case MAC_STDC:
+                       t.type = NUMBER;
+                       t.line = l;
+                       t.name = "1";
+                       print_space(ls);
+                       print_token(ls, &t, 0);
+                       break;
+               case MAC_PRAGMA:
+                       if (reject_nested > 0) {
+                               /* do not replace _Pragma() unless toplevel */
+                               t.type = NAME;
+                               t.line = ls->line;
+                               t.name = mname;
+                               print_token(ls, &t, 0);
+                               return 0;
+                       }
+                       pragma_op = 1;
+                       goto collect_args;
+#ifdef AUDIT
+               default:
+                       ouch("unbekanntes fliegendes macro");
+#endif
+               }
+               return 0;
+       }
+
+       /*
+        * If the macro has arguments, collect them.
+        */
+collect_args:
+       if (m->narg >= 0) {
+               unsigned long save_flags = ls->flags;
+               int wr = 0;
+
+               ls->flags |= LEXER;
+               if (m->narg > 0 || m->vaarg)
+                       atl = getmem((m->narg + m->vaarg)
+                               * sizeof(struct token_fifo));
+               switch (collect_arguments(ls, tfi, penury, atl,
+                       m->narg, m->vaarg, &wr)) {
+               case 1:
+                       /* the macro expected arguments, but we did not
+                          find any; the last read token should be read
+                          again. */
+                       ls->flags = save_flags | READ_AGAIN;
+                       goto no_argument_next;
+               case 2:
+                       tfi->art --;
+                       /* fall through */
+               case 3:
+                       ls->flags = save_flags;
+               no_argument_next:
+                       t.type = NAME;
+                       t.line = l;
+                       t.name = mname;
+                       print_token_nailed(ls, &t, l);
+                       if (wr) {
+                               t.type = NONE;
+                               t.line = l;
+#ifdef SEMPER_FIDELIS
+                               t.name = " ";
+#endif
+                               print_token(ls, &t, 0);
+                               goto exit_macro_2;
+                       }
+                       goto exit_macro_1;
+               case 4:
+                       ls->flags = save_flags;
+                       goto exit_error_1;
+               }
+               ls->flags = save_flags;
+       }
+
+       /*
+        * If the macro is _Pragma, and we got here, then we have
+        * exactly one argument. We check it, unstringize it, and
+        * emit a PRAGMA token.
+        */
+       if (pragma_op) {
+               char *pn;
+
+               if (atl[0].nt != 1 || atl[0].t[0].type != STRING) {
+                       error(ls->line, "invalid argument to _Pragma");
+                       if (atl[0].nt) freemem(atl[0].t);
+                       freemem(atl);
+                       goto exit_error;
+               }
+               pn = atl[0].t[0].name;
+               if ((pn[0] == '"' && pn[1] == '"') || (pn[0] == 'L'
+                       && pn[1] == '"' && pn[2] == '"')) {
+                       /* void pragma -- just ignore it */
+                       freemem(atl[0].t);
+                       freemem(atl);
+                       return 0;
+               }
+               if (ls->flags & TEXT_OUTPUT) {
+#ifdef PRAGMA_DUMP
+       /*
+        * This code works because we actually evaluate arguments in a
+        * lazy way: we scan a macro argument only if it appears in the
+        * output, and exactly as many times as it appears. Therefore,
+        * _Pragma() will get evaluated just like they should.
+        */
+                       char *c = atl[0].t[0].name, *d;
+
+                       for (d = "\n#pragma "; *d; d ++) put_char(ls, *d);
+                       d = (*c == 'L') ? c + 2 : c + 1;
+                       for (; *d != '"'; d ++) {
+                               if (*d == '\\' && (*(d + 1) == '\\'
+                                       || *(d + 1) == '"')) {
+                                       d ++;
+                               }
+                               put_char(ls, *d);
+                       }
+                       put_char(ls, '\n');
+                       ls->oline = ls->line;
+                       enter_file(ls, ls->flags);
+#else
+                       if (ls->flags & WARN_PRAGMA)
+                               warning(ls->line,
+                                       "_Pragma() ignored and not dumped");
+#endif
+               } else if (ls->flags & HANDLE_PRAGMA) {
+                       char *c = atl[0].t[0].name, *d, *buf;
+                       struct token t;
+
+                       /* a wide string is a string */
+                       if (*c == 'L') c ++;
+                       c ++;
+                       for (buf = d = getmem(strlen(c)); *c != '"'; c ++) {
+                               if (*c == '\\' && (*(c + 1) == '\\'
+                                       || *(c + 1) == '"')) {
+                                       *(d ++) = *(++ c);
+                               } else *(d ++) = *c;
+                       }
+                       *d = 0;
+                       t.type = PRAGMA;
+                       t.line = ls->line;
+#ifdef PRAGMA_TOKENIZE
+                       t.name = tokenize_string(ls, buf);
+                       freemem(buf);
+                       buf = t.name;
+                       if (!buf) {
+                               freemem(atl[0].t);
+                               freemem(atl);
+                               goto exit_error;
+                       }
+#else
+                       t.name = buf;
+#endif
+                       aol(ls->toplevel_of->t, ls->toplevel_of->nt,
+                               t, TOKEN_LIST_MEMG);
+                       throw_away(ls->gf, buf);
+               }
+               freemem(atl[0].t);
+               freemem(atl);
+               return 0;
+       }
+
+       /*
+        * Now we expand and replace the arguments in the macro; we
+        * also handle '#' and '##'. If we find an argument, that has
+        * to be replaced, we expand it in its own token list, then paste
+        * it. Tricky point: when we paste an argument, we must scan
+        * again the resulting list for further replacements. This
+        * implies problems with regards to nesting self-referencing
+        * macros.
+        *
+        * We do then YAUH (yet another ugly hack): if a macro is replaced,
+        * and nested replacement exhibit the same macro, we mark it with
+        * a negative line number. All produced negative line numbers
+        * must be cleaned in the end.
+        */
+
+#define ZAP_LINE(t)    do { \
+               if ((t).type == NAME) { \
+                       struct macro *zlm = HTT_get(&macros, (t).name); \
+                       if (zlm && zlm->nest > reject_nested) \
+                               (t).line = -1 - (t).line; \
+               } \
+       } while (0)
+
+#ifdef LOW_MEM
+       save_art = m->cval.rp;
+       m->cval.rp = 0;
+#else
+       save_art = m->val.art;
+       m->val.art = 0;
+#endif
+       etl.art = etl.nt = 0;
+       m->nest = reject_nested + 1;
+       ltwds = ntwds = 0;
+#ifdef LOW_MEM
+       while (m->cval.rp < m->cval.length) {
+#else
+       while (m->val.art < m->val.nt) {
+#endif
+               size_t next, z;
+#ifdef LOW_MEM
+               struct token uu;
+
+               ct = &uu;
+               ct->line = 1;
+               t.type = ct->type = m->cval.t[m->cval.rp ++];
+               if (ct->type == MACROARG) {
+                       unsigned anum = m->cval.t[m->cval.rp ++];
+
+                       if (anum >= 128U) anum = ((anum & 127U) << 8)
+                               | (unsigned)m->cval.t[m->cval.rp ++];
+                       ct->line = anum;
+               } else if (S_TOKEN(ct->type)) {
+                       t.name = ct->name = (char *)(m->cval.t + m->cval.rp);
+                       m->cval.rp += 1 + strlen(ct->name);
+               }
+#ifdef SEMPER_FIDELIS
+               else if (ct->type == OPT_NONE) {
+                       t.type = ct->type = NONE;
+                       t.name = ct->name = " ";
+               }
+#endif
+               t.line = ls->line;
+               next = m->cval.rp;
+               if ((next < m->cval.length && (m->cval.t[z = next] == DSHARP
+                       || m->cval.t[z = next] == DIG_DSHARP))
+                       || ((next + 1) < m->cval.length
+                          && ttWHI(m->cval.t[next])
+                          && (m->cval.t[z = next + 1] == DSHARP
+                           || m->cval.t[z = next + 1] == DIG_DSHARP))) {
+                       ntwds = 1;
+                       m->cval.rp = z;
+               } else ntwds = 0;
+#else
+               ct = m->val.t + (m->val.art ++);
+               next = m->val.art;
+               t.type = ct->type;
+               t.line = ls->line;
+#ifdef SEMPER_FIDELIS
+               if (t.type == OPT_NONE) {
+                       t.type = NONE;
+                       t.name = " ";
+               } else
+#endif
+               t.name = ct->name;
+               if ((next < m->val.nt && (m->val.t[z = next].type == DSHARP
+                       || m->val.t[z = next].type == DIG_DSHARP))
+                       || ((next + 1) < m->val.nt
+                          && ttWHI(m->val.t[next].type)
+                          && (m->val.t[z = next + 1].type == DSHARP
+                           || m->val.t[z = next + 1].type == DIG_DSHARP))) {
+                       ntwds = 1;
+                       m->val.art = z;
+               } else ntwds = 0;
+#endif
+               if (ct->type == MACROARG) {
+#ifdef DSHARP_TOKEN_MERGE
+                       int need_opt_space = 1;
+#endif
+                       z = ct->line;   /* the argument number is there */
+                       if (ltwds && atl[z].nt != 0 && etl.nt) {
+                               if (concat_token(etl.t + (-- etl.nt),
+                                       atl[z].t)) {
+                                       warning(ls->line, "operator '##' "
+                                               "produced the invalid token "
+                                               "'%s%s'",
+                                               token_name(etl.t + etl.nt),
+                                               token_name(atl[z].t));
+#if 0
+/* obsolete */
+#ifdef LOW_MEM
+                                       m->cval.rp = save_art;
+#else
+                                       m->val.art = save_art;
+#endif
+                                       etl.nt ++;
+                                       goto exit_error_2;
+#endif
+                                       etl.nt ++;
+                                       atl[z].art = 0;
+#ifdef DSHARP_TOKEN_MERGE
+                                       need_opt_space = 0;
+#endif
+                               } else {
+                                       if (etl.nt == 0) freemem(etl.t);
+                                       else if (!ttWHI(etl.t[etl.nt - 1]
+                                               .type)) {
+                                               t.type = OPT_NONE;
+                                               t.line = ls->line;
+                                               aol(etl.t, etl.nt, t,
+                                                       TOKEN_LIST_MEMG);
+                                       }
+                                       t.type = dsharp_lexer.ctok->type;
+                                       t.line = ls->line;
+                                       if (S_TOKEN(t.type)) {
+                                               t.name = sdup(dsharp_lexer
+                                                       .ctok->name);
+                                               throw_away(ls->gf, t.name);
+                                       }
+                                       ZAP_LINE(t);
+                                       aol(etl.t, etl.nt, t, TOKEN_LIST_MEMG);
+                                       atl[z].art = 1;
+                               }
+                       } else atl[z].art = 0;
+                       if (
+#ifdef DSHARP_TOKEN_MERGE
+                               need_opt_space &&
+#endif
+                               atl[z].art < atl[z].nt && (!etl.nt
+                                       || !ttWHI(etl.t[etl.nt - 1].type))) {
+                               t.type = OPT_NONE;
+                               t.line = ls->line;
+                               aol(etl.t, etl.nt, t, TOKEN_LIST_MEMG);
+                       }
+                       if (ltwds || ntwds) {
+                               while (atl[z].art < atl[z].nt) {
+                                       t = atl[z].t[atl[z].art ++];
+                                       t.line = ls->line;
+                                       ZAP_LINE(t);
+                                       aol(etl.t, etl.nt, t, TOKEN_LIST_MEMG);
+                               }
+                       } else {
+                               struct token_fifo *save_tf;
+                               unsigned long save_flags;
+                               int ret = 0;
+
+                               atl[z].art = 0;
+                               save_tf = ls->output_fifo;
+                               ls->output_fifo = &etl;
+                               save_flags = ls->flags;
+                               ls->flags |= LEXER;
+                               while (atl[z].art < atl[z].nt) {
+                                       struct macro *nm;
+                                       struct token *cct;
+
+                                       cct = atl[z].t + (atl[z].art ++);
+                                       if (cct->type == NAME
+                                               && cct->line >= 0
+                                               && (nm = HTT_get(&macros,
+                                                   cct->name))
+                                               && nm->nest <=
+                                                   (reject_nested + 1)) {
+                                               ret |= substitute_macro(ls,
+                                                       nm, atl + z, 0,
+                                                       reject_nested + 1, l);
+                                               continue;
+                                       }
+                                       t = *cct;
+                                       ZAP_LINE(t);
+                                       aol(etl.t, etl.nt, t, TOKEN_LIST_MEMG);
+                               }
+                               ls->output_fifo = save_tf;
+                               ls->flags = save_flags;
+                               if (ret) {
+#ifdef LOW_MEM
+                                       m->cval.rp = save_art;
+#else
+                                       m->val.art = save_art;
+#endif
+                                       goto exit_error_2;
+                               }
+                       }
+                       if (!ntwds && (!etl.nt
+                               || !ttWHI(etl.t[etl.nt - 1].type))) {
+                               t.type = OPT_NONE;
+                               t.line = ls->line;
+                               aol(etl.t, etl.nt, t, TOKEN_LIST_MEMG);
+                       }
+                       ltwds = 0;
+                       continue;
+               }
+               /*
+                * This code is definitely cursed.
+                *
+                * For the extremely brave reader who tries to understand
+                * what is happening: ltwds is a flag meaning "last token
+                * was double-sharp" and ntwds means "next token will be
+                * double-sharp". The tokens are from the macro definition,
+                * and scanned from left to right. Arguments that are
+                * not implied into a #/## construction are macro-expanded
+                * seperately, then included into the token stream.
+                */
+               if (ct->type == DSHARP || ct->type == DIG_DSHARP) {
+                       if (ltwds) {
+                               error(ls->line, "quad sharp");
+#ifdef LOW_MEM
+                               m->cval.rp = save_art;
+#else
+                               m->val.art = save_art;
+#endif
+                               goto exit_error_2;
+                       }
+#ifdef LOW_MEM
+                       if (m->cval.rp < m->cval.length
+                               && ttMWS(m->cval.t[m->cval.rp]))
+                                       m->cval.rp ++;
+#else
+                       if (m->val.art < m->val.nt
+                               && ttMWS(m->val.t[m->val.art].type))
+                                       m->val.art ++;
+#endif
+                       ltwds = 1;
+                       continue;
+               } else if (ltwds && etl.nt != 0) {
+                       if (concat_token(etl.t + (-- etl.nt), ct)) {
+                               warning(ls->line, "operator '##' produced "
+                                       "the invalid token '%s%s'",
+                                       token_name(etl.t + etl.nt),
+                                       token_name(ct));
+#if 0
+/* obsolete */
+#ifdef LOW_MEM
+                               m->cval.rp = save_art;
+#else
+                               m->val.art = save_art;
+#endif
+                               etl.nt ++;
+                               goto exit_error_2;
+#endif
+                               etl.nt ++;
+                       } else {
+                               if (etl.nt == 0) freemem(etl.t);
+                               t.type = dsharp_lexer.ctok->type;
+                               t.line = ls->line;
+                               if (S_TOKEN(t.type)) {
+                                       t.name = sdup(dsharp_lexer.ctok->name);
+                                       throw_away(ls->gf, t.name);
+                               }
+                               ct = &t;
+                       }
+               }
+               ltwds = 0;
+#ifdef LOW_MEM
+               if ((ct->type == SHARP || ct->type == DIG_SHARP)
+                       && next < m->cval.length
+                       && (m->cval.t[next] == MACROARG
+                       || (ttMWS(m->cval.t[next])
+                       && (next + 1) < m->cval.length
+                       && m->cval.t[next + 1] == MACROARG))) {
+
+                       unsigned anum;
+#else
+               if ((ct->type == SHARP || ct->type == DIG_SHARP)
+                       && next < m->val.nt
+                       && (m->val.t[next].type == MACROARG
+                       || (ttMWS(m->val.t[next].type)
+                       && (next + 1) < m->val.nt
+                       && m->val.t[next + 1].type == MACROARG))) {
+#endif
+                       /*
+                        * We have a # operator followed by (an optional
+                        * whitespace and) a macro argument; this means
+                        * stringification. So be it.
+                        */
+#ifdef LOW_MEM
+                       if (ttMWS(m->cval.t[next])) m->cval.rp ++;
+#else
+                       if (ttMWS(m->val.t[next].type)) m->val.art ++;
+#endif
+                       t.type = STRING;
+#ifdef LOW_MEM
+                       anum = m->cval.t[++ m->cval.rp];
+                       if (anum >= 128U) anum = ((anum & 127U) << 8)
+                               | (unsigned)m->cval.t[++ m->cval.rp];
+                       t.name = stringify(atl + anum);
+                       m->cval.rp ++;
+#else
+                       t.name = stringify(atl +
+                               (size_t)(m->val.t[m->val.art ++].line));
+#endif
+                       throw_away(ls->gf, t.name);
+                       ct = &t;
+                       /*
+                        * There is no need for extra spaces here.
+                        */
+               }
+               t = *ct;
+               ZAP_LINE(t);
+               aol(etl.t, etl.nt, t, TOKEN_LIST_MEMG);
+       }
+#ifdef LOW_MEM
+       m->cval.rp = save_art;
+#else
+       m->val.art = save_art;
+#endif
+
+       /*
+        * Now etl contains the expanded macro, to be parsed again for
+        * further expansions -- much easier, since '#' and '##' have
+        * already been handled.
+        * However, we might need some input from tfi. So, we paste
+        * the contents of tfi after etl, and we put back what was
+        * not used.
+        *
+        * Some adjacent spaces are merged; only unique NONE, or sequences
+        * OPT_NONE NONE are emitted.
+        */
+       etl_limit = etl.nt;
+       if (tfi) {
+               save_tfi = tfi->art;
+               while (tfi->art < tfi->nt) aol(etl.t, etl.nt,
+                       tfi->t[tfi->art ++], TOKEN_LIST_MEMG);
+       }
+       ltwws = 0;
+       while (etl.art < etl_limit) {
+               struct macro *nm;
+
+               ct = etl.t + (etl.art ++);
+               if (ct->type == NAME && ct->line >= 0
+                       && (nm = HTT_get(&macros, ct->name))) {
+                       if (substitute_macro(ls, nm, &etl,
+                               penury, reject_nested, l)) {
+                               m->nest = save_nest;
+                               goto exit_error_2;
+                       }
+                       ltwws = 0;
+                       continue;
+               }
+               if (ttMWS(ct->type)) {
+                       if (ltwws == 1) {
+                               if (ct->type == OPT_NONE) continue;
+                               ltwws = 2;
+                       } else if (ltwws == 2) continue;
+                       else if (ct->type == OPT_NONE) ltwws = 1;
+                       else ltwws = 2;
+               } else ltwws = 0;
+               if (ct->line >= 0) ct->line = l;
+               print_token(ls, ct, reject_nested ? 0 : l);
+       }
+       if (etl.nt) freemem(etl.t);
+       if (tfi) {
+               tfi->art = save_tfi + (etl.art - etl_limit);
+       }
+
+exit_macro_1:
+       print_space(ls);
+exit_macro_2:
+       for (i = 0; i < (m->narg + m->vaarg); i ++)
+               if (atl[i].nt) freemem(atl[i].t);
+       if (m->narg > 0 || m->vaarg) freemem(atl);
+       m->nest = save_nest;
+       return 0;
+
+exit_error_2:
+       if (etl.nt) freemem(etl.t);
+exit_error_1:
+       for (i = 0; i < (m->narg + m->vaarg); i ++)
+               if (atl[i].nt) freemem(atl[i].t);
+       if (m->narg > 0 || m->vaarg) freemem(atl);
+       m->nest = save_nest;
+exit_error:
+       return 1;
+}
+
+/*
+ * print already defined macros
+ */
+void print_defines(void)
+{
+       HTT_scan(&macros, print_macro);
+}
+
+/*
+ * define_macro() defines a new macro, whom definition is given in
+ * the command-line syntax: macro=def
+ * The '=def' part is optional.
+ *
+ * It returns non-zero on error.
+ */
+int define_macro(struct lexer_state *ls, char *def)
+{
+       char *c = sdup(def), *d;
+       int with_def = 0;
+       int ret = 0;
+
+       for (d = c; *d && *d != '='; d ++);
+       if (*d) {
+               *d = ' ';
+               with_def = 1;
+       }
+       if (with_def) {
+               struct lexer_state lls;
+               size_t n = strlen(c) + 1;
+
+               if (c == d) {
+                       error(-1, "void macro name");
+                       ret = 1;
+               } else {
+                       *(c + n - 1) = '\n';
+                       init_buf_lexer_state(&lls, 0);
+                       lls.flags = ls->flags | LEXER;
+                       lls.input = 0;
+                       lls.input_string = (unsigned char *)c;
+                       lls.pbuf = 0;
+                       lls.ebuf = n;
+                       lls.line = -1;
+                       ret = handle_define(&lls);
+                       free_lexer_state(&lls);
+               }
+       } else {
+               struct macro *m;
+
+               if (!*c) {
+                       error(-1, "void macro name");
+                       ret = 1;
+               } else if ((m = HTT_get(&macros, c))
+#ifdef LOW_MEM
+                       && (m->cval.length != 3
+                       || m->cval.t[0] != NUMBER
+                       || strcmp((char *)(m->cval.t + 1), "1"))) {
+#else
+                       && (m->val.nt != 1
+                       || m->val.t[0].type != NUMBER
+                       || strcmp(m->val.t[0].name, "1"))) {
+#endif
+                       error(-1, "macro %s already defined", c);
+                       ret = 1;
+               } else {
+#ifndef LOW_MEM
+                       struct token t;
+#endif
+
+                       m = new_macro();
+#ifdef LOW_MEM
+                       m->cval.length = 3;
+                       m->cval.t = getmem(3);
+                       m->cval.t[0] = NUMBER;
+                       m->cval.t[1] = '1';
+                       m->cval.t[2] = 0;
+#else
+                       t.type = NUMBER;
+                       t.name = sdup("1");
+                       aol(m->val.t, m->val.nt, t, TOKEN_LIST_MEMG);
+#endif
+                       HTT_put(&macros, m, c);
+               }
+       }
+       freemem(c);
+       return ret;
+}
+
+/*
+ * undef_macro() undefines the macro whom name is given as "def";
+ * it is not an error to try to undef a macro that does not exist.
+ *
+ * It returns non-zero on error (undefinition of a special macro,
+ * void macro name).
+ */
+int undef_macro(struct lexer_state *ls, char *def)
+{
+       char *c = def;
+
+       if (!*c) {
+               error(-1, "void macro name");
+               return 1;
+       }
+       if (HTT_get(&macros, c)) {
+               if (check_special_macro(c)) {
+                       error(-1, "trying to undef special macro %s", c);
+                       return 1;
+               } else HTT_del(&macros, c);
+       }
+       return 0;
+}
+
+/*
+ * We saw a #ifdef directive. Parse the line.
+ * return value: 1 if the macro is defined, 0 if it is not, -1 on error
+ */
+int handle_ifdef(struct lexer_state *ls)
+{
+       while (!next_token(ls)) {
+               int tgd = 1;
+
+               if (ls->ctok->type == NEWLINE) break;
+               if (ttMWS(ls->ctok->type)) continue;
+               if (ls->ctok->type == NAME) {
+                       int x = (HTT_get(&macros, ls->ctok->name) != 0);
+                       while (!next_token(ls) && ls->ctok->type != NEWLINE)
+                               if (tgd && !ttWHI(ls->ctok->type)
+                                       && (ls->flags & WARN_STANDARD)) {
+                                       warning(ls->line, "trailing garbage "
+                                               "in #ifdef");
+                                       tgd = 0;
+                               }
+                       return x;
+               }
+               error(ls->line, "illegal macro name for #ifdef");
+               while (!next_token(ls) && ls->ctok->type != NEWLINE)
+                       if (tgd && !ttWHI(ls->ctok->type)
+                               && (ls->flags & WARN_STANDARD)) {
+                               warning(ls->line, "trailing garbage in "
+                                       "#ifdef");
+                               tgd = 0;
+                       }
+               return -1;
+       }
+       error(ls->line, "unfinished #ifdef");
+       return -1;
+}
+
+/*
+ * for #undef
+ * return value: 1 on error, 0 on success. Undefining a macro that was
+ * already not defined is not an error.
+ */
+int handle_undef(struct lexer_state *ls)
+{
+       while (!next_token(ls)) {
+               if (ls->ctok->type == NEWLINE) break;
+               if (ttMWS(ls->ctok->type)) continue;
+               if (ls->ctok->type == NAME) {
+                       struct macro *m = HTT_get(&macros, ls->ctok->name);
+                       int tgd = 1;
+
+                       if (m != 0) {
+                               if (check_special_macro(ls->ctok->name)) {
+                                       error(ls->line, "trying to undef "
+                                               "special macro %s",
+                                               ls->ctok->name);
+                                       goto undef_error;
+                               }
+                               if (emit_defines)
+                                       fprintf(emit_output, "#undef %s\n",
+                                               ls->ctok->name);
+                               HTT_del(&macros, ls->ctok->name);
+                       }
+                       while (!next_token(ls) && ls->ctok->type != NEWLINE)
+                               if (tgd && !ttWHI(ls->ctok->type)
+                                       && (ls->flags & WARN_STANDARD)) {
+                                       warning(ls->line, "trailing garbage "
+                                               "in #undef");
+                                       tgd = 0;
+                               }
+                       return 0;
+               }
+               error(ls->line, "illegal macro name for #undef");
+       undef_error:
+               while (!next_token(ls) && ls->ctok->type != NEWLINE);
+               return 1;
+       }
+       error(ls->line, "unfinished #undef");
+       return 1;
+}
+
+/*
+ * for #ifndef
+ * return value: 0 if the macro is defined, 1 if it is not, -1 on error.
+ */
+int handle_ifndef(struct lexer_state *ls)
+{
+       while (!next_token(ls)) {
+               int tgd = 1;
+
+               if (ls->ctok->type == NEWLINE) break;
+               if (ttMWS(ls->ctok->type)) continue;
+               if (ls->ctok->type == NAME) {
+                       int x = (HTT_get(&macros, ls->ctok->name) == 0);
+
+                       while (!next_token(ls) && ls->ctok->type != NEWLINE)
+                               if (tgd && !ttWHI(ls->ctok->type)
+                                       && (ls->flags & WARN_STANDARD)) {
+                                       warning(ls->line, "trailing garbage "
+                                               "in #ifndef");
+                                       tgd = 0;
+                               }
+                       if (protect_detect.state == 1) {
+                               protect_detect.state = 2;
+                               protect_detect.macro = sdup(ls->ctok->name);
+                       }
+                       return x;
+               }
+               error(ls->line, "illegal macro name for #ifndef");
+               while (!next_token(ls) && ls->ctok->type != NEWLINE)
+                       if (tgd && !ttWHI(ls->ctok->type)
+                               && (ls->flags & WARN_STANDARD)) {
+                               warning(ls->line, "trailing garbage in "
+                                       "#ifndef");
+                               tgd = 0;
+                       }
+               return -1;
+       }
+       error(ls->line, "unfinished #ifndef");
+       return -1;
+}
+
+/*
+ * erase the macro table.
+ */
+void wipe_macros(void)
+{
+       if (macros_init_done) HTT_kill(&macros);
+       macros_init_done = 0;
+}
+
+/*
+ * initialize the macro table
+ */
+void init_macros(void)
+{
+       wipe_macros();
+       HTT_init(&macros, del_macro);
+       macros_init_done = 1;
+       if (!no_special_macros) add_special_macros();
+}
+
+/*
+ * find a macro from its name
+ */
+struct macro *get_macro(char *name)
+{
+       return HTT_get(&macros, name);
+}
diff --git a/libexec/auxcpp/mem.c b/libexec/auxcpp/mem.c
new file mode 100644 (file)
index 0000000..dabde95
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Memory manipulation routines
+ * (c) Thomas Pornin 1998 - 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "mem.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Shifting a pointer of that some bytes is supposed to satisfy
+ * alignment requirements. This is *not* guaranteed by the standard
+ * but should work everywhere anyway.
+ */
+#define ALIGNSHIFT     (sizeof(long) > sizeof(long double) \
+                       ? sizeof(long) : sizeof(long double))
+
+#ifdef AUDIT
+void die(void)
+{
+       abort();
+}
+
+static void suicide(unsigned long e)
+{
+       fprintf(stderr, "ouch: Schrodinger's beef is not dead ! %lx\n", e);
+       die();
+}
+#else
+void die(void)
+{
+       exit(EXIT_FAILURE);
+}
+#endif
+
+#if defined AUDIT || defined MEM_CHECK || defined MEM_DEBUG
+/*
+ * This function is equivalent to a malloc(), but will display an error
+ * message and exit if the wanted memory is not available
+ */
+#ifdef MEM_DEBUG
+static void *getmem_raw(size_t x)
+#else
+void *(getmem)(size_t x)
+#endif
+{
+       void *m;
+
+#ifdef AUDIT
+       m = malloc(x + ALIGNSHIFT);
+#else
+       m = malloc(x);
+#endif
+       if (m == 0) {
+               fprintf(stderr, "ouch: malloc() failed\n");
+               die();
+       }
+#ifdef AUDIT
+       *((unsigned long *)m) = 0xdeadbeefUL;
+       return (void *)(((char *)m) + ALIGNSHIFT);
+#else
+       return m;
+#endif
+}
+#endif
+
+#ifndef MEM_DEBUG
+/*
+ * This function is equivalent to a realloc(); if the realloc() call
+ * fails, it will try a malloc() and a memcpy(). If not enough memory is
+ * available, the program exits with an error message
+ */
+void *(incmem)(void *m, size_t x, size_t nx)
+{
+       void *nm;
+
+#ifdef AUDIT
+       m = (void *)(((char *)m) - ALIGNSHIFT);
+       if (*((unsigned long *)m) != 0xdeadbeefUL)
+               suicide(*((unsigned long *)m));
+       x += ALIGNSHIFT; nx += ALIGNSHIFT;
+#endif
+       if (!(nm = realloc(m, nx))) {
+               if (x > nx) x = nx;
+               nm = (getmem)(nx);
+               memcpy(nm, m, x);
+               /* free() and not freemem(), because of the Schrodinger beef */
+               free(m);
+       }
+#ifdef AUDIT
+       return (void *)(((char *)nm) + ALIGNSHIFT);
+#else
+       return nm;
+#endif
+}
+#endif
+
+#if defined AUDIT || defined MEM_DEBUG
+/*
+ * This function frees the given block
+ */
+#ifdef MEM_DEBUG
+static void freemem_raw(void *x)
+#else
+void (freemem)(void *x)
+#endif
+{
+#ifdef AUDIT
+       void *y = (void *)(((char *)x) - ALIGNSHIFT);
+
+       if ((*((unsigned long *)y)) != 0xdeadbeefUL)
+               suicide(*((unsigned long *)y));
+       *((unsigned long *)y) = 0xfeedbabeUL;
+       free(y);
+#else
+       free(x);
+#endif
+}
+#endif
+
+#ifdef AUDIT
+/*
+ * This function copies n bytes from src to dest
+ */
+void *mmv(void *dest, void *src, size_t n)
+{
+       return memcpy(dest, src, n);
+}
+
+/*
+ * This function copies n bytes from src to dest
+ */
+void *mmvwo(void *dest, void *src, size_t n)
+{
+       return memmove(dest, src, n);
+}
+#endif
+
+#ifndef MEM_DEBUG
+/*
+ * This function creates a new char * and fills it with a copy of src
+ */
+char *(sdup)(char *src)
+{
+       size_t n = 1 + strlen(src);
+       char *x = getmem(n);
+
+       mmv(x, src, n);
+       return x;
+}
+#endif
+
+#ifdef MEM_DEBUG
+/*
+ * We include here special versions of getmem(), freemem() and incmem()
+ * that track allocations and are used to detect memory leaks.
+ *
+ * Each allocation is referenced in a list, with a serial number.
+ */
+
+/*
+ * Define "true" functions for applications that need pointers
+ * to such functions.
+ */
+void *(getmem)(size_t n)
+{
+       return getmem(n);
+}
+
+void (freemem)(void *x)
+{
+       freemem(x);
+}
+
+void *(incmem)(void *x, size_t s, size_t ns)
+{
+       return incmem(x, s, ns);
+}
+
+char *(sdup)(char *s)
+{
+       return sdup(s);
+}
+
+static long current_serial = 0L;
+
+/* must be a power of two */
+#define MEMDEBUG_MEMG  128U
+
+static struct mem_track {
+       void *block;
+       long serial;
+       char *file;
+       int line;
+} *mem = 0;
+
+static size_t meml = 0;
+
+static unsigned int current_ptr = 0;
+
+static void *true_incmem(void *x, size_t old_size, size_t new_size)
+{
+       void * y = realloc(x, new_size);
+
+       if (y == 0) {
+               y = malloc(new_size);
+               if (y == 0) {
+                       fprintf(stderr, "ouch: malloc() failed\n");
+                       die();
+               }
+               mmv(y, x, old_size < new_size ? old_size : new_size);
+               free(x);
+       }
+       return y;
+}
+
+static long find_free_block(void)
+{
+       unsigned int n;
+       size_t i;
+
+       for (i = 0, n = current_ptr; i < meml; i ++) {
+               if (mem[n].block == 0) {
+                       current_ptr = n;
+                       return n;
+               }
+               n = (n + 1) & (meml - 1U);
+       }
+       if (meml == 0) {
+               size_t j;
+
+               meml = MEMDEBUG_MEMG;
+               mem = malloc(meml * sizeof(struct mem_track));
+               current_ptr = 0;
+               for (j = 0; j < meml ; j ++) mem[j].block = 0;
+       } else {
+               size_t j;
+
+               mem = true_incmem(mem, meml * sizeof(struct mem_track),
+                       2 * meml * sizeof(struct mem_track));
+               current_ptr = meml;
+               for (j = meml; j < 2 * meml ; j ++) mem[j].block = 0;
+               meml *= 2;
+       }
+       return current_ptr;
+}
+
+void *getmem_debug(size_t n, char *file, int line)
+{
+       void *x = getmem_raw(n + ALIGNSHIFT);
+       long i = find_free_block();
+
+       *(long *)x = i;
+       mem[i].block = x;
+       mem[i].serial = current_serial ++;
+       mem[i].file = file;
+       mem[i].line = line;
+       return (void *)((unsigned char *)x + ALIGNSHIFT);
+}
+
+void freemem_debug(void *x, char *file, int line)
+{
+       void *y = (unsigned char *)x - ALIGNSHIFT;
+       long i = *(long *)y;
+
+       if (i < 0 || (size_t)i >= meml || mem[i].block != y) {
+               fprintf(stderr, "ouch: freeing free people (from %s:%d)\n",
+                       file, line);
+               die();
+       }
+       mem[i].block = 0;
+       freemem_raw(y);
+}
+
+void *incmem_debug(void *x, size_t ol, size_t nl, char *file, int line)
+{
+       void *y = getmem_debug(nl, file, line);
+       mmv(y, x, ol < nl ? ol : nl);
+       freemem_debug(x, file, line);
+       return y;
+}
+
+char *sdup_debug(char *src, char *file, int line)
+{
+       size_t n = 1 + strlen(src);
+       char *x = getmem_debug(n, file, line);
+
+       mmv(x, src, n);
+       return x;
+}
+
+void report_leaks(void)
+{
+       size_t i;
+
+       for (i = 0; i < meml; i ++) {
+               if (mem[i].block) fprintf(stderr, "leak: serial %ld, %s:%d\n",
+                       mem[i].serial, mem[i].file, mem[i].line);
+       }
+}
+
+#endif
diff --git a/libexec/auxcpp/mem.h b/libexec/auxcpp/mem.h
new file mode 100644 (file)
index 0000000..4403c2f
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * (c) Thomas Pornin 1998 - 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef UCPP__MEM__
+#define UCPP__MEM__
+
+#include <stdlib.h>
+
+void die(void);
+
+#if defined AUDIT || defined MEM_CHECK || defined MEM_DEBUG
+void *getmem(size_t);
+#else
+#define getmem         malloc
+#endif
+
+#if defined MEM_DEBUG
+void *getmem_debug(size_t, char *, int);
+#undef getmem
+#define getmem(x)      getmem_debug(x, __FILE__, __LINE__)
+#endif
+
+#if defined AUDIT || defined MEM_DEBUG
+void freemem(void *);
+#else
+#define freemem                free
+#endif
+
+#if defined MEM_DEBUG
+void freemem_debug(void *, char *, int);
+#undef freemem
+#define freemem(x)     freemem_debug(x, __FILE__, __LINE__)
+#endif
+
+void *incmem(void *, size_t, size_t);
+char *sdup(char *);
+
+#if defined MEM_DEBUG
+void *incmem_debug(void *, size_t, size_t, char *, int);
+#undef incmem
+#define incmem(x, y, z)        incmem_debug(x, y, z, __FILE__, __LINE__)
+void report_leaks(void);
+char *sdup_debug(char *, char *, int);
+#define sdup(x)                sdup_debug(x, __FILE__, __LINE__)
+#endif
+
+#ifdef AUDIT
+void *mmv(void *, void *, size_t);
+void *mmvwo(void *, void *, size_t);
+#else
+#define mmv    memcpy
+#define mmvwo  memmove
+#endif
+
+/*
+ * this macro adds the object obj at the end of the array list, handling
+ * memory allocation when needed; ptr contains the number of elements in
+ * the array, and memg is the granularity of memory allocations (a power
+ * of 2 is recommanded, for optimization reasons).
+ *
+ * list and ptr may be updated, and thus need to be lvalues.
+ */
+#define aol(list, ptr, obj, memg)      do { \
+               if (((ptr) % (memg)) == 0) { \
+                       if ((ptr) != 0) { \
+                               (list) = incmem((list), (ptr) * sizeof(obj), \
+                                       ((ptr) + (memg)) * sizeof(obj)); \
+                       } else { \
+                               (list) = getmem((memg) * sizeof(obj)); \
+                       } \
+               } \
+               (list)[(ptr) ++] = (obj); \
+       } while (0)
+
+/*
+ * bol() does the same as aol(), but adds the new item at the beginning
+ * of the list; beware, the computational cost is greater.
+ */
+#define bol(list, ptr, obj, memg)      do { \
+               if (((ptr) % (memg)) == 0) { \
+                       if ((ptr) != 0) { \
+                               (list) = incmem((list), (ptr) * sizeof(obj), \
+                                       ((ptr) + (memg)) * sizeof(obj)); \
+                       } else { \
+                               (list) = getmem((memg) * sizeof(obj)); \
+                       } \
+               } \
+               if ((ptr) != 0) \
+                       mmvwo((list) + 1, (list), (ptr) * sizeof(obj)); \
+               (ptr) ++; \
+               (list)[0] = (obj); \
+       } while (0)
+
+/*
+ * mbol() does the same as bol(), but adds the new item at the given
+ * emplacement; bol() is equivalent to mbol with 0 as last argument.
+ */
+#define mbol(list, ptr, obj, memg, n)  do { \
+               if (((ptr) % (memg)) == 0) { \
+                       if ((ptr) != 0) { \
+                               (list) = incmem((list), (ptr) * sizeof(obj), \
+                                       ((ptr) + (memg)) * sizeof(obj)); \
+                       } else { \
+                               (list) = getmem((memg) * sizeof(obj)); \
+                       } \
+               } \
+               if ((ptr) > n) \
+                       mmvwo((list) + n + 1, (list) + n, \
+                               ((ptr) - n) * sizeof(obj)); \
+               (ptr) ++; \
+               (list)[n] = (obj); \
+       } while (0)
+
+/*
+ * this macro adds the object obj at the end of the array list, doubling
+ * the size of list when needed; as for aol(), ptr and list must be
+ * lvalues, and so must be llng
+ */
+
+#define wan(list, ptr, obj, llng)      do { \
+               if ((ptr) == (llng)) { \
+                       (llng) += (llng); \
+                       (list) = incmem((list), (ptr) * sizeof(obj), \
+                               (llng) * sizeof(obj)); \
+               } \
+               (list)[(ptr) ++] = (obj); \
+       } while (0)
+
+#endif
diff --git a/libexec/auxcpp/nhash.c b/libexec/auxcpp/nhash.c
new file mode 100644 (file)
index 0000000..6e5e4f7
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * Mixed hash table / binary tree code.
+ * (c) Thomas Pornin 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <limits.h>
+#include "nhash.h"
+#include "mem.h"
+
+/*
+ * Hash a string into an `unsigned' value. This function is derived
+ * from the hash function used in the ELF binary object file format
+ * hash tables. The result size is a 32-bit number if the `unsigned'
+ * type is big enough to hold 32-bit arbitrary numbers, a 16-bit number
+ * otherwise.
+ */
+static unsigned hash_string(char *name)
+{
+       unsigned h = 0;
+
+       for (h = 0; *name; name ++) {
+               unsigned g;
+
+               h = (h << 4) + *(unsigned char *)name;
+#if UINT_MAX >= 0xffffffffU
+               g = h & 0xF0000000U;
+               h ^= (g >> 24);
+#else
+               g = h & 0xF000U;
+               h ^= (g >> 12);
+#endif
+               h &= ~g;
+       }
+       return h;
+}
+
+/*
+ * Each item in the table is a structure beginning with a `hash_item_header'
+ * structure. Those headers define binary trees such that all left-descendants
+ * (respectively right-descendants) of a given tree node have an associated
+ * hash value strictly smaller (respectively greater) than the hash value
+ * associated with this node.
+ *
+ * The `ident' field points to an array of char. The `sizeof(unsigned)'
+ * first `char' contain a copy of an `unsigned' value which is the hashed
+ * string, except the least significant bit. When this bit is set to 0,
+ * the node contains the unique item using that hash value. If the bit
+ * is set to 1, then there are several items with that hash value.
+ *
+ * When several items share the same hash value, they are linked together
+ * in a linked list by their `left' field. The node contains no data;
+ * it is a "fake item".
+ *
+ * The `char' following the hash value encode the item name for true items.
+ * For fake items, they contain the pointer to the first true item of the
+ * corresponding link list (suitably aligned).
+ *
+ * There are HTT_NUM_TREES trees; the items are sorted among trees by the
+ * lest significant bits of their hash value.
+ */
+
+static void internal_init(HTT *htt, void (*deldata)(void *), int reduced)
+{
+       htt->deldata = deldata;
+       if (reduced) {
+               HTT2 *htt2 = (HTT2 *)htt;
+
+               htt2->tree[0] = htt2->tree[1] = NULL;
+       } else {
+               unsigned u;
+
+               for (u = 0; u < HTT_NUM_TREES; u ++) htt->tree[u] = NULL;
+       }
+}
+
+/* see nhash.h */
+void HTT_init(HTT *htt, void (*deldata)(void *))
+{
+       internal_init(htt, deldata, 0);
+}
+
+/* see nhash.h */
+void HTT2_init(HTT2 *htt, void (*deldata)(void *))
+{
+       internal_init((HTT *)htt, deldata, 1);
+}
+
+#define PTR_SHIFT    (sizeof(hash_item_header *) * \
+                      ((sizeof(unsigned) + sizeof(hash_item_header *) - 1) / \
+                       sizeof(hash_item_header *)))
+
+#define TREE(u)    (*(reduced ? ((HTT2 *)htt)->tree + ((u) & 1) \
+                              : htt->tree + ((u) & (HTT_NUM_TREES - 1))))
+
+/*
+ * Find a node for the given hash value. If `father' is not NULL, fill
+ * `*father' with a pointer to the node's father.
+ * If the return value is NULL, then no existing node was found; if `*father'
+ * is also  NULL, the tree is empty. If the return value is not NULL but
+ * `*father' is NULL, then the found node is the tree root.
+ *
+ * If `father' is not NULL, then `*leftson' is filled with 1 if the node
+ * was looked for as the father left son, 0 otherwise.
+ */
+static hash_item_header *find_node(HTT *htt, unsigned u,
+       hash_item_header **father, int *leftson, int reduced)
+{
+       hash_item_header *node = TREE(u);
+       hash_item_header *nodef = NULL;
+       int ls;
+
+       u &= ~1U;
+       while (node != NULL) {
+               unsigned v = *(unsigned *)(node->ident);
+               unsigned w = v & ~1U;
+
+               if (u == w) break;
+               nodef = node;
+               if (u < w) {
+                       node = node->left;
+                       ls = 1;
+               } else {
+                       node = node->right;
+                       ls = 0;
+               }
+       }
+       if (father != NULL) {
+               *father = nodef;
+               *leftson = ls;
+       }
+       return node;
+}
+
+static void *internal_get(HTT *htt, char *name, int reduced)
+{
+       unsigned u = hash_string(name), v;
+       hash_item_header *node = find_node(htt, u, NULL, NULL, reduced);
+
+       if (node == NULL) return NULL;
+       v = *(unsigned *)(node->ident);
+       if ((v & 1U) == 0) {
+               return (strcmp(HASH_ITEM_NAME(node), name) == 0) ? node : NULL;
+       }
+       node = *(hash_item_header **)(node->ident + PTR_SHIFT);
+       while (node != NULL) {
+               if (strcmp(HASH_ITEM_NAME(node), name) == 0) return node;
+               node = node->left;
+       }
+       return NULL;
+}
+
+/* see nhash.h */
+void *HTT_get(HTT *htt, char *name)
+{
+       return internal_get(htt, name, 0);
+}
+
+/* see nhash.h */
+void *HTT2_get(HTT2 *htt, char *name)
+{
+       return internal_get((HTT *)htt, name, 1);
+}
+
+/*
+ * Make an item identifier from its name and its hash value.
+ */
+static char *make_ident(char *name, unsigned u)
+{
+       size_t n = strlen(name) + 1;
+       char *ident = getmem(n + sizeof(unsigned));
+
+       *(unsigned *)ident = u & ~1U;
+       memcpy(ident + sizeof(unsigned), name, n);
+       return ident;
+}
+
+/*
+ * Make an identifier for a fake item, pointing to a true item.
+ */
+static char *make_fake_ident(unsigned u, hash_item_header *next)
+{
+       char *ident = getmem(PTR_SHIFT + sizeof(hash_item_header *));
+
+       *(unsigned *)ident = u | 1U;
+       *(hash_item_header **)(ident + PTR_SHIFT) = next;
+       return ident;
+}
+
+/*
+ * Adding an item is straightforward:
+ *  1. look for its emplacement
+ *  2. if no node is found, use the item as a new node and link it to the tree
+ *  3. if a node is found:
+ *     3.1. if the node is real, check for name inequality, then create a
+ *          fake node and assemble the two-element linked list
+ *     3.2. if the node is fake, look for the name in the list; if not found,
+ *          add the node at the list end
+ */
+static void *internal_put(HTT *htt, void *item, char *name, int reduced)
+{
+       unsigned u = hash_string(name), v;
+       int ls;
+       hash_item_header *father;
+       hash_item_header *node = find_node(htt, u, &father, &ls, reduced);
+       hash_item_header *itemg = item, *pnode;
+
+       if (node == NULL) {
+               itemg->left = itemg->right = NULL;
+               itemg->ident = make_ident(name, u);
+               if (father == NULL) {
+                       TREE(u) = itemg;
+               } else if (ls) {
+                       father->left = itemg;
+               } else {
+                       father->right = itemg;
+               }
+               return NULL;
+       }
+       v = *(unsigned *)(node->ident);
+       if ((v & 1U) == 0) {
+               if (strcmp(HASH_ITEM_NAME(node), name) == 0)
+                       return node;
+               pnode = getmem(sizeof *pnode);
+               pnode->left = node->left;
+               pnode->right = node->right;
+               pnode->ident = make_fake_ident(u, node);
+               node->left = itemg;
+               node->right = NULL;
+               itemg->left = itemg->right = NULL;
+               itemg->ident = make_ident(name, u);
+               if (father == NULL) {
+                       TREE(u) = pnode;
+               } else if (ls) {
+                       father->left = pnode;
+               } else {
+                       father->right = pnode;
+               }
+               return NULL;
+       }
+       node = *(hash_item_header **)(node->ident + PTR_SHIFT);
+       while (node != NULL) {
+               if (strcmp(HASH_ITEM_NAME(node), name) == 0) return node;
+               pnode = node;
+               node = node->left;
+       }
+       itemg->left = itemg->right = NULL;
+       itemg->ident = make_ident(name, u);
+       pnode->left = itemg;
+       return NULL;
+}
+
+/* see nhash.h */
+void *HTT_put(HTT *htt, void *item, char *name)
+{
+       return internal_put(htt, item, name, 0);
+}
+
+/* see nhash.h */
+void *HTT2_put(HTT2 *htt, void *item, char *name)
+{
+       return internal_put((HTT *)htt, item, name, 1);
+}
+
+/*
+ * A fake node subnode list has shrunk to one item only; make the
+ * node real again.
+ *   fnode    the fake node
+ *   node     the last remaining node
+ *   father   the fake node father (NULL if the fake node is root)
+ *   leftson  1 if the fake node is a left son, 0 otehrwise
+ *   u        the hash value for this node
+ */
+static void shrink_node(HTT *htt, hash_item_header *fnode,
+       hash_item_header *node, hash_item_header *father, int leftson,
+       unsigned u, int reduced)
+{
+       node->left = fnode->left;
+       node->right = fnode->right;
+       if (father == NULL) {
+               TREE(u) = node;
+       } else if (leftson) {
+               father->left = node;
+       } else {
+               father->right = node;
+       }
+       freemem(fnode->ident);
+       freemem(fnode);
+}
+
+/*
+ * Deletion algorithm:
+ *  1. look for the node; if not found, exit
+ *  2. if the node is real:
+ *     2.1. check for equality; exit otherwise
+ *     2.2. delete the node
+ *     2.3. promote the leftest of right descendants or rightest of left
+ *          descendants
+ *  3. if the node is fake:
+ *     3.1. check the list items for equality; exit otherwise
+ *     3.2. delete the correct item
+ *     3.3. if there remains only one item, supress the fake node
+ */
+static int internal_del(HTT *htt, char *name, int reduced)
+{
+       unsigned u = hash_string(name), v;
+       int ls;
+       hash_item_header *father;
+       hash_item_header *node = find_node(htt, u, &father, &ls, reduced);
+       hash_item_header *pnode, *fnode, *znode;
+       char *tmp;
+
+       if (node == NULL) return 0;
+       v = *(unsigned *)(node->ident);
+       if ((v & 1U) != 0) {
+               fnode = node;
+               node = znode = *(hash_item_header **)(node->ident + PTR_SHIFT);
+               pnode = NULL;
+               while (node != NULL) {
+                       if (strcmp(HASH_ITEM_NAME(node), name) == 0) break;
+                       pnode = node;
+                       node = node->left;
+               }
+               if (node == NULL) return 0;
+               if (pnode == NULL) {
+                       /*
+                        * We supress the first item in the list.
+                        */
+                       *(hash_item_header **)(fnode->ident + PTR_SHIFT) =
+                               node->left;
+                       if (node->left->left == NULL) {
+                               shrink_node(htt, fnode, node->left,
+                                       father, ls, u, reduced);
+                       }
+               } else {
+                       pnode->left = node->left;
+                       if (pnode->left == NULL && znode == pnode) {
+                               shrink_node(htt, fnode, pnode,
+                                       father, ls, u, reduced);
+                       }
+               }
+       } else {
+               if (strcmp(HASH_ITEM_NAME(node), name) != 0) return 0;
+               if (node->left != NULL) {
+                       for (znode = node, pnode = node->left; pnode->right;
+                               znode = pnode, pnode = pnode->right);
+                       if (znode != node) {
+                               znode->right = pnode->left;
+                               pnode->left = node->left;
+                       }
+                       pnode->right = node->right;
+               } else if (node->right != NULL) {
+                       for (znode = node, pnode = node->right; pnode->left;
+                               znode = pnode, pnode = pnode->left);
+                       if (znode != node) {
+                               znode->left = pnode->right;
+                               pnode->right = node->right;
+                       }
+                       pnode->left = node->left;
+               } else pnode = NULL;
+               if (father == NULL) {
+                       TREE(u) = pnode;
+               } else if (ls) {
+                       father->left = pnode;
+               } else {
+                       father->right = pnode;
+               }
+       }
+       tmp = node->ident;
+       htt->deldata(node);
+       freemem(tmp);
+       return 1;
+}
+
+/* see nhash.h */
+int HTT_del(HTT *htt, char *name)
+{
+       return internal_del(htt, name, 0);
+}
+
+/* see nhash.h */
+int HTT2_del(HTT2 *htt, char *name)
+{
+       return internal_del((HTT *)htt, name, 1);
+}
+
+/*
+ * Apply `action()' on all nodes of the tree whose root is given as
+ * parameter `node'. If `wipe' is non-zero, the nodes are removed
+ * from memory.
+ */
+static void scan_node(hash_item_header *node, void (*action)(void *), int wipe)
+{
+       unsigned v;
+
+       if (node == NULL) return;
+       scan_node(node->left, action, wipe);
+       scan_node(node->right, action, wipe);
+       v = *(unsigned *)(node->ident);
+       if ((v & 1U) != 0) {
+               hash_item_header *pnode, *nnode;
+
+               for (pnode = *(hash_item_header **)(node->ident + PTR_SHIFT);
+                       pnode != NULL; pnode = nnode) {
+                       char *tmp = pnode->ident;
+
+                       nnode = pnode->left;
+                       action(pnode);
+                       if (wipe) freemem(tmp);
+               }
+               if (wipe) {
+                       freemem(node->ident);
+                       freemem(node);
+               }
+       } else {
+               char *tmp = node->ident;
+
+               action(node);
+               if (wipe) freemem(tmp);
+       }
+}
+
+/* see nhash.h */
+void HTT_scan(HTT *htt, void (*action)(void *))
+{
+       unsigned u;
+
+       for (u = 0; u < HTT_NUM_TREES; u ++) {
+               scan_node(htt->tree[u], action, 0);
+       }
+}
+
+/* see nhash.h */
+void HTT2_scan(HTT2 *htt, void (*action)(void *))
+{
+       scan_node(htt->tree[0], action, 0);
+       scan_node(htt->tree[1], action, 0);
+}
+
+/* see nhash.h */
+void HTT_kill(HTT *htt)
+{
+       unsigned u;
+
+       for (u = 0; u < HTT_NUM_TREES; u ++) {
+               scan_node(htt->tree[u], htt->deldata, 1);
+       }
+}
+
+/* see nhash.h */
+void HTT2_kill(HTT2 *htt)
+{
+       scan_node(htt->tree[0], htt->deldata, 1);
+       scan_node(htt->tree[1], htt->deldata, 1);
+}
diff --git a/libexec/auxcpp/nhash.h b/libexec/auxcpp/nhash.h
new file mode 100644 (file)
index 0000000..00156f5
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * (c) Thomas Pornin 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef UCPP__NHASH__
+#define UCPP__NHASH__
+
+/*
+ * Each item stored in the hash table should be a structure beginning
+ * with the following header.
+ */
+typedef struct hash_item_header_ {
+       char *ident;
+       struct hash_item_header_ *left, *right;
+} hash_item_header;
+
+/*
+ * This macro takes as argument a pointer to a hash table item (a
+ * structure beginning with `hash_item_header') and returns a pointer to
+ * the item name. This name should be considered as read-only. The
+ * retrieved pointer can become invalid whenever a new item is inserted
+ * in or removed from the table.
+ */
+#define HASH_ITEM_NAME(s) (((hash_item_header *)(s))->ident + sizeof(unsigned))
+
+/*
+ * Number of lists for the primary hash step. Can be reduced to save more
+ * memory, or increased to speed things up. It should be a power of 2
+ * greater or equal than 2 and smaller than UINT_MAX.
+ */
+#define HTT_NUM_TREES   128
+
+/*
+ * Type for a hash table.
+ */
+typedef struct {
+       void (*deldata)(void *);
+       hash_item_header *tree[HTT_NUM_TREES];
+} HTT;
+
+/*
+ * Type for a reduced version of HTT with only two binary trees. That
+ * version has a lower initialization time and is suitable for situation
+ * where only a limited number of elements will be stored, but new tables
+ * need frequent initializations.
+ */
+typedef struct {
+       void (*deldata)(void *);
+       hash_item_header *tree[2];
+} HTT2;
+
+/*
+ * Initialize a hash table. The `deldata' parameter should point to a
+ * function which will be invoked on any item removed from the table;
+ * that function should take care of the release of memory allocated for
+ * that item (except the hash_item_header contents, which are handled
+ * internally).
+ */
+void HTT_init(HTT *htt, void (*deldata)(void *));
+
+/*
+ * Link an item into the hash table under the given name. If another
+ * item of identical name is already present in the table, a pointer to
+ * that item is returned; otherwise, the new item is linked into the
+ * table and NULL is returned. The object pointed to by `item' is
+ * linked from the table, but not the string pointed to by `name'.
+ */
+void *HTT_put(HTT *htt, void *item, char *name);
+
+/*
+ * Retrieve an item by name from the hash table. NULL is returned if
+ * the object is not found.
+ */
+void *HTT_get(HTT *htt, char *name);
+
+/*
+ * Remove an item from the hash table. 1 is returned if the item was
+ * removed, 0 if it was not found.
+ */
+int HTT_del(HTT *htt, char *name);
+
+/*
+ * For all items stored within the hash table, invoke the provided
+ * function with the item as parameter. The function may abort the
+ * scan by performing a longjmp() to a context encapsulating the
+ * call to that function.
+ */
+void HTT_scan(HTT *htt, void (*action)(void *));
+
+/*
+ * Release the whole table contents. After a call to this function,
+ * the table is ready to accept new items.
+ */
+void HTT_kill(HTT *htt);
+
+/*
+ * The following functions are identical to the HTT_*() functions, except
+ * that they operate on the reduced HTT2 tables.
+ */
+void HTT2_init(HTT2 *htt, void (*deldata)(void *));
+void *HTT2_put(HTT2 *htt, void *item, char *name);
+void *HTT2_get(HTT2 *htt, char *name);
+int HTT2_del(HTT2 *htt, char *name);
+void HTT2_scan(HTT2 *htt, void (*action)(void *));
+void HTT2_kill(HTT2 *htt);
+
+#endif
diff --git a/libexec/auxcpp/sample.c b/libexec/auxcpp/sample.c
new file mode 100644 (file)
index 0000000..f94f5c9
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Sample code showing how to use ucpp as an integrated lexer.
+ * This file is public domain.
+ */
+
+/*
+ * This is an example of how to use ucpp as a preprocessor and lexer
+ * into another project. The steps are those described in ucpp README
+ * file. To use this code, compile the ucpp source files with
+ * STAND_ALONE not defined, and link them with this code. The resulting
+ * binary will take a C source file as standard input, preprocess it,
+ * and output each non-whitespace token on stdout, with its numerical
+ * value (defined as an enum in cpp.h) and its contents. This code
+ * defines no system include path.
+ *
+ * This code supposes that the ucpp files are compiled with PRAGMA_TOKENIZE
+ * enabled (see the tune.h file).
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mem.h"
+#include "cpp.h"
+
+int main(int argc, char *argv[])
+{
+       int i, r;
+       struct lexer_state ls;
+
+       /* step 1 */
+       init_cpp();
+
+       /* step 2 */
+       no_special_macros = 0;
+       emit_defines = emit_assertions = 0;
+
+       /* step 3 -- with assertions */
+       init_tables(1);
+
+       /* step 4 -- no default include path */
+       init_include_path(0);
+
+       /* step 5 -- no need to reset the two emit_* variables set in 2 */
+       emit_dependencies = 0;
+
+       /* step 6 -- we work with stdin, this is not a real filename */
+       set_init_filename("[stdin]", 0);
+
+       /* step 7 -- we make sure that assertions are on, and pragma are
+          handled */
+       init_lexer_state(&ls);
+       init_lexer_mode(&ls);
+       ls.flags |= HANDLE_ASSERTIONS | HANDLE_PRAGMA | LINE_NUM;
+
+       /* step 8 -- input is from stdin */
+       ls.input = stdin;
+
+       /* step 9 -- we do not have any macro to define, but we add any
+          argument as an include path */
+       for (i = 1; i < argc; i ++) add_incpath(argv[i]);
+
+       /* step 10 -- we are a lexer and we want CONTEXT tokens */
+       enter_file(&ls, ls.flags);
+
+       /* read tokens until end-of-input is reached -- errors (non-zero
+          return values different from CPPERR_EOF) are ignored */
+       while ((r = lex(&ls)) < CPPERR_EOF) {
+               if (r) {
+                       /* error condition -- no token was retrieved */
+                       continue;
+               }
+               /* we print each token: its numerical value, and its
+                  string content; if this is a PRAGMA token, the
+                  string content is in fact a compressed token list,
+                  that we uncompress and print. */
+               if (ls.ctok->type == PRAGMA) {
+                       unsigned char *c = (unsigned char *)(ls.ctok->name);
+
+                       printf("line %ld: <#pragma>\n", ls.line);
+                       for (; *c; c ++) {
+                               int t = *c;
+
+                               if (STRING_TOKEN(t)) {
+                                       printf("  <%2d>  ", t);
+                                       for (c ++; *c != PRAGMA_TOKEN_END;
+                                               c ++) putchar(*c);
+                                       putchar('\n');
+                               } else {
+                                       printf("  <%2d>  `%s'\n", t,
+                                               operators_name[t]);
+                               }
+                       }
+               } else if (ls.ctok->type == CONTEXT) {
+                       printf("new context: file '%s', line %ld\n",
+                               ls.ctok->name, ls.ctok->line);
+               } else if (ls.ctok->type == NEWLINE) {
+                       printf("[newline]\n");
+               } else {
+                       printf("line %ld: <%2d>  `%s'\n", ls.ctok->line,
+                               ls.ctok->type,
+                               STRING_TOKEN(ls.ctok->type) ? ls.ctok->name
+                               : operators_name[ls.ctok->type]);
+               }
+       }
+
+       /* give back memory and exit */
+       wipeout();
+       free_lexer_state(&ls);
+#ifdef MEM_DEBUG
+       report_leaks();
+#endif
+       return 0;
+}
diff --git a/libexec/auxcpp/tune.h b/libexec/auxcpp/tune.h
new file mode 100644 (file)
index 0000000..e4afc31
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * (c) Thomas Pornin 1999 - 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef UCPP__TUNE__
+#define UCPP__TUNE__
+
+#ifdef UCPP_CONFIG
+#include "config.h"
+#else
+
+/* ====================================================================== */
+/*
+ * The LOW_MEM macro triggers the use of macro storage which uses less
+ * memory. It actually also improves performance on large, modern machines
+ * (due to less cache pressure). This option implies no limitation (except
+ * on the number of arguments a macro may, which is then limited to 32766)
+ * so it is on by default. Non-LOW_MEM code is considered deprecated.
+ */
+#define LOW_MEM
+
+/* ====================================================================== */
+/*
+ * Define AMIGA for systems using "drive letters" at the beginning of
+ * some paths; define MSDOS on systems with drive letters and using
+ * backslashes to seperate directory components.
+ */
+/* #define AMIGA */
+/* #define MSDOS */
+
+/* ====================================================================== */
+/*
+ * Define this if your compiler does not know the strftime() function;
+ * TurboC 2.01 under Msdos does not know strftime().
+ */
+/* #define NOSTRFTIME */
+
+/* ====================================================================== */
+/*
+ * Buffering: there are two levels of buffering on input and output streams:
+ * the standard libc buffering (manageable with setbuf() and setvbuf())
+ * and some buffering provided by ucpp itself. The ucpp buffering uses
+ * two buffers, of size respectively INPUT_BUF_MEMG and OUTPUT_BUF_MEMG
+ * (as defined below).
+ * You can disable one or both of these bufferings by defining the macros
+ * NO_LIBC_BUF and NO_UCPP_BUF.
+ */
+/* #define NO_LIBC_BUF */
+/* #define NO_UCPP_BUF */
+
+/*
+ * On Unix stations, the system call mmap() might be used on input files.
+ * This option is a subclause of ucpp internal buffering. On one station,
+ * a 10% speed improvement was observed. Do not define this unless the
+ * host architecture has the following characteristics:
+ *  -- Posix / Single Unix compliance
+ *  -- Text files correspond one to one with memory representation
+ * If a file is not seekable or not mmapable, ucpp will revert to the
+ * standard fread() solution.
+ *
+ * This feature is still considered beta quality. On some systems where
+ * files can be bigger than memory address space (mainly, 32-bit systems
+ * with files bigger than 4 GB), this option makes ucpp fail to operate
+ * on those extremely large files.
+ */
+#define UCPP_MMAP
+
+/*
+ * Performance issues:
+ * -- On memory-starved systems, such as Minix-i86, do not use ucpp
+ * buffering; keep only libc buffering.
+ * -- If you do not use libc buffering, activate the UCPP_MMAP option.
+ * Note that the UCPP_MMAP option is ignored if ucpp buffering is not
+ * activated.
+ *
+ * On an Athlon 1200 running FreeBSD 4.7, the best performances are
+ * achieved when libc buffering is activated and/or UCPP_MMAP is on.
+ */
+
+/* ====================================================================== */
+/*
+ * Define this if you want ucpp to generate tokenized PRAGMA tokens;
+ * otherwise, it will generate raw string contents. This setting is
+ * irrelevant to the stand-alone version of ucpp.
+ */
+#define PRAGMA_TOKENIZE
+
+/*
+ * Define this to the special character that marks the end of tokens with
+ * a string value inside a tokenized PRAGMA token. The #pragma and _Pragma()
+ * directives which use this character will be a bit more difficult to
+ * decode (but ucpp will not mind). 0 cannot be used. '\n' is fine because
+ * it cannot appear inside a #pragma or _Pragma(), since newlines cannot be
+ * embedded inside tokens, neither directly nor by macro substitution and
+ * stringization. Besides, '\n' is portable.
+ */
+#define PRAGMA_TOKEN_END       ((unsigned char)'\n')
+
+/*
+ * Define this if you want ucpp to include encountered #pragma directives
+ * in its output in non-lexer mode; _Pragma() are translated to equivalent
+ * #pragma directives.
+ */
+#define PRAGMA_DUMP
+
+/*
+ * According to my interpretation of the C99 standard, _Pragma() are
+ * evaluated wherever macro expansion could take place. However, Neil Booth,
+ * whose mother language is English (contrary to me) and who is well aware
+ * of the C99 standard (and especially the C preprocessor) told me that
+ * it was unclear whether _Pragma() are evaluated inside directives such
+ * as #if, #include and #line. If you want to disable the evaluation of
+ * _Pragma() inside such directives, define the following macro.
+ */
+/* #define NO_PRAGMA_IN_DIRECTIVE */
+
+/*
+ * The C99 standard mandates that the operator `##' must yield a single,
+ * valid token, lest undefined behaviour befall upon thy head. Hence,
+ * for instance, `+ ## +=' is forbidden, because `++=' is not a valid
+ * token (although it is a valid list of two tokens, `++' and `=').
+ * However, ucpp only emits a warning for such sin, and unmerges the
+ * tokens (thus emitting `+' then `+=' for that example). When ucpp
+ * produces text output, those two tokens will be separated by a space
+ * character so that the basic rule of text output is preserved: when
+ * parsed again, text output yields the exact same stream of tokens.
+ * That extra space is virtual: it does not count as a true whitespace
+ * token for stringization.
+ *
+ * However, it might be desirable, for some uses other than preprocessing
+ * C source code, not to emit that extra space at all. To make ucpp behave
+ * that way, define the DSHARP_TOKEN_MERGE macro. Please note that this
+ * can trigger spurious token merging. For instance, with that macro
+ * activated, `+ ## +=' will be output as `++=' which, if preprocessed
+ * again, will read as `++' followed by `='.
+ *
+ * All this is irrelevant to lexer mode; and trying to merge incompatible
+ * tokens is a shooting offence, anyway.
+ */
+/* #define DSHARP_TOKEN_MERGE */
+
+/* ====================================================================== */
+/*
+ * Define INMACRO_FLAG to include two flags to the structure lexer_state,
+ * that tell whether tokens come from a macro-replacement, and count those
+ * macro-replacements.
+ */
+/* #define INMACRO_FLAG */
+
+/* ====================================================================== */
+/*
+ * Paths where files are looked for by default, when #include is used.
+ * Typical path is /usr/local/include and /usr/include, in that order.
+ * If you want to set up no path, define the macro to 0.
+ *
+ * For Linux, get gcc includes too, or you will miss things like stddef.h.
+ * The exact path varies much, depending on the distribution.
+ */
+#define STD_INCLUDE_PATH       "/usr/local/include", "/usr/include"
+
+/* ====================================================================== */
+/*
+ * Arithmetic code for evaluation of #if expressions. Evaluation
+ * uses either a native machine type, or an emulated two's complement
+ * type. Division by 0 and overflow on division are considered as errors
+ * and reported as such. If ARITHMETIC_CHECKS is defined, all other
+ * operations that imply undefined or implementation-defined behaviour
+ * are reported as warnings but otherwise performed nonetheless.
+ *
+ * For native type evaluation, the following macros should be defined:
+ *   NATIVE_SIGNED           the native signed type
+ *   NATIVE_UNSIGNED         the native corresponding unsigned type
+ *   NATIVE_UNSIGNED_BITS    the native unsigned type width, in bits
+ *   NATIVE_SIGNED_MIN       the native signed type minimum value
+ *   NATIVE_SIGNED_MAX       the native signed type maximum value
+ *
+ * The code in the arith.c file performs some tricky detection
+ * operations on the native type representation and possible existence
+ * of a trap representation. These operations assume a C99-compliant
+ * compiler; on a C90-only compiler, the operations are valid but may
+ * yield incorrect results. You may force those settings with some
+ * more macros: see the comments in arith.c (look for "ARCH_DEFINED").
+ * Remember that this is mostly a non-issue, unless you are building
+ * ucpp with a pre-C99 cross-compiler and either the host or target
+ * architecture uses a non-two's complement representation of signed
+ * integers. Such a combination is pretty rare nowadays, so the best
+ * you can do is forgetting completely this paragraph and live in peace.
+ *
+ *
+ * If you do not have a handy native type (for instance, you compile ucpp
+ * with a C90 compiler which lacks the "long long" type, or you compile
+ * ucpp for a cross-compiler which should support an evaluation integer
+ * type of a size that is not available on the host machine), you may use
+ * a simulated type. The type uses two's complement representation and
+ * may have any width from 2 bits to twice the underlying native type
+ * width, inclusive (odd widths are allowed). To use an emulated type,
+ * make sure that NATIVE_SIGNED is not defined, and define the following
+ * macros:
+ *   SIMUL_ARITH_SUBTYPE     the native underlying type to use
+ *   SIMUL_SUBTYPE_BITS      the native underlying type width
+ *   SIMUL_NUMBITS           the emulated type width
+ *
+ * Undefined and implementation-defined behaviours are warned upon, if
+ * ARITHMETIC_CHECKS is defined. Results are truncated to the type
+ * width; shift count for the << and >> operators is reduced modulo the
+ * emulatd type width; right shifting of a signed negative value performs
+ * sign extension (the result is left-padded with bits set to 1).
+ */
+
+/*
+ * For native type evaluation with a 64-bit "long long" type.
+ */
+#define NATIVE_SIGNED           long long
+#define NATIVE_UNSIGNED         unsigned long long
+#define NATIVE_UNSIGNED_BITS    64
+#define NATIVE_SIGNED_MIN       (-9223372036854775807LL - 1)
+#define NATIVE_SIGNED_MAX       9223372036854775807LL
+
+/*
+ * For emulation of a 64-bit type using a native 32-bit "unsigned long"
+ * type.
+#undef NATIVE_SIGNED
+#define SIMUL_ARITH_SUBTYPE     unsigned long
+#define SIMUL_SUBTYPE_BITS      32
+#define SIMUL_NUMBITS           64
+ */
+
+/*
+ * Comment out the following line if you want to deactivate arithmetic
+ * checks (warnings upon undefined and implementation-defined
+ * behaviour). Arithmetic checks slow down a bit arithmetic operations,
+ * especially multiplications, but this should not be an issue with
+ * typical C source code.
+ */
+#define ARITHMETIC_CHECKS
+
+/* ====================================================================== */
+/*
+ * To force signedness of wide character constants, define WCHAR_SIGNEDNESS
+ * to 0 for unsigned, 1 for signed. By default, wide character constants
+ * are signed if the native `char' type is signed, and unsigned otherwise.
+#define WCHAR_SIGNEDNESS       0
+ */
+
+/*
+ * Standard assertions. They should include one cpu() assertion, one machine()
+ * assertion (identical to cpu()), and one or more system() assertions.
+ *
+ * for Linux/PC:      cpu(i386),  machine(i386),  system(unix), system(linux)
+ * for Linux/Alpha:   cpu(alpha), machine(alpha), system(unix), system(linux)
+ * for Sparc/Solaris: cpu(sparc), machine(sparc), system(unix), system(solaris)
+ *
+ * These are only suggestions. On Solaris, machine() should be defined
+ * for i386 or sparc (standard system header use such an assertion). For
+ * cross-compilation, define assertions related to the target architecture.
+ *
+ * If you want no standard assertion, define STD_ASSERT to 0.
+ */
+/*
+#define STD_ASSERT     "cpu(i386)", "machine(i386)", "system(unix)", \
+                       "system(freebsd)"
+*/
+
+/* ====================================================================== */
+/*
+ * System predefined macros. Nothing really mandatory, but some programs
+ * might rely on those.
+ * Each string must be either "name" or "name=token-list". If you want
+ * no predefined macro, define STD_MACROS to 0.
+ */
+/*
+#define STD_MACROS     "__FreeBSD=4", "__unix", "__i386", \
+                       "__FreeBSD__=4", "__unix__", "__i386__"
+*/
+
+/* ====================================================================== */
+/*
+ * Default flags; HANDLE_ASSERTIONS is required for Solaris system headers.
+ * See cpp.h for the definition of these flags.
+ */
+#define DEFAULT_CPP_FLAGS      (DISCARD_COMMENTS | WARN_STANDARD \
+                               | WARN_PRAGMA | FAIL_SHARP | MACRO_VAARG \
+                               | CPLUSPLUS_COMMENTS | LINE_NUM | TEXT_OUTPUT \
+                               | KEEP_OUTPUT | HANDLE_TRIGRAPHS \
+                               | HANDLE_ASSERTIONS)
+#define DEFAULT_LEXER_FLAGS    (DISCARD_COMMENTS | WARN_STANDARD | FAIL_SHARP \
+                               | MACRO_VAARG | CPLUSPLUS_COMMENTS | LEXER \
+                               | HANDLE_TRIGRAPHS | HANDLE_ASSERTIONS)
+
+/* ====================================================================== */
+/*
+ * Define this to use sigsetjmp()/siglongjmp() instead of setjmp()/longjmp().
+ * This is non-ANSI, but it improves performance on some POSIX system.
+ * On typical C source code, such improvement is completely negligeable.
+ */
+/* #define POSIX_JMP */
+
+/* ====================================================================== */
+/*
+ * Maximum value (plus one) of a character handled by the lexer; 128 is
+ * alright for ASCII native source code, but 256 is needed for EBCDIC.
+ * 256 is safe in both cases; you will have big problems if you set
+ * this value to INT_MAX or above. On Minix-i86 or Msdos (small memory
+ * model), define MAX_CHAR_VAL to 128.
+ *
+ * Set MAX_CHAR_VAL to a power of two to increase lexing speed. Beware
+ * that lexer.c defines a static array of size MSTATE * MAX_CHAR_VAL
+ * values of type int (MSTATE is defined in lexer.c and is about 40).
+ */
+#define MAX_CHAR_VAL   128
+
+/*
+ * If you want some extra character to be considered as whitespace,
+ * define this macro to that space. On ISO-8859-1 machines, 160 is
+ * the code for the unbreakable space.
+ */
+/* #define UNBREAKABLE_SPACE   160 */
+
+/*
+ * If you want whitespace tokens contents to be recorded (making them
+ * tokens with a string content), define this. The macro STRING_TOKEN
+ * will be adjusted accordingly.
+ * Without this option, whitespace tokens are not even returned by the
+ * lex() function. This is irrelevant for the non-lexer mode (almost --
+ * it might slow down a bit ucpp, and with this option, comments will be
+ * kept inside #pragma directives).
+ */
+/* #define SEMPER_FIDELIS */
+
+#endif
+/* End of options overridable by UCPP_CONFIG and config.h */
+
+/* ====================================================================== */
+/*
+ * Some constants used for memory increment granularity. Increasing these
+ * values reduces the number of calls to malloc() but increases memory
+ * consumption.
+ *
+ * Values should be powers of 2.
+ */
+
+/* for cpp.c */
+#define COPY_LINE_LENGTH       80
+#define INPUT_BUF_MEMG         8192
+#define OUTPUT_BUF_MEMG                8192
+#define TOKEN_NAME_MEMG                64      /* must be at least 4 */
+#define TOKEN_LIST_MEMG                32
+#define INCPATH_MEMG           16
+#define GARBAGE_LIST_MEMG      32
+#define LS_STACK_MEMG          4
+#define FNAME_MEMG             32
+
+/* ====================================================================== */
+
+/* To protect the innocent. */
+#if defined(NO_UCPP_BUF) && defined(UCPP_MMAP)
+#undef UCPP_MMAP
+#endif
+
+#if defined(UCPP_MMAP) || defined(POSIX_JMP)
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE  1
+#endif
+#endif
+
+/*
+ * C90 does not know about the "inline" keyword, but C99 does know,
+ * and some C90 compilers know it as an extension. This part detects
+ * these occurrences.
+ */
+
+#ifndef INLINE
+
+#if __STDC__ && __STDC_VERSION__ >= 199901L
+/* this is a C99 compiler, keep inline unchanged */
+#elif defined(__GNUC__)
+/* this is GNU gcc; modify inline. The semantics is not identical to C99
+   but the differences are irrelevant as long as inline functions are static */
+#undef inline
+#define inline __inline__
+#elif defined(__DECC) && defined(__linux__)
+/* this is Compaq C under Linux, use __inline__ */
+#undef inline
+#define inline __inline__
+#else
+/* unknown compiler -> deactivate inline */
+#undef inline
+#define inline
+#endif
+
+#else
+/* INLINE has been set, use its value */
+#undef inline
+#define inline INLINE
+#endif
+
+#endif
diff --git a/libexec/auxcpp/ucpp.1 b/libexec/auxcpp/ucpp.1
new file mode 100644 (file)
index 0000000..c6c3051
--- /dev/null
@@ -0,0 +1,212 @@
+.TH UCPP 1 "Oct 21 2000"
+.SH NAME
+ucpp \- C preprocessor
+.SH SYNOPSIS
+.B ucpp 
+[
+.I options
+]
+[
+.I file
+]
+.SH DESCRIPTION
+.LP
+.B ucpp
+is a C preprocessor mostly compatible with ISO-C99.
+It is rather strict and uses only a small amount of memory. It uses
+standard input as primary input if no file argument is given.
+.SH OPTIONS
+There are several classes of options.
+.TP
+.B Language Options
+.TP
+.BI \-C
+keep comments in the output.
+.TP
+.BI \-s
+if a rogue '#' is encountered, do not emit an error and keep it in
+the output.
+.TP
+.BI \-l
+supress the emission of '#line' directives in the output.
+.TP
+.BI \-lg
+convert the '#line' to the gcc-style equivalent.
+.TP
+.BI \-CC
+disable C++-like comments (a '//' begins a comment, up to the end
+of the line). Use this option to get closer to C90 behaviour.
+.TP
+.B \-a, \-na
+handle assertions (defined with #assert);
+.B \-a
+also defines the standard assertions
+.I #machine
+,
+.I #cpu
+and
+.I #system
+(see
+.B \-e
+to get the local definition of such assertions).
+.TP
+.BI \-a0
+disable assertion support.
+.TP
+.BI \-V
+disable support for macros with a variable number of arguments: in C99,
+a macro may be declared with
+.I ...
+as the last argument; inside the replacement list,
+.I __VA_ARGS__
+is replaced with the optional extra arguments given in the call to the macro.
+Use this option to get closer to C90 behaviour.
+.TP
+.BI \-u
+enable UTF-8 support: with this option, the source is considered as
+an ISO/10646 source, encoded in UTF-8. Characters represented as two bytes
+or more are considered as alphabetic characters, like letters, and
+therefore usable in identifiers. These characters hold the same
+syntactic value than the corresponding Universal Character Names.
+.TP
+.BI \-X
+enable
+.B \-a, \-u
+and
+.B \-Y.
+This should make
+.B ucpp
+behave closer to what is requested from a "modern" C preprocessor.
+.TP
+.BI \-c90
+enable
+.B \-V
+and
+.B \-CC,
+and do not define
+.B __STDC_VERSION__.
+This should make
+.B ucpp
+mimic older C90 behaviour.
+.TP
+.BI \-t
+disable trigraph support; this seems to be required for some legacy code.
+.TP
+.B Warning Options
+.TP
+.BI \-wt
+emit a final warning when trigraphs are encountered.
+.TP
+.BI \-wtt
+emit warnings for each trigraph encountered.
+.TP
+.BI \-wa
+emit annoying warnings (these are usually useless).
+.TP
+.BI \-w0
+supress standard warnings.
+.TP
+.B Directory Options
+.TP
+.BI \-I directory
+.TP
+.BI "\-I " directory
+add
+.I directory
+to the include path, before the standard include path.
+.TP
+.BI \-J directory
+.TP
+.BI "\-J " directory
+add
+.I directory
+to the include path, after the standard include path.
+.TP
+.BI \-zI
+do not use the standard (compile-time) include path.
+.TP
+.BI \-M
+emit only the names of encountered files, separated by spaces; this is
+intended for automatic generation of Makefile dependencies.
+.TP
+.BI \-Ma
+do the same as
+.B \-M
+but also for system files.
+.TP
+.BI "\-o " file
+direct the ouput to
+.I file
+instead of standard output.
+.TP
+.B Macro Options
+.TP
+.BI \-D macro
+predefine
+.I macro
+with content
+.B 1.
+.TP
+.BI \-D macro=def
+predefine
+.I macro
+with the content
+.I def.
+.TP
+.BI \-U macro
+undefine
+.I macro.
+.TP
+.BI \-Y
+predefine system-dependant macros.
+.TP
+.BI \-Z
+do not predefine special macros such as
+.B __TIME__.
+.TP
+.BI \-A foo(bar)
+add
+.I foo(bar)
+to the list of assertions.
+.TP
+.BI \-B foo(bar)
+remove
+.I foo(bar)
+of the list of assertions; you may also use
+.BI \-B foo
+to remove all
+.BI \-B foo(xxx)
+from the list of assertions.
+.TP
+.BI \-d
+instead of normal output, emit '#define' directives representing all
+macros defined during processing.
+.TP
+.BI \-e
+instead of normal output, emit '#assert' directives representing all
+assertions defined during processing.
+.TP
+.B Miscellaneous Options
+.TP
+.BI \-v
+print version number, include path and (optionaly) defined assertions.
+.TP
+.BI \-h
+print some help.
+.SH ENVIRONMENT
+.PP
+.B ucpp
+is not itself affected by environment variables. However, it uses
+library functions that might be affected, depending on the system.
+.SH AUTHOR
+Thomas Pornin <pornin@bolet.org>
+.SH BUGS
+.PP
+.B ucpp
+is considered stable software. However improbable it is, please report
+bugs to the author (possibly with a file that exhibits the problem) if
+the latest version, available from this site:
+.TP
+http://pornin.nerim.net/ucpp/
+.PP
+has the bug.
diff --git a/libexec/auxcpp/ucppi.h b/libexec/auxcpp/ucppi.h
new file mode 100644 (file)
index 0000000..ce4df74
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * (c) Thomas Pornin 1999 - 2002
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. The name of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef UCPP__UCPPI__
+#define UCPP__UCPPI__
+
+#include "tune.h"
+#include "cpp.h"
+#include "nhash.h"
+
+/*
+ * A macro represented in a compact form; simple tokens are represented
+ * by one byte, containing their number. Tokens with a string value are
+ * followed by the value (string finished by a 0). Macro arguments are
+ * followed by the argument number (in one byte -- thus implying a hard
+ * limit of 254 arguments (number 255 is for __VA_ARGS__).
+ */
+struct comp_token_fifo {
+       size_t length;
+       size_t rp;
+       unsigned char *t;
+};
+
+/* These declarations are used only internally by ucpp */
+
+/*
+ * S_TOKEN(x)  checks whether x is a token type with an embedded string
+ * ttMWS(x)    checks whether x is macro whitespace (space, comment...)
+ * ttWHI(x)    checks whether x is whitespace (MWS or newline)
+ */
+#define S_TOKEN(x)     STRING_TOKEN(x)
+#define ttMWS(x)       ((x) == NONE || (x) == COMMENT || (x) == OPT_NONE)
+#define ttWHI(x)       (ttMWS(x) || (x) == NEWLINE)
+
+/*
+ * Function prototypes
+ */
+/*
+ * from lexer.c
+ */
+#define init_cppm      ucpp_init_cppm
+#define put_char       ucpp_put_char
+#define discard_char   ucpp_discard_char
+#define next_token     ucpp_next_token
+#define grap_char      ucpp_grap_char
+#define space_char     ucpp_space_char
+
+void init_cppm(void);
+void put_char(struct lexer_state *, unsigned char);
+void discard_char(struct lexer_state *);
+int next_token(struct lexer_state *);
+int grap_char(struct lexer_state *);
+int space_char(int);
+
+/*
+ * from assert.c
+ */
+struct assert {
+       hash_item_header head;    /* first field */
+       size_t nbval;
+       struct token_fifo *val;
+};
+
+#define cmp_token_list         ucpp_cmp_token_list
+#define handle_assert          ucpp_handle_assert
+#define handle_unassert                ucpp_handle_unassert
+#define get_assertion          ucpp_get_assertion
+#define wipe_assertions                ucpp_wipe_assertions
+
+int cmp_token_list(struct token_fifo *, struct token_fifo *);
+int handle_assert(struct lexer_state *);
+int handle_unassert(struct lexer_state *);
+struct assert *get_assertion(char *);
+void wipe_assertions(void);
+
+/*
+ * from macro.c
+ */
+struct macro {
+       hash_item_header head;     /* first field */
+       int narg;
+       char **arg;
+       int nest;
+       int vaarg;
+#ifdef LOW_MEM
+       struct comp_token_fifo cval;
+#else
+       struct token_fifo val;
+#endif
+};
+
+#define print_token            ucpp_print_token
+#define handle_define          ucpp_handle_define
+#define handle_undef           ucpp_handle_undef
+#define handle_ifdef           ucpp_handle_ifdef
+#define handle_ifndef          ucpp_handle_ifndef
+#define substitute_macro       ucpp_substitute_macro
+#define get_macro              ucpp_get_macro
+#define wipe_macros            ucpp_wipe_macros
+#define dsharp_lexer           ucpp_dsharp_lexer
+#define compile_time           ucpp_compile_time
+#define compile_date           ucpp_compile_date
+#ifdef PRAGMA_TOKENIZE
+#define tokenize_lexer         ucpp_tokenize_lexer
+#endif
+
+void print_token(struct lexer_state *, struct token *, long);
+int handle_define(struct lexer_state *);
+int handle_undef(struct lexer_state *);
+int handle_ifdef(struct lexer_state *);
+int handle_ifndef(struct lexer_state *);
+int substitute_macro(struct lexer_state *, struct macro *,
+       struct token_fifo *, int, int, long);
+struct macro *get_macro(char *);
+void wipe_macros(void);
+
+extern struct lexer_state dsharp_lexer;
+extern char compile_time[], compile_date[];
+#ifdef PRAGMA_TOKENIZE
+extern struct lexer_state tokenize_lexer;
+#endif
+
+/*
+ * from eval.c
+ */
+#define strtoconst     ucpp_strtoconst
+#define eval_expr      ucpp_eval_expr
+#define eval_line      ucpp_eval_line
+
+unsigned long strtoconst(char *);
+unsigned long eval_expr(struct token_fifo *, int *, int);
+extern long eval_line;
+
+#define eval_exception ucpp_eval_exception
+
+#ifdef POSIX_JMP
+#define JMP_BUF        sigjmp_buf
+#define catch(x)       sigsetjmp((x), 0)
+#define throw(x)       siglongjmp((x), 1)
+#else
+#define JMP_BUF        jmp_buf
+#define catch(x)       setjmp((x))
+#define throw(x)       longjmp((x), 1)
+#endif
+extern JMP_BUF eval_exception;
+
+/*
+ * from cpp.c
+ */
+#define token_name             ucpp_token_name
+#define throw_away             ucpp_throw_away
+#define garbage_collect                ucpp_garbage_collect
+#define init_buf_lexer_state   ucpp_init_buf_lexer_state
+#ifdef PRAGMA_TOKENIZE
+#define compress_token_list    ucpp_compress_token_list
+#endif
+
+char *token_name(struct token *);
+void throw_away(struct garbage_fifo *, char *);
+void garbage_collect(struct garbage_fifo *);
+void init_buf_lexer_state(struct lexer_state *, int);
+#ifdef PRAGMA_TOKENIZE
+struct comp_token_fifo compress_token_list(struct token_fifo *);
+#endif
+
+#define ouch           ucpp_ouch
+#define error          ucpp_error
+#define warning                ucpp_warning
+
+#endif