diff options
author | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2014-07-12 14:25:41 +0000 |
---|---|---|
committer | Matthieu Herrb <matthieu@cvs.openbsd.org> | 2014-07-12 14:25:41 +0000 |
commit | 149ce9f1859ab815ace42e0bbcd2a6b2ef07a480 (patch) | |
tree | 7008b71e9d5a083d34ef452f457c36fc0c0a8063 /app | |
parent | 8b8ad04a2794612cb22e22ac3481e8dcc6435b26 (diff) |
Import a copy of ucpp, lightweight cpp for use by xrdb so that it
doesn't depend on the 'comp' set. ok espie@ deraadt@
Diffstat (limited to 'app')
-rw-r--r-- | app/xrdb-cpp/CHANGELOG | 21 | ||||
-rw-r--r-- | app/xrdb-cpp/Makefile | 115 | ||||
-rw-r--r-- | app/xrdb-cpp/README | 877 | ||||
-rw-r--r-- | app/xrdb-cpp/arith.c | 1462 | ||||
-rw-r--r-- | app/xrdb-cpp/arith.h | 255 | ||||
-rw-r--r-- | app/xrdb-cpp/assert.c | 420 | ||||
-rw-r--r-- | app/xrdb-cpp/atest.c | 236 | ||||
-rw-r--r-- | app/xrdb-cpp/config.h | 352 | ||||
-rw-r--r-- | app/xrdb-cpp/cpp.c | 2565 | ||||
-rw-r--r-- | app/xrdb-cpp/cpp.h | 317 | ||||
-rw-r--r-- | app/xrdb-cpp/eval.c | 699 | ||||
-rw-r--r-- | app/xrdb-cpp/hash.c | 329 | ||||
-rw-r--r-- | app/xrdb-cpp/hash.h | 58 | ||||
-rw-r--r-- | app/xrdb-cpp/lexer.c | 1020 | ||||
-rw-r--r-- | app/xrdb-cpp/macro.c | 1921 | ||||
-rw-r--r-- | app/xrdb-cpp/mem.c | 328 | ||||
-rw-r--r-- | app/xrdb-cpp/mem.h | 155 | ||||
-rw-r--r-- | app/xrdb-cpp/nhash.c | 481 | ||||
-rw-r--r-- | app/xrdb-cpp/nhash.h | 132 | ||||
-rw-r--r-- | app/xrdb-cpp/sample.c | 114 | ||||
-rw-r--r-- | app/xrdb-cpp/tune.h | 422 | ||||
-rw-r--r-- | app/xrdb-cpp/ucpp.1 | 212 | ||||
-rw-r--r-- | app/xrdb-cpp/ucppi.h | 196 |
23 files changed, 12687 insertions, 0 deletions
diff --git a/app/xrdb-cpp/CHANGELOG b/app/xrdb-cpp/CHANGELOG new file mode 100644 index 000000000..071b4682d --- /dev/null +++ b/app/xrdb-cpp/CHANGELOG @@ -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/app/xrdb-cpp/Makefile b/app/xrdb-cpp/Makefile new file mode 100644 index 000000000..c37f0b438 --- /dev/null +++ b/app/xrdb-cpp/Makefile @@ -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/app/xrdb-cpp/README b/app/xrdb-cpp/README new file mode 100644 index 000000000..f47ba9f3e --- /dev/null +++ b/app/xrdb-cpp/README @@ -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/app/xrdb-cpp/arith.c b/app/xrdb-cpp/arith.c new file mode 100644 index 000000000..bef258052 --- /dev/null +++ b/app/xrdb-cpp/arith.c @@ -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/app/xrdb-cpp/arith.h b/app/xrdb-cpp/arith.h new file mode 100644 index 000000000..ae64e5cda --- /dev/null +++ b/app/xrdb-cpp/arith.h @@ -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/app/xrdb-cpp/assert.c b/app/xrdb-cpp/assert.c new file mode 100644 index 000000000..579d47e0a --- /dev/null +++ b/app/xrdb-cpp/assert.c @@ -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/app/xrdb-cpp/atest.c b/app/xrdb-cpp/atest.c new file mode 100644 index 000000000..7137d930f --- /dev/null +++ b/app/xrdb-cpp/atest.c @@ -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/app/xrdb-cpp/config.h b/app/xrdb-cpp/config.h new file mode 100644 index 000000000..a1bbe993b --- /dev/null +++ b/app/xrdb-cpp/config.h @@ -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/app/xrdb-cpp/cpp.c b/app/xrdb-cpp/cpp.c new file mode 100644 index 000000000..7cdc358ca --- /dev/null +++ b/app/xrdb-cpp/cpp.c @@ -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/app/xrdb-cpp/cpp.h b/app/xrdb-cpp/cpp.h new file mode 100644 index 000000000..2bb707a32 --- /dev/null +++ b/app/xrdb-cpp/cpp.h @@ -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/app/xrdb-cpp/eval.c b/app/xrdb-cpp/eval.c new file mode 100644 index 000000000..82971973f --- /dev/null +++ b/app/xrdb-cpp/eval.c @@ -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/app/xrdb-cpp/hash.c b/app/xrdb-cpp/hash.c new file mode 100644 index 000000000..60bd21241 --- /dev/null +++ b/app/xrdb-cpp/hash.c @@ -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/app/xrdb-cpp/hash.h b/app/xrdb-cpp/hash.h new file mode 100644 index 000000000..3adbb777d --- /dev/null +++ b/app/xrdb-cpp/hash.h @@ -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/app/xrdb-cpp/lexer.c b/app/xrdb-cpp/lexer.c new file mode 100644 index 000000000..381256763 --- /dev/null +++ b/app/xrdb-cpp/lexer.c @@ -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/app/xrdb-cpp/macro.c b/app/xrdb-cpp/macro.c new file mode 100644 index 000000000..5b9540c67 --- /dev/null +++ b/app/xrdb-cpp/macro.c @@ -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(¯os, new_macro(), "__LINE__"); + HTT_put(¯os, new_macro(), "__FILE__"); + HTT_put(¯os, new_macro(), "__DATE__"); + HTT_put(¯os, new_macro(), "__TIME__"); + HTT_put(¯os, new_macro(), "__STDC__"); + m = new_macro(); m->narg = 1; + m->arg = getmem(sizeof(char *)); m->arg[0] = sdup("foo"); + HTT_put(¯os, 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(¯os, 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(¯os, 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), <, 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(¯os, 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(¯os, 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(¯os, (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(¯os, + 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(¯os, 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(¯os, 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(¯os, 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(¯os, 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(¯os, c)) { + if (check_special_macro(c)) { + error(-1, "trying to undef special macro %s", c); + return 1; + } else HTT_del(¯os, 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(¯os, 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(¯os, 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(¯os, 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(¯os, 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(¯os); + macros_init_done = 0; +} + +/* + * initialize the macro table + */ +void init_macros(void) +{ + wipe_macros(); + HTT_init(¯os, 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(¯os, name); +} diff --git a/app/xrdb-cpp/mem.c b/app/xrdb-cpp/mem.c new file mode 100644 index 000000000..dabde952e --- /dev/null +++ b/app/xrdb-cpp/mem.c @@ -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/app/xrdb-cpp/mem.h b/app/xrdb-cpp/mem.h new file mode 100644 index 000000000..4403c2fc4 --- /dev/null +++ b/app/xrdb-cpp/mem.h @@ -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/app/xrdb-cpp/nhash.c b/app/xrdb-cpp/nhash.c new file mode 100644 index 000000000..6e5e4f7f8 --- /dev/null +++ b/app/xrdb-cpp/nhash.c @@ -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/app/xrdb-cpp/nhash.h b/app/xrdb-cpp/nhash.h new file mode 100644 index 000000000..00156f57b --- /dev/null +++ b/app/xrdb-cpp/nhash.h @@ -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/app/xrdb-cpp/sample.c b/app/xrdb-cpp/sample.c new file mode 100644 index 000000000..f94f5c9d7 --- /dev/null +++ b/app/xrdb-cpp/sample.c @@ -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/app/xrdb-cpp/tune.h b/app/xrdb-cpp/tune.h new file mode 100644 index 000000000..e4afc31f9 --- /dev/null +++ b/app/xrdb-cpp/tune.h @@ -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/app/xrdb-cpp/ucpp.1 b/app/xrdb-cpp/ucpp.1 new file mode 100644 index 000000000..c6c305150 --- /dev/null +++ b/app/xrdb-cpp/ucpp.1 @@ -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/app/xrdb-cpp/ucppi.h b/app/xrdb-cpp/ucppi.h new file mode 100644 index 000000000..ce4df74be --- /dev/null +++ b/app/xrdb-cpp/ucppi.h @@ -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 |