From db5d1420f94101e07414cc550066657d4693c4d1 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 3 Jan 2020 12:46:35 +0800 Subject: [PATCH] Add mcpp from github.com/ned14/mcpp --- CMakeLists.txt | 14 + lib/mcpp/CMakeLists.txt | 16 + lib/mcpp/config.h | 89 + lib/mcpp/configed.H | 382 +++ lib/mcpp/directive.c | 1699 ++++++++++++++ lib/mcpp/eval.c | 1673 +++++++++++++ lib/mcpp/expand.c | 2980 +++++++++++++++++++++++ lib/mcpp/internal.H | 564 +++++ lib/mcpp/main.c | 1131 +++++++++ lib/mcpp/main_libmcpp.c | 9 + lib/mcpp/mbchar.c | 869 +++++++ lib/mcpp/mcpp_lib.h | 31 + lib/mcpp/mcpp_out.h | 13 + lib/mcpp/preproc.c | 9 + lib/mcpp/support.c | 2811 ++++++++++++++++++++++ lib/mcpp/system.H | 396 ++++ lib/mcpp/system.c | 4940 +++++++++++++++++++++++++++++++++++++++ 17 files changed, 17626 insertions(+) create mode 100644 lib/mcpp/CMakeLists.txt create mode 100644 lib/mcpp/config.h create mode 100644 lib/mcpp/configed.H create mode 100644 lib/mcpp/directive.c create mode 100644 lib/mcpp/eval.c create mode 100644 lib/mcpp/expand.c create mode 100644 lib/mcpp/internal.H create mode 100644 lib/mcpp/main.c create mode 100644 lib/mcpp/main_libmcpp.c create mode 100644 lib/mcpp/mbchar.c create mode 100644 lib/mcpp/mcpp_lib.h create mode 100644 lib/mcpp/mcpp_out.h create mode 100644 lib/mcpp/preproc.c create mode 100644 lib/mcpp/support.c create mode 100644 lib/mcpp/system.H create mode 100644 lib/mcpp/system.c diff --git a/CMakeLists.txt b/CMakeLists.txt index c201fcd68..87c171ca7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -239,6 +239,19 @@ if(NOT SERVER_ONLY) include_directories("${PROJECT_SOURCE_DIR}/lib/graphics_utils") endif() +# Libmcpp +find_library(MCPP_LIBRARY NAMES mcpp libmcpp) +find_path(MCPP_INCLUDEDIR NAMES mcpp_lib.h PATHS) +if (NOT MCPP_LIBRARY OR NOT MCPP_INCLUDEDIR) + add_subdirectory("${PROJECT_SOURCE_DIR}/lib/mcpp") + include_directories("${PROJECT_SOURCE_DIR}/lib/mcpp") + SET(MCPP_LIBRARY mcpp) + message(STATUS "System libmcpp not found, use the bundled one.") +else() + include_directories("${MCPP_INCLUDEDIR}") + MESSAGE(STATUS "Use system libmcpp: ${MCPP_LIBRARY}") +endif() + if(NOT SERVER_ONLY) if(USE_SYSTEM_SQUISH) find_library(SQUISH_LIBRARY NAMES squish libsquish) @@ -578,6 +591,7 @@ target_link_libraries(supertuxkart stkirrlicht ${Angelscript_LIBRARIES} ${CURL_LIBRARIES} + mcpp ) if (USE_SQLITE3) diff --git a/lib/mcpp/CMakeLists.txt b/lib/mcpp/CMakeLists.txt new file mode 100644 index 000000000..d465edca7 --- /dev/null +++ b/lib/mcpp/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 2.6) +if (UNIX OR MINGW) + add_definitions(-O3) +endif() +add_definitions(-DMCPP_LIB) +add_definitions(-DHAVE_CONFIG_H) +add_library(mcpp STATIC + directive.c + eval.c + expand.c + main.c + main_libmcpp.c + mbchar.c + support.c + system.c +) diff --git a/lib/mcpp/config.h b/lib/mcpp/config.h new file mode 100644 index 000000000..410a5304b --- /dev/null +++ b/lib/mcpp/config.h @@ -0,0 +1,89 @@ +// ********************************************************************** +// +// Copyright (c) 2015 ZeroC, Inc. All rights reserved. +// +// ********************************************************************** + +// Simplified and reduced version of config.h, with support for Windows, +// OS X and Linux. + +#define COMPILER INDEPENDENT + +// Windows support for MSC and MINGW +#if defined(_WIN32) + +#define HOST_COMPILER MSC +#define HOST_SYSTEM SYS_WIN +#define SYSTEM SYS_WIN +#define OBJEXT "obj" + +#elif defined(__APPLE__) + +/* Define if the cases of file name are folded. */ +#define FNAME_FOLD 1 + +/* Define to 1 if the system has the type `intmax_t'. */ +#define HAVE_INTMAX_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if the system has the type `long long'. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the `stpcpy' function. */ +#define HAVE_STPCPY 1 + +/* Define the host compiler. */ +#define HOST_COMPILER GNUC + +/* Define the host system. */ +#define HOST_SYSTEM SYS_MAC + +/* Define printf length modifier for the longest integer. */ +#define LL_FORM "j" + +/* Define the suffix of object file. */ +#define OBJEXT "o" + +/* Define the target system. */ +#define SYSTEM SYS_MAC + +#else + +// Linux. + +/* Define to 1 if the system has the type `intmax_t'. */ +#define HAVE_INTMAX_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if the system has the type `long long'. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the `stpcpy' function. */ +#define HAVE_STPCPY 1 + +/* Define the host compiler. */ +#define HOST_COMPILER GNUC + +/* Define the host system. */ +#define HOST_SYSTEM SYS_LINUX + +/* Define printf length modifier for the longest integer. */ +#define LL_FORM "j" + +/* Define the suffix of object file. */ +#define OBJEXT "o" + +/* Define the target system. */ +#define SYSTEM SYS_LINUX + +#endif diff --git a/lib/mcpp/configed.H b/lib/mcpp/configed.H new file mode 100644 index 000000000..b4d1ebf3a --- /dev/null +++ b/lib/mcpp/configed.H @@ -0,0 +1,382 @@ +/* + * configed.H + * Configurations for MCPP using config.h genarated by configure script. + * + * WARNING: These settings assume HOST == TARGET. In case of HOST + * differs from TARGET, you must edit this file here and there. + */ + +#define TRUE 1 +#define FALSE 0 +#define DATE "2008/11" /* Date of mcpp */ + +/* + * 'Target' means the O.S. and the compiler to which cpp is implemented. + * 'Host' means the O.S. and the compiler with which cpp is compiled. + */ + +#include "config.h" + +#ifndef COMPILER /* No target compiler specified */ +#define COMPILER COMPILER_UNKNOWN +#endif +#ifndef HOST_COMPILER /* No host compiler specified */ +#define HOST_COMPILER COMPILER +#endif + +/* + * P A R T 1 Configurations for target-operating-system + * and target-compiler. + */ + +/* + * Names of the SYSTEM (i.e. target operating system). This is needed so that + * cpp can use appropriate filename conventions. + */ +#define SYS_UNKNOWN 0 +#define SYS_UNIX 0x1000 +#define SYS_LINUX 0x1800 /* (SYS_LINUX & 0xF000) == SYS_UNIX */ +#define SYS_FREEBSD 0x1A00 /* (SYS_FREEBSD & 0xF000) == SYS_UNIX */ +#define SYS_CYGWIN 0x1C00 /* (SYS_CYGWIN & 0xF000) == SYS_UNIX */ +#define SYS_MAC 0x1E00 /* (SYS_MAC & 0xF000) == SYS_UNIX */ +#define SYS_WIN 0x7000 +#define SYS_WIN32 0x7400 /* (SYS_WIN32 & 0xF000) == SYS_WIN */ +#define SYS_MINGW 0x7C00 /* (SYS_MINGW & 0xF000) == SYS_WIN */ + +/* COMPILER */ +#define COMPILER_UNKNOWN 0 +#define MSC 0x7400 /* Microsoft C, Visual C++ */ +#define BORLANDC 0x7440 /* Borland C */ +#define WIN_SYMANTECC 0x7470 /* Symantec for Windows */ +#define LCC 0x74C0 /* LCC-Win32 */ +#define GNUC 0x00E0 /* GNU C (GCC) */ +#define INDEPENDENT 0xFFFF /* No target, compiler-independent-build*/ + +#define SYS_FAMILY (SYSTEM & 0xF000) +#define COMPILER_FAMILY (COMPILER & 0xF0) +#define HOST_SYS_FAMILY (HOST_SYSTEM & 0xF000) + +/* Default MBCHAR (multi-byte character) encoding. */ +#define EUC_JP 0x10 /* Extended UNIX code of JIS X 0208 */ +#define GB2312 0x20 /* EUC-like encoding of Chinese GB 2312-80 */ +#define KSC5601 0x30 /* EUC-like encoding of Korean KS C 5601 */ +#define SJIS 0x80 /* Shift-JIS encoding of JIS X 0208 */ +#define BIGFIVE 0x90 /* Encoding of Taiwanese Big Five */ +#define ISO2022_JP 0x100 /* ISO-2022-JP (ISO-2022-JP1) encoding */ +#define UTF8 0x1000 /* UTF-8 encoding */ + +/* + * MBCHAR means multi-byte character encoding. + * MBCHAR means the default encoding, and you can change the encoding by + * #pragma MCPP setlocale, -e option or environment variable + * LC_ALL, LC_CTYPE, LANG. + * MBCHAR == 0 means not to recognize any multi-byte character encoding. + */ + +/* + * In order to predefine target-dependent macros, + * several macros are defined here: + * *_OLD define the macro beginning with an alphabetic letter, + * *_STD, *_STD?, *_EXT, *_EXT2 define the macro beginning with an '_'. + * *_STD1 define the macro beginning with '__' and ending with an alpha- + * numeric letter. + * *_STD2 define the macro beginning with '__' and ending with '__'. + * These may not be defined, if they are not needed. + * They should not be #defined to no token or to "". + * + * SYSTEM_OLD, SYSTEM_STD1, SYSTEM_STD2, SYSTEM_EXT, SYSTEM_EXT2 + * define the target operating system (by name). + * SYSTEM_SP_OLD, SYSTEM_SP_STD define the target-OS specific macro name + * COMPILER_OLD, COMPILER_STD1, COMPILER_STD2, COMPILER_EXT, COMPILER_EXT2 + * , COMPILER_SP_OLD, COMPILER_SP_STD + * define the target compiler (by name). + * COMPILER_CPLUS defines the target C++ compiler. + * COMPILER_SP1, COMPILER_SP2, COMPILER_SP3 + * define the compiler-specific macros. + * + * _VAL specify the value of the . + * If not specified, these values default to "1". + * To define the value of no-token, specify as "" rather than no-token. + * SYSTEM_OLD, SYSTEM_STD?, COMPILER_OLD have the value of "1". + */ + +/* + * target-compiler-dependent definitions: + * + * LINE_PREFIX defines the output line prefix, if not "#line 123". + * This should be defined as "# " to represent "# 123" format + * ("#line " represents "#line 123" format). + * + * ENV_C_INCLUDE_DIR may be defined to the name of environment-variable for + * C include directory. + * ENV_CPLUS_INCLUDE_DIR is name of environment-variable for C++ include + * directory which exists other than ENV_C_INCLUDE_DIR. + * ENV_SEP is the separator (other than space) of include-paths in an + * environment-variable. e.g. the ':' in + * "/usr/abc/include:/usr/xyz/include" + * + * EMFILE should be defined to the macro to represent errno of 'too many + * open files' if the macro is different from EMFILE. + * + * ONE_PASS should be set TRUE, if COMPILER is "one pass compiler". + * + * FNAME_FOLD means that target-system folds upper and lower cases of + * directory and file-name. + * + * SEARCH_INIT specifies the default value of 'search_rule' (in system.c). + * 'search_rule' holds searching rule of #include "header.h" to + * search first before searching user specified or system- + * specific include directories. + * CURRENT means to search the directory relative to "current + * directory" which is current at cpp invocation. + * SOURCE means to search the directory relative to that of the + * source file (i.e. "includer"). + * (CURRENT & SOURCE) means to search current directory first + * source directory next. + * 'search_rule' is initialized to SEARCH_INIT. + */ +#define CURRENT 1 +#define SOURCE 2 + +#if SYS_FAMILY == SYS_UNIX +#define SYSTEM_OLD "unix" +#define SYSTEM_STD1 "__unix" +#define SYSTEM_STD2 "__unix__" +#endif + +#if SYSTEM == SYS_FREEBSD +#define SYSTEM_EXT "__FreeBSD__" +#endif + +#if SYSTEM == SYS_LINUX +#define SYSTEM_EXT "__linux__" +#endif + +#if SYSTEM == SYS_MAC +#define SYSTEM_EXT "__APPLE__" +#endif + +#if SYSTEM == SYS_CYGWIN +#define SYSTEM_EXT "__CYGWIN__" +#if defined (__CYGWIN64__) +#define SYSTEM_EXT2 "__CYGWIN64__" +#else +#define SYSTEM_EXT2 "__CYGWIN32__" +#endif +#ifndef MBCHAR +#define MBCHAR SJIS +#endif +#endif + +#if SYSTEM == SYS_MINGW +#define SYSTEM_EXT "__MINGW__" +#if defined (__MINGW64__) +#define SYSTEM_EXT2 "__MINGW64__" +#else +#define SYSTEM_EXT2 "__MINGW32__" +#endif +#ifndef MBCHAR +#define MBCHAR SJIS +#endif +#endif + +#if SYS_FAMILY == SYS_UNIX +#ifndef MBCHAR +#define MBCHAR EUC_JP /* UTF8 if you like */ +#endif +#endif + +#if COMPILER == GNUC +#define COMPILER_EXT "__GNUC__" +#define COMPILER_EXT_VAL GCC_MAJOR_VERSION +#define COMPILER_EXT2 "__GNUC_MINOR__" +#define COMPILER_EXT2_VAL GCC_MINOR_VERSION +#define COMPILER_CPLUS "__GNUG__" +#define COMPILER_CPLUS_VAL GCC_MAJOR_VERSION +#ifndef ENV_C_INCLUDE_DIR +#define ENV_C_INCLUDE_DIR "C_INCLUDE_PATH" +#define ENV_CPLUS_INCLUDE_DIR "CPLUS_INCLUDE_PATH" +#endif +/* + * __SIZE_TYPE__, __PTRDIFF_TYPE__ and __WCHAR_TYPE__ are the predefines of + * GCC and undocumented in GCC 2. + * On GCC V.3.*, V.4.*, these macros are known by mcpp_g*_predef_*.h files. + */ +#if __GNUC__ == 2 +#define COMPILER_SP1 "__SIZE_TYPE__" +#define COMPILER_SP2 "__PTRDIFF_TYPE__" +#define COMPILER_SP3 "__WCHAR_TYPE__" +#endif + +#define CMP_NAME "GCC" +#endif /* COMPILER == GNUC */ + +#if COMPILER == INDEPENDENT +/* specifications of compiler-independent-build */ +#define LINE_PREFIX "#line " +#define STD_LINE_PREFIX TRUE /* Output #line by C source format */ +#define HAVE_DIGRAPHS TRUE /* Output digraphs as it is */ +#define SEARCH_INIT SOURCE /* Include directory relative to source */ +#define SJIS_IS_ESCAPE_FREE TRUE /* Do not treat SJIS specially */ +#define BIGFIVE_IS_ESCAPE_FREE TRUE /* Do not treat specially */ +#define ISO2022_JP_IS_ESCAPE_FREE TRUE /* Do not treat specially */ +#define TARGET_HAVE_LONG_LONG TRUE /* dummy */ +#define STDC_VERSION 199409L /* Initial value of __STDC_VERSION__ */ +#endif + +/* + * defaults + */ + +#ifdef SYSTEM_EXT +#ifndef SYSTEM_EXT_VAL +#define SYSTEM_EXT_VAL "1" +#endif +#endif +#ifdef SYSTEM_EXT2 +#ifndef SYSTEM_EXT2_VAL +#define SYSTEM_EXT2_VAL "1" +#endif +#endif +#ifdef COMPILER_STD1 +#ifndef COMPILER_STD1_VAL +#define COMPILER_STD1_VAL "1" +#endif +#endif +#ifdef COMPILER_STD2 +#ifndef COMPILER_STD2_VAL +#define COMPILER_STD2_VAL "1" +#endif +#endif +#ifdef COMPILER_EXT +#ifndef COMPILER_EXT_VAL +#define COMPILER_EXT_VAL "1" +#endif +#endif +#ifdef COMPILER_EXT2 +#ifndef COMPILER_EXT2_VAL +#define COMPILER_EXT2_VAL "1" +#endif +#endif +#ifdef COMPILER_CPLUS +#ifndef COMPILER_CPLUS_VAL +#define COMPILER_CPLUS_VAL "1" +#endif +#endif + +#ifndef ENV_C_INCLUDE_DIR +#define ENV_C_INCLUDE_DIR "INCLUDE" +#endif +#ifndef ENV_CPLUS_INCLUDE_DIR +#define ENV_CPLUS_INCLUDE_DIR "CPLUS_INCLUDE" +#endif + +#ifndef ENV_SEP +#if SYS_FAMILY == SYS_WIN +#define ENV_SEP ';' +#else +#define ENV_SEP ':' +#endif +#endif + +#ifndef ONE_PASS +#define ONE_PASS FALSE +#endif + +/* + * CHARBIT, UCHARMAX are respectively CHAR_BIT, UCHAR_MAX of target compiler. + * CHARBIT should be defined to the number of bits per character. + * It is needed only for processing of multi-byte character constants. + * UCHARMAX should be defined to the maximum value of type unsigned char + * or maximum value of unsigned int which is converted from type (signed) + * char. + * + * LONGMAX should be defined to the LONG_MAX in . + * ULONGMAX should be defined to the ULONG_MAX in . + */ + +/* _POSIX_* only to get PATH_MAX */ +#define _POSIX_ 1 +#define _POSIX_SOURCE 1 +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 1 +#define _POSIX_C_SOURCE_defined 1 +#endif +#include "limits.h" +#undef _POSIX_ +#undef _POSIX_SOURCE +#ifdef _POSIX_C_SOURCE_defined +#undef _POSIX_C_SOURCE +#undef _POSIX_C_SOURCE_defined +#endif +#define CHARBIT CHAR_BIT +#define UCHARMAX UCHAR_MAX +#define USHRTMAX USHRT_MAX +#define LONGMAX LONG_MAX +#define ULONGMAX ULONG_MAX + +/* + * Define MBCHAR (multi-byte character encoding) to SJIS, EUC_JP or other. + */ +#ifndef MBCHAR +#define MBCHAR 0 +#endif + +/* + * SJIS_IS_ESCAPE_FREE means the compiler does not escape '0x5c' ('\\') in + * shift-JIS encoded multi-byte character. SJIS_IS_ESCAPE_FREE == FALSE + * enables cpp to insert * '\\' before '\\' of the 2nd byte of SJIS code in + * literal. This insertion is for the compiler-proper which can't recognize + * SJIS literal. + * BIGFIVE_IS_ESCAPE_FREE means similar case on BIGFIVE encoding. + * ISO2022_JP_IS_ESCAPE_FREE means similar case on ISO2022_JP encoding. + */ +#ifndef SJIS_IS_ESCAPE_FREE +#define SJIS_IS_ESCAPE_FREE FALSE /* or TRUE following your compiler */ +#endif +#ifndef BIGFIVE_IS_ESCAPE_FREE +#define BIGFIVE_IS_ESCAPE_FREE FALSE /* or TRUE following your compiler */ +#endif +#ifndef ISO2022_JP_IS_ESCAPE_FREE +#define ISO2022_JP_IS_ESCAPE_FREE FALSE /* or TRUE following compiler */ +#endif + +/* + * P A R T 2 Configurations for host-compiler. + * + * WARNING: In case of HOST_COMPILER differs from COMPILER, you must + * edit here and there of this part. + */ + +#define HOST_HAVE_STPCPY HAVE_STPCPY + +/* + * Declaration of standard library functions and macros. + */ + +/* stdin, stdout, stderr, FILE, NULL, fgets(), fputs() and other functions. */ +#include "stdio.h" + +/* PATHMAX is the maximum length of path-list on the host system. */ +#ifdef PATH_MAX +#define PATHMAX PATH_MAX /* Posix macro */ +#else +#define PATHMAX FILENAME_MAX +#endif + +/* islower(), isupper(), toupper(), isdigit(), isxdigit(), iscntrl() */ +#include "ctype.h" + +/* errno */ +#include "errno.h" + +#include "string.h" +#include "stdlib.h" +#include "time.h" +#include "setjmp.h" + +/* For debugging malloc systems by kmatsui */ +#if KMMALLOC && _MEM_DEBUG +#include "xalloc.h" +#endif + diff --git a/lib/mcpp/directive.c b/lib/mcpp/directive.c new file mode 100644 index 000000000..b56a3f3b5 --- /dev/null +++ b/lib/mcpp/directive.c @@ -0,0 +1,1699 @@ +/*- + * Copyright (c) 1998, 2002-2008 Kiyoshi Matsui + * All rights reserved. + * + * Some parts of this code are derived from the public domain software + * DECUS cpp (1984,1985) written by Martin Minow. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * 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 AUTHOR 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. + */ + +/* + * D I R E C T I V E . C + * P r o c e s s D i r e c t i v e L i n e s + * + * The routines to handle directives other than #include and #pragma + * are placed here. + */ + +#if PREPROCESSED +#include "mcpp.H" +#else +#include "system.H" +#include "internal.H" +#endif + +static int do_if( int hash, const char * directive_name); + /* #if, #elif, #ifdef, #ifndef */ +static void sync_linenum( void); + /* Synchronize number of newlines */ +static long do_line( void); + /* Process #line directive */ +static int get_parm( void); + /* Get parameters of macro, its nargs, names, lengths */ +static int get_repl( const char * macroname); + /* Get replacement text embedding parameter number */ +static char * is_formal( const char * name, int conv); + /* If formal parameter, save the number */ +static char * def_stringization( char * repl_cur); + /* Define stringization */ +static char * mgtoken_save( const char * macroname); + /* Prefix DEF_MAGIC to macro name in repl-text */ +static char * str_parm_scan( char * string_end); + /* Scan the parameter in quote */ +static void do_undef( void); + /* Process #undef directive */ +static void dump_repl( const DEFBUF * dp, FILE * fp, int gcc2_va); + /* Dump replacement text */ + +/* + * Generate (by hand-inspection) a set of unique values for each directive. + * MCPP won't compile if there are hash conflicts. + */ +#define L_if ('i' ^ (EOS << 1)) +#define L_ifdef ('i' ^ ('d' << 1)) +#define L_ifndef ('i' ^ ('n' << 1)) +#define L_elif ('e' ^ ('i' << 1)) +#define L_else ('e' ^ ('s' << 1)) +#define L_endif ('e' ^ ('d' << 1)) +#define L_define ('d' ^ ('f' << 1)) +#define L_undef ('u' ^ ('d' << 1)) +#define L_line ('l' ^ ('n' << 1)) +#define L_include ('i' ^ ('c' << 1)) +#if COMPILER == GNUC +#define L_include_next ('i' ^ ('c' << 1) ^ ('_' << 1)) +#endif +#if SYSTEM == SYS_MAC +#define L_import ('i' ^ ('p' << 1)) +#endif +#define L_error ('e' ^ ('r' << 1)) +#define L_pragma ('p' ^ ('a' << 1)) + +static const char * const not_ident + = "Not an identifier \"%s\""; /* _E_ */ +static const char * const no_arg = "No argument"; /* _E_ */ +static const char * const excess + = "Excessive token sequence \"%s\""; /* _E_ _W1_ */ + +void directive( void) +/* + * Process #directive lines. Each directive have their own subroutines. + */ +{ + const char * const many_nesting = +"More than %.0s%ld nesting of #if (#ifdef) sections%s"; /* _F_ _W4_ _W8_ */ + const char * const not_in_section + = "Not in a #if (#ifdef) section in a source file"; /* _E_ _W1_ */ + const char * const illeg_dir + = "Illegal #directive \"%s%.0ld%s\""; /* _E_ _W1_ _W8_ */ + const char * const in_skipped = " (in skipped block)"; /* _W8_ */ + FILEINFO * file; + int token_type; + int hash; + int c; + char * tp; + + in_directive = TRUE; + if (keep_comments) { + mcpp_fputc( '\n', OUT); /* Possibly flush out comments */ + newlines--; + } + c = skip_ws(); + if (c == '\n') /* 'null' directive */ + goto ret; + token_type = scan_token( c, (workp = work_buf, &workp), work_end); + if (in_asm && (token_type != NAM + || (! str_eq( identifier, "asm") + && ! str_eq( identifier, "endasm")))) + /* In #asm block, ignore #anything other than #asm or #endasm */ + goto skip_line; + if (token_type != NAM) { + if (mcpp_mode == OLD_PREP && token_type == NUM) { /* # 123 [fname]*/ + strcpy( identifier, "line"); + } else { + if (compiling) { + if (option_flags.lang_asm) { + if (warn_level & 1) + cwarn( illeg_dir, work_buf, 0L, NULL); + } else { + cerror( illeg_dir, work_buf, 0L, NULL); + } + } else if (warn_level & 8) { + cwarn( illeg_dir, work_buf, 0L, in_skipped); + } + goto skip_line; + } + } + hash = (identifier[ 1] == EOS) ? identifier[ 0] + : (identifier[ 0] ^ (identifier[ 2] << 1)); + if (strlen( identifier) > 7) + hash ^= (identifier[ 7] << 1); + + /* hash is set to a unique value corresponding to the directive.*/ + switch (hash) { + case L_if: tp = "if"; break; + case L_ifdef: tp = "ifdef"; break; + case L_ifndef: tp = "ifndef"; break; + case L_elif: tp = "elif"; break; + case L_else: tp = "else"; break; + case L_endif: tp = "endif"; break; + case L_define: tp = "define"; break; + case L_undef: tp = "undef"; break; + case L_line: tp = "line"; break; + case L_include: tp = "include"; break; +#if COMPILER == GNUC + case L_include_next: tp = "include_next"; break; +#endif +#if SYSTEM == SYS_MAC + case L_import: tp = "import"; break; +#endif + case L_error: tp = "error"; break; + case L_pragma: tp = "pragma"; break; + default: tp = NULL; break; + } + + if (tp != NULL && ! str_eq( identifier, tp)) { /* Hash conflict*/ + hash = 0; /* Unknown directive, will */ + tp = NULL; /* be handled by do_old() */ + } + + if (! compiling) { /* Not compiling now */ + switch (hash) { + case L_elif : + if (! standard) { + if (warn_level & 8) + do_old(); /* Unknown directive */ + goto skip_line; /* Skip the line */ + } /* Else fall through */ + case L_else : /* Test the #if's nest, if 0, compile */ + case L_endif: /* Un-nest #if */ + break; + case L_if : /* These can't turn */ + case L_ifdef: /* compilation on, but */ + case L_ifndef : /* we must nest #if's.*/ + if (&ifstack[ BLK_NEST] < ++ifptr) + goto if_nest_err; + if (standard && (warn_level & 8) + && &ifstack[ std_limits.blk_nest + 1] == ifptr) + cwarn( many_nesting, NULL, (long) std_limits.blk_nest + , in_skipped); + ifptr->stat = 0; /* !WAS_COMPILING */ + ifptr->ifline = src_line; /* Line at section start*/ + goto skip_line; + default : /* Other directives */ + if (tp == NULL && (warn_level & 8)) + do_old(); /* Unknown directive ? */ + goto skip_line; /* Skip the line */ + } + } + + macro_line = 0; /* Reset error flag */ + file = infile; /* Remember the current file */ + + switch (hash) { + + case L_if: + case L_ifdef: + case L_ifndef: + if (&ifstack[ BLK_NEST] < ++ifptr) + goto if_nest_err; + if (standard && (warn_level & 4) && + &ifstack[ std_limits.blk_nest + 1] == ifptr) + cwarn( many_nesting, NULL , (long) std_limits.blk_nest, NULL); + ifptr->stat = WAS_COMPILING; + ifptr->ifline = src_line; + goto ifdo; + + case L_elif: + if (! standard) { + do_old(); /* Unrecognized directive */ + break; + } + if (ifptr == &ifstack[0]) + goto nest_err; + if (ifptr == infile->initif) { + goto in_file_nest_err; + } + if (ifptr->stat & ELSE_SEEN) + goto else_seen_err; + if ((ifptr->stat & (WAS_COMPILING | TRUE_SEEN)) != WAS_COMPILING) { + compiling = FALSE; /* Done compiling stuff */ + goto skip_line; /* Skip this group */ + } + hash = L_if; +ifdo: + c = do_if( hash, tp); + if (mcpp_debug & IF) { + mcpp_fprintf( DBG + , "#if (#elif, #ifdef, #ifndef) evaluate to %s.\n" + , compiling ? "TRUE" : "FALSE"); + mcpp_fprintf( DBG, "line %ld: %s", src_line, infile->buffer); + } + if (c == FALSE) { /* Error */ + compiling = FALSE; /* Skip this group */ + goto skip_line; /* Prevent an extra error message */ + } + break; + + case L_else: + if (ifptr == &ifstack[0]) + goto nest_err; + if (ifptr == infile->initif) { + if (standard) + goto in_file_nest_err; + else if (warn_level & 1) + cwarn( not_in_section, NULL, 0L, NULL); + } + if (ifptr->stat & ELSE_SEEN) + goto else_seen_err; + ifptr->stat |= ELSE_SEEN; + ifptr->elseline = src_line; + if (ifptr->stat & WAS_COMPILING) { + if (compiling || (ifptr->stat & TRUE_SEEN) != 0) + compiling = FALSE; + else + compiling = TRUE; + } + if ((mcpp_debug & MACRO_CALL) && (ifptr->stat & WAS_COMPILING)) { + sync_linenum(); + mcpp_fprintf( OUT, "/*else %ld:%c*/\n", src_line + , compiling ? 'T' : 'F'); /* Show that #else is seen */ + } + break; + + case L_endif: + if (ifptr == &ifstack[0]) + goto nest_err; + if (ifptr <= infile->initif) { + if (standard) + goto in_file_nest_err; + else if (warn_level & 1) + cwarn( not_in_section, NULL, 0L, NULL); + } + if (! compiling && (ifptr->stat & WAS_COMPILING)) + wrong_line = TRUE; + compiling = (ifptr->stat & WAS_COMPILING); + if ((mcpp_debug & MACRO_CALL) && compiling) { + sync_linenum(); + mcpp_fprintf( OUT, "/*endif %ld*/\n", src_line); + /* Show that #if block has ended */ + } + --ifptr; + break; + + case L_define: + do_define( FALSE, 0); + break; + + case L_undef: + do_undef(); + break; + + case L_line: + if ((c = do_line()) > 0) { + src_line = c; + sharp( NULL, 0); /* Putout the new line number and file name */ + infile->line = --src_line; /* Next line number is 'src_line' */ + newlines = -1; + } else { /* Error already diagnosed by do_line() */ + skip_nl(); + } + break; + + case L_include: + in_include = TRUE; + if (do_include( FALSE) == TRUE && file != infile) + newlines = -1; /* File has been included. Clear blank lines */ + in_include = FALSE; + break; + + case L_error: + if (! standard) { + do_old(); /* Unrecognized directive */ + break; + } + cerror( infile->buffer, NULL, 0L, NULL); /* _E_ */ + break; + + case L_pragma: + if (! standard) { + do_old(); /* Unrecognized directive */ + break; + } + do_pragma(); + newlines = -1; /* Do not putout excessive '\n' */ + break; + + default: /* Non-Standard or unknown directives */ + do_old(); + break; + } + + switch (hash) { + case L_if : + case L_elif : + case L_define : + case L_line : + goto skip_line; /* To prevent duplicate error message */ +#if COMPILER == GNUC + case L_include_next : + if (file != infile) /* File has been included */ + newlines = -1; +#endif +#if SYSTEM == SYS_MAC + case L_import : + if (file != infile) /* File has been included */ + newlines = -1; +#endif + case L_error : + if (standard) + goto skip_line; + /* Else fall through */ + case L_include : + case L_pragma : + if (standard) + break; /* Already read over the line */ + /* Else fall through */ + default : /* L_else, L_endif, L_undef, etc. */ + if (mcpp_mode == OLD_PREP) { + /* + * Ignore the rest of the #directive line so you can write + * #if foo + * #endif foo + */ + ; + } else if (skip_ws() != '\n') { +#if COMPILER == GNUC + if (standard && hash != L_endif) +#else + if (standard) +#endif + cerror( excess, infile->bptr-1, 0L, NULL); + else if (warn_level & 1) + cwarn( excess, infile->bptr-1, 0L, NULL); + } + skip_nl(); + } + goto ret; + +in_file_nest_err: + cerror( not_in_section, NULL, 0L, NULL); + goto skip_line; +nest_err: + cerror( "Not in a #if (#ifdef) section", NULL, 0L, NULL); /* _E_ */ + goto skip_line; +else_seen_err: + cerror( "Already seen #else at line %.0s%ld" /* _E_ */ + , NULL, ifptr->elseline, NULL); +skip_line: + skip_nl(); /* Ignore rest of line */ + goto ret; + +if_nest_err: + cfatal( many_nesting, NULL, (long) BLK_NEST, NULL); + +ret: + in_directive = FALSE; + keep_comments = option_flags.c && compiling && !no_output; + keep_spaces = option_flags.k && compiling; + /* keep_spaces is on for #define line even if no_output is TRUE */ + if (! wrong_line) + newlines++; +} + +static int do_if( int hash, const char * directive_name) +/* + * Process an #if (#elif), #ifdef or #ifndef. The latter two are straight- + * forward, while #if needs a subroutine to evaluate the expression. + * do_if() is called only if compiling is TRUE. If false, compilation is + * always supressed, so we don't need to evaluate anything. This supresses + * unnecessary warnings. + */ +{ + int c; + int found; + DEFBUF * defp; + + if ((c = skip_ws()) == '\n') { + unget_ch(); + cerror( no_arg, NULL, 0L, NULL); + return FALSE; + } + if (mcpp_debug & MACRO_CALL) { + sync_linenum(); + mcpp_fprintf( OUT, "/*%s %ld*/", directive_name, src_line); + } + if (hash == L_if) { /* #if or #elif */ + unget_ch(); + found = (eval_if() != 0L); /* Evaluate expression */ + if (mcpp_debug & MACRO_CALL) + in_if = FALSE; /* 'in_if' is dynamically set in eval_lex() */ + hash = L_ifdef; /* #if is now like #ifdef */ + } else { /* #ifdef or #ifndef */ + if (scan_token( c, (workp = work_buf, &workp), work_end) != NAM) { + cerror( not_ident, work_buf, 0L, NULL); + return FALSE; /* Next token is not an identifier */ + } + found = ((defp = look_id( identifier)) != NULL); /* Look in table*/ + if (mcpp_debug & MACRO_CALL) { + if (found) + mcpp_fprintf( OUT, "/*%s*/", defp->name); + } + } + if (found == (hash == L_ifdef)) { + compiling = TRUE; + ifptr->stat |= TRUE_SEEN; + } else { + compiling = FALSE; + } + if (mcpp_debug & MACRO_CALL) { + mcpp_fprintf( OUT, "/*i %c*/\n", compiling ? 'T' : 'F'); + /* Report wheather the directive is evaluated TRUE or FALSE */ + } + return TRUE; +} + +static void sync_linenum( void) +/* + * Put out newlines or #line line to synchronize line number with the + * annotations about #if, #elif, #ifdef, #ifndef, #else or #endif on -K option. + */ +{ + if (wrong_line || newlines > 10) { + sharp( NULL, 0); + } else { + while (newlines-- > 0) + mcpp_fputc('\n', OUT); + } + newlines = -1; +} + +static long do_line( void) +/* + * Parse the line to update the line number and "filename" field for the next + * input line. + * Values returned are as follows: + * -1: syntax error or out-of-range error (diagnosed by do_line(), + * eval_num()). + * [1,32767]: legal line number for C90, [1,2147483647] for C99. + * Line number [32768,2147483647] in C90 mode is only warned (not an error). + * do_line() always absorbs the line (except the ). + */ +{ + const char * const not_digits + = "Line number \"%s\" isn't a decimal digits sequence"; /* _E_ _W1_ */ + const char * const out_of_range + = "Line number \"%s\" is out of range of [1,%ld]"; /* _E_ _W1_ */ + int token_type; + VAL_SIGN * valp; + char * save; + int c; + + if ((c = skip_ws()) == '\n') { + cerror( no_arg, NULL, 0L, NULL); + unget_ch(); /* Push back */ + return -1L; /* Line number is not changed */ + } + + if (standard) { + token_type = get_unexpandable( c, FALSE); + if (macro_line == MACRO_ERROR) /* Unterminated macro */ + return -1L; /* already diagnosed. */ + if (token_type == NO_TOKEN) /* Macro expanded to 0 token */ + goto no_num; + if (token_type != NUM) + goto illeg_num; + } else if (scan_token( c, (workp = work_buf, &workp), work_end) != NUM) { + goto illeg_num; + } + for (workp = work_buf; *workp != EOS; workp++) { + if (! isdigit( *workp & UCHARMAX)) { + if (standard) { + cerror( not_digits, work_buf, 0L, NULL); + return -1L; + } else if (warn_level & 1) { + cwarn( not_digits, work_buf, 0L, NULL); + } + } + } + valp = eval_num( work_buf); /* Evaluate number */ + if (valp->sign == VAL_ERROR) { /* Error diagnosed by eval_num()*/ + return -1; + } else if (standard + && (std_limits.line_num < valp->val || valp->val <= 0L)) { + if (valp->val < LINE99LIMIT && valp->val > 0L) { + if (warn_level & 1) + cwarn( out_of_range, work_buf, std_limits.line_num, NULL); + } else { + cerror( out_of_range, work_buf, std_limits.line_num, NULL); + return -1L; + } + } + + if (standard) { + token_type = get_unexpandable( skip_ws(), FALSE); + if (macro_line == MACRO_ERROR) + return -1L; + if (token_type != STR) { + if (token_type == NO_TOKEN) { /* Filename is absent */ + return (long) valp->val; + } else { /* Expanded macro should be a quoted string */ + goto not_fname; + } + } + } else { + if ((c = skip_ws()) == '\n') { + unget_ch(); + return (long) valp->val; + } + if (scan_token( c, (workp = work_buf, &workp), work_end) != STR) + goto not_fname; + } +#if COMPILER == GNUC + if (memcmp( workp - 3, "//", 2) == 0) { /* "/cur-dir//" */ + save = infile->filename; /* Do not change the file name */ + } else +#endif + { + *(workp - 1) = EOS; /* Ignore right '"' */ + save = save_string( &work_buf[ 1]); /* Ignore left '"' */ + } + + if (standard) { + if (get_unexpandable( skip_ws(), FALSE) != NO_TOKEN) { + cerror( excess, work_buf, 0L, NULL); + free( save); + return -1L; + } + } else if (mcpp_mode == OLD_PREP) { + skip_nl(); + unget_ch(); + } else if ((c = skip_ws()) == '\n') { + unget_ch(); + } else { + if (warn_level & 1) { + scan_token( c, (workp = work_buf, &workp), work_end); + cwarn( excess, work_buf, 0, NULL); + } + skip_nl(); + unget_ch(); + } + + if (infile->filename) + free( infile->filename); + infile->filename = save; /* New file name */ + /* Note that this does not change infile->real_fname */ + return (long) valp->val; /* New line number */ + +no_num: + cerror( "No line number", NULL, 0L, NULL); /* _E_ */ + return -1L; +illeg_num: + cerror( "Not a line number \"%s\"", work_buf, 0L, NULL); /* _E_ */ + return -1L; +not_fname: + cerror( "Not a file name \"%s\"", work_buf, 0L, NULL); /* _E_ */ + return -1L; +} + +/* + * M a c r o D e f i n i t i o n s + */ + +/* + * look_id() Looks for the name in the defined symbol table. Returns a + * pointer to the definition if found, or NULL if not present. + * install_macro() Installs the definition. Updates the symbol table. + * undefine() Deletes the definition from the symbol table. + */ + +/* + * Global work_buf[] are used to store #define parameter lists and + * parms[].name point to them. + * 'nargs' contains the actual number of parameters stored. + */ +typedef struct { + char * name; /* -> Start of each parameter */ + size_t len; /* Length of parameter name */ +} PARM; +static PARM parms[ NMACPARS]; +static int nargs; /* Number of parameters */ +static char * token_p; /* Pointer to the token scanned */ +static char * repl_base; /* Base of buffer for repl-text */ +static char * repl_end; /* End of buffer for repl-text */ +static const char * const no_ident = "No identifier"; /* _E_ */ +#if COMPILER == GNUC +static int gcc2_va_arg; /* GCC2-spec variadic macro */ +#endif + +DEFBUF * do_define( + int ignore_redef, /* Do not redefine */ + int predefine /* Predefine compiler-specific name */ + /* + * Note: The value of 'predefine' should be one of 0, DEF_NOARGS_PREDEF + * or DEF_NOARGS_PREDEF_OLD, the other values cause errors. + */ +) +/* + * Called from directive() when a #define is scanned or called from + * do_options() when a -D option is scanned. This module parses formal + * parameters by get_parm() and the replacement text by get_repl(). + * + * There is some special case code to distinguish + * #define foo bar -- object-like macro + * from #define foo() bar -- function-like macro with no parameter + * + * Also, we make sure that + * #define foo foo + * expands to "foo" but doesn't put MCPP into an infinite loop. + * + * A warning is printed if you redefine a symbol with a non-identical + * text. I.e, + * #define foo 123 + * #define foo 123 + * is ok, but + * #define foo 123 + * #define foo +123 + * is not. + * + * The following subroutines are called from do_define(): + * get_parm() parsing and remembering parameter names. + * get_repl() parsing and remembering replacement text. + * + * The following subroutines are called from get_repl(): + * is_formal() is called when an identifier is scanned. It checks through + * the array of formal parameters. If a match is found, the + * identifier is replaced by a control byte which will be used + * to locate the parameter when the macro is expanded. + * def_stringization() is called when '#' operator is scanned. It surrounds + * the token to stringize with magic-codes. + * + * modes other than STD ignore difference of parameter names in macro + * redefinition. + */ +{ + const char * const predef = "\"%s\" shouldn't be redefined"; /* _E_ */ + char repl_list[ NMACWORK + IDMAX]; /* Replacement text */ + char macroname[ IDMAX + 1]; /* Name of the macro defining */ + DEFBUF * defp; /* -> Old definition */ + DEFBUF ** prevp; /* -> Pointer to previous def in list */ + int c; + int redefined; /* TRUE if redefined */ + int dnargs = 0; /* defp->nargs */ + int cmp; /* Result of name comparison */ + size_t def_start, def_end; /* Column of macro definition */ + + repl_base = repl_list; + repl_end = & repl_list[ NMACWORK]; + c = skip_ws(); + if ((mcpp_debug & MACRO_CALL) && src_line) /* Start of definition */ + def_start = infile->bptr - infile->buffer - 1; + if (c == '\n') { + cerror( no_ident, NULL, 0L, NULL); + unget_ch(); + return NULL; + } else if (scan_token( c, (workp = work_buf, &workp), work_end) != NAM) { + cerror( not_ident, work_buf, 0L, NULL); + return NULL; + } else { + prevp = look_prev( identifier, &cmp); + /* Find place in the macro list to insert the definition */ + defp = *prevp; + if (standard) { + if (cmp || defp->push) { /* Not known or 'pushed' macro */ + if (str_eq( identifier, "defined") + || ((stdc_val || cplus_val) + && str_eq( identifier, "__VA_ARGS__"))) { + cerror( + "\"%s\" shouldn't be defined", identifier, 0L, NULL); /* _E_ */ + return NULL; + } + redefined = FALSE; /* Quite new definition */ + } else { /* It's known: */ + if (ignore_redef) + return defp; + dnargs = (defp->nargs == DEF_NOARGS_STANDARD + || defp->nargs == DEF_NOARGS_PREDEF + || defp->nargs == DEF_NOARGS_PREDEF_OLD) + ? DEF_NOARGS : defp->nargs; + if (dnargs <= DEF_NOARGS_DYNAMIC /* __FILE__ and such */ + || dnargs == DEF_PRAGMA /* _Pragma() pseudo-macro */ + ) { + cerror( predef, identifier, 0L, NULL); + return NULL; + } else { + redefined = TRUE; /* Remember this fact */ + } + } + } else { + if (cmp) { + redefined = FALSE; /* Quite new definition */ + } else { /* It's known: */ + if (ignore_redef) + return defp; + dnargs = (defp->nargs == DEF_NOARGS_STANDARD + || defp->nargs == DEF_NOARGS_PREDEF + || defp->nargs == DEF_NOARGS_PREDEF_OLD) + ? DEF_NOARGS : defp->nargs; + redefined = TRUE; + } + } + } + strcpy( macroname, identifier); /* Remember the name */ + + in_define = TRUE; /* Recognize '#', '##' */ + if (get_parm() == FALSE) { /* Get parameter list */ + in_define = FALSE; + return NULL; /* Syntax error */ + } + if (get_repl( macroname) == FALSE) { /* Get replacement text */ + in_define = FALSE; + return NULL; /* Syntax error */ + } + if ((mcpp_debug & MACRO_CALL) && src_line) { + /* Remember location on source */ + char * cp; + cp = infile->bptr - 1; /* Before '\n' */ + while (char_type[ *cp & UCHARMAX] & HSP) + cp--; /* Trailing space */ + cp++; /* Just after the last token */ + def_end = cp - infile->buffer; /* End of definition */ + } + + in_define = FALSE; + if (redefined) { + if (dnargs != nargs || ! str_eq( defp->repl, repl_list) + || (mcpp_mode == STD && ! str_eq( defp->parmnames, work_buf)) + ) { /* Warn if differently redefined */ + if (warn_level & 1) { + cwarn( + "The macro is redefined", NULL, 0L, NULL); /* _W1_ */ + if (! option_flags.no_source_line) + dump_a_def( " previously macro", defp, FALSE, TRUE + , fp_err); + } + } else { /* Identical redefinition */ + return defp; + } + } /* Else new or re-definition*/ + defp = install_macro( macroname, nargs, work_buf, repl_list, prevp, cmp + , predefine); + if ((mcpp_debug & MACRO_CALL) && src_line) { + /* Get location on source file */ + LINE_COL s_line_col, e_line_col; + s_line_col.line = src_line; + s_line_col.col = def_start; + get_src_location( & s_line_col); + /* Convert to pre-line-splicing data */ + e_line_col.line = src_line; + e_line_col.col = def_end; + get_src_location( & e_line_col); + /* Putout the macro definition information embedded in comment */ + mcpp_fprintf( OUT, "/*m%s %ld:%d-%ld:%d*/\n", defp->name + , s_line_col.line, s_line_col.col + , e_line_col.line, e_line_col.col); + wrong_line = TRUE; /* Need #line later */ + } + if (mcpp_mode == STD && cplus_val && id_operator( macroname) + && (warn_level & 1)) + /* These are operators, not identifiers, in C++98 */ + cwarn( "\"%s\" is defined as macro", macroname /* _W1_ */ + , 0L, NULL); + return defp; +} + +static int get_parm( void) +/* + * Get parameters i.e. numbers into nargs, name into work_buf[], name-length + * into parms[].len. parms[].name point into work_buf. + * Return TRUE if the parameters are legal, else return FALSE. + * In STD mode preprocessor must remember the parameter names, only for + * checking the validity of macro redefinitions. This is required by the + * Standard (what an overhead !). + */ +{ + const char * const many_parms + = "More than %.0s%ld parameters"; /* _E_ _W4_ */ + const char * const illeg_parm + = "Illegal parameter \"%s\""; /* _E_ */ + const char * const misplaced_ellip + = "\"...\" isn't the last parameter"; /* _E_ */ + int token_type; + int c; + + parms[ 0].name = workp = work_buf; + work_buf[ 0] = EOS; +#if COMPILER == GNUC + gcc2_va_arg = FALSE; +#endif + + /* POST_STD mode */ + insert_sep = NO_SEP; /* Clear the inserted token separator */ + c = get_ch(); + + if (c == '(') { /* With arguments? */ + nargs = 0; /* Init parms counter */ + if (skip_ws() == ')') + return TRUE; /* Macro with 0 parm */ + else + unget_ch(); + + do { /* Collect parameters */ + if (nargs >= NMACPARS) { + cerror( many_parms, NULL, (long) NMACPARS, NULL); + return FALSE; + } + parms[ nargs].name = workp; /* Save its start */ + if ((token_type = scan_token( c = skip_ws(), &workp, work_end)) + != NAM) { + if (c == '\n') { + break; + } else if (c == ',' || c == ')') { + cerror( "Empty parameter", NULL, 0L, NULL); /* _E_ */ + return FALSE; + } else if (standard && (stdc_val || cplus_val) + && token_type == OPE && openum == OP_ELL) { + /* + * Enable variable argument macro which is a feature of + * C99. We enable this even on C90 or C++ for GCC + * compatibility. + */ + if (skip_ws() != ')') { + cerror( misplaced_ellip, NULL, 0L, NULL); + return FALSE; + } + parms[ nargs++].len = 3; + nargs |= VA_ARGS; + goto ret; + } else { + cerror( illeg_parm, parms[ nargs].name, 0L, NULL); + return FALSE; /* Bad parameter syntax */ + } + } + if (standard && (stdc_val || cplus_val) + && str_eq( identifier, "__VA_ARGS__")) { + cerror( illeg_parm, parms[ nargs].name, 0L, NULL); + return FALSE; + /* __VA_ARGS__ should not be used as a parameter */ + } + if (is_formal( parms[ nargs].name, FALSE)) { + cerror( "Duplicate parameter name \"%s\"" /* _E_ */ + , parms[ nargs].name, 0L, NULL); + return FALSE; + } + parms[ nargs].len = (size_t) (workp - parms[ nargs].name); + /* Save length of param */ + *workp++ = ','; + nargs++; + } while ((c = skip_ws()) == ','); /* Get another parameter*/ + + *--workp = EOS; /* Remove excessive ',' */ + if (c != ')') { /* Must end at ) */ +#if COMPILER == GNUC + /* Handle GCC2 variadic params like par... */ + char * tp = workp; + if (mcpp_mode == STD + &&(token_type = scan_token( c, &workp, work_end)) == OPE + && openum == OP_ELL) { + if ((c = skip_ws()) != ')') { + cerror( misplaced_ellip, NULL, 0L, NULL); + return FALSE; + } + *tp = EOS; /* Remove "..." */ + nargs |= VA_ARGS; + gcc2_va_arg = TRUE; + goto ret; + } +#endif + unget_ch(); /* Push back '\n' */ + cerror( + "Missing \",\" or \")\" in parameter list \"(%s\"" /* _E_ */ + , work_buf, 0L, NULL); + return FALSE; + } + } else { + /* + * DEF_NOARGS is needed to distinguish between + * "#define foo" and "#define foo()". + */ + nargs = DEF_NOARGS; /* Object-like macro */ + unget_ch(); + } +ret: +#if NMACPARS > NMACPARS90MIN + if ((warn_level & 4) && (nargs & ~AVA_ARGS) > std_limits.n_mac_pars) + cwarn( many_parms, NULL , (long) std_limits.n_mac_pars, NULL); +#endif + return TRUE; +} + +static int get_repl( + const char * macroname +) +/* + * Get replacement text i.e. names of formal parameters are converted to + * the magic numbers, and operators #, ## is converted to magic characters. + * Return TRUE if replacement list is legal, else return FALSE. + * Any token separator in the text is converted to a single space, no token + * sepatator is inserted by MCPP. Those are required by the Standard for + * stringizing of an argument by # operator. + * In POST_STD mode, inserts a space between any tokens in source (except a + * macro name and the next '(' in macro definition), hence presence or absence + * of token separator makes no difference. + */ +{ + const char * const mixed_ops + = "Macro with mixing of ## and # operators isn't portable"; /* _W4_ */ + const char * const multiple_cats + = "Macro with multiple ## operators isn't portable"; /* _W4_ */ + char * prev_token = NULL; /* Preceding token */ + char * prev_prev_token = NULL; /* Pre-preceding token */ + int multi_cats = FALSE; /* Multiple ## operators*/ + int c; + int token_type; /* Type of token */ + char * temp; + char * repl_cur = repl_base; /* Pointer into repl-text buffer*/ + + *repl_cur = EOS; + token_p = NULL; + if (mcpp_mode == STD) { + c = get_ch(); + unget_ch(); + if (((char_type[ c] & SPA) == 0) && (nargs < 0) && (warn_level & 1)) + cwarn( "No space between macro name \"%s\" and repl-text"/* _W1_ */ + , macroname, 0L, NULL); + } + c = skip_ws(); /* Get to the body */ + + while (c != '\n') { + if (standard) { + prev_prev_token = prev_token; + prev_token = token_p; + } + token_p = repl_cur; /* Remember the pointer */ + token_type = scan_token( c, &repl_cur, repl_end); + + switch (token_type) { + case OPE: /* Operator or punctuator */ + if (! standard) + break; + switch (openum) { + case OP_CAT: /* ## */ + if (prev_token == NULL) { + cerror( "No token before ##" /* _E_ */ + , NULL, 0L, NULL); + return FALSE; + } else if (*prev_token == CAT) { + cerror( "## after ##", NULL, 0L, NULL); /* _E_ */ + return FALSE; + } else if (prev_prev_token && *prev_prev_token == CAT) { + multi_cats = TRUE; + } else if (prev_prev_token && *prev_prev_token == ST_QUOTE + && (warn_level & 4)) { /* # parm ## */ + cwarn( mixed_ops, NULL, 0L, NULL); + } + repl_cur = token_p; + *repl_cur++ = CAT; /* Convert to CAT */ + break; + case OP_STR: /* # */ + if (nargs < 0) /* In object-like macro */ + break; /* '#' is an usual char */ + if (prev_token && *prev_token == CAT + && (warn_level & 4)) /* ## # */ + cwarn( mixed_ops, NULL, 0L, NULL); + repl_cur = token_p; /* Overwrite on # */ + if ((temp = def_stringization( repl_cur)) == NULL) { + return FALSE; /* Error */ + } else { + repl_cur = temp; + } + break; + default: /* Any operator as it is */ + break; + } + break; + case NAM: + /* + * Replace this name if it's a parm. Note that the macro name is a + * possible replacement token. We stuff DEF_MAGIC in front of the + * token which is treated as a LETTER by the token scanner and eaten + * by the macro expanding routine. This prevents the macro expander + * from looping if someone writes "#define foo foo". + */ + temp = is_formal( identifier, TRUE); + if (temp == NULL) { /* Not a parameter name */ + if (! standard) + break; + if ((stdc_val || cplus_val) + && str_eq( identifier, "__VA_ARGS__")) { +#if COMPILER == GNUC + if (gcc2_va_arg) { + cerror( "\"%s\" cannot be used in GCC2-spec variadic macro" /* _E_ */ + , identifier, 0L, NULL); + return FALSE; + } +#endif + cerror( "\"%s\" without corresponding \"...\"" /* _E_ */ + , identifier, 0L, NULL); + return FALSE; + } + if ((temp = mgtoken_save( macroname)) != NULL) + repl_cur = temp; /* Macro name */ + } else { /* Parameter name */ + repl_cur = temp; +#if COMPILER == GNUC + if (mcpp_mode == STD && (nargs & VA_ARGS) + && *(repl_cur - 1) == (nargs & ~AVA_ARGS)) { + if (! str_eq( identifier, "__VA_ARGS__") + && (warn_level & 2)) + cwarn( + "GCC2-spec variadic macro is defined" /* _W2_ */ + , NULL, 0L, NULL); + if (prev_token && *prev_token == CAT + && prev_prev_token && *prev_prev_token == ',') + /* ", ## __VA_ARGS__" is sequence peculiar */ + /* to GCC3-spec variadic macro. */ + /* Or ", ## last_arg" is sequence peculiar */ + /* to GCC2-spec variadic macro. */ + nargs |= GVA_ARGS; + /* Mark as sequence peculiar to GCC */ + /* This will be warned at expansion time */ + } +#endif + } + break; + + case STR: /* String in mac. body */ + case CHR: /* Character constant */ + if (mcpp_mode == OLD_PREP) + repl_cur = str_parm_scan( repl_cur); + break; + case SEP: + if (mcpp_mode == OLD_PREP && c == COM_SEP) + repl_cur--; /* Skip comment now */ + break; + default: /* Any token as it is */ + break; + } + + if ((c = get_ch()) == ' ' || c == '\t') { + *repl_cur++ = ' '; /* Space */ + while ((c = get_ch()) == ' ' || c == '\t') + ; /* Skip excessive spaces */ + } + } + + while (repl_base < repl_cur + && (*(repl_cur - 1) == ' ' || *(repl_cur - 1) == '\t')) + repl_cur--; /* Remove trailing spaces */ + *repl_cur = EOS; /* Terminate work */ + + unget_ch(); /* For syntax check */ + if (standard) { + if (token_p && *token_p == CAT) { + cerror( "No token after ##", NULL, 0L, NULL); /* _E_ */ + return FALSE; + } + if (multi_cats && (warn_level & 4)) + cwarn( multiple_cats, NULL, 0L, NULL); + if ((nargs & VA_ARGS) && stdc_ver < 199901L && (warn_level & 2)) + /* Variable arg macro is the spec of C99, not C90 nor C++98 */ + cwarn( "Variable argument macro is defined", /* _W2_ */ + NULL, 0L, NULL); + } + + return TRUE; +} + +static char * is_formal( + const char * name, + int conv /* Convert to magic number? */ +) +/* + * If the identifier is a formal parameter, save the MAC_PARM and formal + * offset, returning the advanced pointer into the replacement text. + * Else, return NULL. + */ +{ + char * repl_cur; + const char * va_arg = "__VA_ARGS__"; + PARM parm; + size_t len; + int i; + + len = strlen( name); + for (i = 0; i < (nargs & ~AVA_ARGS); i++) { /* For each parameter */ + parm = parms[ i]; + if ((len == parm.len + /* Note: parms[].name are comma separated */ + && memcmp( name, parm.name, parm.len) == 0) + || (standard && (nargs & VA_ARGS) + && i == (nargs & ~AVA_ARGS) - 1 && conv + && str_eq( name, va_arg))) { /* __VA_ARGS__ */ + /* If it's known */ +#if COMPILER == GNUC + if (gcc2_va_arg && str_eq( name, va_arg)) + return NULL; /* GCC2 variadic macro */ +#endif + if (conv) { + repl_cur = token_p; /* Overwrite on the name*/ + *repl_cur++ = MAC_PARM; /* Save the signal */ + *repl_cur++ = i + 1; /* Save the parm number */ + return repl_cur; /* Return "gotcha" */ + } else { + return parm.name; /* Duplicate parm name */ + } + } + } + + return NULL; /* Not a formal param */ +} + +static char * def_stringization( char * repl_cur) +/* + * Define token stringization. + * We store a magic cookie (which becomes surrouding " on expansion) preceding + * the parameter as an operand of # operator. + * Return the current pointer into replacement text if the token following # + * is a parameter name, else return NULL. + */ +{ + int c; + char * temp; + + *repl_cur++ = ST_QUOTE; /* Prefix */ + if (char_type[ c = get_ch()] & HSP) { /* There is a space */ + *repl_cur++ = ' '; + while (char_type[ c = get_ch()] & HSP) /* Skip excessive spaces*/ + ; + } + token_p = repl_cur; /* Remember the pointer */ + if (scan_token( c, &repl_cur, repl_end) == NAM) { + if ((temp = is_formal( identifier, TRUE)) != NULL) { + repl_cur = temp; + return repl_cur; + } + } + cerror( "Not a formal parameter \"%s\"", token_p, 0L, NULL); /* _E_ */ + return NULL; +} + +static char * mgtoken_save( const char * macroname) +/* + * A magic cookie is inserted if the token is identical to the macro name, + * so the expansion doesn't recurse. + * Return the advanced pointer into the replacement text or NULL. + */ +{ + char * repl_cur; + + if (str_eq( macroname, identifier)) { /* Macro name in body */ + repl_cur = token_p; /* Overwrite on token */ + *repl_cur++ = DEF_MAGIC; /* Save magic marker */ + repl_cur = stpcpy( repl_cur, identifier); + /* And save the token */ + return repl_cur; + } else { + return NULL; + } +} + +static char * str_parm_scan( char * string_end) +/* + * String parameter scan. + * This code -- if enabled -- recognizes a formal parameter in a string + * literal or in a character constant. + * #define foo(bar, v) printf("%bar\n", v) + * foo( d, i) + * expands to: + * printf("%d\n", i) + * str_parm_scan() return the advanced pointer into the replacement text. + * This has been superceded by # stringizing and string concatenation. + * This routine is called only in OLD_PREP mode. + */ +{ + int delim; + int c; + char * tp; + char * wp; /* Pointer into the quoted literal */ + + delim = *token_p; + unget_string( ++token_p, NULL); + /* Pseudo-token-parsing in a string literal */ + wp = token_p; + while ((c = get_ch()) != delim) { + token_p = wp; + if (scan_token( c, &wp, string_end) != NAM) + continue; + if ((tp = is_formal( token_p, TRUE)) != NULL) + wp = tp; + } + *wp++ = delim; + return wp; +} + +static void do_undef( void) +/* + * Remove the symbol from the defined list. + * Called from directive(). + */ +{ + DEFBUF * defp; + int c; + + if ((c = skip_ws()) == '\n') { + cerror( no_ident, NULL, 0L, NULL); + unget_ch(); + return; + } + if (scan_token( c, (workp = work_buf, &workp), work_end) != NAM) { + cerror( not_ident, work_buf, 0L, NULL); + skip_nl(); + unget_ch(); + } else { + if ((defp = look_id( identifier)) == NULL) { + if (warn_level & 8) + cwarn( "\"%s\" wasn't defined" /* _W8_ */ + , identifier, 0L, NULL); + } else if (standard && (defp->nargs <= DEF_NOARGS_STANDARD + /* Standard predef */ + || defp->nargs == DEF_PRAGMA)) { + /* _Pragma() pseudo-macro */ + cerror( "\"%s\" shouldn't be undefined" /* _E_ */ + , identifier, 0L, NULL); + } else if (standard) { + c = skip_ws(); + unget_ch(); + if (c != '\n') /* Trailing junk */ + return; + else + undefine( identifier); + } else { + undefine( identifier); + } + } +} + +/* + * C P P S y m b o l T a b l e s + * + * SBSIZE defines the number of hash-table slots for the symbol table. + * It must be a power of 2. + */ + +/* Symbol table queue headers. */ +static DEFBUF * symtab[ SBSIZE]; +static long num_of_macro = 0; + +#if MCPP_LIB +void init_directive( void) +/* Initialize static variables. */ +{ + num_of_macro = 0; +} +#endif + +DEFBUF * look_id( const char * name) +/* + * Look for the identifier in the symbol table. + * If found, return the table pointer; Else return NULL. + */ +{ + DEFBUF ** prevp; + int cmp; + + prevp = look_prev( name, &cmp); + + if (standard) + return ((cmp == 0 && (*prevp)->push == 0) ? *prevp : NULL); + else + return ((cmp == 0) ? *prevp : NULL); +} + +DEFBUF ** look_prev( + const char * name, /* Name of the macro */ + int * cmp /* Result of comparison */ +) +/* + * Look for the place to insert the macro definition. + * Return a pointer to the previous member in the linked list. + */ +{ + const char * np; + DEFBUF ** prevp; + DEFBUF * dp; + size_t s_name; + int hash; + + for (hash = 0, np = name; *np != EOS; ) + hash += *np++; + hash += s_name = (size_t)(np - name); + s_name++; + prevp = & symtab[ hash & SBMASK]; + *cmp = -1; /* Initialize */ + + while ((dp = *prevp) != NULL) { + if ((*cmp = memcmp( dp->name, name, s_name)) >= 0) + break; + prevp = &dp->link; + } + + return prevp; +} + +DEFBUF * look_and_install( + const char * name, /* Name of the macro */ + int numargs, /* The numbers of parms */ + const char * parmnames, /* Names of parameters concatenated */ + const char * repl /* Replacement text */ +) +/* + * Look for the name and (re)define it. + * Returns a pointer to the definition block. + * Returns NULL if the symbol was Standard-predefined. + */ +{ + DEFBUF ** prevp; /* Place to insert definition */ + DEFBUF * defp; /* New definition block */ + int cmp; /* Result of comparison of new name and old */ + + prevp = look_prev( name, &cmp); + defp = install_macro( name, numargs, parmnames, repl, prevp, cmp, 0); + return defp; +} + +DEFBUF * install_macro( + const char * name, /* Name of the macro */ + int numargs, /* The numbers of parms */ + const char * parmnames, /* Names of parameters concatenated */ + const char * repl, /* Replacement text */ + DEFBUF ** prevp, /* The place to insert definition */ + int cmp, /* Result of comparison of new name and old */ + int predefine /* Predefined macro without leading '_' */ +) +/* + * Enter this name in the lookup table. + * Returns a pointer to the definition block. + * Returns NULL if the symbol was Standard-predefined. + * Note that predefinedness can be specified by either of 'numargs' or + * 'predefine'. + */ +{ + DEFBUF * dp; + DEFBUF * defp; + size_t s_name, s_parmnames, s_repl; + + defp = *prevp; /* Old definition, if cmp == 0 */ + if (cmp == 0 && defp->nargs < DEF_NOARGS - 1) + return NULL; /* Standard predefined */ + if (parmnames == NULL || repl == NULL || (predefine && numargs > 0) + || (predefine && predefine != DEF_NOARGS_PREDEF + && predefine != DEF_NOARGS_PREDEF_OLD)) + /* Shouldn't happen */ + cfatal( "Bug: Illegal macro installation of \"%s\"" /* _F_ */ + , name, 0L, NULL); /* Use "" instead of NULL */ + s_name = strlen( name); + if (mcpp_mode == STD) + s_parmnames = strlen( parmnames) + 1; + else + s_parmnames = 0; + s_repl = strlen( repl) + 1; + dp = (DEFBUF *) + xmalloc( sizeof (DEFBUF) + s_name + s_parmnames + s_repl); + if (cmp || (standard && (*prevp)->push)) { /* New definition */ + dp->link = defp; /* Insert to linked list */ + *prevp = dp; + } else { /* Redefinition */ + dp->link = defp->link; /* Replace old def with new */ + *prevp = dp; + free( defp); + } + dp->nargs = predefine ? predefine : numargs; + if (standard) { + dp->push = 0; + dp->parmnames = (char *)dp + sizeof (DEFBUF) + s_name; + dp->repl = dp->parmnames + s_parmnames; + if (mcpp_mode == STD) + memcpy( dp->parmnames, parmnames, s_parmnames); + } else { + dp->repl = (char *)dp + sizeof (DEFBUF) + s_name; + } + memcpy( dp->name, name, s_name + 1); + memcpy( dp->repl, repl, s_repl); + /* Remember where the macro is defined */ + dp->fname = cur_fullname; /* Full-path-list of current file */ + dp->mline = src_line; + if (standard && cmp && ++num_of_macro == std_limits.n_macro + 1 + && std_limits.n_macro && (warn_level & 4)) + /* '&& std_limits.n_macro' to avoid warning before initialization */ + cwarn( "More than %.0s%ld macros defined" /* _W4_ */ + , NULL , std_limits.n_macro, NULL); + return dp; +} + +int undefine( + const char * name /* Name of the macro */ +) +/* + * Delete the macro definition from the symbol table. + * Returns TRUE, if deleted; + * Else returns FALSE (when the macro was not defined or was Standard + * predefined). + */ +{ + DEFBUF ** prevp; /* Preceding definition in list */ + DEFBUF * dp; /* Definition to delete */ + int cmp; /* 0 if defined, else not defined */ + + prevp = look_prev( name, &cmp); + dp = *prevp; /* Definition to delete */ + if (cmp || dp->nargs <= DEF_NOARGS_STANDARD) + return FALSE; /* Not defined or Standard predefined */ + if (standard && dp->push) + return FALSE; /* 'Pushed' macro */ + *prevp = dp->link; /* Link the previous and the next */ + if ((mcpp_debug & MACRO_CALL) && dp->mline) { + /* Notice this directive unless the macro is predefined */ + mcpp_fprintf( OUT, "/*undef %ld*//*%s*/\n", src_line, dp->name); + wrong_line = TRUE; + } + free( dp); /* Delete the definition */ + if (standard) + num_of_macro--; + return TRUE; +} + +static void dump_repl( + const DEFBUF * dp, + FILE * fp, + int gcc2_va +) +/* + * Dump replacement text. + */ +{ + int numargs = dp->nargs; + char * cp1; + size_t i; + int c; + const char * cp; + + for (cp = dp->repl; (c = *cp++ & UCHARMAX) != EOS; ) { + + switch (c) { + case MAC_PARM: /* Parameter */ + c = (*cp++ & UCHARMAX) - 1; + if (standard) { + PARM parm = parms[ c]; + if ((numargs & VA_ARGS) && c == (numargs & ~AVA_ARGS) - 1) { + mcpp_fputs( gcc2_va ? parm.name : "__VA_ARGS__" + , FP2DEST( fp)); + /* gcc2_va is possible only in STD mode */ + } else { + if (mcpp_mode == STD) { + for (i = 0, cp1 = parm.name; i < parm.len; i++) + mcpp_fputc( *cp1++, FP2DEST( fp)); + } else { + mcpp_fputc( 'a' + c % 26, FP2DEST( fp)); + if (c > 26) + mcpp_fputc( '0' + c / 26, FP2DEST( fp)); + } + } + } else { + mcpp_fputc( 'a' + c % 26, FP2DEST( fp)); + if (c > 26) + mcpp_fputc( '0' + c / 26, FP2DEST( fp)); + } + break; + case DEF_MAGIC: + if (! standard) + mcpp_fputc( c, FP2DEST( fp)); + /* Else skip */ + break; + case CAT: + if (standard) + mcpp_fputs( "##", FP2DEST( fp)); + else + mcpp_fputc( c, FP2DEST( fp)); + break; + case ST_QUOTE: + if (standard) + mcpp_fputs( "#", FP2DEST( fp)); + else + mcpp_fputc( c, FP2DEST( fp)); + break; + case COM_SEP: + /* + * Though TOK_SEP coincides to COM_SEP, this cannot appear in + * Standard mode. + */ + if (mcpp_mode == OLD_PREP) + mcpp_fputs( "/**/", FP2DEST( fp)); + break; + default: + mcpp_fputc( c, FP2DEST( fp)); + break; + } + } +} + +/* + * If the compiler is so-called "one-pass" compiler, compiler-predefined + * macros are commented out to avoid redefinition. + */ +#if ONE_PASS +#define CAN_REDEF DEF_NOARGS +#else +#define CAN_REDEF DEF_NOARGS_PREDEF +#endif + +void dump_a_def( + const char * why, + const DEFBUF * dp, + int newdef, /* TRUE if parmnames are currently in parms[] */ + int comment, /* Show location of the definition in comment */ + FILE * fp +) +/* + * Dump a macro definition. + */ +{ + char * cp, * cp1; + int numargs = dp->nargs & ~AVA_ARGS; + int commented; /* To be commented out */ + int gcc2_va = FALSE; /* GCC2-spec variadic */ + int i; + + if (standard && numargs == DEF_PRAGMA) /* _Pragma pseudo-macro */ + return; + if ((numargs < CAN_REDEF) || (standard && dp->push)) + commented = TRUE; + else + commented = FALSE; + if (! comment && commented) /* For -dM option */ + return; + if (why) + mcpp_fprintf( FP2DEST( fp), "%s \"%s\" defined as: ", why, dp->name); + mcpp_fprintf( FP2DEST( fp), "%s#define %s", commented ? "/* " : "", + dp->name); /* Macro name */ + if (numargs >= 0) { /* Parameter list */ + if (mcpp_mode == STD) { + const char * appendix = null; + if (! newdef) { + /* Make parms[] for dump_repl() */ + for (i = 0, cp = dp->parmnames; i < numargs; + i++, cp = cp1 + 1) { + if ((cp1 = strchr( cp, ',')) == NULL) /* The last arg */ + parms[ i].len = strlen( cp); + else + parms[ i].len = (size_t) (cp1 - cp); + parms[ i].name = cp; + } + } +#if COMPILER == GNUC + if ((dp->nargs & VA_ARGS) + && memcmp( parms[ numargs - 1].name, "...", 3) != 0) { + appendix = "..."; /* Append ... so as to become 'args...' */ + gcc2_va = TRUE; + } +#endif + mcpp_fprintf( FP2DEST( fp), "(%s%s)", dp->parmnames, appendix); + } else { + if (newdef) { + mcpp_fprintf( FP2DEST( fp), "(%s)", parms[ 0].name); + } else if (numargs == 0) { + mcpp_fputs( "()", FP2DEST( fp)); + } else { + /* Print parameter list automatically made as: */ + /* a, b, c, ..., a0, b0, c0, ..., a1, b1, c1, ... */ + mcpp_fputc( '(', FP2DEST( fp)); + for (i = 0; i < numargs; i++) { /* Make parameter list */ + mcpp_fputc( 'a' + i % 26, FP2DEST( fp)); + if (i >= 26) + mcpp_fputc( '0' + i / 26, FP2DEST( fp)); + if (i + 1 < numargs) + mcpp_fputc( ',', FP2DEST( fp)); + } + mcpp_fputc( ')', FP2DEST( fp)); + } + } + } + if (*dp->repl) { + mcpp_fputc( ' ', FP2DEST( fp)); + dump_repl( dp, fp, gcc2_va); /* Replacement text */ + } + if (commented) + /* Standard predefined or one-pass-compiler-predefined */ + mcpp_fputs( " */", FP2DEST( fp)); + if (comment) /* Not -dM option */ + mcpp_fprintf( FP2DEST( fp), " \t/* %s:%ld\t*/", dp->fname, dp->mline); + mcpp_fputc( '\n', FP2DEST( fp)); +} + +void dump_def( + int comment, /* Location of definition in comment */ + int K_opt /* -K option is specified */ +) +/* + * Dump all the current macro definitions to output stream. + */ +{ + DEFBUF * dp; + DEFBUF ** symp; + + sharp( NULL, 0); /* Report the current source file & line */ + if (comment) + mcpp_fputs( "/* Currently defined macros. */\n", OUT); + for (symp = symtab; symp < &symtab[ SBSIZE]; symp++) { + if ((dp = *symp) != NULL) { + do { + if (K_opt) + mcpp_fprintf( OUT, "/*m%s*/\n", dp->name); + else + dump_a_def( NULL, dp, FALSE, comment, fp_out); + } while ((dp = dp->link) != NULL); + } + } + wrong_line = TRUE; /* Line number is out of sync */ +} + +#if MCPP_LIB +void clear_symtable( void) +/* + * Free all the macro definitions. + */ +{ + DEFBUF * next; + DEFBUF * dp; + DEFBUF ** symp; + + for (symp = symtab; symp < &symtab[ SBSIZE]; symp++) { + for (next = *symp; next != NULL; ) { + dp = next; + next = dp->link; + free( dp); /* Free the symbol */ + } + *symp = NULL; + } +} +#endif + diff --git a/lib/mcpp/eval.c b/lib/mcpp/eval.c new file mode 100644 index 000000000..a66fa6e1a --- /dev/null +++ b/lib/mcpp/eval.c @@ -0,0 +1,1673 @@ +/*- + * Copyright (c) 1998, 2002-2008 Kiyoshi Matsui + * All rights reserved. + * + * Some parts of this code are derived from the public domain software + * DECUS cpp (1984,1985) written by Martin Minow. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * 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 AUTHOR 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. + */ + +/* + * E V A L . C + * E x p r e s s i o n E v a l u a t i o n + * + * The routines to evaluate #if expression are placed here. + * Some routines are used also to evaluate the value of numerical tokens. + */ + +#if PREPROCESSED +#include "mcpp.H" +#else +#include "system.H" +#include "internal.H" +#endif + +typedef struct optab { + char op; /* Operator */ + char prec; /* Its precedence */ + char skip; /* Short-circuit: non-0 to skip */ +} OPTAB; + +static int eval_lex( void); + /* Get type and value of token */ +static int chk_ops( void); + /* Check identifier-like ops */ +static VAL_SIGN * eval_char( char * const token); + /* Evaluate character constant */ +static expr_t eval_one( char ** seq_pp, int wide, int mbits, int * ucn8); + /* Evaluate a character */ +static VAL_SIGN * eval_eval( VAL_SIGN * valp, int op); + /* Entry to #if arithmetic */ +static expr_t eval_signed( VAL_SIGN ** valpp, expr_t v1, expr_t v2, int op); + /* Do signed arithmetic of expr.*/ +static expr_t eval_unsigned( VAL_SIGN ** valpp, uexpr_t v1u, uexpr_t v2u + , int op); + /* Do unsigned arithmetic */ +static void overflow( const char * op_name, VAL_SIGN ** valpp + , int ll_overflow); + /* Diagnose overflow of expr. */ +static int do_sizeof( void); + /* Evaluate sizeof (type) */ +static int look_type( int typecode); + /* Look for type of the name */ +static void dump_val( const char * msg, const VAL_SIGN * valp); + /* Print value of an operand */ +static void dump_stack( const OPTAB * opstack, const OPTAB * opp + , const VAL_SIGN * value, const VAL_SIGN * valp); + /* Print stacked operators */ + +/* For debug and error messages. */ +static const char * const opname[ OP_END + 1] = { + "end of expression", "val", "(", + "unary +", "unary -", "~", "!", + "*", "/", "%", + "+", "-", "<<", ">>", + "<", "<=", ">", ">=", "==", "!=", + "&", "^", "|", "&&", "||", + "?", ":", + ")", "(none)" +}; + +/* + * opdope[] has the operator (and operand) precedence: + * Bits + * 7 Unused (so the value is always positive) + * 6-2 Precedence (0000 .. 0174) + * 1-0 Binary op. flags: + * 10 The next binop flag (binop should/not follow). + * 01 The binop flag (should be set/cleared when this op is seen). + * Note: next binop + * value 1 0 Value doesn't follow value. + * Binop, ), END should follow value, value or unop doesn't. + * ( 0 0 ( doesn't follow value. Value follows. + * unary 0 0 Unop doesn't follow value. Value follows. + * binary 0 1 Binary op follows value. Value follows. + * ) 1 1 ) follows value. Binop, ), END follows. + * END 0 1 END follows value, doesn't follow ops. + */ + +static const char opdope[ OP_END + 1] = { + 0001, /* End of expression */ + 0002, 0170, /* VAL (constant), LPA */ +/* Unary op's */ + 0160, 0160, 0160, 0160, /* PLU, NEG, COM, NOT */ +/* Binary op's */ + 0151, 0151, 0151, /* MUL, DIV, MOD, */ + 0141, 0141, 0131, 0131, /* ADD, SUB, SL, SR */ + 0121, 0121, 0121, 0121, 0111, 0111, /* LT, LE, GT, GE, EQ, NE */ + 0101, 0071, 0061, 0051, 0041, /* AND, XOR, OR, ANA, ORO */ + 0031, 0031, /* QUE, COL */ +/* Parens */ + 0013, 0023 /* RPA, END */ +}; +/* + * OP_QUE, OP_RPA and unary operators have alternate precedences: + */ +#define OP_RPA_PREC 0013 +#define OP_QUE_PREC 0024 /* From right to left grouping */ +#define OP_UNOP_PREC 0154 /* ditto */ + +/* + * S_ANDOR and S_QUEST signal "short-circuit" boolean evaluation, so that + * #if FOO != 0 && 10 / FOO ... + * doesn't generate an error message. They are stored in optab.skip. + */ +#define S_ANDOR 2 +#define S_QUEST 1 + +static VAL_SIGN ev; /* Current value and signedness */ +static int skip = 0; /* 3-way signal of skipping expr*/ +static const char * const non_eval + = " (in non-evaluated sub-expression)"; /* _W8_ */ + +#if HAVE_LONG_LONG && COMPILER == INDEPENDENT + static int w_level = 1; /* warn_level at overflow of long */ +#else + static int w_level = 2; +#endif + +/* + * In KR and OLD_PREP modes. + * Define bits for the basic types and their adjectives. + */ +#define T_CHAR 1 +#define T_INT 2 +#define T_FLOAT 4 +#define T_DOUBLE 8 +#define T_LONGDOUBLE 16 +#define T_SHORT 32 +#define T_LONG 64 +#define T_LONGLONG 128 +#define T_SIGNED 256 +#define T_UNSIGNED 512 +#define T_PTR 1024 /* Pointer to data objects */ +#define T_FPTR 2048 /* Pointer to functions */ + +/* + * The SIZES structure is used to store the values for #if sizeof. + */ +typedef struct sizes { + int bits; /* If this bit is set, */ + int size; /* this is the datum size value */ + int psize; /* this is the pointer size */ +} SIZES; + +/* + * S_CHAR, etc. define the sizeof the basic TARGET machine word types. + * By default, sizes are set to the values for the HOST computer. If + * this is inappropriate, see those tables for details on what to change. + * Also, if you have a machine where sizeof (signed int) differs from + * sizeof (unsigned int), you will have to edit those tables and code in + * eval.c. + * Note: sizeof in #if expression is disallowed by Standard. + */ + +#define S_CHAR (sizeof (char)) +#define S_SINT (sizeof (short int)) +#define S_INT (sizeof (int)) +#define S_LINT (sizeof (long int)) +#define S_FLOAT (sizeof (float)) +#define S_DOUBLE (sizeof (double)) +#define S_PCHAR (sizeof (char *)) +#define S_PSINT (sizeof (short int *)) +#define S_PINT (sizeof (int *)) +#define S_PLINT (sizeof (long int *)) +#define S_PFLOAT (sizeof (float *)) +#define S_PDOUBLE (sizeof (double *)) +#define S_PFPTR (sizeof (int (*)())) +#if HAVE_LONG_LONG +#if (HOST_COMPILER == BORLANDC) \ + || (HOST_COMPILER == MSC && defined(_MSC_VER) && (_MSC_VER < 1300)) +#define S_LLINT (sizeof (__int64)) +#define S_PLLINT (sizeof (__int64 *)) +#else +#define S_LLINT (sizeof (long long int)) +#define S_PLLINT (sizeof (long long int *)) +#endif +#endif +#define S_LDOUBLE (sizeof (long double)) +#define S_PLDOUBLE (sizeof (long double *)) + +typedef struct types { + int type; /* This is the bits for types */ + char * token_name; /* this is the token word */ + int excluded; /* but these aren't legal here. */ +} TYPES; + +#define ANYSIGN (T_SIGNED | T_UNSIGNED) +#define ANYFLOAT (T_FLOAT | T_DOUBLE | T_LONGDOUBLE) +#if HAVE_LONG_LONG +#define ANYINT (T_CHAR | T_SHORT | T_INT | T_LONG | T_LONGLONG) +#else +#define ANYINT (T_CHAR | T_SHORT | T_INT | T_LONG) +#endif + +static const TYPES basic_types[] = { + { T_CHAR, "char", ANYFLOAT | ANYINT }, + { T_SHORT, "short", ANYFLOAT | ANYINT }, + { T_INT, "int", ANYFLOAT | T_CHAR | T_INT }, + { T_LONG, "long", ANYFLOAT | ANYINT }, +#if HAVE_LONG_LONG +#if HOST_COMPILER == BORLANDC + { T_LONGLONG, "__int64", ANYFLOAT | ANYINT }, +#else + { T_LONGLONG, "long long", ANYFLOAT | ANYINT }, +#endif +#endif + { T_FLOAT, "float", ANYFLOAT | ANYINT | ANYSIGN }, + { T_DOUBLE, "double", ANYFLOAT | ANYINT | ANYSIGN }, + { T_LONGDOUBLE, "long double", ANYFLOAT | ANYINT | ANYSIGN }, + { T_SIGNED, "signed", ANYFLOAT | ANYINT | ANYSIGN }, + { T_UNSIGNED, "unsigned", ANYFLOAT | ANYINT | ANYSIGN }, + { 0, NULL, 0 } /* Signal end */ +}; + +/* + * In this table, T_FPTR (pointer to function) should be placed last. + */ +static const SIZES size_table[] = { + { T_CHAR, S_CHAR, S_PCHAR }, /* char */ + { T_SHORT, S_SINT, S_PSINT }, /* short int */ + { T_INT, S_INT, S_PINT }, /* int */ + { T_LONG, S_LINT, S_PLINT }, /* long */ +#if HAVE_LONG_LONG + { T_LONGLONG, S_LLINT, S_PLLINT }, /* long long */ +#endif + { T_FLOAT, S_FLOAT, S_PFLOAT }, /* float */ + { T_DOUBLE, S_DOUBLE, S_PDOUBLE }, /* double */ + { T_LONGDOUBLE, S_LDOUBLE, S_PLDOUBLE }, /* long double */ + { T_FPTR, 0, S_PFPTR }, /* int (*()) */ + { 0, 0, 0 } /* End of table */ +}; + +#define is_binary(op) (FIRST_BINOP <= op && op <= LAST_BINOP) +#define is_unary(op) (FIRST_UNOP <= op && op <= LAST_UNOP) + + +#if MCPP_LIB +void init_eval( void) +{ + skip = 0; +} +#endif + +expr_t eval_if( void) +/* + * Evaluate a #if expression. Straight-forward operator precedence. + * This is called from directive() on encountering an #if directive. + * It calls the following routines: + * eval_lex() Lexical analyser -- returns the type and value of + * the next input token. + * eval_eval() Evaluates the current operator, given the values on the + * value stack. Returns a pointer to the (new) value stack. + */ +{ + VAL_SIGN value[ NEXP * 2 + 1]; /* Value stack */ + OPTAB opstack[ NEXP * 3 + 1]; /* Operator stack */ + int parens = 0; /* Nesting levels of (, ) */ + int prec; /* Operator precedence */ + int binop = 0; /* Set if binary op. needed */ + int op1; /* Operator from stack */ + int skip_cur; /* For short-circuit testing */ + VAL_SIGN * valp = value; /* -> Value and signedness */ + OPTAB * opp = opstack; /* -> Operator stack */ + int op; /* Current operator */ + + opp->op = OP_END; /* Mark bottom of stack */ + opp->prec = opdope[ OP_END]; /* And its precedence */ + skip = skip_cur = opp->skip = 0; /* Not skipping now */ + + while (1) { + if (mcpp_debug & EXPRESSION) + mcpp_fprintf( DBG + , "In eval loop skip = %d, binop = %d, line is: %s\n" + , opp->skip, binop, infile->bptr); + skip = opp->skip; + op = eval_lex(); + skip = 0; /* Reset to be ready to return */ + switch (op) { + case OP_SUB : + if (binop == 0) + op = OP_NEG; /* Unary minus */ + break; + case OP_ADD : + if (binop == 0) + op = OP_PLU; /* Unary plus */ + break; + case OP_FAIL: + return 0L; /* Token error */ + } + if (mcpp_debug & EXPRESSION) + mcpp_fprintf( DBG + , "op = %s, opdope = %04o, binop = %d, skip = %d\n" + , opname[ op], opdope[ op], binop, opp->skip); + if (op == VAL) { /* Value? */ + if (binop != 0) { /* Binop is needed */ + cerror( "Misplaced constant \"%s\"" /* _E_ */ + , work_buf, 0L, NULL); + return 0L; + } else if (& value[ NEXP * 2] <= valp) { + cerror( "More than %.0s%ld constants stacked at %s" /* _E_ */ + , NULL, (long) (NEXP * 2 - 1), work_buf); + return 0L; + } else { + if (mcpp_debug & EXPRESSION) { + dump_val( "pushing ", &ev); + mcpp_fprintf( DBG, " onto value stack[%d]\n" + , (int)(valp - value)); + } + valp->val = ev.val; + (valp++)->sign = ev.sign; + binop = 1; /* Binary operator or so should follow */ + } + continue; + } /* Else operators */ + prec = opdope[ op]; + if (binop != (prec & 1)) { + if (op == OP_EOE) + cerror( "Unterminated expression" /* _E_ */ + , NULL, 0L, NULL); + else + cerror( "Operator \"%s\" in incorrect context" /* _E_ */ + , opname[ op], 0L, NULL); + return 0L; + } + binop = (prec & 2) >> 1; /* Binop should follow? */ + + while (1) { + if (mcpp_debug & EXPRESSION) + mcpp_fprintf( DBG + , "op %s, prec %d, stacked op %s, prec %d, skip %d\n" + , opname[ op], prec, opname[ opp->op], opp->prec, opp->skip); + + /* Stack coming sub-expression of higher precedence. */ + if (opp->prec < prec) { + if (op == OP_LPA) { + prec = OP_RPA_PREC; + if (standard && (warn_level & 4) + && ++parens == std_limits.exp_nest + 1) + cwarn( + "More than %.0s%ld nesting of parens" /* _W4_ */ + , NULL, (long) std_limits.exp_nest, NULL); + } else if (op == OP_QUE) { + prec = OP_QUE_PREC; + } else if (is_unary( op)) { + prec = OP_UNOP_PREC; + } + op1 = opp->skip; /* Save skip for test */ + /* + * Push operator onto operator stack. + */ + opp++; + if (& opstack[ NEXP * 3] <= opp) { + cerror( + "More than %.0s%ld operators and parens stacked at %s" /* _E_ */ + , NULL, (long) (NEXP * 3 - 1), opname[ op]); + return 0L; + } + opp->op = op; + opp->prec = prec; + if (&value[0] < valp) + skip_cur = (valp[-1].val != 0L); + /* Short-circuit tester */ + /* + * Do the short-circuit stuff here. Short-circuiting + * stops automagically when operators are evaluated. + */ + if ((op == OP_ANA && ! skip_cur) + || (op == OP_ORO && skip_cur)) { + opp->skip = S_ANDOR; /* And/or skip starts */ + if (skip_cur) /* Evaluate non-zero */ + valp[-1].val = 1L; /* value to 1 */ + } else if (op == OP_QUE) { /* Start of ?: operator */ + opp->skip = (op1 & S_ANDOR) | (!skip_cur ? S_QUEST : 0); + } else if (op == OP_COL) { /* : inverts S_QUEST */ + opp->skip = (op1 & S_ANDOR) + | (((op1 & S_QUEST) != 0) ? 0 : S_QUEST); + } else { /* Other operators leave*/ + opp->skip = op1; /* skipping unchanged. */ + } + if (mcpp_debug & EXPRESSION) { + mcpp_fprintf( DBG, "stacking %s, ", opname[ op]); + if (&value[0] < valp) + dump_val( "valp[-1].val == ", valp - 1); + mcpp_fprintf( DBG, " at %s\n", infile->bptr); + dump_stack( opstack, opp, value, valp); + } + break; + } + + /* + * Coming sub-expression is of lower precedence. + * Evaluate stacked sub-expression. + * Pop operator from operator stack and evaluate it. + * End of stack and '(', ')' are specials. + */ + skip_cur = opp->skip; /* Remember skip value */ + switch ((op1 = opp->op)) { /* Look at stacked op */ + case OP_END: /* Stack end marker */ + if (op == OP_RPA) { /* No corresponding ( */ + cerror( "Excessive \")\"", NULL, 0L, NULL); /* _E_ */ + return 0L; + } + if (op == OP_EOE) + return valp[-1].val; /* Finished ok. */ + break; + case OP_LPA: /* ( on stack */ + if (op != OP_RPA) { /* Matches ) on input? */ + cerror( "Missing \")\"", NULL, 0L, NULL); /* _E_ */ + return 0L; + } + opp--; /* Unstack it */ + parens--; /* Count down nest level*/ + break; + case OP_QUE: /* Evaluate true expr. */ + break; + case OP_COL: /* : on stack */ + opp--; /* Unstack : */ + if (opp->op != OP_QUE) { /* Matches ? on stack? */ + cerror( + "Misplaced \":\", previous operator is \"%s\"" /* _E_ */ + , opname[opp->op], 0L, NULL); + return 0L; + } + /* Evaluate op1. Fall through */ + default: /* Others: */ + opp--; /* Unstack the operator */ + if (mcpp_debug & EXPRESSION) { + mcpp_fprintf( DBG, "Stack before evaluation of %s\n" + , opname[ op1]); + dump_stack( opstack, opp, value, valp); + } + if (op1 == OP_COL) + skip = 0; + else + skip = skip_cur; + valp = eval_eval( valp, op1); + if (valp->sign == VAL_ERROR) + return 0L; /* Out of range or divide by 0 */ + valp++; + skip = 0; + if (mcpp_debug & EXPRESSION) { + mcpp_fprintf( DBG, "Stack after evaluation\n"); + dump_stack( opstack, opp, value, valp); + } + } /* op1 switch end */ + + if (op1 == OP_END || op1 == OP_LPA || op1 == OP_QUE) + break; /* Read another op. */ + } /* Stack unwind loop */ + + } + + return 0L; /* Never reach here */ +} + +static int eval_lex( void) +/* + * Return next operator or constant to evaluate. Called from eval_if(). It + * calls a special-purpose routines for character constants and numeric values: + * eval_char() called to evaluate 'x' + * eval_num() called to evaluate numbers + * C++98 treats 11 identifier-like tokens as operators. + * POST_STD forbids character constants in #if expression. + */ +{ + int c1; + VAL_SIGN * valp; + int warn = ! skip || (warn_level & 8); + int token_type; + int c; + + ev.sign = SIGNED; /* Default signedness */ + ev.val = 0L; /* Default value (on error or 0 value) */ + in_if = ! skip; /* Inform to expand_macro() that the macro is */ + /* in #if line and not skipped expression. */ + c = skip_ws(); + if (c == '\n') { + unget_ch(); + return OP_EOE; /* End of expression */ + } + token_type = get_unexpandable( c, warn); + if (standard && macro_line == MACRO_ERROR) + return OP_FAIL; /* Unterminated macro call */ + if (token_type == NO_TOKEN) + return OP_EOE; /* Only macro(s) expanding to 0-token */ + + switch (token_type) { + case NAM: + if (standard && str_eq( identifier, "defined")) { /* defined name */ + c1 = c = skip_ws(); + if (c == '(') /* Allow defined (name) */ + c = skip_ws(); + if (scan_token( c, (workp = work_buf, &workp), work_end) == NAM) { + DEFBUF * defp = look_id( identifier); + if (warn) { + ev.val = (defp != NULL); + if ((mcpp_debug & MACRO_CALL) && ! skip && defp) + /* Annotate if the macro is in non-skipped expr. */ + mcpp_fprintf( OUT, "/*%s*/", defp->name); + } + if (c1 != '(' || skip_ws() == ')') /* Balanced ? */ + return VAL; /* Parsed ok */ + } + cerror( "Bad defined syntax: %s" /* _E_ */ + , infile->fp ? "" : infile->buffer, 0L, NULL); + break; + } else if (cplus_val) { + if (str_eq( identifier, "true")) { + ev.val = 1L; + return VAL; + } else if (str_eq( identifier, "false")) { + ev.val = 0L; + return VAL; + } else if (mcpp_mode != POST_STD + && (openum = id_operator( identifier)) != 0) { + /* Identifier-like operator in C++98 */ + strcpy( work_buf, identifier); + return chk_ops(); + } + } else if (! standard && str_eq( identifier, "sizeof")) { + /* sizeof hackery */ + return do_sizeof(); /* Gets own routine */ + } + /* + * The ANSI C Standard says that an undefined symbol + * in an #if has the value zero. We are a bit pickier, + * warning except where the programmer was careful to write + * #if defined(foo) ? foo : 0 + */ + if ((! skip && (warn_level & 4)) || (skip && (warn_level & 8))) + cwarn( "Undefined symbol \"%s\"%.0ld%s" /* _W4_ _W8_ */ + , identifier, 0L, skip ? non_eval : ", evaluated to 0"); + return VAL; + case CHR: /* Character constant */ + case WCHR: /* Wide char constant */ + if (mcpp_mode == POST_STD) { + cerror( "Can't use a character constant %s" /* _E_ */ + , work_buf, 0L, NULL); + break; + } + valp = eval_char( work_buf); /* 'valp' points 'ev' */ + if (valp->sign == VAL_ERROR) + break; + if (mcpp_debug & EXPRESSION) { + dump_val( "eval_char returns ", &ev); + mcpp_fputc( '\n', DBG); + } + return VAL; /* Return a value */ + case STR: /* String literal */ + case WSTR: /* Wide string literal */ + cerror( + "Can't use a string literal %s", work_buf, 0L, NULL); /* _E_ */ + break; + case NUM: /* Numbers are harder */ + valp = eval_num( work_buf); /* 'valp' points 'ev' */ + if (valp->sign == VAL_ERROR) + break; + if (mcpp_debug & EXPRESSION) { + dump_val( "eval_num returns ", &ev); + mcpp_fputc( '\n', DBG); + } + return VAL; + case OPE: /* Operator or punctuator */ + return chk_ops(); + + default: /* Total nonsense */ + cerror( "Can't use the character %.0s0x%02lx" /* _E_ */ + , NULL, (long) c, NULL); + break; + } + + return OP_FAIL; /* Any errors */ +} + +static int chk_ops( void) +/* + * Check the operator. + * If it can't be used in #if expression return OP_FAIL + * else return openum. + */ +{ + switch (openum) { + case OP_STR: case OP_CAT: case OP_ELL: + case OP_1: case OP_2: case OP_3: + cerror( "Can't use the operator \"%s\"" /* _E_ */ + , work_buf, 0L, NULL); + return OP_FAIL; + default: + return openum; + } +} + +static int do_sizeof( void) +/* + * Process the sizeof (basic type) operation in an #if string. + * Sets ev.val to the size and returns + * VAL success + * OP_FAIL bad parse or something. + * This routine is never called in STD and POST_STD mode. + */ +{ + const char * const no_type = "sizeof: No type specified"; /* _E_ */ + int warn = ! skip || (warn_level & 8); + int type_end = FALSE; + int typecode = 0; + int token_type = NO_TOKEN; + const SIZES * sizp = NULL; + + if (get_unexpandable( skip_ws(), warn) != OPE || openum != OP_LPA) + goto no_good; /* Not '(' */ + + /* + * Scan off the tokens. + */ + + while (! type_end) { + token_type = get_unexpandable( skip_ws(), warn); + /* Get next token expanding macros */ + switch (token_type) { + case OPE: + if (openum == OP_LPA) { /* thing (*)() func ptr */ + if (get_unexpandable( skip_ws(), warn) == OPE + && openum == OP_MUL + && get_unexpandable( skip_ws(), warn) == OPE + && openum == OP_RPA) { /* (*) */ + if (get_unexpandable( skip_ws(), warn) != OPE + || openum != OP_LPA + || get_unexpandable( skip_ws(), warn) != OPE + || openum != OP_RPA) /* Not () */ + goto no_good; + typecode |= T_FPTR; /* Function pointer */ + } else { /* Junk is an error */ + goto no_good; + } + } else { /* '*' or ')' */ + type_end = TRUE; + } + break; + case NAM: /* Look for type comb. */ + if ((typecode = look_type( typecode)) == 0) + return OP_FAIL; /* Illegal type or comb.*/ + break; + default: goto no_good; /* Illegal token */ + } + } /* End of while */ + + /* + * We are at the end of the type scan. Chew off '*' if necessary. + */ + if (token_type == OPE) { + if (openum == OP_MUL) { /* '*' */ + typecode |= T_PTR; + if (get_unexpandable( skip_ws(), warn) != OPE) + goto no_good; + } + if (openum == OP_RPA) { /* ')' */ + /* + * Last syntax check + * We assume that all function pointers are the same size: + * sizeof (int (*)()) == sizeof (float (*)()) + * We assume that signed and unsigned don't change the size: + * sizeof (signed int) == sizeof (unsigned int) + */ + if ((typecode & T_FPTR) != 0) { /* Function pointer */ + typecode = T_FPTR | T_PTR; + } else { /* Var or var * datum */ + typecode &= ~(T_SIGNED | T_UNSIGNED); +#if HAVE_LONG_LONG + if ((typecode & (T_SHORT | T_LONG | T_LONGLONG)) != 0) +#else + if ((typecode & (T_SHORT | T_LONG)) != 0) +#endif + typecode &= ~T_INT; + } + if ((typecode & ~T_PTR) == 0) { + cerror( no_type, NULL, 0L, NULL); + return OP_FAIL; + } else { + /* + * Exactly one bit (and possibly T_PTR) may be set. + */ + for (sizp = size_table; sizp->bits != 0; sizp++) { + if ((typecode & ~T_PTR) == sizp->bits) { + ev.val = ((typecode & T_PTR) != 0) + ? sizp->psize : sizp->size; + break; + } + } + } + } else { + goto no_good; + } + } else { + goto no_good; + } + + if (mcpp_debug & EXPRESSION) { + if (sizp) + mcpp_fprintf( DBG, + "sizp->bits:0x%x sizp->size:0x%x sizp->psize:0x%x ev.val:0x%lx\n" + , sizp->bits, sizp->size, sizp->psize + , (unsigned long) ev.val); + } + return VAL; + +no_good: + unget_ch(); + cerror( "sizeof: Syntax error", NULL, 0L, NULL); /* _E_ */ + return OP_FAIL; +} + +static int look_type( + int typecode +) +{ + const char * const unknown_type + = "sizeof: Unknown type \"%s\"%.0ld%s"; /* _E_ _W8_ */ + const char * const illeg_comb + = "sizeof: Illegal type combination with \"%s\"%.0ld%s"; /* _E_ _W8_ */ + int token_type; + const TYPES * tp; + + if (str_eq( identifier, "long")) { + if ((token_type + = get_unexpandable( skip_ws(), !skip || (warn_level & 8))) + == NO_TOKEN) + return typecode; + if (token_type == NAM) { +#if HAVE_LONG_LONG + if (str_eq( identifier, "long")) { + strcpy( work_buf, "long long"); + goto basic; + } +#endif + if (str_eq( identifier, "double")) { + strcpy( work_buf, "long double"); + goto basic; + } + } + unget_string( work_buf, NULL); /* Not long long */ + strcpy( work_buf, "long"); /* nor long double */ + } + + /* + * Look for this unexpandable token in basic_types. + */ +basic: + for (tp = basic_types; tp->token_name != NULL; tp++) { + if (str_eq( work_buf, tp->token_name)) + break; + } + + if (tp->token_name == NULL) { + if (! skip) { + cerror( unknown_type, work_buf, 0L, NULL); + return 0; + } else if (warn_level & 8) { + cwarn( unknown_type, work_buf, 0L, non_eval); + } + } + if ((typecode & tp->excluded) != 0) { + if (! skip) { + cerror( illeg_comb, work_buf, 0L, NULL); + return 0; + } else if (warn_level & 8) { + cwarn( illeg_comb, work_buf, 0L, non_eval); + } + } + + if (mcpp_debug & EXPRESSION) { + if (tp->token_name) + mcpp_fprintf( DBG, + "sizeof -- typecode:0x%x tp->token_name:\"%s\" tp->type:0x%x\n" + , typecode, tp->token_name, tp->type); + } + return typecode |= tp->type; /* Or in the type bit */ +} + +VAL_SIGN * eval_num( + const char * nump /* Preprocessing number */ +) +/* + * Evaluate number for #if lexical analysis. Note: eval_num recognizes + * the unsigned suffix, but only returns a signed expr_t value, and stores + * the signedness to ev.sign, which is set UNSIGNED (== unsigned) if the + * value is not in the range of positive (signed) expr_t. + */ +{ + const char * const not_integer = "Not an integer \"%s\""; /* _E_ */ + const char * const out_of_range + = "Constant \"%s\"%.0ld%s is out of range"; /* _E_ _W1_ _W8_ */ + expr_t value; + uexpr_t v, v1; /* unsigned long long or unsigned long */ + int uflag = FALSE; + int lflag = FALSE; + int erange = FALSE; + int base; + int c, c1; + const char * cp = nump; +#if HAVE_LONG_LONG + const char * const out_of_range_long = + "Constant \"%s\"%.0ld%s is out of range " /* _E_ _W1_ _W2_ _W8_ */ + "of (unsigned) long"; + const char * const ll_suffix = +"LL suffix is used in other than C99 mode \"%s\"%.0ld%s"; /* _W1_ _W2_ _W8_ */ +#if COMPILER == MSC || COMPILER == BORLANDC + const char * const i64_suffix = +"I64 suffix is used in other than C99 mode \"%s\"%.0ld%s"; /* _W2_ _W8_ */ +#endif + int llflag = FALSE; + int erange_long = FALSE; +#endif + + ev.sign = SIGNED; /* Default signedness */ + ev.val = 0L; /* Default value */ + if ((char_type[ c = *cp++ & UCHARMAX] & DIG) == 0) /* Dot */ + goto num_err; + if (c != '0') { /* Decimal */ + base = 10; + } else if ((c = *cp++ & UCHARMAX) == 'x' || c == 'X') { + base = 16; /* Hexadecimal */ + c = *cp++ & UCHARMAX; + } else if (c == EOS) { /* 0 */ + return & ev; + } else { /* Octal or illegal */ + base = 8; + } + + v = v1 = 0L; + for (;;) { + c1 = c; + if (isupper( c1)) + c1 = tolower( c1); + if (c1 >= 'a') + c1 -= ('a' - 10); + else + c1 -= '0'; + if (c1 < 0 || base <= c1) + break; + v1 *= base; + v1 += c1; + if (v1 / base < v) { /* Overflow */ + if (! skip) + goto range_err; + else + erange = TRUE; + } +#if HAVE_LONG_LONG + if (! stdc3 && v1 > ULONGMAX) + /* Overflow of long or unsigned long */ + erange_long = TRUE; +#endif + v = v1; + c = *cp++ & UCHARMAX; + } + + value = v; + while (c == 'u' || c == 'U' || c == 'l' || c == 'L') { + if (c == 'u' || c == 'U') { + if (uflag) + goto num_err; + uflag = TRUE; + } else if (c == 'l' || c == 'L') { +#if HAVE_LONG_LONG + if (llflag) { + goto num_err; + } else if (lflag) { + llflag = TRUE; + if (! stdc3 && ((! skip && (warn_level & w_level)) + || (skip && (warn_level & 8)))) + cwarn( ll_suffix, nump, 0L, skip ? non_eval : NULL); + } else { + lflag = TRUE; + } +#else + if (lflag) + goto num_err; + else + lflag = TRUE; +#endif + } + c = *cp++; + } +#if HAVE_LONG_LONG && (COMPILER == MSC || COMPILER == BORLANDC) + if (tolower( c) == 'i') { + c1 = atoi( cp); + if (c1 == 64) { + if (! stdc3 && ((! skip && (warn_level & w_level)) + || (skip && (warn_level & 8)))) + cwarn( i64_suffix, nump, 0L, skip ? non_eval : NULL); + cp += 2; + } else if (c1 == 32 || c1 == 16) { + cp += 2; + } else if (c1 == 8) { + cp++; + } + c = *cp++; + } +#endif + + if (c != EOS) + goto num_err; + + if (standard) { + if (uflag) /* If 'U' suffixed, uexpr_t is treated as unsigned */ + ev.sign = UNSIGNED; + else + ev.sign = (value >= 0L); +#if HAVE_LONG_LONG + } else { + if (value > LONGMAX) + erange_long = TRUE; +#endif + } + + ev.val = value; + if (erange && (warn_level & 8)) + cwarn( out_of_range, nump, 0L, non_eval); +#if HAVE_LONG_LONG + else if (erange_long && ((skip && (warn_level & 8)) + || (! stdc3 && ! skip && (warn_level & w_level)))) + cwarn( out_of_range_long, nump, 0L, skip ? non_eval : NULL); +#endif + return & ev; + +range_err: + cerror( out_of_range, nump, 0L, NULL); + ev.sign = VAL_ERROR; + return & ev; +num_err: + cerror( not_integer, nump, 0L, NULL); + ev.sign = VAL_ERROR; + return & ev; +} + +static VAL_SIGN * eval_char( + char * const token +) +/* + * Evaluate a character constant. + * This routine is never called in POST_STD mode. + */ +{ + const char * const w_out_of_range + = "Wide character constant %s%.0ld%s is out of range"; /* _E_ _W8_ */ + const char * const c_out_of_range + = "Integer character constant %s%.0ld%s is out of range"; /* _E_ _W8_ */ + uexpr_t value; + uexpr_t tmp; + expr_t cl; + int erange = FALSE; + int wide = (*token == 'L'); + int ucn8; + int i; + int bits, mbits, u8bits, bits_save; + char * cp = token + 1; /* Character content */ +#if HAVE_LONG_LONG + const char * const w_out_of_range_long = + "Wide character constant %s%.0ld%s is " /* _E_ _W1_ _W2_ _W8_ */ + "out of range of unsigned long"; + const char * const c_out_of_range_long = + "Integer character constant %s%.0ld%s is " /* _E_ _W1_ _W2_ _W8_ */ + "out of range of unsigned long"; + int erange_long = FALSE; +#endif + + bits = CHARBIT; + u8bits = CHARBIT * 4; + if (mbchar & UTF8) + mbits = CHARBIT * 4; + else + mbits = CHARBIT * 2; + if (mcpp_mode == STD && wide) { /* Wide character constant */ + cp++; /* Skip 'L' */ + bits = mbits; + } + if (char_type[ *cp & UCHARMAX] & mbchk) { + cl = mb_eval( &cp); + bits = mbits; + } else if ((cl = eval_one( &cp, wide, mbits, (ucn8 = FALSE, &ucn8))) + == -1L) { + ev.sign = VAL_ERROR; + return & ev; + } + bits_save = bits; + value = cl; + + for (i = 0; *cp != '\'' && *cp != EOS; i++) { + if (char_type[ *cp & UCHARMAX] & mbchk) { + cl = mb_eval( &cp); + if (cl == 0) + /* Shift-out sequence of multi-byte or wide character */ + continue; + bits = mbits; + } else { + cl = eval_one( &cp, wide, mbits, (ucn8 = FALSE, &ucn8)); + if (cl == -1L) { + ev.sign = VAL_ERROR; + return & ev; + } +#if OK_UCN + if (ucn8 == TRUE) + bits = u8bits; + else + bits = bits_save; +#endif + } + tmp = value; + value = (value << bits) | cl; /* Multi-char or multi-byte char */ + if ((value >> bits) < tmp) { /* Overflow */ + if (! skip) + goto range_err; + else + erange = TRUE; + } +#if HAVE_LONG_LONG + if ((mcpp_mode == STD && (! stdc3 && value > ULONGMAX)) + || (! standard && value > LONGMAX)) + erange_long = TRUE; +#endif + } + + ev.sign = ((expr_t) value >= 0L); + ev.val = value; + + if (erange && skip && (warn_level & 8)) { + if (wide) + cwarn( w_out_of_range, token, 0L, non_eval); + else + cwarn( c_out_of_range, token, 0L, non_eval); +#if HAVE_LONG_LONG + } else if (erange_long && ((skip && (warn_level & 8)) + || (! stdc3 && ! skip && (warn_level & w_level)))) { + if (wide) + cwarn( w_out_of_range_long, token, 0L, skip ? non_eval : NULL); + else + cwarn( c_out_of_range_long, token, 0L, skip ? non_eval : NULL); +#endif + } + + if (i == 0) /* Constant of single (character or wide-character) */ + return & ev; + + if ((! skip && (warn_level & 4)) || (skip && (warn_level & 8))) { + if (mcpp_mode == STD && wide) + cwarn( +"Multi-character wide character constant %s%.0ld%s isn't portable" /* _W4_ _W8_ */ + , token, 0L, skip ? non_eval : NULL); + else + cwarn( +"Multi-character or multi-byte character constant %s%.0ld%s isn't portable" /* _W4_ _W8_ */ + , token, 0L, skip ? non_eval : NULL); + } + return & ev; + +range_err: + if (wide) + cerror( w_out_of_range, token, 0L, NULL); + else + cerror( c_out_of_range, token, 0L, NULL); + ev.sign = VAL_ERROR; + return & ev; +} + +static expr_t eval_one( + char ** seq_pp, /* Address of pointer to sequence */ + /* eval_one() advances the pointer to sequence */ + int wide, /* Flag of wide-character */ + int mbits, /* Number of bits of a wide-char */ + int * ucn8 /* Flag of UCN-32 bits */ +) +/* + * Called from eval_char() above to get a single character, single multi- + * byte character or wide character (with or without \ escapes). + * Returns the value of the character or -1L on error. + */ +{ +#if OK_UCN + const char * const ucn_malval + = "UCN cannot specify the value %.0s\"%08lx\""; /* _E_ _W8_ */ +#endif + const char * const out_of_range + = "%s%ld bits can't represent escape sequence '%s'"; /* _E_ _W8_ */ + uexpr_t value; + int erange = FALSE; + char * seq = *seq_pp; /* Initial seq_pp for diagnostic*/ + const char * cp; + const char * digits; + unsigned uc; + unsigned uc1; + int count; + int bits; + size_t wchar_max; + + uc = *(*seq_pp)++ & UCHARMAX; + + if (uc != '\\') /* Other than escape sequence */ + return (expr_t) uc; + + /* escape sequence */ + uc1 = uc = *(*seq_pp)++ & UCHARMAX; + switch (uc) { + case 'a': + return '\a'; + case 'b': + return '\b'; + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case 'v': + return '\v'; +#if OK_UCN + case 'u': case 'U': + if (! stdc2) + goto undefined; + /* Else Universal character name */ + /* Fall through */ +#endif + case 'x': /* '\xFF' */ + if (! standard) + goto undefined; + digits = "0123456789abcdef"; + bits = 4; + uc = *(*seq_pp)++ & UCHARMAX; + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + digits = "01234567"; + bits = 3; + break; + case '\'': case '"': case '?': case '\\': + return (expr_t) uc; + default: + goto undefined; + } + + wchar_max = (UCHARMAX << CHARBIT) | UCHARMAX; + if (mbits == CHARBIT * 4) { + if (mcpp_mode == STD) + wchar_max = (wchar_max << CHARBIT * 2) | wchar_max; + else + wchar_max = LONGMAX; + } + + value = 0L; + for (count = 0; ; ++count) { + if (isupper( uc)) + uc = tolower( uc); + if ((cp = strchr( digits, uc)) == NULL) + break; + if (count >= 3 && bits == 3) + break; /* Octal escape sequence at most 3 digits */ +#if OK_UCN + if ((count >= 4 && uc1 == 'u') || (count >= 8 && uc1 == 'U')) + break; +#endif + value = (value << bits) | (cp - digits); +#if OK_UCN + if (wchar_max < value && uc1 != 'u' && uc1 != 'U') +#else + if (wchar_max < value) +#endif + { + if (! skip) + goto range_err; + else + erange = TRUE; + } + uc = *(*seq_pp)++ & UCHARMAX; + } + (*seq_pp)--; + + if (erange) { + value &= wchar_max; + goto range_err; + } + + if (count == 0 && bits == 4) /* '\xnonsense' */ + goto undefined; +#if OK_UCN + if (uc1 == 'u' || uc1 == 'U') { + if ((count < 4 && uc1 == 'u') || (count < 8 && uc1 == 'U')) + goto undefined; + if ((value >= 0L && value <= 0x9FL + && value != 0x24L && value != 0x40L && value != 0x60L) + || (!stdc3 && value >= 0xD800L && value <= 0xDFFFL)) { + if (!skip) + cerror( ucn_malval, NULL, (long) value, NULL); + else if (warn_level & 8) + cwarn( ucn_malval, NULL, (long) value, NULL); + } + if (count >= 8 && uc1 == 'U') + *ucn8 = TRUE; + return (expr_t) value; + } +#endif /* OK_UCN */ + if (! wide && (UCHARMAX < value)) { + value &= UCHARMAX; + goto range_err; + } + return (expr_t) value; + +undefined: + uc1 = **seq_pp; + **seq_pp = EOS; /* For diagnostic */ + if ((! skip && (warn_level & 1)) || (skip && (warn_level & 8))) + cwarn( + "Undefined escape sequence%s %.0ld'%s'" /* _W1_ _W8_ */ + , skip ? non_eval : NULL, 0L, seq); + **seq_pp = uc1; + *seq_pp = seq + 1; + return (expr_t) '\\'; /* Returns the escape char */ + +range_err: + uc1 = **seq_pp; + **seq_pp = EOS; /* For diagnostic */ + if (wide) { + if (! skip) + cerror( out_of_range, NULL, (long) mbits, seq); + else if (warn_level & 8) + cwarn( out_of_range, non_eval, (long) mbits, seq); + } else { + if (! skip) + cerror( out_of_range, NULL, (long) CHARBIT, seq); + else if (warn_level & 8) + cwarn( out_of_range, non_eval, (long) CHARBIT, seq); + } + + **seq_pp = uc1; + if (! skip) + return -1L; + else + return (expr_t) value; +} + +static VAL_SIGN * eval_eval( + VAL_SIGN * valp, + int op +) +/* + * One or two values are popped from the value stack and do arithmetic. + * The result is pushed onto the value stack. + * eval_eval() returns the new pointer to the top of the value stack. + */ +{ + const char * const zero_div = "%sDivision by zero%.0ld%s"; /* _E_ _W8_ */ +#if HAVE_LONG_LONG + const char * const neg_format = +"Negative value \"%" LL_FORM "d\" is converted to positive \"%" /* _W1_ _W8_*/ + LL_FORM "u\"%%s"; +#else + const char * const neg_format = +"Negative value \"%ld\" is converted to positive \"%lu\"%%s"; /* _W1_ _W8_*/ +#endif + expr_t v1, v2; + int sign1, sign2; + + if (is_binary( op)) { + v2 = (--valp)->val; + sign2 = valp->sign; + } else { + v2 = 0L; /* Dummy */ + sign2 = SIGNED; /* Dummy */ + } + v1 = (--valp)->val; + sign1 = valp->sign; + if (mcpp_debug & EXPRESSION) { + mcpp_fprintf( DBG, "%s op %s", (is_binary( op)) ? "binary" : "unary" + , opname[ op]); + dump_val( ", v1 = ", valp); + if (is_binary( op)) + dump_val( ", v2 = ", valp + 1); + mcpp_fputc( '\n', DBG); + } + + if (standard + && (sign1 == UNSIGNED || sign2 == UNSIGNED) && is_binary( op) + && op != OP_ANA && op != OP_ORO && op != OP_SR && op != OP_SL) { + if (((sign1 == SIGNED && v1 < 0L) || (sign2 == SIGNED && v2 < 0L) + ) && ((! skip && (warn_level & 1)) + || (skip && (warn_level & 8)))) { + char negate[(((sizeof (expr_t) * 8) / 3) + 1) * 2 + 50]; + expr_t v3; + + v3 = (sign1 == SIGNED ? v1 : v2); + sprintf( negate, neg_format, v3, v3); + cwarn( negate, skip ? non_eval : NULL, 0L, NULL); + } + valp->sign = sign1 = sign2 = UNSIGNED; + } + if ((op == OP_SL || op == OP_SR) + && ((! skip && (warn_level & 1)) || (skip && (warn_level & 8)))) { + if (v2 < 0L || v2 >= sizeof (expr_t) * CHARBIT) + cwarn( "Illegal shift count %.0s\"%ld\"%s" /* _W1_ _W8_ */ + , NULL, (long) v2, skip ? non_eval : NULL); +#if HAVE_LONG_LONG + else if (! stdc3 && v2 >= sizeof (long) * CHARBIT + && ((! skip && (warn_level & w_level)) + || (skip && (warn_level & 8)))) + cwarn( +"Shift count %.0s\"%ld\" is larger than bit count of long%s" /* _W1_ _W8_*/ + , NULL, (long) v2, skip ? non_eval : NULL); +#endif + } + if ((op == OP_DIV || op == OP_MOD) && v2 == 0L) { + if (! skip) { + cerror( zero_div, NULL, 0L, NULL); + valp->sign = VAL_ERROR; + return valp; + } else { + if (warn_level & 8) + cwarn( zero_div, NULL, 0L, non_eval); + valp->sign = sign1; + valp->val = (expr_t) EXPR_MAX; + return valp; + } + } + + if (! standard || sign1 == SIGNED) + v1 = eval_signed( & valp, v1, v2, op); + else + v1 = eval_unsigned( & valp, (uexpr_t) v1, (uexpr_t) v2, op); + + if (valp->sign == VAL_ERROR) /* Out of range */ + return valp; + + switch (op) { + case OP_NOT: case OP_EQ: case OP_NE: + case OP_LT: case OP_LE: case OP_GT: case OP_GE: + case OP_ANA: case OP_ORO: + valp->sign = SIGNED; + break; + default: + valp->sign = sign1; + break; + } + valp->val = v1; + return valp; +} + +static expr_t eval_signed( + VAL_SIGN ** valpp, + expr_t v1, + expr_t v2, + int op +) +/* + * Apply the argument operator to the signed data. + * OP_COL is a special case. + */ +{ + const char * const illeg_op + = "Bug: Illegal operator \"%s\" in eval_signed()"; /* _F_ */ + const char * const not_portable + = "\"%s\" of negative number isn't portable%.0ld%s"; /* _W1_ _W8_*/ + const char * op_name = opname[ op]; + VAL_SIGN * valp = *valpp; + expr_t val; + int chk; /* Flag of overflow in long long */ + + switch (op) { + case OP_EOE: + case OP_PLU: break; + case OP_NEG: + chk = v1 && v1 == -v1; + if (chk +#if HAVE_LONG_LONG + || (! stdc3 && v1 && (long) v1 == (long) -v1) +#endif + ) + overflow( op_name, valpp, chk); + v1 = -v1; + break; + case OP_COM: v1 = ~v1; break; + case OP_NOT: v1 = !v1; break; + case OP_MUL: + val = v1 * v2; + chk = v1 && v2 && (val / v1 != v2 || val / v2 != v1); + if (chk +#if HAVE_LONG_LONG + || (! stdc3 && v1 && v2 + && ((long)val / (long)v1 != (long)v2 + || (long)val / (long)v2 != (long)v1)) +#endif + ) + overflow( op_name, valpp, chk); + v1 = val; + break; + case OP_DIV: + case OP_MOD: + /* Division by 0 has been already diagnosed by eval_eval(). */ + chk = -v1 == v1 && v2 == -1; + if (chk /* LONG_MIN / -1 on two's complement */ +#if HAVE_LONG_LONG + || (! stdc3 + && (long)-v1 == (long)v1 && (long)v2 == (long)-1) +#endif + ) + overflow( op_name, valpp, chk); + else if (! stdc3 && (v1 < 0L || v2 < 0L) + && ((! skip && (warn_level & 1)) + || (skip && (warn_level & 8)))) + cwarn( not_portable, op_name, 0L, skip ? non_eval : NULL); + if (op == OP_DIV) + v1 /= v2; + else + v1 %= v2; + break; + case OP_ADD: + val = v1 + v2; + chk = (v2 > 0L && v1 > val) || (v2 < 0L && v1 < val); + if (chk +#if HAVE_LONG_LONG + || (! stdc3 + && (((long)v2 > 0L && (long)v1 > (long)val) + || ((long)v2 < 0L && (long)v1 < (long)val))) +#endif + ) + overflow( op_name, valpp, chk); + v1 = val; + break; + case OP_SUB: + val = v1 - v2; + chk = (v2 > 0L && val > v1) || (v2 < 0L && val < v1); + if (chk +#if HAVE_LONG_LONG + || (! stdc3 + && (((long)v2 > 0L && (long)val > (long)v1) + || ((long)v2 < 0L && (long)val < (long)v1))) +#endif + ) + overflow( op_name, valpp, chk); + v1 = val; + break; + case OP_SL: v1 <<= v2; break; + case OP_SR: + if (v1 < 0L + && ((!skip && (warn_level & 1)) + || (skip && (warn_level & 8)))) + cwarn( not_portable, op_name, 0L, skip ? non_eval : NULL); + v1 >>= v2; + break; + case OP_LT: v1 = (v1 < v2); break; + case OP_LE: v1 = (v1 <= v2); break; + case OP_GT: v1 = (v1 > v2); break; + case OP_GE: v1 = (v1 >= v2); break; + case OP_EQ: v1 = (v1 == v2); break; + case OP_NE: v1 = (v1 != v2); break; + case OP_AND: v1 &= v2; break; + case OP_XOR: v1 ^= v2; break; + case OP_OR: v1 |= v2; break; + case OP_ANA: v1 = (v1 && v2); break; + case OP_ORO: v1 = (v1 || v2); break; + case OP_COL: + /* + * If v1 has the "true" value, v2 has the "false" value. + * The top of the value stack has the test. + */ + v1 = (--*valpp)->val ? v1 : v2; + break; + default: + cfatal( illeg_op, op_name, 0L, NULL); + } + + *valpp = valp; + return v1; +} + +static expr_t eval_unsigned( + VAL_SIGN ** valpp, + uexpr_t v1u, + uexpr_t v2u, + int op +) +/* + * Apply the argument operator to the unsigned data. + * Called from eval_eval() only in Standard mode. + */ +{ + const char * const illeg_op + = "Bug: Illegal operator \"%s\" in eval_unsigned()"; /* _F_ */ + const char * op_name = opname[ op]; + VAL_SIGN * valp = *valpp; + uexpr_t v1 = 0; + int chk; /* Flag of overflow in unsigned long long */ + int minus; /* Big integer converted from signed long */ + + minus = ! stdc3 && (v1u > ULONGMAX || v2u > ULONGMAX); + + switch (op) { + case OP_EOE: + case OP_PLU: v1 = v1u; break; + case OP_NEG: + v1 = -v1u; + if (v1u) + overflow( op_name, valpp, TRUE); + break; + case OP_COM: v1 = ~v1u; break; + case OP_NOT: v1 = !v1u; break; + case OP_MUL: + v1 = v1u * v2u; + chk = v1u && v2u && (v1 / v2u != v1u || v1 / v1u != v2u); + if (chk +#if HAVE_LONG_LONG + || (! stdc3 && ! minus && v1 > ULONGMAX) +#endif + ) + overflow( op_name, valpp, chk); + break; + case OP_DIV: + /* Division by 0 has been already diagnosed by eval_eval(). */ + v1 = v1u / v2u; + break; + case OP_MOD: + v1 = v1u % v2u; + break; + case OP_ADD: + v1 = v1u + v2u; + chk = v1 < v1u; + if (chk +#if HAVE_LONG_LONG + || (! stdc3 && ! minus && v1 > ULONGMAX) +#endif + ) + overflow( op_name, valpp, chk); + break; + case OP_SUB: + v1 = v1u - v2u; + chk = v1 > v1u; + if (chk +#if HAVE_LONG_LONG + || (! stdc3 && ! minus && v1 > ULONGMAX) +#endif + ) + overflow( op_name, valpp, chk); + break; + case OP_SL: v1 = v1u << v2u; break; + case OP_SR: v1 = v1u >> v2u; break; + case OP_LT: v1 = (v1u < v2u); break; + case OP_LE: v1 = (v1u <= v2u); break; + case OP_GT: v1 = (v1u > v2u); break; + case OP_GE: v1 = (v1u >= v2u); break; + case OP_EQ: v1 = (v1u == v2u); break; + case OP_NE: v1 = (v1u != v2u); break; + case OP_AND: v1 = v1u & v2u; break; + case OP_XOR: v1 = v1u ^ v2u; break; + case OP_OR: v1 = v1u | v2u; break; + case OP_ANA: v1 = (v1u && v2u); break; + case OP_ORO: v1 = (v1u || v2u); break; + case OP_COL: valp--; + if (valp->val) + v1 = v1u; + else + v1 = v2u; + break; + default: + cfatal( illeg_op, op_name, 0L, NULL); + } + + *valpp = valp; + return v1; +} + +static void overflow( + const char * op_name, + VAL_SIGN ** valpp, + int ll_overflow /* Flag of overflow in long long */ +) +{ + const char * const out_of_range + = "Result of \"%s\" is out of range%.0ld%s"; /* _E_ _W1_ _W8_ */ + +#if HAVE_LONG_LONG + if (standard && ! ll_overflow) { + /* Overflow of long not in C99 mode */ + if ((! skip && (warn_level & w_level)) || (skip && (warn_level & 8))) + cwarn( out_of_range, op_name, 0L, " of (unsigned) long"); + } else +#endif + if (skip) { + if (warn_level & 8) + cwarn( out_of_range, op_name, 0L, non_eval); + /* Else don't warn */ + } else if (standard && (*valpp)->sign == UNSIGNED) {/* Never overflow */ + if (warn_level & 1) + cwarn( out_of_range, op_name, 0L, NULL); + } else { + cerror( out_of_range, op_name, 0L, NULL); + (*valpp)->sign = VAL_ERROR; + } +} + +static void dump_val( + const char * msg, + const VAL_SIGN * valp +) +/* + * Dump a value by internal representation. + */ +{ +#if HAVE_LONG_LONG + const char * const format + = "%s(%ssigned long long) 0x%016" LL_FORM "x"; +#else + const char * const format = "%s(%ssigned long) 0x%08lx"; +#endif + int sign = valp->sign; + + mcpp_fprintf( DBG, format, msg, sign ? "" : "un", valp->val); +} + +static void dump_stack( + const OPTAB * opstack, /* Operator stack */ + const OPTAB * opp, /* Pointer into operator stack */ + const VAL_SIGN * value, /* Value stack */ + const VAL_SIGN * valp /* -> value vector */ +) +/* + * Dump stacked operators and values. + */ +{ + if (opstack < opp) + mcpp_fprintf( DBG, "Index op prec skip name -- op stack at %s" + , infile->bptr); + + while (opstack < opp) { + mcpp_fprintf( DBG, " [%2d] %2d %04o %d %s\n", (int)(opp - opstack) + , opp->op, opp->prec, opp->skip, opname[ opp->op]); + opp--; + } + + while (value <= --valp) { + mcpp_fprintf( DBG, "value[%d].val = ", (int)(valp - value)); + dump_val( "", valp); + mcpp_fputc( '\n', DBG); + } +} + diff --git a/lib/mcpp/expand.c b/lib/mcpp/expand.c new file mode 100644 index 000000000..08f829a63 --- /dev/null +++ b/lib/mcpp/expand.c @@ -0,0 +1,2980 @@ +/*- + * Copyright (c) 1998, 2002-2008 Kiyoshi Matsui + * All rights reserved. + * + * Some parts of this code are derived from the public domain software + * DECUS cpp (1984,1985) written by Martin Minow. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * 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 AUTHOR 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. + */ + +/* + * E X P A N D . C + * M a c r o E x p a n s i o n + * + * The macro expansion routines are placed here. + */ + +#if PREPROCESSED +#include "mcpp.H" +#else +#include "system.H" +#include "internal.H" +#endif + +#define ARG_ERROR (-255) +#define CERROR 1 +#define CWARN 2 + +typedef struct location { /* Where macro or arg locate */ + long start_line; /* Beginning at 1 */ + size_t start_col; /* Beginning at 0 */ + long end_line; + size_t end_col; +} LOCATION; +typedef struct magic_seq { /* Data of a sequence inserted between tokens */ + char * magic_start; /* First MAC_INF sequence */ + char * magic_end; /* End of last MAC_INF seq */ + int space; /* Space succeeds or not */ +} MAGIC_SEQ; + +static int compat_mode; +/* Expand recursive macro more than Standard (for compatibility with GNUC) */ +#if COMPILER == GNUC +static int ansi; /* __STRICT_ANSI__ flag */ +#endif + +static char * expand_std( DEFBUF * defp, char * out, char * out_end + , LINE_COL line_col, int * pragma_op); + /* Expand a macro completely (for Standard modes) */ +static char * expand_prestd( DEFBUF * defp, char * out, char * out_end + , LINE_COL line_col, int * pragma_op); + /* Expand a macro completely (for pre-Standard modes) */ +static DEFBUF * is_macro_call( DEFBUF * defp, char ** cp, char ** endf + , MAGIC_SEQ * mgc_seq); /* Is this really a macro call ? */ +static int collect_args( const DEFBUF * defp, char ** arglist, int m_num); + /* Collect arguments of a macro call*/ +static int get_an_arg( int c, char ** argpp, char * arg_end + , char ** seqp, int var_arg, int nargs, LOCATION ** locp, int m_num + , MAGIC_SEQ * mgc_prefix); /* Get an argument */ +static int squeeze_ws( char ** out, char ** endf, MAGIC_SEQ * mgc_seq); + /* Squeeze white spaces to a space */ +static void skip_macro( void); + /* Skip the rest of macro call */ +static void diag_macro( int severity, const char * format + , const char * arg1, long arg2, const char * arg3, const DEFBUF * defp1 + , const DEFBUF * defp2) ; + /* Supplement diagnostic information*/ +static void dump_args( const char * why, int nargs, const char ** arglist); + /* Dump arguments list */ + +static int rescan_level; /* Times of macro rescan */ + +static const char * const macbuf_overflow + = "Buffer overflow expanding macro \"%s\" at %.0ld\"%s\""; /* _E_ */ +static const char * const empty_arg + = "Empty argument in macro call \"%s\""; /* _W2_ */ +static const char * const unterm_macro + = "Unterminated macro call \"%s\""; /* _E_ */ +static const char * const narg_error + = "%s than necessary %ld argument(s) in macro call \"%s\""; /* _E_ _W1_ */ +static const char * const only_name + = "Macro \"%s\" needs arguments"; /* _W8_ */ + +void expand_init( + int compat, /* "Compatible" to GNUC expansion of recursive macro*/ + int strict_ansi /* __STRICT_ANSI__ flag for GNUC */ +) +/* Set expand_macro() function */ +{ + expand_macro = standard ? expand_std : expand_prestd; + compat_mode = compat; +#if COMPILER == GNUC + ansi = strict_ansi; +#endif +} + +DEFBUF * is_macro( + char ** cp +) +/* + * The name is already in 'identifier', the next token is not yet read. + * Return the definition info if the name is a macro call, else return NULL. + */ +{ + DEFBUF * defp; + + if ((defp = look_id( identifier)) != NULL) /* Is a macro name */ + return is_macro_call( defp, cp, NULL, NULL); + else + return NULL; +} + +static DEFBUF * is_macro_call( + DEFBUF * defp, + char ** cp, /* Pointer to output buffer */ + char ** endf, /* Pointer to indicate end of infile buffer */ + MAGIC_SEQ * mgc_seq /* Infs on MAC_INF sequences and space */ +) +/* + * Return DEFBUF if the defp->name is a macro call, else return NULL. + */ +{ + int c; + + if (defp->nargs >= 0 /* Function-like macro */ + || defp->nargs == DEF_PRAGMA) { /* _Pragma() pseudo-macro */ + c = squeeze_ws( cp, endf, mgc_seq); /* See the next char. */ + if (c == CHAR_EOF) /* End of file */ + unget_string( "\n", NULL); /* Restore skipped '\n' */ + else if (! standard || c != RT_END) + /* Still in the file and rescan boundary ? */ + unget_ch(); /* To see it again */ + if (c != '(') { /* Only the name of function-like macro */ + if (! standard && warn_level & 8) + cwarn( only_name, defp->name, 0L, NULL); + return NULL; + } + } + return defp; /* Really a macro call */ +} + +/* + * expand_macro() expands a macro call completely, and writes out the result + * to the specified output buffer and returns the advanced pointer. + */ + + +/* + * T h e S T A N D A R D C o n f o r m i n g M o d e + * o f M a c r o E x p a n s i o n + * + * 1998/08 First released. kmatsui + */ + +/* For debug of -K option: should be turned off on release version. */ +#define DEBUG_MACRO_ANN FALSE + +/* Return value of is_able_repl() */ +#define NO 0 /* "Blue-painted" */ +#define YES 1 /* Not blue-painted */ +#define READ_OVER 2 + /* Still "blue-painted", yet has read over repl-list */ + +/* + * Macros related to macro notification mode. + * The macro notification routines are hacks on the normal processing + * routines, and very complicated and cumbersome. Be sure to keep symmetry + * of starting and closing magic sequences. Enable the routines enclosed + * by #if 0 - #endif for debugging. + * Any byte in the sequences beginning with MAC_INF can coincide with any + * other character. Hence, the data stream should be read from the top, + * not from the tail, in principle. + */ +/* Length of sequence of MAC_INF, MAC_CALL_START, mac_num-1, mac_num-2 */ +#define MAC_S_LEN 4 +/* Length of sequence of MAC_INF, MAC_ARG_START, mac_num1, mac_num2, arg-num*/ +#define ARG_S_LEN 5 +#define MAC_E_LEN 2 /* Length of MAC_INF, MAC_CALL_END sequence */ +#define ARG_E_LEN MAC_E_LEN /* Lenght of MAC_INF, MAC_ARG_END sequence */ +#define MAC_E_LEN_V 4 /* Length of macro closing sequence in verbose mode */ + /* MAC_INF, MAC_CALL_END, mac_num1, mac_num2 */ +#define ARG_E_LEN_V 5 /* Length of argument closing sequence in verbose */ + /* MAC_INF, MAC_ARG_END, mac_num1, mac_num2, arg_num */ +#define IN_SRC_LEN 3 /* Length of sequence of IN_SRC, num-1, num-2 */ +#define INIT_MAC_INF 0x100 /* Initial num of elements in mac_inf[] */ +#define MAX_MAC_INF 0x1000 /* Maximum num of elements in mac_inf[] */ +#define INIT_IN_SRC_NUM 0x100 /* Initial num of elements in in_src[] */ +#define MAX_IN_SRC_NUM 0x1000 /* Maximum num of elements in in_src[] */ + +/* Variables for macro notification mode */ +typedef struct macro_inf { /* Informations of a macro */ + const DEFBUF * defp; /* Definition of the macro */ + char * args; /* Arguments, if any */ + int num_args; /* Number of real arguments */ + int recur; /* Recurrence of this macro */ + LOCATION locs; /* Location of macro call */ + LOCATION * loc_args; /* Location of arguments */ +} MACRO_INF; +static MACRO_INF * mac_inf; +static int max_mac_num; /* Current num of elements in mac_inf[] */ +static int mac_num; /* Index into mac_inf[] */ +static LOCATION * in_src; /* Location of identifiers in macro arguments */ +static int max_in_src_num; /* Current num of elements in in_src[] */ +static int in_src_num; /* Index into in_src[] */ +static int trace_macro; /* Enable to trace macro infs */ + +static struct { + const DEFBUF * def; /* Macro definition */ + int read_over; /* Has read over repl-list */ + /* 'read_over' is never used in POST_STD mode and in compat_mode*/ +} replacing[ RESCAN_LIMIT]; /* Macros currently replacing */ +static int has_pragma = FALSE; /* Flag of _Pragma() operator */ + +static int print_macro_inf( int c, char ** cpp, char ** opp); + /* Embed macro infs into comments */ +static char * print_macro_arg( char *out, MACRO_INF * m_inf, int argn + , int real_arg, int start); + /* Embed macro arg inf into comments*/ +static char * chk_magic_balance( char * buf, char * buf_end, int move + , int diag); /* Check imbalance of magics */ +static char * replace( DEFBUF * defp, char * out, char * out_end + , const DEFBUF * outer, FILEINFO * rt_file, LINE_COL line_col + , int in_src_n); + /* Replace a macro recursively */ +static char * close_macro_inf( char * out_p, int m_num, int in_src_n); + /* Put closing mark for a macro call*/ +static DEFBUF * def_special( DEFBUF * defp); + /* Re-define __LINE__, __FILE__ */ +static int prescan( const DEFBUF * defp, const char ** arglist + , char * out, char * out_end); + /* Process #, ## operator */ +static char * catenate( const DEFBUF * defp, const char ** arglist + , char * out, char * out_end, char ** token_p); + /* Catenate tokens */ +static const char * remove_magics( const char * argp, int from_last); + /* Remove pair of magic characters */ +#if DEBUG_MACRO_ANN +static void chk_symmetry( char * start_id, char * end_id, size_t len); + /* Check if a pair of magics are symmetrical */ +#endif +static char * stringize( const DEFBUF * defp, const char * argp, char * out); + /* Stringize an argument */ +static char * substitute( const DEFBUF * defp, const char ** arglist + , const char * in, char * out, char * out_end); + /* Substitute parms with arguments */ +static char * rescan( const DEFBUF * outer, const char * in, char * out + , char * out_end); + /* Rescan once replaced sequences */ +static int disable_repl( const DEFBUF * defp); + /* Disable the macro once replaced */ +static void enable_repl( const DEFBUF * defp, int done); + /* Enable the macro for later use */ +static int is_able_repl( const DEFBUF * defp); + /* Is the macro allowed to replace? */ +static char * insert_to_bptr( char * ins, size_t len); + /* Insert a sequence into infile->bptr */ + +static char * expand_std( + DEFBUF * defp, /* Macro definition */ + char * out, /* Output buffer */ + char * out_end, /* End of output buffer */ + LINE_COL line_col, /* Location of macro */ + int * pragma_op /* _Pragma() is found ? */ +) +/* + * Expand a macro call completely, write the results to the specified buffer + * and return the advanced output pointer. + */ +{ + char macrobuf[ NMACWORK + IDMAX]; /* Buffer for replace() */ + char * out_p = out; + size_t len; + int c, c1; + char * cp; + + has_pragma = FALSE; /* Have to re-initialize*/ + macro_line = src_line; /* Line number for diag */ + macro_name = defp->name; + rescan_level = 0; + trace_macro = (mcpp_mode == STD) && (mcpp_debug & MACRO_CALL) + && ! in_directive; + if (trace_macro) { + max_mac_num = INIT_MAC_INF; + mac_inf = (MACRO_INF *) xmalloc( sizeof (MACRO_INF) * max_mac_num); + memset( mac_inf, 0, sizeof (MACRO_INF) * max_mac_num); + max_in_src_num = INIT_IN_SRC_NUM; + in_src = (LOCATION *) xmalloc( sizeof (LOCATION) * max_in_src_num); + memset( in_src, 0, sizeof (LOCATION) * max_in_src_num); + mac_num = in_src_num = 0; /* Initialize */ + } + if (replace( defp, macrobuf, macrobuf + NMACWORK, NULL, infile, line_col + , 0) == NULL) { /* Illegal macro call */ + skip_macro(); + macro_line = MACRO_ERROR; + goto exp_end; + } + len = (size_t) (out_end - out); + if (strlen( macrobuf) > len) { + cerror( macbuf_overflow, macro_name, 0, macrobuf); + memcpy( out, macrobuf, len); + out_p = out + len; + macro_line = MACRO_ERROR; + goto exp_end; + } + +#if DEBUG_MACRO_ANN + chk_magic_balance( macrobuf, macrobuf + strlen( macrobuf), FALSE, TRUE); +#endif + cp = macrobuf; + c1 = '\0'; /* The char previous to 'c' */ + while ((c = *cp++) != EOS) { + if (c == DEF_MAGIC) + continue; /* Skip DEF_MAGIC */ + if (mcpp_mode == STD) { + if (c == IN_SRC) { /* Skip IN_SRC */ + if (trace_macro) + cp += 2; /* Skip also the number (coded in 2 bytes) */ + continue; + } else if (c == TOK_SEP) { + /* Remove redundant token separator */ + if ((char_type[ c1 & UCHARMAX] & HSP) + || (char_type[ *cp & UCHARMAX] & HSP) + || in_include || option_flags.lang_asm + || (*cp == MAC_INF && *(cp + 1) == MAC_CALL_END) + || (!option_flags.v && c1 == MAC_CALL_END) + || (option_flags.v + && *(cp - MAC_E_LEN_V - 1) == MAC_INF + && *(cp - MAC_E_LEN_V) == MAC_CALL_END)) + continue; + /* Skip separator just after ' ', '\t' */ + /* and just after MAC_CALL_END. */ + /* Also skip this in lang_asm mode, #include */ + /* Skip just before another TOK_SEP, ' ', '\t' */ + /* Skip just before MAC_INF,MAC_CALL_END seq too*/ + else + c = ' '; /* Else convert to ' ' */ + } else if (trace_macro && (c == MAC_INF)) { + /* Embed macro expansion informations into comments */ + c = *cp++; + c1 = print_macro_inf( c, &cp, &out_p); + if (out_end <= out_p) { + cerror( macbuf_overflow, macro_name, 0, out); + macro_line = MACRO_ERROR; + goto exp_end; + } + continue; + } + } + *out_p++ = c1 = c; + } + + macro_line = 0; +exp_end: + *out_p = EOS; + if (mcpp_debug & EXPAND) + dump_string( "expand_std exit", out); + macro_name = NULL; + clear_exp_mac(); /* Clear the information for diagnostic */ + if (trace_macro) { /* Clear macro informations */ + int num; + for (num = 1; num < mac_num; num++) { /* 'num' start at 1 */ + if (mac_inf[ num].num_args >= 0) { /* Macro with args */ + free( mac_inf[ num].args); /* Saved arguments */ + free( mac_inf[ num].loc_args); /* Location of args */ + } + } + free( mac_inf); + free( in_src); + } + *pragma_op = has_pragma; + + return out_p; +} + +static int print_macro_inf( + int c, + char ** cpp, /* Magic character sequence */ + char ** opp /* Output for macro information */ +) +/* + * Embed macro expansion information into comments. + * Enabled by '#pragma MCPP debug macro_call' or -K option in STD mode. + */ +{ + MACRO_INF * m_inf; + int num; + int num_args; /* Number of actual args (maybe less than expected) */ + int i; + + if (*((*opp) - 1) == '/' && *((*opp) - 2) != '*') + /* Immediately preceding token is '/' (not '*' and '/') */ + *((*opp)++) = ' '; + /* Insert a space to separate with following '/' and '*' */ + if (option_flags.v || c == MAC_CALL_START || c == MAC_ARG_START) { + num = ((*(*cpp)++ & UCHARMAX) - 1) * UCHARMAX; + num += (*(*cpp)++ & UCHARMAX) - 1; + m_inf = & mac_inf[ num]; /* Saved information */ + } + switch (c) { + case MAC_CALL_START : /* Start of a macro expansion */ + *opp += sprintf( *opp, "/*<%s", m_inf->defp->name); /* Macro name */ + if (m_inf->locs.start_line) { + /* Location of the macro call in source file */ + *opp += sprintf( *opp, " %ld:%d-%ld:%d" + , m_inf->locs.start_line, (int) m_inf->locs.start_col + , m_inf->locs.end_line, (int) m_inf->locs.end_col); + } + *opp = stpcpy( *opp, "*/"); + if ((num_args = m_inf->num_args) >= 1) { + /* The macro has arguments. Show the locations. */ + for (i = 0; i < num_args; i++) /* Arg num begins at 0 */ + *opp = print_macro_arg( *opp, m_inf, i, TRUE, TRUE); + } + break; + case MAC_ARG_START : /* Start of an argument */ + i = (*(*cpp)++ & UCHARMAX) - 1; /* Argument number */ + *opp = print_macro_arg( *opp, m_inf, i, FALSE, TRUE); + break; + case MAC_CALL_END : /* End of a macro expansion */ + if (option_flags.v) { /* Verbose mode */ + *opp += sprintf( *opp, "/*%s>*/", m_inf->defp->name); + break; + } + /* Else fall through */ + case MAC_ARG_END : /* End of an argument */ + if (option_flags.v) { + i = (*(*cpp)++ & UCHARMAX) - 1; + /* Output verbose infs symmetrical to start of the arg infs */ + *opp = print_macro_arg( *opp, m_inf, i, FALSE, FALSE); + } else { + *opp = stpcpy( *opp, "/*>*/"); + } + break; + } + + return **cpp & UCHARMAX; +} + +static char * print_macro_arg( + char * out, /* Output buffer */ + MACRO_INF * m_inf, /* &mac_inf[ m_num] */ + int argn, /* Argument number */ + int real_arg, /* Real argument or expanded argument ? */ + int start /* Start of an argument or end ? */ +) +/* + * Embed an argument information into a comment. + * This routine is only called from above print_macro_inf(). + */ +{ + LOCATION * loc = m_inf->loc_args + argn; + + out += sprintf( out, "/*%s%s:%d-%d", real_arg ? "!" : (start ? "<" : "") + , m_inf->defp->name, m_inf->recur, argn); + + if (real_arg && m_inf->loc_args && loc->start_line) { + /* Location of the argument in source file */ + out += sprintf( out, " %ld:%d-%ld:%d", loc->start_line + , (int) loc->start_col, loc->end_line, (int) loc->end_col); + } + if (! start) /* End of an argument in verbose mode */ + out = stpcpy( out, ">"); + out = stpcpy( out, "*/"); + + return out; +} + +static char * chk_magic_balance( + char * buf, /* Sequence to check */ + char * buf_end, /* End of the sequence */ + int move, /* Move a straying magic ? */ + int diag /* Output a diagnostic? */ +) +/* + * Check imbalance of macro information magics and warn it. + * get_an_arg() calls this routine setting 'move' argument on, hence a stray + * magic is moved to an edge if found. + * This routine does not do token parsing. Yet it will do fine practically. + */ +{ +#define MAX_NEST_MAGICS 255 + char mac_id[ MAX_NEST_MAGICS][ MAC_E_LEN_V - 2]; + char arg_id[ MAX_NEST_MAGICS][ ARG_E_LEN_V - 2]; + char * mac_loc[ MAX_NEST_MAGICS]; + char * arg_loc[ MAX_NEST_MAGICS]; + char * mesg = "%s %ld %s-closing-comment(s) in tracing macro"; + int mac, arg; + int mac_s_n, mac_e_n, arg_s_n, arg_e_n; + char * buf_p = buf; /* Save 'buf' for debugging purpose */ + + mac = arg = 0; + + while (buf_p < buf_end) { + if (*buf_p++ != MAC_INF) + continue; + switch (*buf_p++) { + case MAC_CALL_START : + if (option_flags.v) { + mac_loc[ mac] = buf_p - 2; + memcpy( mac_id[ mac], buf_p, MAC_S_LEN - 2); + } + mac++; + buf_p += MAC_S_LEN - 2; + break; + case MAC_ARG_START : + if (option_flags.v) { + arg_loc[ arg] = buf_p - 2; + memcpy( arg_id[ arg], buf_p, ARG_S_LEN - 2); + } + arg++; + buf_p += ARG_S_LEN - 2; + break; + case MAC_ARG_END : + arg--; + if (option_flags.v) { + if (arg < 0) { /* Perhaps moved magic */ + if (diag) + cwarn( mesg, "Redundant", (long) -arg, "argument"); + } else if (memcmp( arg_id[ arg], buf_p, ARG_E_LEN_V - 2) != 0) + { + char * to_be_edge = NULL; + char * cur_edge; + + if (arg >= 1 && memcmp( arg_id[ 0], buf_p, ARG_E_LEN_V - 2) + == 0) { + to_be_edge = arg_loc[ arg]; + /* To be moved to top */ + cur_edge = arg_loc[ 0]; /* Current top */ + } else if (arg == 0) { + char arg_end_magic[ 2] = { MAC_INF, MAC_ARG_END}; + cur_edge = buf_end - ARG_E_LEN_V; + /* Search the last magic */ + /* Sequence from get_an_arg() is always */ + /* surrounded by starting of an arg magic */ + /* and its corresponding closing magic. */ + while (buf_p + (ARG_E_LEN_V - 2) <= cur_edge + && memcmp( cur_edge, arg_end_magic, 2) != 0) + cur_edge--; + if (buf_p + (ARG_E_LEN_V - 2) <= cur_edge + && memcmp( arg_id[ 0], cur_edge + 2 + , ARG_E_LEN_V - 2) == 0) { + to_be_edge = buf_p - 2; /* To be moved to end */ + } + } + if (to_be_edge) { /* Appropriate place found */ + if (diag) { + mac_s_n = ((to_be_edge[ 2] & UCHARMAX) - 1) + * UCHARMAX; + mac_s_n += (to_be_edge[ 3] & UCHARMAX) - 1; + arg_s_n = (to_be_edge[ 4] & UCHARMAX) - 1; + mcpp_fprintf( ERR, + "Stray arg inf of macro: %d:%d at line:%d\n" + , mac_s_n, arg_s_n, src_line); + } + if (move) { + /* Move a stray magic to outside of sequences */ + char magic[ ARG_E_LEN_V]; + size_t len = ARG_E_LEN_V; + memcpy( magic, cur_edge, len); + /* Save current edge */ + if (to_be_edge == arg_loc[ arg]) + /* Shift followings to cur_edge */ + memmove( cur_edge, cur_edge + len + , to_be_edge - cur_edge); + else /* Shift precedents to cur_edge */ + memmove( to_be_edge + len, to_be_edge + , cur_edge - to_be_edge); + memcpy( to_be_edge, magic, len); + /* Restore old 'cur_edge' into old 'to_be_edge' */ + } + } else { /* Serious imbalance, just warn */ + char * arg_p = arg_id[ arg]; + arg_s_n = ((arg_p[ 0] & UCHARMAX) - 1) * UCHARMAX; + arg_s_n += (arg_p[ 1] & UCHARMAX) - 1; + arg_e_n = ((buf_p[ 0] & UCHARMAX) - 1) * UCHARMAX; + arg_e_n += (buf_p[ 1] & UCHARMAX) - 1; + mcpp_fprintf( ERR, + "Asymmetry of arg inf found: start %d, end %d at line:%d\n" + , arg_s_n, arg_e_n, src_line); + } + } + buf_p += ARG_E_LEN_V - 2; + } + break; + case MAC_CALL_END : + mac--; + if (option_flags.v) { + if (mac < 0) { + if (diag) + cwarn( mesg, "Redundant", (long) -mac, "macro"); + } else if (memcmp( mac_id[ mac], buf_p, MAC_E_LEN_V - 2) != 0) + { + char * mac_p = mac_id[ mac]; + mac_s_n = ((mac_p[ 0] & UCHARMAX) - 1) * UCHARMAX; + mac_s_n += (mac_p[ 1] & UCHARMAX) - 1; + mac_e_n = ((buf_p[ 0] & UCHARMAX) - 1) * UCHARMAX; + mac_e_n += (buf_p[ 1] & UCHARMAX) - 1; + mcpp_fprintf( ERR, + "Asymmetry of macro inf found: start %d, end %d at line:%d\n" + , mac_s_n, mac_e_n, src_line); + } + buf_p += MAC_E_LEN_V - 2; + } + break; + default : /* Not a MAC_INF sequence */ + break; /* Continue */ + } + } + + if (diag && (warn_level & 1)) { + if (mac > 0) + cwarn( mesg, "Lacking", (long) mac, "macro"); + if (arg > 0) + cwarn( mesg, "Lacking", (long) arg, "argument"); + if ((mac || arg) && (mcpp_debug & EXPAND)) + mcpp_fputs( +"Imbalance of magics occurred (perhaps a moved magic), see and diagnostics.\n" + , DBG); + } + + return buf; +} + +static char * replace( + DEFBUF * defp, /* Macro to be replaced */ + char * out, /* Output Buffer */ + char * out_end, /* End of output buffer */ + const DEFBUF * outer, /* Outer macro replacing*/ + FILEINFO * rt_file, /* Repl-text "file" */ + LINE_COL line_col, /* Location of macro */ + int in_src_n /* Index into in_src[] */ +) +/* + * Replace a possibly nested macro recursively. + * replace() and rescan() call each other recursively. + * Return the advanced output pointer or NULL on error. + */ +{ + char ** arglist = NULL; /* Pointers to arguments*/ + int nargs; /* Number of arguments expected */ + char * catbuf; /* Buffer for prescan() */ + char * expbuf; /* Buffer for substitute() */ + char * out_p; /* Output pointer */ + char * cur_out = out; /* One more output pointer */ + int num_args; + /* Number of actual arguments (maybe less than expected) */ + int enable_trace_macro; /* To exclude _Pragma() pseudo macro */ + int m_num = 0; /* 'mac_num' of current macro */ + MACRO_INF * m_inf; /* Pointer into mac_inf[] */ + + if (mcpp_debug & EXPAND) { + dump_a_def( "replace entry", defp, FALSE, TRUE, fp_debug); + dump_unget( "replace entry"); + } + if ((mcpp_debug & MACRO_CALL) && in_if) + mcpp_fprintf( OUT, "/*%s*/", defp->name); + + enable_trace_macro = trace_macro && defp->nargs != DEF_PRAGMA; + if (enable_trace_macro) { + int num; + int recurs; + + if (mac_num >= MAX_MAC_INF - 1) { + cerror( "Too many nested macros in tracing %s" /* _E_ */ + , defp->name, 0L, NULL); + return NULL; + } else if (mac_num >= max_mac_num - 1) { + size_t len = sizeof (MACRO_INF) * max_mac_num; + /* Enlarge the array */ + mac_inf = (MACRO_INF *) xrealloc( (char *) mac_inf, len * 2); + memset( mac_inf + max_mac_num, 0, len); + /* Clear the latter half */ + max_mac_num *= 2; + } + m_num = ++mac_num; /* Remember this number */ + /* Note 'mac_num' starts at 1 */ + *cur_out++ = MAC_INF; /* Embed a magic char */ + *cur_out++ = MAC_CALL_START; /* A macro call */ + /* Its index number, can be greater than UCHARMAX */ + /* We represent the number by 2 bytes where each byte is not '\0' */ + *cur_out++ = (m_num / UCHARMAX) + 1; + *cur_out++ = (m_num % UCHARMAX) + 1; + *cur_out = EOS; + m_inf = & mac_inf[ m_num]; + m_inf->defp = defp; /* The macro definition */ + m_inf->num_args = 0; /* Default num of args */ + if (line_col.line) { + get_src_location( & line_col); + m_inf->locs.start_line = line_col.line; + m_inf->locs.start_col = line_col.col; + } else { + m_inf->locs.start_col = m_inf->locs.start_line = 0L; + } + m_inf->args = m_inf->loc_args = NULL; /* Default args */ + for (num = 1, recurs = 0; num < m_num; num++) + if (mac_inf[ num].defp == defp) + recurs++; /* Recursively nested macro */ + m_inf->recur = recurs; + } + + nargs = (defp->nargs == DEF_PRAGMA) ? 1 : (defp->nargs & ~AVA_ARGS); + + if (nargs < DEF_NOARGS_DYNAMIC) { /* __FILE__, __LINE__ */ + defp = def_special( defp); /* These are redefined dynamically */ + if (mcpp_mode == STD) { + /* Wrap repl-text with token separators to prevent token merging */ + *cur_out++ = TOK_SEP; + cur_out = stpcpy( cur_out, defp->repl); + *cur_out++ = TOK_SEP; + *cur_out = EOS; + } else { + cur_out = stpcpy( cur_out, defp->repl); + } + if (enable_trace_macro) { + m_inf->defp = defp; /* Redefined dynamically*/ + cur_out = close_macro_inf( cur_out, m_num, in_src_n); + } + return cur_out; + } else if (nargs == DEF_NOARGS_PREDEF_OLD && standard + && (warn_level & 1)) { /* Some macros on GCC */ + cwarn( "Old style predefined macro \"%s\" is used", /* _W2_ */ + defp->name, 0L, NULL); + } else if (nargs >= 0) { /* Function-like macro */ + squeeze_ws( NULL, NULL, NULL); /* Skip to '(' */ + /* Magic sequences are already read over by is_macro_call() */ + arglist = (char **) xmalloc( (nargs + 1) * sizeof (char *)); + arglist[ 0] = xmalloc( (size_t) (NMACWORK + IDMAX * 2)); + /* Note: arglist[ n] may be reallocated */ + /* and re-written by collect_args() */ + if ((num_args = collect_args( defp, arglist, m_num)) == ARG_ERROR) { + free( arglist[ 0]); /* Syntax error */ + free( arglist); + return NULL; + } + if (enable_trace_macro) { + /* Save the arglist for later informations */ + m_inf->args = arglist[ 0]; + m_inf->num_args = num_args; /* Number of actual args*/ + } + if (mcpp_mode == STD && outer && rt_file != infile) { + /* Has read over replacement-text */ + if (compat_mode) { + enable_repl( outer, FALSE); /* Enable re-expansion */ + if (mcpp_debug & EXPAND) + dump_string( "enabled re-expansion" + , outer ? outer->name : ""); + } else { + replacing[ rescan_level-1].read_over = READ_OVER; + } + } + } + + catbuf = xmalloc( (size_t) (NMACWORK + IDMAX)); + if (mcpp_debug & EXPAND) { + mcpp_fprintf( DBG, "(%s)", defp->name); + dump_string( "prescan entry", defp->repl); + } + if (prescan( defp, (const char **) arglist, catbuf, catbuf + NMACWORK) + == FALSE) { /* Process #, ## operators */ + diag_macro( CERROR, macbuf_overflow, defp->name, 0L, catbuf, defp + , NULL); + if (nargs >= 0) { + if (! enable_trace_macro) + /* arglist[0] is needed for macro infs */ + free( arglist[ 0]); + free( arglist); + } + free( catbuf); + return NULL; + } + catbuf = xrealloc( catbuf, strlen( catbuf) + 1); + /* Use memory sparingly */ + if (mcpp_debug & EXPAND) { + mcpp_fprintf( DBG, "(%s)", defp->name); + dump_string( "prescan exit", catbuf); + } + + if (nargs > 0) { /* Function-like macro with any argument */ + expbuf = xmalloc( (size_t) (NMACWORK + IDMAX)); + if (mcpp_debug & EXPAND) { + mcpp_fprintf( DBG, "(%s)", defp->name); + dump_string( "substitute entry", catbuf); + } + out_p = substitute( defp, (const char **) arglist, catbuf, expbuf + , expbuf + NMACWORK); /* Expand each arguments */ + if (! enable_trace_macro) + free( arglist[ 0]); + free( arglist); + free( catbuf); + expbuf = xrealloc( expbuf, strlen( expbuf) + 1); + /* Use memory sparingly */ + if (mcpp_debug & EXPAND) { + mcpp_fprintf( DBG, "(%s)", defp->name); + dump_string( "substitute exit", expbuf); + } + } else { /* Object-like macro or */ + if (nargs == 0 && ! enable_trace_macro) + /* Function-like macro with no argument */ + free( arglist[ 0]); + free( arglist); + out_p = expbuf = catbuf; + } + + if (out_p) + out_p = rescan( defp, expbuf, cur_out, out_end); + if (out_p && defp->nargs == DEF_PRAGMA) + has_pragma = TRUE; + /* Inform mcpp_main() that _Pragma() was found */ + free( expbuf); + if (enable_trace_macro && out_p) + out_p = close_macro_inf( out_p, m_num, in_src_n); + if (mcpp_debug & EXPAND) + dump_string( "replace exit", out); + + if (trace_macro && defp->nargs == DEF_PRAGMA) { + /* Remove intervening magics if the macro is _Pragma pseudo-macro */ + /* These magics have been inserted by macros in _Pragma()'s args */ + int c; + cur_out = out_p = out; + while ((c = *cur_out++) != EOS) { + if (c == MAC_INF) { + if (! option_flags.v) { + switch (*cur_out) { + case MAC_ARG_START : + cur_out++; + /* Fall through */ + case MAC_CALL_START : + cur_out++; + cur_out++; + /* Fall through */ + default: + cur_out++; + break; + } + } else { + switch (*cur_out) { + case MAC_ARG_START : + case MAC_ARG_END : + cur_out++; + /* Fall through */ + default: + cur_out += 3; + break; + } + } + } else { + *out_p++ = c; + } + } + *out_p = EOS; + } + + return out_p; +} + +static char * close_macro_inf( + char * out_p, /* Current output pointer */ + int m_num, /* 'mac_num' of this macro */ + int in_src_n /* Location of macro in arg */ +) +/* + * Mark up closing of a macro expansion. + * Note that 'm_num' argument is necessary rather than 'm_inf' from replace(), + * because mac_inf[] may have been reallocated while rescanning. + */ +{ + MACRO_INF * m_inf; + LINE_COL e_line_col; + + m_inf = & mac_inf[ m_num]; + *out_p++ = MAC_INF; /* Magic for end of macro expansion */ + *out_p++ = MAC_CALL_END; + if (option_flags.v) { + *out_p++ = (m_num / UCHARMAX) + 1; + *out_p++ = (m_num % UCHARMAX) + 1; + } + *out_p = EOS; + get_ch(); /* Clear the garbage */ + unget_ch(); + if (infile->fp || in_src_n) { + if (infile->fp) { /* Macro call on source file */ + e_line_col.line = src_line; + e_line_col.col = infile->bptr - infile->buffer; + } else { /* Macro in argument of parent macro and from source */ + e_line_col.line = in_src[ in_src_n].end_line; + e_line_col.col = in_src[ in_src_n].end_col; + } + /* Get the location before line splicing by */ + /* or by a line-crossing comment */ + get_src_location( & e_line_col); + m_inf->locs.end_line = e_line_col.line; + m_inf->locs.end_col = e_line_col.col; + } else { + m_inf->locs.end_col = m_inf->locs.end_line = 0L; + } + + return out_p; +} + +static DEFBUF * def_special( + DEFBUF * defp /* Macro definition */ +) +/* + * Re-define __LINE__, __FILE__. + * Return the new definition. + */ +{ + const FILEINFO * file; + DEFBUF ** prevp; + int cmp; + + switch (defp->nargs) { + case DEF_NOARGS_DYNAMIC - 1: /* __LINE__ */ + if ((src_line > std_limits.line_num || src_line <= 0) + && (warn_level & 1)) + diag_macro( CWARN + , "Line number %.0s\"%ld\" is out of range" /* _W1_ */ + , NULL, src_line, NULL, defp, NULL); + sprintf( defp->repl, "%ld", src_line); /* Re-define */ + break; + case DEF_NOARGS_DYNAMIC - 2: /* __FILE__ */ + for (file = infile; file != NULL; file = file->parent) { + if (file->fp != NULL) { + sprintf( work_buf, "\"%s\"", file->filename); + if (str_eq( work_buf, defp->repl)) + break; /* No change */ + defp->nargs = DEF_NOARGS; /* Enable to redefine */ + prevp = look_prev( defp->name, &cmp); + defp = install_macro( "__FILE__", DEF_NOARGS_DYNAMIC - 2, "" + , work_buf, prevp, cmp, 0); /* Re-define */ + break; + } + } + break; + } + return defp; +} + +static int prescan( + const DEFBUF * defp, /* Definition of the macro */ + const char ** arglist, /* Pointers to actual arguments */ + char * out, /* Output buffer */ + char * out_end /* End of output buffer */ +) +/* + * Concatenate the tokens surounding ## by catenate(), and stringize the + * argument following # by stringize(). + */ +{ + FILEINFO * file; + char * prev_token = NULL; /* Preceding token */ + char * horiz_space = NULL; /* Horizontal white space */ + int c; /* Value of a character */ + /* + * The replacement lists are -- + * stuff1stuff2 + * or stuff1stuff2stuff3... + * where is CAT, maybe with preceding space and following space, + * stuff might be + * ordinary-token + * MAC_PARM + * or MAC_PARM + * where is ST_QUO, possibly with following space. + * + * DEF_MAGIC may has been inserted sometimes. + * In other than POST_STD modes, TOK_SEP and IN_SRC may have been + * inserted, and TOK_SEPs are inserted also in this routine. + * In trace_macro mode, many magic character sequences may have been + * inserted here and there. + */ + + if (mcpp_mode == POST_STD) { + file = unget_string( defp->repl, defp->name); + } else { + *out++ = TOK_SEP; /* Wrap replacement */ + workp = work_buf; /* text with token */ + workp = stpcpy( workp, defp->repl); /* separators to */ + *workp++ = TOK_SEP; /* prevent unintended*/ + *workp = EOS; /* token merging. */ + file = unget_string( work_buf, defp->name); + } + + while (c = get_ch(), file == infile) { /* To the end of repl */ + + switch (c) { + case ST_QUOTE: + skip_ws(); /* Skip spaces and the returned MAC_PARM*/ + c = get_ch() - 1; /* Parameter number */ + prev_token = out; /* Remember the token */ + out = stringize( defp, arglist[ c], out); + /* Stringize without expansion */ + horiz_space = NULL; + break; + case CAT: + if (*prev_token == DEF_MAGIC || *prev_token == IN_SRC) { + /* Rare case yet possible after catenate() */ + size_t len = 1; + /* Remove trailing white space prior to removing DEF_MAGIC */ + if (horiz_space == out - 1) { + *--out = EOS; + horiz_space = NULL; + } + if (*prev_token == IN_SRC && trace_macro) + len = IN_SRC_LEN; + memmove( prev_token, prev_token + len + , strlen( prev_token + len)); + out -= len; + *out = EOS; /* Remove DEF_MAGIC, IN_SRC */ + } +#if COMPILER == GNUC + if (*prev_token == ',') + break; /* ', ##' sequence (peculiar to GCC) */ +#endif + if (horiz_space == out - 1) { + *--out = EOS; /* Remove trailing white space */ + horiz_space = NULL; + } + out = catenate( defp, arglist, out, out_end, &prev_token); + if (char_type[ *(out - 1) & UCHARMAX] & HSP) + horiz_space = out - 1; /* TOK_SEP has been appended */ + break; + case MAC_PARM: + prev_token = out; + *out++ = MAC_PARM; + *out++ = get_ch(); /* Parameter number */ + break; + case TOK_SEP: + case ' ': + case '\t' : + if (out - 1 == horiz_space) + continue; /* Squeeze white spaces */ + horiz_space = out; + *out++ = c; + break; + default: + prev_token = out; + scan_token( c, &out, out_end); /* Ordinary token */ + break; + } + + *out = EOS; /* Ensure termination */ + if (out_end <= out) /* Buffer overflow */ + return FALSE; + } + + *out = EOS; /* Ensure terminatation in case of no token */ + unget_ch(); + return TRUE; +} + +static char * catenate( + const DEFBUF * defp, /* The macro definition */ + const char ** arglist, /* Pointers to actual arguments */ + char * out, /* Output buffer */ + char * out_end, /* End of output buffer */ + char ** token_p /* Address of preceding token pointer */ +) +/* + * Concatenate the previous and the following tokens. + * Note: The parameter codes may coincide with white spaces or any + * other characters. + */ +{ + FILEINFO * file; + char * prev_prev_token = NULL; + const char * invalid_token + = "Not a valid preprocessing token \"%s\""; /* _E_ _W2_ */ + const char * argp; /* Pointer to an actual argument*/ + char * prev_token = *token_p; /* Preceding token */ + int in_arg = FALSE; + int c; /* Value of a character */ + + /* Get the previous token */ + if (*prev_token == MAC_PARM) { /* Formal parameter */ + c = (*(prev_token + 1) & UCHARMAX) - 1; /* Parm number */ + argp = arglist[ c]; /* Actual argument */ + out = prev_token; /* To overwrite */ + if (trace_macro) + argp = remove_magics( argp, TRUE); /* Remove pair of magics */ + if ((mcpp_mode == POST_STD && *argp == EOS) + || (mcpp_mode == STD && *argp == RT_END)) { + *out = EOS; /* An empty argument */ + } else { + if (mcpp_mode == POST_STD) { + file = unget_string( argp, NULL); + while (c = get_ch(), file == infile) { + prev_token = out; /* Remember the last token */ + scan_token( c, &out, out_end); + } /* Copy actual argument without expansion */ + unget_ch(); + } else { + unget_string( argp, NULL); + if (trace_macro) + free( (char *) argp); + /* malloc()ed in remove_magics() */ + while ((c = get_ch()) != RT_END) { + prev_prev_token = prev_token; + prev_token = out; /* Remember the last token */ + scan_token( c, &out, out_end); + } /* Copy actual argument without expansion */ + if (*prev_token == TOK_SEP) { + out = prev_token; + prev_token = prev_prev_token; /* Skip separator */ + } + } + if (*prev_token == DEF_MAGIC + || (mcpp_mode == STD && *prev_token == IN_SRC)) { + size_t len = 1; + if (trace_macro && *prev_token == IN_SRC) + len = IN_SRC_LEN; + memmove( prev_token, prev_token + len + , (size_t) ((out -= len) - prev_token)); + /* Remove DEF_MAGIC enabling the name to replace later */ + } + } + } /* Else the previous token is an ordinary token, not an argument */ + + c = skip_ws(); + + /* Catenate */ + switch (c) { + case ST_QUOTE: /* First stringize and then catenate */ + skip_ws(); /* Skip MAC_PARM, ST_QUOTE */ + c = get_ch() - 1; + out = stringize( defp, arglist[ c], out); + break; + case MAC_PARM: + c = get_ch() - 1; /* Parameter number */ + argp = arglist[ c]; /* Actual argument */ + if (trace_macro) + argp = remove_magics( argp, FALSE); /* Remove pair of magics */ + if ((mcpp_mode == POST_STD && *argp == EOS) + || (mcpp_mode == STD && *argp == RT_END)) { + *out = EOS; /* An empty argument */ + } else { + unget_string( argp, NULL); + if (trace_macro) + free( (char *) argp); + if ((c = get_ch()) == DEF_MAGIC) { /* Remove DEF_MAGIC */ + c = get_ch(); /* enabling to replace */ + } else if (c == IN_SRC) { /* Remove IN_SRC */ + if (trace_macro) { + get_ch(); /* Also its number */ + get_ch(); + } + c = get_ch(); + } + scan_token( c, &out, out_end); /* The first token */ + if (*infile->bptr) /* There are more tokens*/ + in_arg = TRUE; + } + break; + case IN_SRC: + if (trace_macro) { + get_ch(); + get_ch(); + } + /* Fall through */ + case DEF_MAGIC: + c = get_ch(); /* Skip DEF_MAGIC, IN_SRC */ + /* Fall through */ + default: + scan_token( c, &out, out_end); /* Copy the token */ + break; + } + + /* The generated sequence is a valid preprocessing-token ? */ + if (*prev_token) { /* There is any token */ + unget_string( prev_token, NULL); /* Scan once more */ + c = get_ch(); /* This line should be before the next line. */ + infile->fp = (FILE *)-1; /* To check token length*/ + if (mcpp_debug & EXPAND) + dump_string( "checking generated token", infile->buffer); + scan_token( c, (workp = work_buf, &workp), work_end); + infile->fp = NULL; + if (*infile->bptr != EOS) { /* More than a token */ + if (option_flags.lang_asm) { /* Assembler source */ + if (warn_level & 2) + diag_macro( CWARN, invalid_token, prev_token, 0L, NULL + , defp, NULL); + } else { + diag_macro( CERROR, invalid_token, prev_token, 0L, NULL, defp + , NULL); + } + infile->bptr += strlen( infile->bptr); + } + get_ch(); /* To the parent "file" */ + unget_ch(); + } + + if (mcpp_mode == STD && ! option_flags.lang_asm) { + *out++ = TOK_SEP; /* Prevent token merging*/ + *out = EOS; + } + if (in_arg) { /* There are more tokens after the generated one */ + if (mcpp_mode == POST_STD) { + file = infile; + while (c = get_ch(), file == infile) { + prev_token = out; /* Remember the last token */ + scan_token( c, &out, out_end); + } /* Copy rest of argument without expansion */ + unget_ch(); + } else { + while ((c = get_ch()) != RT_END) { + if (c == TOK_SEP) + continue; /* Skip separator */ + prev_token = out; /* Remember the last token */ + scan_token( c, &out, out_end); + } /* Copy rest of argument without expansion */ + } + } + *token_p = prev_token; /* Report back the last token */ + + return out; +} + +static const char * remove_magics( + const char * argp, /* The argument list */ + int from_last /* token is the last or first? */ +) +/* + * Remove pair of magic character sequences in an argument in order to catenate + * the last or first token to another. + * Or remove pair of magic character sequences surrounding an argument in order + * to keep symmetry of magics. + */ +{ +#define INIT_MAGICS 128 + + char (* mac_id)[ MAC_S_LEN]; + char (* arg_id)[ ARG_S_LEN]; + char ** mac_loc; + char ** arg_loc; + char * mgc_index; + size_t max_magics; + int mac_n, arg_n, ind, n; + char * first = NULL; + char * last = NULL; + char * token; + char * arg_p; + char * ap; + char * ep; + char * tp; + char * space = NULL; + int with_rtend; + int c; + FILEINFO * file; + + mac_id = (char (*)[ MAC_S_LEN]) xmalloc( MAC_S_LEN * INIT_MAGICS); + arg_id = (char (*)[ ARG_S_LEN]) xmalloc( ARG_S_LEN * INIT_MAGICS * 2); + mac_loc = (char **) xmalloc( sizeof (char *) * INIT_MAGICS); + arg_loc = (char **) xmalloc( sizeof (char *) * INIT_MAGICS * 2); + mgc_index = xmalloc( INIT_MAGICS * 3); + max_magics = INIT_MAGICS; + + mac_n = arg_n = ind = 0; + ap = arg_p = xmalloc( strlen( argp) + 1); + strcpy( arg_p, argp); + ep = arg_p + strlen( arg_p); + if (*(ep - 1) == RT_END) { + with_rtend = TRUE; + ep--; /* Point to RT_END */ + } else { + with_rtend = FALSE; + } + file = unget_string( arg_p, NULL); /* Stack to "file" for token parsing*/ + + /* Search all the magics in argument, as well as first and last token */ + /* Read stacked arg_p and write it to arg_p as a dummy buffer */ + while ((*ap++ = c = get_ch()) != RT_END && file == infile) { + if (c == MAC_INF) { + if (mac_n >= max_magics || arg_n >= max_magics * 2) { + max_magics *= 2; + mac_id = (char (*)[ MAC_S_LEN]) xrealloc( (void *) mac_id + , MAC_S_LEN * max_magics); + arg_id = (char (*)[ ARG_S_LEN]) xrealloc( (void *) arg_id + , ARG_S_LEN * max_magics * 2); + mac_loc = (char **) xrealloc( (void *) mac_loc + , sizeof (char *) * max_magics); + arg_loc = (char **) xrealloc( (void *) arg_loc + , sizeof (char *) * max_magics * 2); + mgc_index = xrealloc( mgc_index, max_magics * 3); + } + *ap++ = c = get_ch(); + switch (c) { + case MAC_CALL_START : + *ap++ = get_ch(); + *ap++ = get_ch(); + mac_loc[ mac_n] = ap - MAC_S_LEN; /* Location of the seq */ + memcpy( mac_id[ mac_n], ap - (MAC_S_LEN - 1), MAC_S_LEN - 1); + /* Copy the sequence from its second byte */ + mac_id[ mac_n++][ MAC_S_LEN - 1] = FALSE; + /* Mark of to-be-removed or not */ + break; + case MAC_ARG_START : + *ap++ = get_ch(); + *ap++ = get_ch(); + *ap++ = get_ch(); + arg_loc[ arg_n] = ap - ARG_S_LEN; + memcpy( arg_id[ arg_n], ap - (ARG_S_LEN - 1), ARG_S_LEN - 1); + arg_id[ arg_n++][ ARG_S_LEN - 1] = FALSE; + break; + case MAC_CALL_END : + mac_loc[ mac_n] = ap - MAC_E_LEN; + mac_id[ mac_n][ 0] = c; + mac_id[ mac_n++][ MAC_E_LEN_V - 1] = FALSE; + break; + case MAC_ARG_END : + arg_loc[ arg_n] = ap - ARG_E_LEN; + arg_id[ arg_n][ 0] = c; + arg_id[ arg_n++][ ARG_E_LEN_V - 1] = FALSE; + break; + } + if (option_flags.v) { + switch (c) { + case MAC_CALL_END : + mac_id[ mac_n - 1][ 1] = *ap++ = get_ch(); + mac_id[ mac_n - 1][ 2] = *ap++ = get_ch(); + break; + case MAC_ARG_END : + arg_id[ arg_n - 1][ 1] = *ap++ = get_ch(); + arg_id[ arg_n - 1][ 2] = *ap++ = get_ch(); + arg_id[ arg_n - 1][ 3] = *ap++ = get_ch(); + break; + } + } + mgc_index[ ind++] = c; /* Index to mac_id[] and arg_id[] */ + continue; + } else if (char_type[ c & UCHARMAX] & HSP) { + if (! first) { + ap--; /* Skip white space on top of the argument */ + ep--; + } + continue; + } + last = --ap; + if (! first) + first = ap; + if (char_type[ c & UCHARMAX] & HSP) + space = ap; /* Remember the last white space */ + scan_token( c, &ap, ep); + } + if (file == infile) + get_ch(); /* Clear the "file" */ + unget_ch(); + if (space == ep - 1) + ep--; /* Remove trailing white space */ + if (with_rtend) + *ep++ = RT_END; + *ep = EOS; + if ((from_last && !last) || (!from_last && !first)) + return arg_p; + if (mac_n == 0 && arg_n == 0) /* No magic sequence */ + return arg_p; + token = from_last ? last : first; + + /* Remove pair of magics surrounding the last (or first) token */ + if (mac_n) { + /* Remove pair of macro magics surrounding the token */ + int magic, mac_s, mac_e; + int nest_s, nest_e; + + nest_s = 0; + for (mac_s = 0; mac_loc[ mac_s] < token; mac_s++) { + magic = mac_id[ mac_s][ 0]; + if (magic == MAC_CALL_START) { /* Starting magic */ + nest_e = ++nest_s; + /* Search the corresponding closing magic */ + for (mac_e = mac_s + 1; mac_e < mac_n; mac_e++) { + magic = mac_id[ mac_e][ 0]; + if (magic == MAC_CALL_START) { + nest_e++; + } else { /* MAC_CALL_END: Closing magic */ + nest_e--; + /* Search after the token */ + if (token < mac_loc[ mac_e] && nest_e == nest_s - 1) { +#if DEBUG_MACRO_ANN + if (option_flags.v) + chk_symmetry( mac_id[ mac_s], mac_id[ mac_e] + , MAC_E_LEN - 2); +#endif + mac_id[ mac_e][ MAC_S_LEN - 1] = TRUE; + /* To be removed */ + break; /* Done for this mac_s */ + } + } + } + if (mac_e < mac_n) /* Found corresponding magic */ + mac_id[ mac_s][ MAC_S_LEN - 1] = TRUE; /* To be removed*/ + else /* Not found */ + break; + } else { + nest_s--; /* MAC_CALL_END: Closing magic */ + } + } + } + if (arg_n) { + /* Remove pair of arg magics surrounding the token */ + int magic, arg_s, arg_e; + int nest_s, nest_e; + + nest_s = 0; + for (arg_s = 0; arg_loc[ arg_s] < token; arg_s++) { + magic = arg_id[ arg_s][ 0]; + if (magic == MAC_ARG_START) { + nest_e = ++nest_s; + for (arg_e = arg_s + 1; arg_e < arg_n; arg_e++) { + magic = arg_id[ arg_e][ 0]; + if (magic == MAC_ARG_START) { + nest_e++; + } else { + nest_e--; + if (token < arg_loc[ arg_e] && nest_e == nest_s - 1) { +#if DEBUG_MACRO_ANN + if (option_flags.v) + chk_symmetry( arg_id[ arg_s], arg_id[ arg_e] + , ARG_E_LEN_V - 2); +#endif + arg_id[ arg_e][ ARG_S_LEN - 1] = TRUE; + break; + } + } + } + if (arg_e < arg_n) + arg_id[ arg_s][ ARG_S_LEN - 1] = TRUE; + else + break; + } else { + nest_s--; + } + } + } + + /* Copy the sequences skipping the to-be-removed magic seqs */ + file = unget_string( arg_p, NULL); /* Stack to "file" for token parsing*/ + tp = arg_p; + ep = arg_p + strlen( arg_p); + mac_n = arg_n = n = 0; + + while ((*tp++ = c = get_ch()) != RT_END && file == infile) { + char ** loc_tab; + int num, mark, rm, magic; + size_t len; + + if (c != MAC_INF) { + scan_token( c, (--tp, &tp), ep); + continue; + } + unget_ch(); /* Pushback MAC_INF */ + tp--; + + switch (magic = mgc_index[ n++]) { + case MAC_CALL_START : + len = MAC_S_LEN; + mark = MAC_S_LEN - 1; + break; + case MAC_CALL_END : + len = option_flags.v ? MAC_E_LEN_V : MAC_E_LEN; + mark = MAC_E_LEN_V - 1; + break; + case MAC_ARG_START : + len = ARG_S_LEN; + mark = ARG_S_LEN - 1; + break; + case MAC_ARG_END : + len = option_flags.v ? ARG_E_LEN_V : ARG_E_LEN; + mark = ARG_E_LEN_V - 1; + break; + } + switch (magic) { + case MAC_CALL_START : + case MAC_CALL_END : + loc_tab = mac_loc; + num = mac_n; + rm = mac_id[ mac_n++][ mark]; + break; + case MAC_ARG_START : + case MAC_ARG_END : + loc_tab = arg_loc; + num = arg_n; + rm = arg_id[ arg_n++][ mark]; + break; + } + if (rm == FALSE) { /* Not to be removed */ + memmove( tp, loc_tab[ num], len); + /* Copy it (from arg_p buffer for convenience) */ + tp += len; + } + infile->bptr += len; + } + if (! with_rtend) + tp--; + *tp = EOS; + if (file == infile) + get_ch(); /* Clear the "file" */ + unget_ch(); + + return arg_p; +} + +#if DEBUG_MACRO_ANN +static void chk_symmetry( + char * start_id, /* Sequence of macro (or arg) starting inf */ + char * end_id, /* Sequence of macro (or arg) closing inf */ + size_t len /* Length of the sequence */ +) +/* + * Check whether starting sequence and corresponding closing sequence is the + * same. + */ +{ + int s_id, e_id, arg_s_n, arg_e_n; + + if (memcmp( start_id + 1, end_id + 1, len) == 0) + return; /* The sequences are the same */ + s_id = ((start_id[ 1] & UCHARMAX) - 1) * UCHARMAX; + s_id += (start_id[ 2] & UCHARMAX) - 1; + e_id = ((end_id[ 1] & UCHARMAX) - 1) * UCHARMAX; + e_id += (end_id[ 2] & UCHARMAX) - 1; + if (len >= 3) { + arg_s_n = (start_id[ 3] & UCHARMAX) - 1; + arg_e_n = (end_id[ 3] & UCHARMAX) - 1; + mcpp_fprintf( ERR, +"Asymmetry of arg inf found removing magics: start %d:%d, end: %d:%d at line:%d\n" + , s_id, arg_s_n, e_id, arg_e_n, src_line); + } else { + mcpp_fprintf( ERR, +"Asymmetry of macro inf found removing magics: start %d, end: %d at line:%d\n" + , s_id, e_id, src_line); + } +} +#endif + +static char * stringize( + const DEFBUF * defp, /* The macro definition */ + const char * argp, /* Pointer to argument */ + char * out /* Output buffer */ +) +/* + * Make a string literal from an argument. + */ +{ + char arg_end_inf[ 8][ ARG_E_LEN_V - 1]; + /* Verbose information of macro arguments */ + FILEINFO * file; + int stray_bsl = FALSE; /* '\\' not in literal */ + char * out_p = out; + int token_type; + int num_arg_magic = 0; + size_t len; + size_t arg_e_len = option_flags.v ? ARG_E_LEN_V : ARG_E_LEN; + int c; + + if (trace_macro) { + while ((*argp == MAC_INF && *(argp + 1) == MAC_ARG_START) + /* Argument is prefixed with macro tracing magics */ + || (char_type[ *argp & UCHARMAX] & HSP)) { + if (*argp == MAC_INF) { /* Move magics to outside of string */ + memcpy( out_p, argp, ARG_S_LEN); + out_p += ARG_S_LEN; + argp += ARG_S_LEN; + num_arg_magic++; + } else { /* Skip white spaces */ + argp++; + } + } + } + + file = unget_string( argp, NULL); + len = strlen( infile->buffer); /* Sequence ends with RT_END */ + if (trace_macro) { /* Remove suffixed argument closing magics */ + /* There are 0 or more argument closing magic sequences and */ + /* 0 or more TOK_SEPs and no space at the end of argp. */ + /* This is assured by get_an_arg(). */ + int nmagic = 0; + while (len > arg_e_len + && (((*(infile->buffer + len - arg_e_len - 1) == MAC_INF + && *(infile->buffer + len - arg_e_len) == MAC_ARG_END) + || *(infile->buffer + len - 2) == TOK_SEP))) { + if (*(infile->buffer + len - arg_e_len - 1) == MAC_INF + && *(infile->buffer + len - arg_e_len) == MAC_ARG_END) { + if (option_flags.v) { + memcpy( arg_end_inf[ nmagic] + , infile->buffer + len - arg_e_len + 1 + , arg_e_len - 2); + arg_end_inf[ nmagic][ arg_e_len - 2] = EOS; + } + nmagic++; + len -= arg_e_len; + *(infile->buffer + len - 1) = RT_END; + *(infile->buffer + len) = EOS; + } else if (*(infile->buffer + len - 2) == TOK_SEP) { + len--; + *(infile->buffer + len - 1) = RT_END; + *(infile->buffer + len) = EOS; + } + } + if (nmagic != num_arg_magic) { /* There are some imbalances */ + /* Some surrounding magics correspond to intervening ones. */ + /* So, unmatched surrounding magics should be removed. */ + if (num_arg_magic > nmagic) { + num_arg_magic = nmagic; /* Ignore the surplus */ + out_p = out + ARG_S_LEN * num_arg_magic; + } /* Else simply ignore the surplus nmagic */ + } + } + *out_p++ = '"'; /* Starting quote */ + + while ((c = get_ch()), ((mcpp_mode == POST_STD && file == infile) + || (mcpp_mode == STD && c != RT_END))) { + if (c == ' ' || c == '\t') { + *out_p++ = c; + continue; + } else if (c == TOK_SEP) { + continue; /* Skip inserted separator */ + } else if (c == IN_SRC) { /* Skip magics */ + if (trace_macro) { + get_ch(); + get_ch(); + } + continue; + } else if (c == '\\') { + stray_bsl = TRUE; /* May cause a trouble */ + } else if (c == MAC_INF) { /* Remove intervening magics */ + switch (c = get_ch()) { + case MAC_ARG_START : + get_ch(); + /* Fall through */ + case MAC_CALL_START : + get_ch(); + get_ch(); + break; + } + if (option_flags.v) { + switch (c) { + case MAC_ARG_END : + get_ch(); + /* Fall through */ + case MAC_CALL_END : + get_ch(); + get_ch(); + break; + } + } + continue; + } + token_type = scan_token( c, (workp = work_buf, &workp), work_end); + + switch (token_type) { + case WSTR: + case WCHR: + case STR: + case CHR: + workp = work_buf; + while ((c = *workp++ & UCHARMAX) != EOS) { + if (char_type[ c] & mbchk) { /* Multi-byte character */ + mb_read( c, &workp, (*out_p++ = c, &out_p)); + /* Copy as it is */ + continue; + } else if (c == '"') { + *out_p++ = '\\'; /* Insert '\\' */ + } else if (c == '\\') { +#if OK_UCN + if (mcpp_mode == POST_STD || ! stdc3 + || (*workp != 'u' && *workp != 'U')) + /* Not UCN */ +#endif + *out_p++ = '\\'; + } + *out_p++ = c; + } + *out_p = EOS; + break; + default: + out_p = stpcpy( out_p, work_buf); + break; + } + } + + if (mcpp_mode == POST_STD) + unget_ch(); + *out_p++ = '"'; /* Closing quote */ + if (trace_macro) { + while (num_arg_magic--) { + *out_p++ = MAC_INF; /* Restore removed magic*/ + *out_p++ = MAC_ARG_END; + if (option_flags.v) + out_p = stpcpy( out_p, arg_end_inf[ num_arg_magic]); + } + } + *out_p = EOS; + + if (stray_bsl) { /* '\\' outside of quotation has been found */ + int invalid = FALSE; + unget_string( out, defp->name); + if (mcpp_debug & EXPAND) + dump_string( "checking generated token", infile->buffer); + scan_quote( get_ch(), work_buf, work_end, TRUE); + /* Unterminated or too long string will be diagnosed */ + if (*infile->bptr != EOS) /* More than a token */ + invalid = TRUE; /* Diagnose after clearing the "file" */ + infile->bptr += strlen( infile->bptr); + get_ch(); /* Clear the "file" */ + unget_ch(); + if (invalid) + diag_macro( CERROR + , "Not a valid string literal %s" /* _E_ */ + , out, 0L, NULL, defp, NULL); + } +#if NWORK-2 > SLEN90MIN + else if ((warn_level & 4) && out_p - out > std_limits.str_len) + diag_macro( CWARN + , "String literal longer than %.0s%ld bytes %s" /* _W4_ */ + , NULL , (long) std_limits.str_len, out, defp, NULL); +#endif + return out_p; +} + +static char * substitute( + const DEFBUF * defp, /* The macro getting arguments */ + const char ** arglist, /* Pointers to actual arguments */ + const char * in, /* Replacement text */ + char * out, /* Output buffer */ + char * out_end /* End of output buffer */ +) +/* + * Replace completely each actual arguments of the macro, and substitute for + * the formal parameters in the replacement list. + */ +{ + char * out_start = out; + const char * arg; + int c; + int gvar_arg; /* gvar_arg'th argument is GCC variable argument */ + + gvar_arg = (defp->nargs & GVA_ARGS) ? (defp->nargs & ~AVA_ARGS) : 0; + *out = EOS; /* Ensure to termanate */ + + while ((c = *in++) != EOS) { + if (c == MAC_PARM) { /* Formal parameter */ + c = *in++ & UCHARMAX; /* Parameter number */ + if (mcpp_debug & EXPAND) { + mcpp_fprintf( DBG, " (expanding arg[%d])", c); + dump_string( NULL, arglist[ c - 1]); + } +#if COMPILER == GNUC || COMPILER == MSC + arg = arglist[ c - 1]; + if (trace_macro) { + if (*arg == MAC_INF) { + if (*++arg == MAC_ARG_START) + arg += ARG_S_LEN - 1; /* Next to magic chars */ + } + } +#if COMPILER == GNUC + if (c == gvar_arg && *arg == RT_END && ! ansi) { + /* + * GCC variadic macro and its variable argument is absent. + * Note that in its "strict-ansi" mode GCC does not remove + * ',', nevertheless it ignores '##' (inconsistent + * behavior). Though GCC2 changes behavior depending the + * ',' is preceded by space or not, we only count on the + * "strict-ansi" flag. + */ +#else + if ((defp->nargs & VA_ARGS) && c == (defp->nargs & ~VA_ARGS) + && *arg == RT_END && mcpp_mode == STD) { + /* Visual C 2005 also removes ',' immediately preceding */ + /* absent variable arguments. It does not use '##' though. */ +#endif + char * tmp; + tmp = out - 1; + while (char_type[ *tmp & UCHARMAX] & HSP) + tmp--; + if (*tmp == ',') { + out = tmp; /* Remove the immediately preceding ',' */ + if (warn_level & 1) { + *out = EOS; + diag_macro( CWARN, + "Removed ',' preceding the absent variable argument: %s" /* _W1_ */ + , out_start, 0L, NULL, defp, NULL); + } + } + } else +#endif + if ((out = rescan( NULL, arglist[ c - 1], out, out_end)) + == NULL) { /* Replace completely */ + return NULL; /* Error */ + } + } else { + *out++ = c; /* Copy the character */ + } + } + *out = EOS; + return out; +} + +static char * rescan( + const DEFBUF * outer, /* Outer macro just replacing */ + const char * in, /* Sequences to be rescanned */ + char * out, /* Output buffer */ + char * out_end /* End of output buffer */ +) +/* + * Re-scan the once replaced sequences to replace the remaining macros + * completely. + * rescan() and replace() call each other recursively. + * + * Note: POST_STD mode does not use IN_SRC nor TOK_SEP and seldom uses RT_END. + * Checking of those are unnecessary overhead for POST_STD mode. To integrate + * the code for POST_STD with STD mode, however, we use these checkings + * commonly. + * Also compat_mode does not use IN_SRC unless in trace_macro mode. + * STD mode has macro notification mode (trace_macro mode), too. Its routines + * are complicated and not easy to understand. + */ +{ + char * cur_cp = NULL; + char * tp = NULL; /* Temporary pointer into buffer*/ + char * out_p = out; /* Current output pointer */ + FILEINFO * file; /* Input sequences stacked on a "file" */ + DEFBUF * inner; /* Inner macro to replace */ + int c; /* First character of token */ + int token_type; + char * mac_arg_start = NULL; +#if COMPILER == GNUC + int within_defined = FALSE; + int within_defined_arg_depth = 0; +#endif + + if (mcpp_debug & EXPAND) { + mcpp_fprintf( DBG, "rescan_level--%d (%s) " + , rescan_level + 1, outer ? outer->name : ""); + dump_string( "rescan entry", in); + } + if (! disable_repl( outer)) /* Don't re-replace replacing macro */ + return NULL; /* Too deeply nested macro call */ + if (mcpp_mode == STD) { + get_ch(); /* Clear empty "file"s */ + unget_ch(); /* for diagnostic */ + cur_cp = infile->bptr; /* Remember current location */ + } + file = unget_string( in, outer ? outer->name : NULL); + /* Stack input on a "file" */ + + while ((c = get_ch()), file == infile + /* Rescanning is limited to the "file" */ + && c != RT_END) { + /* + * This is the trick of STD mode. collect_args() via replace() + * may read over to file->parent (provided the "file" is macro) + * unless stopped by RT_END. + */ + size_t len = 0; + + if (char_type[ c] & HSP) { + *out_p++ = c; + continue; + } else if (c == MAC_INF) { /* Only in STD mode */ + *out_p++ = c; + *out_p++ = c = get_ch(); + switch (c) { + case MAC_ARG_START : + mac_arg_start = out_p - 2; /* Remember the position */ + *out_p++ = get_ch(); + /* Fall through */ + case MAC_CALL_START : + *out_p++ = get_ch(); + *out_p++ = get_ch(); + break; + case MAC_ARG_END : + if (! option_flags.v) + break; + else + *out_p++ = get_ch(); + /* Fall through */ + case MAC_CALL_END : + if (option_flags.v) { + *out_p++ = get_ch(); + *out_p++ = get_ch(); + } + break; + } /* Pass these characters as they are */ + continue; + } + token_type = scan_token( c, (tp = out_p, &out_p), out_end); +#if COMPILER == GNUC + if (mcpp_mode == STD) { + /* Pass stuff within defined() as they are, if in_directive */ + if ((within_defined || within_defined_arg_depth)) { + if (c == '(') { + within_defined_arg_depth++; + within_defined = FALSE; + } else if (within_defined_arg_depth && c == ')') { + within_defined_arg_depth--; + } /* Else should be a name (possibly macro) */ + continue; + } else if (token_type == NAM && in_directive + && str_eq(identifier, "defined")) { + within_defined = TRUE; + /* 'defined' token in directive line */ + continue; + } + } +#endif + if (mcpp_mode == STD && c == IN_SRC) + len = trace_macro ? IN_SRC_LEN : 1; + if (token_type == NAM && c != DEF_MAGIC + && (inner = look_id( tp + len)) != NULL) { /* A macro name */ + int is_able; /* Macro is not "blue-painted" */ + char * endf = NULL; /* Output stream at end of infile */ + MAGIC_SEQ mgc_seq; /* Magics between macro name and '(' */ + + if (trace_macro) + memset( &mgc_seq, 0, sizeof (MAGIC_SEQ)); + if (is_macro_call( inner, &out_p, &endf + , trace_macro ? &mgc_seq : NULL) + && ((mcpp_mode == POST_STD && is_able_repl( inner)) + || (mcpp_mode == STD + && (((is_able = is_able_repl( inner)) == YES) + || (is_able == READ_OVER + && (c == IN_SRC || compat_mode)))))) { + /* Really a macro call */ + LINE_COL in_src_line_col = { 0L, 0}; + int in_src_n = 0; + + if (trace_macro) { + if (c == IN_SRC) { /* Macro in argument from source */ + /* Get the location in source */ + in_src_n = ((*(tp + 1) & UCHARMAX) - 1) * UCHARMAX; + in_src_n += (*(tp + 2) & UCHARMAX) - 1; + in_src_line_col.line = in_src[ in_src_n].start_line; + in_src_line_col.col = in_src[ in_src_n].start_col; + } + if (inner->nargs >= 0 && mgc_seq.magic_start) { + /* Magic sequence is found between macro */ + /* name and '('. This is a nuisance. */ + char * mgc_cleared; + size_t seq_len; + size_t arg_elen = option_flags.v ? ARG_E_LEN_V + : ARG_E_LEN; + if ((tp - ARG_S_LEN) == mac_arg_start + && *mgc_seq.magic_start == MAC_INF + && *(mgc_seq.magic_start + 1) == MAC_ARG_END) { + /* Name of function-like macro is surrounded by */ + /* magics, which were inserted by outer macro. */ + /* Remove the starting magic. (The closing magic*/ + /* has already been removed by is_macro_call(). */ + tp -= ARG_S_LEN; + mgc_seq.magic_start += arg_elen; /* Next seq */ + } + /* Restore once skipped magic sequences, */ + /* then remove "pair"s of sequences. */ + seq_len = mgc_seq.magic_end - mgc_seq.magic_start; + if (seq_len) { + insert_to_bptr( mgc_seq.magic_start, seq_len); + mgc_cleared = remove_magics( + (const char *) infile->bptr, FALSE); + /* Remove pair of magics */ + strcpy( infile->bptr, mgc_cleared); + free( mgc_cleared); + } + } + } + if ((out_p = replace( inner, tp, out_end, outer, file + , in_src_line_col, in_src_n)) == NULL) + break; /* Error of macro call */ + } else { + if (endf && strlen( endf)) { + /* Has read over to parent file: another nuisance. */ + /* Restore the read-over sequence into current buffer. */ + /* Don't use unget_string() here. */ + insert_to_bptr( endf, out_p - endf); + out_p = endf; + *out_p = EOS; + } + if ((is_able = is_able_repl( inner)) == NO + || (mcpp_mode == STD && is_able == READ_OVER + && c != IN_SRC && ! compat_mode)) { + if (mcpp_mode == POST_STD || c != IN_SRC) + memmove( tp + 1, tp, (size_t) (out_p++ - tp)); + *tp = DEF_MAGIC; /* Mark not to replace */ + } /* Else not a macro call*/ + } + } + if (out_end <= out_p) { + *out_p = EOS; + diag_macro( CERROR, macbuf_overflow, outer ? outer->name : in, 0L + , out, outer, inner); + out_p = NULL; + break; + } + } + + if (out_p) { + *out_p = EOS; + if (mcpp_mode == STD) { + if (c != RT_END) { + unget_ch(); + if (outer != NULL) { /* outer isn't a macro in argument */ + if (infile && infile->bptr != cur_cp + /* Have overrun replacement list*/ + && !(tp && *tp == DEF_MAGIC) + /* Macro is enabled */ + && ((!compat_mode && (warn_level & 1)) + || (compat_mode && (warn_level & 8)))) { + diag_macro( CWARN, +"Replacement text \"%s\" of macro %.0ld\"%s\" involved subsequent text" /* _W1_ */ + , in, 0L, outer->name, outer, inner); + } + } + } /* Else remove RT_END */ + } else { + unget_ch(); + } + } + enable_repl( outer, TRUE); /* Enable macro for later text */ + if (mcpp_debug & EXPAND) { + mcpp_fprintf( DBG, "rescan_level--%d (%s) " + , rescan_level + 1, outer ? outer->name : ""); + dump_string( "rescan exit", out); + } + return out_p; +} + +static int disable_repl( + const DEFBUF * defp +) +/* + * Register the macro name currently replacing. + */ +{ + if (defp == NULL) + return TRUE; + if (rescan_level >= RESCAN_LIMIT) { + diag_macro( CERROR, + "Rescanning macro \"%s\" more than %ld times at \"%s\"" /* _E_ */ + , macro_name, (long) RESCAN_LIMIT, defp->name, defp, NULL); + return FALSE; + } + replacing[ rescan_level].def = defp; + replacing[ rescan_level++].read_over = NO; + return TRUE; +} + +static void enable_repl( + const DEFBUF * defp, + int done +) +/* + * Un-register the macro name just replaced for later text. + */ +{ + if (defp == NULL) + return; + replacing[ rescan_level - 1].def = NULL; + if (done && rescan_level) + rescan_level--; +} + +static int is_able_repl( + const DEFBUF * defp +) +/* + * The macro is permitted to replace ? + */ +{ + int i; + + if (defp == NULL) + return YES; + for (i = rescan_level-1; i >= 0; i--) { + if (defp == replacing[ i].def) + return replacing[ i].read_over; + } + return YES; +} + +static char * insert_to_bptr( + char * ins, /* Sequence to be inserted */ + size_t len /* Byte to be inserted */ +) +/* + * Insert a sequence into infile->bptr. + * infile->buffer is reallocated to ensure buffer size. + * This routine changes absolute address of infile->bptr, hence rescan() emits + * a "Replacement text ... involved subsequent text" warning. Anyway, + * a macro which needs this routine deserves that warning. + */ +{ + size_t bptr_offset = infile->bptr - infile->buffer; + + if (infile->fp == NULL) { /* Not source file */ + infile->buffer = xrealloc( infile->buffer + , strlen( infile->buffer) + len + 1); + infile->bptr = infile->buffer + bptr_offset; + } + memmove( infile->bptr + len, infile->bptr, strlen( infile->bptr) + 1); + memcpy( infile->bptr, ins, len); + + return infile->buffer; +} + +/* + * M a c r o E x p a n s i o n i n P R E - S T A N D A R D M o d e + */ + +#include "setjmp.h" + +static jmp_buf jump; + +static char * arglist_pre[ NMACPARS]; /* Pointers to args */ + +static int rescan_pre( int c, char * mp, char * mac_end); + /* Replace a macro repeatedly */ +static int replace_pre( DEFBUF * defp); + /* Replace a macro once */ +static void substitute_pre( DEFBUF * defp); + /* Substitute parms with args */ + +static char * expand_prestd( + DEFBUF * defp, /* Macro definition */ + char * out, /* Output buffer */ + char * out_end, /* End of output buffer */ + LINE_COL line_col, /* Location of macro (not used in prestd) */ + int * pragma_op /* Flag of _Pragma (not used in prestd) */ +) +/* + * Expand a macro call completely, write the results to the specified buffer + * and return the advanced pointer. + */ +{ + char macrobuf[ NMACWORK + IDMAX]; /* Buffer for rescan_pre() */ + char * mac_end = ¯obuf[ NMACWORK]; /* End of macrobuf[] */ + char * out_p; /* Pointer into out[] */ + char * mp = macrobuf; /* Pointer into macrobuf*/ + int len; /* Length of a token */ + int token_type; /* Type of token */ + int c; + + macro_line = src_line; /* Line number for diag.*/ + unget_string( identifier, identifier); /* To re-read */ + macro_name = defp->name; + rescan_level = 0; + if (setjmp( jump) == 1) { + skip_macro(); + mp = macrobuf; + *mp = EOS; + macro_line = MACRO_ERROR; + goto err_end; + } + + while ((c = get_ch()) != CHAR_EOF && infile->fp == NULL) { + /* While the input stream is a macro */ + while (c == ' ' || c == '\t') { /* Output the spaces */ + *mp++ = c; + c = get_ch(); + if (infile == NULL || infile->fp != NULL) + goto exp_end; + } + token_type = rescan_pre( c, mp, mac_end); /* Scan token */ + /* and expand. Result of expansion is written at mp. */ + + switch (token_type) { + case STR: /* String literal */ + case CHR: /* Character constant */ + case NUM: /* Number token */ + case OPE: /* Operator or punct. */ + case NAM: /* Identifier */ + len = strlen( mp); + mp += len; + break; + case SEP: /* Special character */ + switch( *mp) { + case COM_SEP: + if (mcpp_mode == OLD_PREP) + break; /* Zero-length comment is removed now */ + /* Else fall through */ + default: /* Who knows ? */ + mp++; /* Copy the character */ + break; + } + break; + case NO_TOKEN: break; /* End of file */ + default: /* Unkown token char. */ + mp++; /* Copy the character */ + break; + } + + if (mac_end <= mp) { + *mp = EOS; + cerror( macbuf_overflow, macro_name, 0L, macrobuf); + longjmp( jump, 1); + } + if (mcpp_debug & GETC) { + *mp = EOS; + dump_string( "macrobuf", macrobuf); + } + } + +exp_end: + unget_ch(); + while (macrobuf < mp && (*(mp - 1) == ' ' || *(mp - 1) == '\t')) + mp--; /* Remove trailing blank */ + macro_line = 0; + *mp = EOS; + if (mp - macrobuf > out_end - out) { + cerror( macbuf_overflow, macro_name, 0L, macrobuf); + macro_line = MACRO_ERROR; + } +err_end: + out_p = stpcpy( out, macrobuf); + if (mcpp_debug & EXPAND) { + dump_string( "expand_prestd exit", out); + } + macro_name = NULL; + clear_exp_mac(); + *pragma_op = FALSE; + return out_p; +} + +static int rescan_pre( + int c, /* First character of token */ + char * mp, /* Output buffer */ + char * mac_end /* End of output buffer */ +) +/* + * If the token is a macro name, replace the macro repeatedly until the first + * token becomes a non-macro and return the type of token after expansion. + */ +{ + int token_type; /* Type of token */ + char * cp = mp; /* Value of mp should not be changed */ + DEFBUF * defp; + FILEINFO * file; + + while ((token_type = scan_token( c, &cp, mac_end)) == NAM + && (defp = look_id( identifier)) != NULL) { /* Macro */ + if (replace_pre( defp) == FALSE) + break; /* Macro name with no argument */ + file = infile; + c = get_ch(); + if (file != infile) { /* Replaced to 0 token */ + unget_ch(); + token_type = NO_TOKEN; + break; + } + cp = mp; /* Overwrite on the macro call */ + } /* The macro call is replaced */ + return token_type; +} + +static int replace_pre( + DEFBUF * defp /* Definition of the macro */ +) +/* + * Replace a macro one level. Called from expand_prestd() (via rescan_pre()) + * when an identifier is found in the macro table. It calls collect_args() + * to parse actual arguments, checking for the correct number. It then + * creates a "file" containing single line containing the replacement text + * with the actual arguments inserted appropriately. This is "pushed back" + * onto the input stream. (When get_ch() routine runs off the end of the macro + * line, it will dismiss the macro itself.) + */ +{ + int arg_len; + int c; + + if (mcpp_debug & EXPAND) { + dump_a_def( "replace_pre entry", defp, FALSE, TRUE, fp_debug); + dump_unget( "replace_pre entry"); + } + if (++rescan_level >= PRESTD_RESCAN_LIMIT) { + diag_macro( CERROR + , "Recursive macro definition of \"%s\"" /* _E_ */ + , defp->name, 0L, NULL, defp, NULL); + longjmp( jump, 1); + } + + /* + * Here's a macro to replace. + */ + switch (defp->nargs) { + case DEF_NOARGS: /* No argument just stuffs */ + case DEF_NOARGS_PREDEF_OLD: /* Compiler-specific predef without '_' */ + case DEF_NOARGS_PREDEF: /* Compiler-specific predef */ + break; + default: /* defp->nargs >= 0 */ + c = squeeze_ws( NULL, NULL, NULL); /* Look for and skip '('*/ + if (c != '(') { /* Macro name without following '(' */ + unget_ch(); + if (warn_level & 8) + diag_macro( CWARN, only_name, defp->name, 0L, NULL, defp, NULL); + return FALSE; + } else { + arglist_pre[ 0] = xmalloc( (size_t) (NMACWORK + IDMAX * 2)); + arg_len = collect_args( defp, arglist_pre, 0); + /* Collect arguments */ + if (arg_len == ARG_ERROR) { /* End of input */ + free( arglist_pre[ 0]); + longjmp( jump, 1); + } + } + break; + } + + if (defp->nargs > 0) + substitute_pre( defp); /* Do actual arguments */ + else + unget_string( defp->repl, defp->name); + + if (mcpp_debug & EXPAND) + dump_unget( "replace_pre exit"); + if (defp->nargs >= 0) + free( arglist_pre[ 0]); + return TRUE; +} + +static void substitute_pre( + DEFBUF * defp /* Current macro being replaced */ +) +/* + * Stuff the macro body, substituting formal parameters with actual arguments. + */ +{ + int c; /* Current character */ + FILEINFO * file; /* Funny #include */ + char * out_end; /* -> output buffer end */ + char * in_p; /* -> replacement text */ + char * out_p; /* -> macro output buff */ + + file = get_file( defp->name, NULL, NULL, (size_t) (NMACWORK + 1), FALSE); + /* file == infile */ + in_p = defp->repl; /* -> macro replacement */ + out_p = file->buffer; /* -> output buffer */ + out_end = file->buffer + NMACWORK; /* -> buffer end */ + + while ((c = *in_p++) != EOS) { + if (c == MAC_PARM) { + c = (*in_p++ & UCHARMAX) - 1; /* Parm number */ + /* + * Substitute formal parameter with actual argument. + */ + if (out_end <= (out_p + strlen( arglist_pre[ c]))) + goto nospace; + out_p = stpcpy( out_p, arglist_pre[ c]); + } else { + *out_p++ = c; + } + if (out_end <= out_p) + goto nospace; + } + + *out_p = EOS; + file->buffer = xrealloc( file->buffer, strlen( file->buffer) + 1); + file->bptr = file->buffer; /* Truncate buffer */ + if (mcpp_debug & EXPAND) + dump_string( "substitute_pre macroline", file->buffer); + return; + +nospace: + *out_p = EOS; + diag_macro( CERROR, macbuf_overflow, defp->name, 0L, file->buffer, defp + , NULL); + longjmp( jump, 1); +} + + +/* + * C O M M O N R O U T I N E S + * f o r S T A N D A R D a n d p r e - S T A N D A R D M o d e s + */ + +static int collect_args( + const DEFBUF * defp, /* Definition of the macro */ + char ** arglist, /* Pointers to actual arguments */ + int m_num /* Index into mac_inf[] */ +) +/* + * Collect the actual arguments for the macro, checking for correct number + * of arguments. + * Variable arguments (on Standard modes) are read as a merged argument. + * Return number of real arguments, or ARG_ERROR on error of unterminated + * macro. + * collect_args() may read over to the next line unless 'in_directive' is + * set to TRUE. + * collect_args() may read over into file->parent to complete a macro call + * unless stopped by RT_END (provided the "file" is macro). This is a key + * trick of STD mode macro expansion. Meanwhile, POST_STD mode limits the + * arguments in the "file" (macro or not). + * Note: arglist[ n] may be reallocated by collect_args(). + */ +{ + const char * name = defp->name; + char * argp = arglist[ 0]; /* Pointer to an argument */ + char * arg_end; /* End of arguments buffer */ + char * valid_argp = NULL; /* End of valid arguments */ + char * sequence; /* Token sequence for diagnostics */ + char * seq; /* Current pointer into 'sequence' */ + char * seq_end; /* Limit of buffer */ + int args; /* Number of arguments expected */ + int nargs = 0; /* Number of collected args */ + int var_arg = defp->nargs & VA_ARGS; /* Variable args */ + int more_to_come = FALSE; /* Next argument is expected*/ + LOCATION * locs; /* Location of args in source file */ + LOCATION * loc; /* Current locs */ + MAGIC_SEQ mgc_prefix; /* MAC_INF seqs and spaces preceding an arg */ + int c; + + if (mcpp_debug & EXPAND) + dump_unget( "collect_args entry"); + args = (defp->nargs == DEF_PRAGMA) ? 1 : (defp->nargs & ~AVA_ARGS); + if (args == 0) /* Need no argument */ + valid_argp = argp; + *argp = EOS; /* Make sure termination */ + arg_end = argp + NMACWORK/2; + seq = sequence = arg_end + IDMAX; /* Use latter half of argp */ + seq_end = seq + NMACWORK/2; + seq = stpcpy( seq, name); + *seq++ = '('; + if (mcpp_mode == STD) { + /* + * in_getarg is set TRUE while getting macro arguments, for the sake + * of diagnostic's convenience. in_getarg is used only in STD mode. + */ + in_getarg = TRUE; + if (trace_macro && m_num) { + /* #pragma MCPP debug macro_call, and the macro is on source */ + mac_inf[ m_num].loc_args = loc = locs + = (LOCATION *) xmalloc( (sizeof (LOCATION)) * UCHARMAX); + memset( loc, 0, (sizeof (LOCATION)) * UCHARMAX); + /* 0-clear for default values, including empty argument */ + } + } + + while (1) { + memset( &mgc_prefix, 0, sizeof (MAGIC_SEQ)); + c = squeeze_ws( &seq, NULL + , (trace_macro && m_num) ? &mgc_prefix : NULL); + /* Skip MAC_INF seqs and white spaces, still remember */ + /* the sequence in buffer, if necessary. */ + if (c == ')' || c == ',') + scan_token( c, &seq, seq_end); /* Ensure token parsing */ + else + *seq = EOS; + + switch (c) { /* First character of token */ + case ')': + if (! more_to_come) { /* Zero argument */ + if (trace_macro && m_num) + loc++; + break; + } /* Else fall through */ + case ',': /* Empty argument */ + if (trace_macro && m_num) + loc++; /* Advance pointer to infs */ + if (warn_level & 2) + diag_macro( CWARN, empty_arg, sequence, 0L, NULL, defp, NULL); + if (standard && var_arg && nargs == args - 1) { + /* Variable arguments begin with an empty argument */ + c = get_an_arg( c, &argp, arg_end, &seq, 1, nargs, &loc + , m_num, (trace_macro && m_num) ? &mgc_prefix : NULL); + } else { + if (mcpp_mode == STD) + *argp++ = RT_END; + *argp++ = EOS; + } + if (++nargs == args) + valid_argp = argp; + if (c == ',') { + more_to_come = TRUE; + continue; + } else { /* ')' */ + break; + } + case '\n': /* Unterminated macro call in directive line*/ + unget_ch(); /* Fall through */ + case RT_END: /* Error of missing ')' */ + diag_macro( CERROR, unterm_macro, sequence, 0L, NULL, defp, NULL); + /* Fall through */ + case CHAR_EOF: /* End of file in macro call*/ + nargs = ARG_ERROR; + goto arg_ret; /* Diagnosed by at_eof() */ + default: /* Nomal argument */ + break; + } + + if (c == ')') /* At end of all args */ + break; + + c = get_an_arg( c, &argp, arg_end, &seq + , (var_arg && nargs == args - 1) ? 1 : 0, nargs, &loc + , m_num, (trace_macro && m_num) ? &mgc_prefix : NULL); + + if (++nargs == args) + valid_argp = argp; /* End of valid arguments */ + if (c == ')') + break; + if (c == 0) { /* End of file */ + nargs = ARG_ERROR; + goto arg_ret; /* Diagnosed by at_eof() */ + } + if (c == -1) { /* Untermanated macro call */ + diag_macro( CERROR, unterm_macro, sequence, 0L, NULL, defp, NULL); + nargs = ARG_ERROR; + goto arg_ret; + } + more_to_come = (c == ','); + } /* Collected all arguments */ + + if (nargs == 0 && args == 1) { /* Only and empty argument */ + if (warn_level & 2) + diag_macro( CWARN, empty_arg, sequence, 0L, NULL, defp, NULL); + } else if (nargs != args) { /* Wrong number of arguments*/ + if (mcpp_mode != OLD_PREP || (warn_level & 1)) { + if ((standard && var_arg && (nargs == args - 1)) + /* Absence of variable arguments */ + || (mcpp_mode == OLD_PREP)) { + if (warn_level & 1) + diag_macro( CWARN, narg_error, nargs < args ? "Less" + : "More", (long) args, sequence, defp, NULL); + } else { + diag_macro( CERROR, narg_error, nargs < args ? "Less" : "More" + , (long) args, sequence, defp, NULL); + } + } + } + if (args < nargs) { + argp = valid_argp; /* Truncate excess arguments*/ + } else { + for (c = nargs; c < args; c++) { + if (mcpp_mode == STD) + *argp++ = RT_END; /* For rescan() */ + *argp++ = EOS; /* Missing arguments */ + } + if (c == 0) + argp++; /* Ensure positive length */ + } + arglist[ 0] = argp + = xrealloc( arglist[ 0], (size_t) (argp - arglist[ 0])); + /* Use memory sparingly */ + for (c = 1; c < args; c++) + arglist[ c] = argp += strlen( argp) + 1; + if (trace_macro && m_num) + mac_inf[ m_num].loc_args /* Truncate excess memory */ + = (LOCATION *) xrealloc( (char *) locs + , (loc - locs) * sizeof (LOCATION)); + + if (mcpp_debug & EXPAND) { + if (nargs > 0) { + if (nargs > args) + nargs = args; + dump_args( "collect_args exit", nargs, (const char **) arglist); + } + dump_unget( "collect_args exit"); + } +arg_ret: + if (mcpp_mode == STD) + in_getarg = FALSE; + /* Return number of found arguments for function-like macro at most */ + /* defp->nargs, or return defp->nargs for object-like macro. */ + return defp->nargs <= DEF_NOARGS ? defp->nargs : nargs; +} + +static int get_an_arg( + int c, + char ** argpp, /* Address of pointer into argument list */ + char * arg_end, /* End of argument list buffer */ + char ** seqp, /* Buffer for diagnostics */ + int var_arg, /* 1 on __VA_ARGS__, 0 on others*/ + int nargs, /* Argument number */ + LOCATION ** locp, /* Where to save location infs */ + int m_num, /* Macro number to trace */ + MAGIC_SEQ * mgc_prefix /* White space and magics leading to argument */ +) +/* + * Get an argument of macro into '*argpp', return the next punctuator. + * Variable arguments are read as a merged argument. + * Note: nargs, locp and m_num are used only in macro trace mode of + * '#pragma MCPP debug macro_call' or -K option. + */ +{ + struct { + int n_par; + int n_in_src; + } n_paren[ 16]; + int num_paren = 0; + int end_an_arg = FALSE; /* End-of-an-arg flag */ + int paren = var_arg; /* For embedded ()'s */ + int token_type; + char * prevp; + char * argp = *argpp; + int trace_arg = 0; /* Enable tracing arg */ + LINE_COL s_line_col, e_line_col; /* Location of macro in an argument */ + MAGIC_SEQ mgc_seq; /* Magic seqs and spaces succeeding an arg */ + size_t len; + + if (trace_macro) { + trace_arg = m_num && infile->fp; + if (m_num) { + if (trace_arg) { /* The macro call is in source */ + s_line_col.line = src_line; + s_line_col.col = infile->bptr - infile->buffer - 1; + /* '-1': bptr is one byte passed beginning of the token */ + get_src_location( & s_line_col); + (*locp)->start_line = s_line_col.line; + (*locp)->start_col = s_line_col.col; + e_line_col = s_line_col; + /* Save the location, */ + /* also for end of arg in case of empty arg*/ + memset( n_paren, 0, sizeof (n_paren)); + } + *argp++ = MAC_INF; + *argp++ = MAC_ARG_START; + *argp++ = (m_num / UCHARMAX) + 1; + *argp++ = (m_num % UCHARMAX) + 1; + *argp++ = nargs + 1; + /* Argument number internally starts at 1 */ + if (mgc_prefix->magic_start) { + /* Copy the preceding magics, if any */ + len = mgc_prefix->magic_end - mgc_prefix->magic_start; + memcpy( argp, mgc_prefix->magic_start, len); + argp += len; + } + } + memset( &mgc_seq, 0, sizeof (MAGIC_SEQ)); + } + + while (1) { + if (c == '\n' /* In control line */ + || c == RT_END) { /* Boundary of rescan (in STD mode) */ + if (c == '\n') + unget_ch(); + break; + } + if (trace_arg) { /* Save the location */ + s_line_col.line = src_line; /* of the token */ + s_line_col.col = infile->bptr - infile->buffer - 1; + } + token_type = scan_token( c, (prevp = argp, &argp), arg_end); + /* Scan the next token */ + switch (c) { + case '(': /* Worry about balance */ + paren++; /* To know about commas */ + break; + case ')': /* Other side too */ + if (paren-- == var_arg) /* At the end? */ + end_an_arg = TRUE; /* Else more to come */ + if (trace_arg) { + if (num_paren && paren == n_paren[ num_paren].n_par) { + /* Maybe corresponding parentheses for the macro in arg */ + int src_n; + src_n = n_paren[ num_paren].n_in_src; + in_src[ src_n].end_line = s_line_col.line; + in_src[ src_n].end_col = s_line_col.col + 1; + num_paren--; + } + } + break; + case ',': + if (paren == 0) /* Comma delimits arg */ + end_an_arg = TRUE; + break; + case MAC_INF : /* Copy magics as they are */ + switch (*argp++ = get_ch()) { + case MAC_ARG_START : + *argp++ = get_ch(); + /* Fall through */ + case MAC_CALL_START : + *argp++ = get_ch(); + *argp++ = get_ch(); + break; + case MAC_ARG_END : + if (! option_flags.v) + break; + else + *argp++ = get_ch(); + /* Fall through */ + case MAC_CALL_END : + if (option_flags.v) { + *argp++ = get_ch(); + *argp++ = get_ch(); + } + break; + } + break; + case CHAR_EOF : /* Unexpected EOF */ + return 0; + default : /* Any token */ + if (mcpp_mode == STD && token_type == NAM + && c != IN_SRC && c != DEF_MAGIC && infile->fp) { + len = trace_arg ? IN_SRC_LEN : 1; + memmove( prevp + len, prevp, (size_t) (argp - prevp)); + argp += len; + *prevp = IN_SRC; + /* Mark that the name is read from source file */ + if (trace_arg) { + DEFBUF * defp; + + defp = look_id( prevp + IN_SRC_LEN); + if (in_src_num >= MAX_IN_SRC_NUM - 1) { + cerror( + "Too many names in arguments tracing %s" /* _E_ */ + , defp ? defp->name : null, 0L, NULL); + return 0; + } else if (++in_src_num > max_in_src_num) { + size_t old_len; + old_len = sizeof (LOCATION) * max_in_src_num; + /* Enlarge the array */ + in_src = (LOCATION *) xrealloc( (char *) in_src + , old_len * 2); + /* Have to initialize the enlarged area */ + memset( in_src + max_in_src_num, 0, old_len); + max_in_src_num *= 2; + } + /* Insert the identifier number in 2-bytes-encoding */ + *(prevp + 1) = (in_src_num / UCHARMAX) + 1; + *(prevp + 2) = (in_src_num % UCHARMAX) + 1; + if (defp) { /* Macro name in arg */ + in_src[ in_src_num].start_line = s_line_col.line; + in_src[ in_src_num].start_col = s_line_col.col; + /* For object-like macro, also for function-like */ + /* macro in case of parens are not found. */ + in_src[ in_src_num].end_line = s_line_col.line; + in_src[ in_src_num].end_col + = infile->bptr - infile->buffer; + if (defp->nargs >= 0) { + /* Function-like macro: search parentheses */ + n_paren[ ++num_paren].n_par = paren; + n_paren[ num_paren].n_in_src = in_src_num; + } + } /* Else in_src[ in_src_num].* are 0L */ + } + } + break; + } /* End of switch */ + + if (end_an_arg) /* End of an argument */ + break; + if (trace_arg) { /* Save the location */ + e_line_col.line = src_line; /* before spaces */ + e_line_col.col = infile->bptr - infile->buffer; + } + memset( &mgc_seq, 0, sizeof (MAGIC_SEQ)); + c = squeeze_ws( &argp, NULL, &mgc_seq); + /* To the next token */ + } /* Collected an argument*/ + + *argp = EOS; + *seqp = stpcpy( *seqp, *argpp); /* Save the sequence */ + if (c == '\n' || c == RT_END) + return -1; /* Unterminated macro */ + argp--; /* Remove the punctuator*/ + if (mgc_seq.space) + --argp; /* Remove trailing space */ + if (mcpp_mode == STD) { + if (trace_macro && m_num) { + if (trace_arg) { /* Location of end of an arg */ + get_src_location( & e_line_col); + (*locp)->end_line = e_line_col.line; + (*locp)->end_col = e_line_col.col; + } + (*locp)++; /* Advance pointer even if !trace_arg */ + *argp++ = MAC_INF; + *argp++ = MAC_ARG_END; + if (option_flags.v) { + *argp++ = (m_num / UCHARMAX) + 1; + *argp++ = (m_num % UCHARMAX) + 1; + *argp++ = nargs + 1; + *argp = EOS; + *argpp = chk_magic_balance( *argpp, argp, TRUE, FALSE); + /* Check a stray magic caused by abnormal macro */ + /* and move it to an edge if found. */ + } + } + *argp++ = RT_END; /* For rescan() */ + } + *argp++ = EOS; /* Terminate an argument*/ + *argpp = argp; + return c; +} + +static int squeeze_ws( + char ** out, /* Pointer to output pointer */ + char ** endf, /* Pointer to end of infile data*/ + MAGIC_SEQ * mgc_seq /* Sequence of MAC_INFs and space */ + /* mgc_seq should be initialized in the calling routine */ +) +/* + * Squeeze white spaces to one space. + * White spaces are ' ' (and possibly '\t', when keep_spaces == TRUE. Note + * that '\r', '\v', '\f' have been already converted to ' ' by get_ch()), + * and '\n' unless in_directive is set. + * COM_SEP is skipped. TOK_SEPs are squeezed to one TOK_SEP. + * Copy MAC_INF and its sequences as they are. + * If white spaces are found and 'out' is not NULL, write a space to *out and + * increment *out. + * Record start and end of MAC_INF sequences and whether space is found or + * not for a convenience of get_an_arg(). + * Return the next character. + */ +{ + int c; + int space = 0; + int tsep = 0; + FILEINFO * file = infile; + FILE * fp = infile->fp; + int end_of_file = (out && endf) ? FALSE : TRUE; + + while (((char_type[ c = get_ch()] & SPA) && (! standard + || (mcpp_mode == POST_STD && file == infile) + || (mcpp_mode == STD + && ((macro_line != 0 && macro_line != MACRO_ERROR) + || file == infile)))) + || c == MAC_INF) { + if (! end_of_file && file != infile) { /* Infile has been read over*/ + *endf = *out; /* Remember the location */ + end_of_file = TRUE; + } + if (c == '\n' && in_directive) /* If scanning control line */ + break; /* do not skip newline. */ + switch (c) { + case '\n': + space++; + wrong_line = TRUE; + break; + case TOK_SEP: + if (mcpp_mode == STD) + tsep++; + continue; /* Skip COM_SEP in OLD_PREP mode */ + case MAC_INF : /* Copy magics as they are, or skip */ + if (mgc_seq && ! mgc_seq->magic_start) + mgc_seq->magic_start = *out; + /* First occurence of magic seq */ + if (out) + *(*out)++ = c; + c = get_ch(); + if (out) + *(*out)++ = c; + switch (c) { + case MAC_ARG_START : + c = get_ch(); + if (out) + *(*out)++ = c; + /* Fall through */ + case MAC_CALL_START : + c = get_ch(); + if (out) + *(*out)++ = c; + c = get_ch(); + if (out) + *(*out)++ = c; + break; + case MAC_ARG_END : + if (! option_flags.v) { + break; + } else { + c = get_ch(); + if (out) + *(*out)++ = c; + /* Fall through */ + } + case MAC_CALL_END : + if (option_flags.v) { + c = get_ch(); + if (out) + *(*out)++ = c; + c = get_ch(); + if (out) + *(*out)++ = c; + } + break; + } + if (mgc_seq) /* Remember end of last magic seq */ + mgc_seq->magic_end = *out; + break; + default: + space++; + break; + } + } + + if (out) { + if (space) { /* Write a space to output pointer */ + *(*out)++ = ' '; /* and increment the pointer. */ + if (mgc_seq) + mgc_seq->space = TRUE; + } + if (tsep && !space) /* Needs to preserve token separator*/ + *(*out)++ = TOK_SEP; + **out = EOS; + } + if (mcpp_mode == POST_STD && file != infile) { + unget_ch(); /* Arguments cannot cross "file"s */ + c = fp ? CHAR_EOF : RT_END; /* EOF is diagnosed by at_eof() */ + } else if (mcpp_mode == STD && macro_line == MACRO_ERROR + && file != infile) { /* EOF */ + unget_ch(); /* diagnosed by at_eof() or only */ + c = CHAR_EOF; /* name of a function-like macro. */ + } /* at_eof() resets macro_line on error */ + return c; /* Return the next character */ +} + +static void skip_macro( void) +/* + * Clear the stacked (i.e. half-expanded) macro, called on macro error. + */ +{ + if (infile == NULL) /* End of input */ + return; + if (infile->fp) /* Source file */ + return; + while (infile->fp == NULL) { /* Stacked stuff */ + infile->bptr += strlen( infile->bptr); + get_ch(); /* To the parent "file" */ + } + unget_ch(); +} + +static void diag_macro( + int severity, /* Error or warning */ + const char * format, + const char * arg1, + long arg2, + const char * arg3, + const DEFBUF * defp1, /* Macro causing the problem 1 */ + const DEFBUF * defp2 /* 2 */ +) +/* + * Supplement macro information for diagnostic. + */ +{ + + if (defp1 && defp1->name != macro_name) + expanding( defp1->name, FALSE); + /* Inform of the problematic macro call */ + if (defp2 && defp2->name != macro_name) + expanding( defp2->name, FALSE); + if (severity == CERROR) + cerror( format, arg1, arg2, arg3); + else + cwarn( format, arg1, arg2, arg3); +} + +static void dump_args( + const char * why, + int nargs, + const char ** arglist +) +/* + * Dump arguments list. + */ +{ + int i; + + mcpp_fprintf( DBG, "dump of %d actual arguments %s\n", nargs, why); + for (i = 0; i < nargs; i++) { + mcpp_fprintf( DBG, "arg[%d]", i + 1); + dump_string( NULL, arglist[ i]); + } +} + diff --git a/lib/mcpp/internal.H b/lib/mcpp/internal.H new file mode 100644 index 000000000..39aff8c73 --- /dev/null +++ b/lib/mcpp/internal.H @@ -0,0 +1,564 @@ +/*- + * Copyright (c) 1998, 2002-2008 Kiyoshi Matsui + * All rights reserved. + * + * Some parts of this code are derived from the public domain software + * DECUS cpp (1984,1985) written by Martin Minow. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * 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 AUTHOR 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. + */ + +/* + * I N T E R N A L . H + * I n t e r n a l D e f i n i t i o n s f o r M C P P + * + * In general, definitions in this file should not be changed by implementor. + */ + +#ifndef SYSTEM_H + #error "system.H" must be included prior to "internal.H" +#endif + +#include "mcpp_out.h" + +#if MCPP_LIB +#include "mcpp_lib.h" /* External interface when used as library */ +#endif + +#define EOS '\0' /* End of string */ +#define CHAR_EOF 0 /* Returned by get_ch() on eof */ + +#define VA_ARGS (UCHARMAX + 1) /* Signal of variable arguments */ +#define GVA_ARGS (VA_ARGS * 2) /* GCC-specific variable args */ +#define AVA_ARGS (VA_ARGS | GVA_ARGS)/* Any variable arguments */ +#define DEF_PRAGMA (-1 - AVA_ARGS) /* _Pragma() pseudo-macro */ +#define DEF_NOARGS (-2 - AVA_ARGS) /* #define foo vs #define foo() */ +#define DEF_NOARGS_PREDEF_OLD (DEF_NOARGS - 1) + /* Compiler-predefined macro without leading '_' */ +#define DEF_NOARGS_PREDEF (DEF_NOARGS - 2)/* Compiler-predefined macro */ +#define DEF_NOARGS_STANDARD (DEF_NOARGS - 3)/* Standard predefined macro */ +#define DEF_NOARGS_DYNAMIC (DEF_NOARGS - 4)/* Standard dynamically defined */ + +/* + * These magic characters must be control characters which can't be used + * in source file. + */ +/* for '#pragma MCPP debug macro_call' and -K option in STD mode. */ +#define MAC_INF 0x18 /* Magic for macro informations */ +/* In STD and POST_STD modes (IN_SRC and TOK_SEP are for STD mode only). */ +#define DEF_MAGIC 0x19 /* Magic to prevent recursive expansion */ +#define IN_SRC 0x1A /* Magic of name from source */ +#define RT_END 0x1C /* Magic of macro rescan boundary */ +#define ST_QUOTE 0x1D /* Magic for stringizing */ +#define CAT 0x1E /* Token concatenation delim. */ +#define TOK_SEP 0x1F /* Magic to wrap expanded macro */ +/* In OLD_PREP mode. */ +#define COM_SEP 0x1F /* Comment of 0-length */ + +#define MAC_PARM 0x7F /* Macro parameter signal */ + +/* Special character types */ +#define LET 1 /* Letter (alphabet and _) */ +#define DIG 2 /* Digit */ +#define DOT 4 /* . might start a number */ +#define PUNC 8 /* Punctuators and operators */ +#define QUO 0x10 /* Both flavors of quotation ",'*/ +#define SPA 0x20 /* White spaces */ +#define HSP 0x40 + /* Horizontal white spaces (' ', '\t', TOK_SEP) */ + +/* + * Codes for operators used in #if expression. + * The value of the scanned operator is stored in 'openum'. + */ +#define INV 0 /* Invalid, must be zero */ +#define OP_EOE INV /* End of expression */ +#define VAL 1 /* Value (operand) */ +#define OP_LPA 2 /* ( */ +/* The following are unary. */ +#define FIRST_UNOP OP_PLU /* First unary operator */ +#define OP_PLU 3 /* + */ +#define OP_NEG 4 /* - */ +#define OP_COM 5 /* ~ */ +#define OP_NOT 6 /* ! */ +#define LAST_UNOP OP_NOT /* Last unary operator */ +/* The following are binary. */ +#define FIRST_BINOP OP_MUL /* First binary operator */ +#define OP_MUL 7 /* * */ +#define OP_DIV 8 /* / */ +#define OP_MOD 9 /* % */ +#define OP_ADD 10 /* + */ +#define OP_SUB 11 /* - */ +#define OP_SL 12 /* << */ +#define OP_SR 13 /* >> */ +#define OP_LT 14 /* < */ +#define OP_LE 15 /* <= */ +#define OP_GT 16 /* > */ +#define OP_GE 17 /* >= */ +#define OP_EQ 18 /* == */ +#define OP_NE 19 /* != */ +#define OP_AND 20 /* & */ +#define OP_XOR 21 /* ^ */ +#define OP_OR 22 /* | */ +#define OP_ANA 23 /* && */ +#define OP_ORO 24 /* || */ +#define OP_QUE 25 /* ? */ +#define OP_COL 26 /* : */ +#define LAST_BINOP OP_COL /* Last binary operator */ +/* Parenthesis */ +#define OP_RPA 27 /* ) */ +#define OP_END 28 /* End of expression marker */ +#define OP_FAIL (OP_END + 1) /* For error returns */ + +/* + * The following are operators used in macro definition only. + */ +/* In STD and POST_STD modes. */ +#define OP_STR 30 /* # */ +#define OP_CAT 31 /* ## */ +#define OP_ELL 32 /* ... */ + +/* + * The following are C source operators or punctuators, + * not preprocessing operators. + * Note: "sizeof", "defined" are read as identifier for convenience. + */ +#define OP_1 33 /* Any other single byte ops or puncs */ + /* =, ., ;, [, ], {, }, ',' */ +#define OP_2 34 /* Any other two bytes operators */ + /* &=, |=, ++, +=, --, -=, ->, %=, *=, /=, ^=, */ +#define OP_3 35 /* Three bytes operators : <<=, >>= */ +/* + * The following are operators spelled in digraphs. + */ +/* In STD and POST_STD modes. */ +#define OP_LBRACE_D 0x40 /* <% i.e. { */ +#define OP_RBRACE_D 0x41 /* %> i.e. } */ +#define OP_LBRCK_D 0x42 /* <: i.e. [ */ +#define OP_RBRCK_D 0x43 /* :> i.e. ] */ +#define OP_SHARP_D 0x44 /* %: i.e. # */ +#define OP_DSHARP_D 0x45 /* %:%: i.e. ## */ +#define OP_DIGRAPH 0x40 /* (OP_*_D & OP_DIGRAPH) == 0x40 */ + +/* + * The following are for lexical scanning only. + */ +/* Token types */ +#define NO_TOKEN 0 +#define NAM 65 /* Identifier (name, keyword) */ +#define NUM 66 /* Preprocessing number */ +#define STR 67 /* Character string literal */ +#define CHR 69 /* Integer character constant */ + +/* In STD and POST_STD modes. */ +#define WSTR 68 /* Wide string literal */ +#define WCHR 70 /* Wide character constant */ + +#define OPE 71 /* Operator or punctuator */ +#define SPE 72 /* Unknown token (@ or others) */ +#define SEP 73 /* Token separator or magics */ + +/* + * The following are values of 'mcpp_debug' variable which is set by the + * arguments of '#pragma MCPP debug' directive. + */ +#define PATH 1 +#define TOKEN 2 +#define EXPAND 4 +#define MACRO_CALL 8 +#define IF 16 +#define EXPRESSION 32 +#define GETC 64 +#define MEMORY 128 + +/* MAC_* represent macro information types for -K option. */ +#define MAC_CALL_START 1 +#define MAC_CALL_END 2 +#define MAC_ARG_START 3 +#define MAC_ARG_END 4 + +/* MB_ERROR signals wrong multi-byte character sequence. */ +#define MB_ERROR 0x8000 + +#if MCPP_LIB && HOST_COMPILER == GNUC \ + && (SYS_FAMILY == SYS_UNIX && SYSTEM != SYS_CYGWIN) + /* For GCC 4.* on UNIXes */ +#pragma GCC visibility push( hidden) /* Hide these names from outside */ +#endif + +/* + * The DEFBUF structure stores information about #defined macros. + * Note that DEFBUF->parmnames is parameter names catenated with commas, + * which is saved for the check of redefinition for STD mode. + * 'parmnames' and 'repl' are allocated to the area succeding to name. + */ +typedef struct defbuf { + struct defbuf * link; /* Pointer to next def in chain */ + short nargs; /* Number of parameters */ + char * parmnames; /* -> Parameter names catenated by ',' */ + char * repl; /* Pointer to replacement text */ + const char * fname; /* Macro is defined in the source file */ + long mline; /* at the line. */ + char push; /* Push level indicator */ + char name[1]; /* Macro name */ +} DEFBUF; + +/* + * The FILEINFO structure stores information about open files and macros + * being expanded. + */ +typedef struct fileinfo { + char * bptr; /* Current pointer into buffer */ + long line; /* Current line number of file */ + FILE * fp; /* Source file if non-null */ + long pos; /* Position next to #include */ + struct fileinfo * parent; /* Link to includer */ + struct ifinfo * initif; /* Initial ifstack (return there on EOF)*/ + int sys_header; /* System header file or not */ + int include_opt; /* Specified by -include option */ + const char ** dirp; /* Include directory the file resides */ + const char * src_dir; /* Directory of source file */ + const char * real_fname; /* Real file name */ + const char * full_fname; /* Real full path list */ + char * filename; /* File/macro name (maybe changed) */ + char * buffer; /* Buffer of current input line */ +#if MCPP_LIB + /* Save output functions during push/pop of #includes */ + int (* last_fputc) ( int c, OUTDEST od); + int (* last_fputs) ( const char * s, OUTDEST od); + int (* last_fprintf)( OUTDEST od, const char * format, ...); +#endif +} FILEINFO; + +/* + * IFINFO stores information of conditional compilation. + */ +typedef struct ifinfo { + int stat; /* State of compilation */ + long ifline; /* Line #if started */ + long elseline; /* Line #else started */ +} IFINFO; + +/* + * These bits are set in IFINFO.stat + */ +#define WAS_COMPILING 1 /* TRUE if compile set at entry */ +#define ELSE_SEEN 2 /* TRUE when #else processed */ +#define TRUE_SEEN 4 /* TRUE when #if TRUE processed */ + +#define compiling ifstack[0].stat + +#define FP2DEST(fp) \ + (fp == fp_out) ? OUT : \ + ((fp == fp_err) ? ERR : \ + ((fp == fp_debug) ? DBG : \ + ((OUTDEST) -1))) + +/* VAL_SIGN structure stores information about evaluated number. */ +typedef struct val_sign { + expr_t val; /* Value */ + int sign; /* Signed, unsigned, error */ +} VAL_SIGN; + +/* Values of VAL_SIGN.sign. */ +#define SIGNED 1 +#define UNSIGNED 0 +#define VAL_ERROR (-1) + +/* LINE_COL stores information of line and column data */ +typedef struct line_col { + long line; + size_t col; +} LINE_COL; + +/* Value of macro_line on macro call error. */ +#define MACRO_ERROR (-1L) + +/* + * Values of insert_sep (flag of insertion of token separator). + * Used only in POST_STD mode. + */ +#define NO_SEP 0 /* No separator is inserted */ +#define INSERT_SEP 1 /* Next get_ch() insert a separator */ +#define INSERTED_SEP 2 /* Last get_ch() Inserted a separator */ + +#define str_eq(s1, s2) (strcmp(s1, s2) == 0) + +#ifndef IO_SUCCESS +#define IO_SUCCESS 0 +#endif +#ifndef IO_ERROR +#define IO_ERROR (errno ? errno : 1) +#endif + +/* + * Externs + */ + +/* The minimum translation limits specified by the Standards. */ +extern struct std_limits_ { + long str_len; /* Least maximum of string len. */ + size_t id_len; /* Least maximum of ident len. */ + int n_mac_pars; /* Least maximum of num of pars.*/ + int exp_nest; /* Least maximum of expr nest */ + int blk_nest; /* Least maximum of block nest */ + int inc_nest; /* Least maximum of include nest*/ + long n_macro; /* Least maximum of num of macro*/ + long line_num; /* Maximum source line number */ +} std_limits; +/* The boolean flags specified by the execution options. */ +extern struct option_flags_ { + int c; /* -C option (keep comments) */ + int k; /* -k option (keep white spaces)*/ + int z; /* -z option (no-output of included file) */ + int p; /* -P option (no #line output) */ + int q; /* -Q option (diag to mcpp.err) */ + int v; /* -v option (verbose) */ + int trig; /* -3 option (toggle trigraphs) */ + int dig; /* -2 option (toggle digraphs) */ + int lang_asm; /* -a option (assembler source) */ + int no_source_line; /* Do not output line in diag. */ + int dollar_in_name; /* Allow $ in identifiers */ +} option_flags; + +extern int mcpp_mode; /* Mode of preprocessing */ +extern int stdc_val; /* Value of __STDC__ */ +extern long stdc_ver; /* Value of __STDC_VERSION__ */ +extern long cplus_val; /* Value of __cplusplus for C++ */ +extern int stdc2; /* cplus_val or (stdc_ver >= 199901L) */ +extern int stdc3; /* (stdc_ver or cplus_val) >= 199901L */ +extern int standard; /* mcpp_mode is STD or POST_STD */ +extern int std_line_prefix; /* #line in C source style */ +extern int warn_level; /* Level of warning */ +extern int errors; /* Error counter */ +extern long src_line; /* Current source line number */ +extern int wrong_line; /* Force #line to compiler */ +extern int newlines; /* Count of blank lines */ +extern int keep_comments; /* Don't remove comments */ +extern int keep_spaces; /* Don't remove white spaces */ +extern int include_nest; /* Nesting level of #include */ +extern const char * null; /* "" string for convenience */ +extern const char ** inc_dirp; /* Directory of #includer */ +extern const char * cur_fname; /* Current source file name */ +extern int no_output; /* Don't output included file */ +extern int in_directive; /* In process of #directive */ +extern int in_define; /* In #define line */ +extern int in_getarg; /* Collecting arguments of macro*/ +extern int in_include; /* In #include line */ +extern int in_if; /* In #if and non-skipped expr. */ +extern long macro_line; /* Line number of macro call */ +extern char * macro_name; /* Currently expanding macro */ +extern int openum; /* Number of operator or punct. */ +extern IFINFO * ifptr; /* -> current ifstack item */ +extern FILEINFO * infile; /* Current input file or macro */ +extern FILE * fp_in; /* Input stream to preprocess */ +extern FILE * fp_out; /* Output stream preprocessed */ +extern FILE * fp_err; /* Diagnostics stream */ +extern FILE * fp_debug; /* Debugging information stream */ +extern int insert_sep; /* Inserted token separator flag*/ +extern int mkdep; /* Output source file dependency*/ +extern int mbchar; /* Encoding of multi-byte char */ +extern int mbchk; /* Possible multi-byte char */ +extern int bsl_in_mbchar; /* 2nd byte of mbchar has '\\' */ +extern int bsl_need_escape;/* '\\' in mbchar should be escaped */ +extern int mcpp_debug; /* Class of debug information */ +extern long in_asm; /* In #asm - #endasm block */ +extern jmp_buf error_exit; /* Exit on fatal error */ +extern char * cur_fullname; /* Full name of current source */ +extern short * char_type; /* Character classifier */ +extern char * workp; /* Free space in work[] */ +extern char * const work_end; /* End of work[] buffer */ +extern char identifier[]; /* Lastly scanned name */ +extern IFINFO ifstack[]; /* Information of #if nesting */ +extern char work_buf[]; +extern FILEINFO * sh_file; +extern int sh_line; + /* Temporary buffer for directive line and macro expansion */ + +/* main.c */ +extern void un_predefine( int clearall); + /* Undefine predefined macros */ + +/* directive.c */ +extern void directive( void); + /* Process #directive line */ +extern DEFBUF * do_define( int ignore_redef, int predefine); + /* Do #define directive */ +extern DEFBUF * look_id( const char * name); + /* Look for a #define'd thing */ +extern DEFBUF ** look_prev( const char * name, int * cmp); + /* Look for place to insert def.*/ +extern DEFBUF * look_and_install( const char * name, int numargs + , const char * parmnames, const char * repl); + /* Look and insert macro def. */ +extern DEFBUF * install_macro( const char * name, int numargs + , const char * parmnames, const char * repl, DEFBUF ** prevp, int cmp + , int predefine); + /* Install a def to symbol table*/ +extern int undefine( const char * name); + /* Delete from symbol table */ +extern void dump_a_def( const char * why, const DEFBUF * dp, int newdef + , int comment, FILE * fp); + /* Dump a specific macro def */ +extern void dump_def( int comment, int K_opt); + /* Dump current macro defs */ + +/* eval.c */ +extern expr_t eval_if( void); + /* Evaluate #if expression */ +extern VAL_SIGN * eval_num( const char * nump); + /* Evaluate preprocessing number*/ + +/* expand.c */ +extern char * (* expand_macro)( DEFBUF * defp, char * out, char * out_end + , LINE_COL line_col, int * pragma_op); + /* Expand a macro completely */ +extern void expand_init( int compat, int strict_ansi); + /* Initialize expand_macro() */ +extern DEFBUF * is_macro( char ** cp); + /* The sequence is a macro call?*/ + +/* mbchar.c */ +extern size_t (* mb_read)( int c1, char ** in_pp, char ** out_pp); + /* Read mbchar sequence */ +extern const char * set_encoding( char * name, char * env, int pragma); + /* Multi-byte char encoding */ +extern void mb_init( void); + /* Initialize mbchar variables */ +extern uexpr_t mb_eval( char ** seq_pp); + /* Evaluate mbchar in #if */ +extern int last_is_mbchar( const char * in, int len); + /* The line ends with MBCHAR ? */ + +/* support.c */ +extern int get_unexpandable( int c, int diag); + /* Get next unexpandable token */ +extern void skip_nl( void); + /* Skip to the end of the line */ +extern int skip_ws( void); + /* Skip over white-spaces */ +extern int scan_token( int c, char ** out_pp, char * out_end); + /* Get the next token */ +extern char * scan_quote( int delim, char * out, char * out_end, int diag); + /* Scan a quoted literal */ +extern int id_operator( const char * name); + /* Check identifier-like ops */ +extern void expanding( const char * name, int to_be_freed); + /* Save macro name expanding */ +extern void clear_exp_mac( void); + /* Clear expanding macro infs */ +extern int get_ch( void); + /* Get the next char from input */ +extern int cnv_trigraph( char * in); + /* Do trigraph replacement */ +extern int cnv_digraph( char * in); + /* Convert digraphs to usual tok*/ +extern void unget_ch( void); + /* Push back the char to input */ +extern FILEINFO * unget_string( const char * text, const char * name); + /* Push back the string to input*/ +extern char * save_string( const char * text); + /* Stuff string in malloc mem. */ +extern FILEINFO * get_file( const char * name, const char * src_dir + , const char * fullname, size_t bufsize, int include_opt); + /* New FILEINFO initialization */ +extern char * (xmalloc)( size_t size); + /* Get memory or die */ +extern char * (xrealloc)( char * ptr, size_t size); + /* Reallocate memory or die */ +extern LINE_COL * get_src_location( LINE_COL * p_line_col); + /* Get location on source file */ +extern void cfatal( const char * format, const char * arg1, long arg2 + , const char * arg3); + /* Print a fatal error and exit */ +extern void cerror( const char * format, const char * arg1, long arg2 + , const char * arg3); + /* Print an error message */ +extern void cwarn( const char * format, const char * arg1, long arg2 + , const char * arg3); + /* Print a warning message */ +extern void dump_string( const char * why, const char * text); + /* Dump text readably */ +extern void dump_unget( const char * why); + /* Dump all ungotten junk */ +/* Support for alternate output mechanisms (e.g. memory buffers) */ +extern int (* mcpp_fputc)( int c, OUTDEST od), + (* mcpp_fputs)( const char * s, OUTDEST od), + (* mcpp_fprintf)( OUTDEST od, const char * format, ...); + +/* system.c */ +extern void do_options( int argc, char ** argv, char ** in_pp + , char ** out_pp); + /* Process command line args */ +extern void init_sys_macro( void); + /* Define system-specific macro */ +extern void at_start( void); + /* Commands prior to main input */ +extern void put_depend( const char * filename); + /* Output source dependency line*/ +extern int do_include( int next); + /* Process #include directive */ +extern void add_file( FILE * fp, const char * src_dir + , const char * filename, const char * fullname, int include_opt); + /* Chain the included file */ +extern void sharp( FILEINFO * sharp_file, int marker); + /* Output # line number */ +extern void do_pragma( void); + /* Process #pragma directive */ +extern void put_asm( void); + /* Putout an asm code line */ +extern void do_old( void); + /* Process older directives */ +extern void at_end( void); + /* Do the final commands */ +extern void print_heap( void); + /* Print blocks of heap memory */ +#if ! HOST_HAVE_STPCPY +extern char * stpcpy( char * dest, const char * src); + /* Non-Standard library function*/ +#endif + +#if MCPP_LIB /* Setting to use mcpp as a subroutine */ +/* directive.c */ +extern void clear_symtable( void); + /* Free all macro definitions */ +/* system.c */ +extern void clear_filelist( void); + /* Free filename and directory list */ +/* The following 5 functions are to Initialize static variables. */ +/* directive.c */ +extern void init_directive( void); +/* eval.c */ +extern void init_eval( void); +/* support.c */ +extern void init_support( void); +/* system.c */ +extern void init_system( void); + +#if HOST_COMPILER == GNUC && (SYS_FAMILY == SYS_UNIX && SYSTEM != SYS_CYGWIN) +#pragma GCC visibility pop +#endif +#endif + +#if HOST_HAVE_STPCPY && !defined(stpcpy) +extern char * stpcpy( char * dest, const char * src); +#endif diff --git a/lib/mcpp/main.c b/lib/mcpp/main.c new file mode 100644 index 000000000..8da4b5865 --- /dev/null +++ b/lib/mcpp/main.c @@ -0,0 +1,1131 @@ +/*- + * Copyright (c) 1998, 2002-2008 Kiyoshi Matsui + * All rights reserved. + * + * Some parts of this code are derived from the public domain software + * DECUS cpp (1984,1985) written by Martin Minow. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * 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 AUTHOR 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. + */ + +/* + * M A I N . C + * M C P P M a i n P r o g r a m + * + * The main routine and it's supplementary routines are placed here. + * The post-preprocessing routines are also placed here. + */ + +#if PREPROCESSED /* Use "pre-preprocessed" header */ +#include "mcpp.H" +#else +#include "system.H" +#include "internal.H" +#endif + + /* Function pointer to expand_macro() functions. */ + char * (*expand_macro)( DEFBUF * defp, char * out, char * out_end + , LINE_COL line_col, int * pragma_op); + + /* The boolean flags specified by the execution options. */ + struct option_flags_ option_flags = { + FALSE, /* c: -C (keep comments) */ + FALSE, /* k: -k (keep horizontal white spaces) */ + FALSE, /* z: -z (no output of included files) */ + FALSE, /* p: -P (no #line output) */ + FALSE, /* q: -Q (output diagnosis to mcpp.err) */ + FALSE, /* v: -v (verbose, affects macro notification) */ + TRIGRAPHS_INIT, /* trig: -3 (toggle trigraphs) */ + DIGRAPHS_INIT, /* dig: -2 (toggle digraphs recognition) */ + /* + * lang_asm allows the following non-standard features. + * 1. #non-directive. + * 2. in a string-literal. + * 3. invalid pp-token generated by ## operator. + * lang_asm is not available in POST_STD mode. + * When COMPILER == GNUC, -x assembler-with-cpp or -lang-asm options + * are used instead of -a. + */ + FALSE, /* lang_asm: -a (assembler source) */ + FALSE /* no_source_line: -j (no source line in diag) */ + }; + + int mcpp_mode = STD; /* Mode of preprocessing */ + + long cplus_val = 0L; /* Value of __cplusplus for C++ */ + long stdc_ver = 0L; /* Value of __STDC_VERSION__ */ + int stdc_val = 0; /* Value of __STDC__ */ + int stdc2; /* cplus_val || stdc_ver >= 199901L */ + int stdc3; /* cplus_val >= 199901L || stdc_ver >= 199901L. + (cplus_val >= 199901L) specifies compatible mode to C99 (extended + feature of this preprocessor) */ + int standard = TRUE; /* TRUE, if mcpp_mode is STD or POST_STD */ + int std_line_prefix = STD_LINE_PREFIX; + /* Output line and file information in C source style */ + +/* + * Commonly used global variables: + * src_line is the current input line number. + * wrong_line is set in many places when the actual output line is out of + * sync with the numbering, e.g, when expanding a macro with an + * embedded newline. + * identifier holds the last identifier scanned (which might be a candidate + * for macro expansion). + * errors is the running mcpp error counter. + * infile is the head of a linked list of input files (extended by + * #include and macros being expanded). 'infile' always points + * to the current file/macro. 'infile->parent' to the includer, + * etc. 'infile->fp' is NULL if this input stream is not a file. + * inc_dirp Directory of #includer with trailing PATH_DELIM. This points + * to one of incdir[] or to the current directory (represented as + * "". This should not be NULL. + */ + long src_line; /* Current line number */ + int wrong_line; /* Force #line to compiler */ + int newlines; /* Count of blank lines */ + int errors = 0; /* Cpp error counter */ + int warn_level = -1; /* Level of warning (have to initialize)*/ + FILEINFO * infile = NULL; /* Current input file */ + int include_nest = 0; /* Nesting level of #include */ + const char * null = ""; /* "" string for convenience */ + const char ** inc_dirp; /* Directory of #includer */ + const char * cur_fname; /* Current source file name */ + /* cur_fname is not rewritten by #line directive */ + char * cur_fullname; + /* Full path of current source file (i.e. infile->full_fname) */ + int no_source_line; /* Do not output line in diag. */ + char identifier[ IDMAX + IDMAX/8]; /* Current identifier */ + int mcpp_debug = 0; /* != 0 if debugging now */ + +/* + * in_directive is set TRUE while a directive line is scanned by directive(). + * It modifies the behavior of squeeze_ws() in expand.c so that newline is + * not skipped even if getting macro arguments. + */ + int in_directive = FALSE; /* TRUE scanning directive line */ + int in_define = FALSE; /* TRUE scanning #define line */ + int in_getarg = FALSE; /* TRUE collecting macro arguments */ + int in_include = FALSE; /* TRUE scanning #include line */ + int in_if = FALSE; /* TRUE scanning #if and in non-skipped expr. */ + long in_asm = 0L; /* Starting line of #asm - #endasm block*/ + +/* + * macro_line is set to the line number of start of a macro call while + * expanding the macro, else set to 0. Line number is remembered for + * diagnostics of unterminated macro call. On unterminated macro call + * macro_line is set to MACRO_ERROR. + */ + long macro_line = 0L; +/* + * macro_name is the currently expanding macro. + */ + char * macro_name; + +/* + * openum is the return value of scan_op() in support.c. + */ + int openum; + +/* + * mkdep means to output source file dependency line, specified by -M* + * option. The OR of the following values is used. + * MD_MKDEP (1) : Output dependency line. + * MD_SYSHEADER(2) : Print also system headers or headers with + * absolute path not only user headers. + * MD_FILE (4) : Output to the file named *.d instead of fp_out. + * Normal output is done to fp_out as usual. + */ + int mkdep = 0; + +/* + * If option_flags.z is TRUE, no_output is incremented when a file is + * #included, and decremented when the file is finished. + * If no_output is larger than 0, processed files are not output, meanwhile + * the macros in the files are defined. + * If mkdep != 0 && (mkdep & MD_FILE) == 0, no_output is set to 1 initially. + */ + int no_output = 0; + +/* + * keep_comments is set TRUE by the -C option. If TRUE, comments are written + * directly to the output stream. option_flags.c contains the permanent state + * of the -C option. keep_comments is always falsified when compilation is + * supressed by a false #if or when no_output is TRUE. + */ + int keep_comments = 0; /* Write out comments flag */ + +/* + * keep_spaces is set to TRUE by the -k option. If TRUE, spaces and tabs in + * an input line are written out to the output line without squeezing to one + * space. option_flags.k contains the permanent state of the -k option. + * keep_spaces is falsified when compilation is suppressed by a false #if. + */ + int keep_spaces = 0; /* Keep white spaces of line*/ + +/* + * ifstack[] holds information about nested #if's. It is always accessed via + * ifptr->stat. The information is as follows: + * WAS_COMPILING state of compiling flag at outer level. + * ELSE_SEEN set TRUE when #else seen to prevent 2nd #else. + * TRUE_SEEN set TRUE when #if or #elif succeeds + * ifstack[0].stat holds the compiling flag. It is WAS_COMPILING if compila- + * tion is currently enabled. Note that this must be initialized to + * WAS_COMPILING. + */ + IFINFO ifstack[ BLK_NEST + 1] = { {WAS_COMPILING, 0L, 0L}, }; + /* Note: '+1' is necessary for the initial state. */ + IFINFO * ifptr = ifstack; /* -> current ifstack[] */ + +/* + * In POST_STD mode, insert_sep is set to INSERT_SEP when : + * 1. the next get_ch() shall insert a token separator. + * 2. unget_ch() has been called when insert_sep == INSERTED_SEP. + * set to INSERTED_SEP when : + * get_ch() has been called when insert_sep == INSERT_SEP. + * set to NO_SEP when : + * get_ch() has been called when insert_sep == INSERTED_SEP. + */ + int insert_sep = NO_SEP; + +/* File pointers for input and output. */ + FILE * fp_in; /* Input stream to preprocess */ + FILE * fp_out; /* Output stream preprocessed */ + FILE * fp_err; /* Diagnostics stream */ + FILE * fp_debug; /* Debugging information stream */ + +/* Variables on multi-byte character encodings. */ + int mbchar = MBCHAR; /* Encoding of multi-byte char */ + int mbchk; /* Character type of possible multi-byte char */ + int bsl_in_mbchar; /* 2nd byte of mbchar possibly has '\\' */ + int bsl_need_escape; /* '\\' in MBCHAR should be escaped */ + /* Function pointer to mb_read_*() functions. */ + size_t (*mb_read)( int c1, char ** in_pp, char ** out_pp); + + jmp_buf error_exit; /* Exit on fatal error */ + +/* + * Translation limits specified by C90, C99 or C++. + */ + struct std_limits_ std_limits = { + /* The following three are temporarily set for do_options() */ + NBUFF, /* Least maximum of string length */ + IDMAX, /* Least maximum of identifier length */ + NMACPARS, /* Least maximum of number of macro params */ + }; + +/* + * work_buf[] and workp are used to store one piece of text as a temporary + * buffer. + * To initialize storage, set workp = work_buf. Note that the work buffer is + * used by several subroutines -- be sure that your data won't be overwritten. + * work_buf[] is used for: + * 1. macro expansion (def_special(), prescan(), catenate(), + * stringize()). + * 2. processing directive line (directive.c, eval.c, get_unexpandable(), + * do_pragma() and its subroutines). + * 3. processing _Pragma() operator (do_pragma_op()). + * 4. miscellaneous (init_gcc_macro(), curfile()). + */ + char work_buf[ NWORK + IDMAX]; /* Work buffer */ + char * workp; /* Pointer into work_buf[] */ + char * const work_end = & work_buf[ NWORK]; + /* End of buffer of work_buf[] */ + +/* + * src_col is the current input column number, but is rarely used. + * It is used to put spaces after #line line in keep_spaces mode + * on some special cases. + */ +static int src_col = 0; /* Column number of source line */ + +#define MBCHAR_IS_ESCAPE_FREE (SJIS_IS_ESCAPE_FREE && \ + BIGFIVE_IS_ESCAPE_FREE && ISO2022_JP_IS_ESCAPE_FREE) + +#if MCPP_LIB +static void init_main( void); + /* Initialize static variables */ +#endif +static void init_defines( void); + /* Predefine macros */ +static void mcpp_main( void); + /* Main loop to process input lines */ +static void do_pragma_op( void); + /* Execute the _Pragma() operator */ +static void put_seq( char * begin, char * seq); + /* Put out the failed sequence */ +static char * de_stringize( char * in, char * out); + /* "De-stringize" for _Pragma() op. */ +static void putout( char * out); + /* May concatenate adjacent string */ +#if COMPILER != GNUC && COMPILER != MSC +static void devide_line( char * out); + /* Devide long line for compiler */ +#endif +static void put_a_line( char * out); + /* Put out the processed line */ +#if ! HAVE_DIGRAPHS || ! MBCHAR_IS_ESCAPE_FREE +static int post_preproc( char * out); + /* Post-preprocess for older comps */ +#if ! HAVE_DIGRAPHS +static char * conv_a_digraph( char * cp); + /* Convert a digraph in place */ +#endif +#if ! MBCHAR_IS_ESCAPE_FREE +static char * esc_mbchar( char * str, char * str_end); + /* Double \ as 2nd byte of SJIS */ +#endif +#endif + + +#if MCPP_LIB +static void init_main( void) +/* Initialize global variables on re-entering. */ +{ + mcpp_mode = STD; + cplus_val = stdc_ver = 0L; + stdc_val = 0; + standard = TRUE; + std_line_prefix = STD_LINE_PREFIX; + errors = src_col = 0; + warn_level = -1; + infile = NULL; + in_directive = in_define = in_getarg = in_include = in_if = FALSE; + src_line = macro_line = in_asm = 0L; + mcpp_debug = mkdep = no_output = keep_comments = keep_spaces = 0; + include_nest = 0; + insert_sep = NO_SEP; + mbchar = MBCHAR; + ifptr = ifstack; + ifstack[0].stat = WAS_COMPILING; + ifstack[0].ifline = ifstack[0].elseline = 0L; + std_limits.str_len = NBUFF; + std_limits.id_len = IDMAX; + std_limits.n_mac_pars = NMACPARS; + option_flags.c = option_flags.k = option_flags.z = option_flags.p + = option_flags.q = option_flags.v = option_flags.lang_asm + = option_flags.no_source_line = option_flags.dollar_in_name + = FALSE; + option_flags.trig = TRIGRAPHS_INIT; + option_flags.dig = DIGRAPHS_INIT; + sh_file = NULL; + sh_line = 0; +} + +int mcpp_lib_main +#else +int main +#endif +( + int argc, + char ** argv +) +{ + char * in_file = NULL; + char * out_file = NULL; + char * stdin_name = ""; + + if (setjmp( error_exit) == -1) { + errors++; + goto fatal_error_exit; + } + +#if MCPP_LIB + /* Initialize global and static variables. */ + init_main(); + init_directive(); + init_eval(); + init_support(); + init_system(); +#endif + + fp_in = stdin; + fp_out = stdout; + fp_err = stderr; + fp_debug = stdout; + /* + * Debugging information is output to stdout in order to + * synchronize with preprocessed output. + */ + + inc_dirp = &null; /* Initialize to current (null) directory */ + cur_fname = cur_fullname = "(predefined)"; /* For predefined macros */ + init_defines(); /* Predefine macros */ + mb_init(); /* Should be initialized prior to get options */ + do_options( argc, argv, &in_file, &out_file); /* Command line options */ + + /* Open input file, "-" means stdin. */ + if (in_file != NULL && ! str_eq( in_file, "-")) { + if ((fp_in = fopen( in_file, "r")) == NULL) { + mcpp_fprintf( ERR, "Can't open input file \"%s\".\n", in_file); + errors++; +#if MCPP_LIB + goto fatal_error_exit; +#else + return( IO_ERROR); +#endif + } + } else { + in_file = stdin_name; + } + /* Open output file, "-" means stdout. */ + if (out_file != NULL && ! str_eq( out_file, "-")) { + if ((fp_out = fopen( out_file, "w")) == NULL) { + mcpp_fprintf( ERR, "Can't open output file \"%s\".\n", out_file); + errors++; +#if MCPP_LIB + goto fatal_error_exit; +#else + return( IO_ERROR); +#endif + } + fp_debug = fp_out; + } + if (option_flags.q) { /* Redirect diagnostics */ + if ((fp_err = fopen( "mcpp.err", "a")) == NULL) { + errors++; + mcpp_fprintf( OUT, "Can't open \"mcpp.err\"\n"); +#if MCPP_LIB + goto fatal_error_exit; +#else + return( IO_ERROR); +#endif + } + } + init_sys_macro(); /* Initialize system-specific macros */ + add_file( fp_in, NULL, in_file, in_file, FALSE); + /* "open" main input file */ + infile->dirp = inc_dirp; + infile->sys_header = FALSE; + cur_fullname = in_file; + if (mkdep && str_eq( infile->real_fname, stdin_name) == FALSE) + put_depend( in_file); /* Putout target file name */ + at_start(); /* Do the pre-main commands */ + + mcpp_main(); /* Process main file */ + + if (mkdep) + put_depend( NULL); /* Append '\n' to dependency line */ + at_end(); /* Do the final commands */ + +fatal_error_exit: +#if MCPP_LIB + /* Free malloced memory */ + if (mcpp_debug & MACRO_CALL) { + if (in_file != stdin_name) + free( in_file); + } + clear_filelist(); + clear_symtable(); +#endif + + if (fp_in != stdin) + fclose( fp_in); + if (fp_out != stdout) + fclose( fp_out); + if (fp_err != stderr) + fclose( fp_err); + + if (mcpp_debug & MEMORY) + print_heap(); + if (errors > 0 && option_flags.no_source_line == FALSE) { + mcpp_fprintf( ERR, "%d error%s in preprocessor.\n", + errors, (errors == 1) ? "" : "s"); + return IO_ERROR; + } + return IO_SUCCESS; /* No errors */ +} + +/* + * This is the table used to predefine target machine, operating system and + * compiler designators. It may need hacking for specific circumstances. + * The -N option supresses these definitions. + */ +typedef struct pre_set { + const char * name; + const char * val; +} PRESET; + +static PRESET preset[] = { + +#ifdef SYSTEM_OLD + { SYSTEM_OLD, "1"}, +#endif +#ifdef SYSTEM_SP_OLD + { SYSTEM_SP_OLD, "1"}, +#endif +#ifdef COMPILER_OLD + { COMPILER_OLD, "1"}, +#endif +#ifdef COMPILER_SP_OLD + { COMPILER_SP_OLD, "1"}, +#endif + + { NULL, NULL}, /* End of macros beginning with alphabet */ + +#ifdef SYSTEM_STD + { SYSTEM_STD, "1"}, +#endif +#ifdef SYSTEM_STD1 + { SYSTEM_STD1, "1"}, +#endif +#ifdef SYSTEM_STD2 + { SYSTEM_STD2, "1"}, +#endif + +#ifdef SYSTEM_EXT + { SYSTEM_EXT, SYSTEM_EXT_VAL}, +#endif +#ifdef SYSTEM_EXT2 + { SYSTEM_EXT2, SYSTEM_EXT2_VAL}, +#endif +#ifdef SYSTEM_SP_STD + { SYSTEM_SP_STD, SYSTEM_SP_STD_VAL}, +#endif +#ifdef COMPILER_STD + { COMPILER_STD, COMPILER_STD_VAL}, +#endif +#ifdef COMPILER_STD1 + { COMPILER_STD1, COMPILER_STD1_VAL}, +#endif +#ifdef COMPILER_STD2 + { COMPILER_STD2, COMPILER_STD2_VAL}, +#endif +#ifdef COMPILER_EXT + { COMPILER_EXT, COMPILER_EXT_VAL}, +#endif +#ifdef COMPILER_EXT2 + { COMPILER_EXT2, COMPILER_EXT2_VAL}, +#endif +#ifdef COMPILER_SP_STD + { COMPILER_SP_STD, COMPILER_SP_STD_VAL}, +#endif +#ifdef COMPILER_SP1 + { COMPILER_SP1, COMPILER_SP1_VAL}, +#endif +#ifdef COMPILER_SP2 + { COMPILER_SP2, COMPILER_SP2_VAL}, +#endif +#ifdef COMPILER_SP3 + { COMPILER_SP3, COMPILER_SP3_VAL}, +#endif +#ifdef COMPILER_CPLUS + { COMPILER_CPLUS, COMPILER_CPLUS_VAL}, +#endif + { NULL, NULL}, /* End of macros with value of any integer */ +}; + +static void init_defines( void) +/* + * Initialize the built-in #define's. + * Called only on cpp startup prior to do_options(). + * + * Note: the built-in static definitions are removed by the -N option. + */ +{ + int n = sizeof preset / sizeof (PRESET); + int nargs; + PRESET * pp; + + /* Predefine the built-in symbols. */ + nargs = DEF_NOARGS_PREDEF_OLD; + for (pp = preset; pp < preset + n; pp++) { + if (pp->name && *(pp->name)) + look_and_install( pp->name, nargs, null, pp->val); + else if (! pp->name) + nargs = DEF_NOARGS_PREDEF; + } + + look_and_install( "__MCPP", DEF_NOARGS_PREDEF, null, "2"); + /* MCPP V.2.x */ + /* This macro is predefined and is not undefined by -N option, */ + /* yet can be undefined by -U or #undef. */ +} + +void un_predefine( + int clearall /* TRUE for -N option */ +) +/* + * Remove predefined symbols from the symbol table. + */ +{ + PRESET * pp; + DEFBUF * defp; + int n = sizeof preset / sizeof (PRESET); + + for (pp = preset; pp < preset + n; pp++) { + if (pp->name) { + if (*(pp->name) && (defp = look_id( pp->name)) != NULL + && defp->nargs >= DEF_NOARGS_PREDEF) + undefine( pp->name); + } else if (clearall == FALSE) { /* -S option */ + break; + } + } +} + +/* + * output[] and out_ptr are used for: + * buffer to store preprocessed line (this line is put out or handed to + * post_preproc() via putout() in some cases) + */ +static char output[ NMACWORK]; /* Buffer for preprocessed line */ +static char * const out_end = & output[ NWORK - 2]; + /* Limit of output line for other than GCC and VC */ +static char * const out_wend = & output[ NMACWORK - 2]; + /* Buffer end of output line */ +static char * out_ptr; /* Current pointer into output[]*/ + +static void mcpp_main( void) +/* + * Main process for mcpp -- copies tokens from the current input stream + * (main file or included file) to the output file. + */ +{ + int c; /* Current character */ + char * wp; /* Temporary pointer */ + DEFBUF * defp; /* Macro definition */ + int line_top; /* Is in the line top, possibly spaces */ + LINE_COL line_col; /* Location of macro call in source */ + + keep_comments = option_flags.c && !no_output; + keep_spaces = option_flags.k; /* Will be turned off if !compiling */ + line_col.col = line_col.line = 0L; + + /* + * This loop is started "from the top" at the beginning of each line. + * 'wrong_line' is set TRUE in many places if it is necessary to write + * a #line record. (But we don't write them when expanding macros.) + * + * 'newlines' variable counts the number of blank lines that have been + * skipped over. These are then either output via #line records or + * by outputting explicit blank lines. + * 'newlines' will be cleared on end of an included file by get_ch(). + */ + while (1) { /* For the whole input */ + newlines = 0; /* Count empty lines */ + + while (1) { /* For each line, ... */ + out_ptr = output; /* Top of the line buf */ + c = get_ch(); + if (src_col) + break; /* There is a residual tokens on the line */ + while (char_type[ c] & HSP) { /* ' ' or '\t' */ + if (c != COM_SEP) + *out_ptr++ = c; /* Retain line top white spaces */ + /* Else skip 0-length comment */ + c = get_ch(); + } + if (c == '#') { /* Is 1st non-space '#' */ + directive(); /* Do a #directive */ + } else if (mcpp_mode == STD && option_flags.dig && c == '%') { + /* In POST_STD digraphs are already converted */ + if (get_ch() == ':') { /* '%:' i.e. '#' */ + directive(); /* Do a #directive */ + } else { + unget_ch(); + if (! compiling) { + skip_nl(); + newlines++; + } else { + break; + } + } + } else if (c == CHAR_EOF) { /* End of input */ + break; + } else if (! compiling) { /* #ifdef false? */ + skip_nl(); /* Skip to newline */ + newlines++; /* Count it, too. */ + } else if (in_asm && ! no_output) { /* In #asm block */ + put_asm(); /* Put out as it is */ + } else if (c == '\n') { /* Blank line */ + if (keep_comments) + mcpp_fputc( '\n', OUT); /* May flush comments */ + else + newlines++; /* Wait for a token */ + } else { + break; /* Actual token */ + } + } + + if (c == CHAR_EOF) /* Exit process at */ + break; /* end of input */ + + /* + * If the loop didn't terminate because of end of file, we + * know there is a token to compile. First, clean up after + * absorbing newlines. newlines has the number we skipped. + */ + if (no_output) { + wrong_line = FALSE; + } else { + if (wrong_line || newlines > 10) { + sharp( NULL, 0); /* Output # line number */ + if (keep_spaces && src_col) { + while (src_col--) /* Adjust columns */ + mcpp_fputc( ' ', OUT); + src_col = 0; + } + } else { /* If just a few, stuff */ + while (newlines-- > 0) /* them out ourselves */ + mcpp_fputc('\n', OUT); + } + } + + /* + * Process each token on this line. + */ + line_top = TRUE; + while (c != '\n' && c != CHAR_EOF) { /* For the whole line */ + /* + * has_pragma is set to TRUE so as to execute _Pragma() operator + * when the psuedo macro _Pragma() is found. + */ + int has_pragma; + + if ((mcpp_debug & MACRO_CALL) && ! in_directive) { + line_col.line = src_line; /* Location in source */ + line_col.col = infile->bptr - infile->buffer - 1; + } + if (scan_token( c, (wp = out_ptr, &wp), out_wend) == NAM + && (defp = is_macro( &wp)) != NULL) { /* A macro */ + wp = expand_macro( defp, out_ptr, out_wend, line_col + , & has_pragma); /* Expand it completely */ + if (line_top) { /* The first token is a macro */ + char * tp = out_ptr; + while (char_type[ *tp & UCHARMAX] & HSP) + tp++; /* Remove excessive spaces */ + memmove( out_ptr, tp, strlen( tp) + 1); + wp -= (tp - out_ptr); + } + if (has_pragma) { /* Found _Pramga() */ + do_pragma_op(); /* Do _Pragma() operator*/ + out_ptr = output; /* Do the rest of line */ + wrong_line = TRUE; /* Line-num out of sync */ + } else { + out_ptr = wp; + } + if (keep_spaces && wrong_line && infile + && *(infile->bptr) != '\n' && *(infile->bptr) != EOS) { + src_col = infile->bptr - infile->buffer; + /* Remember the current colums */ + break; /* Do sharp() now */ + } + } else { /* Not a macro call */ + out_ptr = wp; /* Advance the place */ + if (wrong_line) /* is_macro() swallowed */ + break; /* the newline */ + } + while (char_type[ c = get_ch()] & HSP) { /* Horizontal space */ + if (c != COM_SEP) /* Skip 0-length comment*/ + *out_ptr++ = c; + } + line_top = FALSE; /* Read over some token */ + } /* Loop for line */ + + putout( output); /* Output the line */ + } /* Continue until EOF */ +} + +static void do_pragma_op( void) +/* + * Execute the _Pragma() operator contained in an expanded macro. + * Note: _Pragma() operator is also implemented as a special macro. Therefore + * it is always searched as a macro. + * There might be more than one _Pragma() in a expanded macro and those may be + * surrounded by other token sequences. + * Since all the macros have been expanded completely, any name identical to + * macro should not be re-expanded. + * However, a macro in the string argument of _Pragma() may be expanded by + * do_pragma() after de_stringize(), if EXPAND_PRAGMA == TRUE. + */ +{ + FILEINFO * file; + DEFBUF * defp; + int prev = output < out_ptr; /* There is a previous sequence */ + int token_type; + char * cp1, * cp2; + int c; + + file = unget_string( out_ptr, NULL); + while (c = get_ch(), file == infile) { + if (char_type[ c] & HSP) { + *out_ptr++ = c; + continue; + } + if (scan_token( c, (cp1 = out_ptr, &cp1), out_wend) + == NAM && (defp = is_macro( &cp1)) != NULL + && defp->nargs == DEF_PRAGMA) { /* _Pragma() operator */ + if (prev) { + putout( output); /* Putout the previous sequence */ + cp1 = stpcpy( output, "pragma "); /* From top of buffer */ + } + /* is_macro() already read over possible spaces after _Pragma */ + *cp1++ = get_ch(); /* '(' */ + while (char_type[ c = get_ch()] & HSP) + *cp1++ = c; + if (((token_type = scan_token( c, (cp2 = cp1, &cp1), out_wend)) + != STR && token_type != WSTR)) { + /* Not a string literal */ + put_seq( output, cp1); + return; + } + workp = de_stringize( cp2, work_buf); + while (char_type[ c = get_ch()] & HSP) + *cp1++ = c; + if (c != ')') { /* More than a string literal */ + unget_ch(); + put_seq( output, cp1); + return; + } + strcpy( workp, "\n"); /* Terminate with */ + unget_string( work_buf, NULL); + do_pragma(); /* Do the #pragma "line" */ + infile->bptr += strlen( infile->bptr); /* Clear sequence */ + cp1 = out_ptr = output; /* From the top of buffer */ + prev = FALSE; + } else { /* Not pragma sequence */ + out_ptr = cp1; + prev = TRUE; + } + } + unget_ch(); + if (prev) + putout( output); +} + +static void put_seq( + char * begin, /* Sequence already in buffer */ + char * seq /* Sequence to be read */ +) +/* + * Put out the failed sequence as it is. + */ +{ + FILEINFO * file = infile; + int c; + + cerror( "Operand of _Pragma() is not a string literal" /* _E_ */ + , NULL, 0L, NULL); + while (c = get_ch(), file == infile) + *seq++ = c; + unget_ch(); + out_ptr = seq; + putout( begin); +} + +static char * de_stringize( + char * in, /* Null terminated string literal */ + char * out /* Output buffer */ +) +/* + * Make token sequence from a string literal for _Pragma() operator. + */ +{ + char * in_p; + int c1, c; + + in_p = in; + if (*in_p == 'L') + in_p++; /* Skip 'L' prefix */ + while ((c = *++in_p) != EOS) { + if (c == '\\' && ((c1 = *(in_p + 1), c1 == '\\') || c1 == '"')) + c = *++in_p; /* "De-escape" escape sequence */ + *out++ = c; + } + *--out = EOS; /* Remove the closing '"' */ + return out; +} + +static void putout( + char * out /* Output line (line-end is always 'out_ptr') */ +) +/* + * Put out a line with or without "post-preprocessing". + */ +{ + size_t len; + + *out_ptr++ = '\n'; /* Put out a newline */ + *out_ptr = EOS; + +#if ! MBCHAR_IS_ESCAPE_FREE + post_preproc( out); +#elif ! HAVE_DIGRAPHS + if (mcpp_mode == STD && option_flag.dig) + post_preproc( out); +#endif + /* Else no post-preprocess */ +#if COMPILER != GNUC && COMPILER != MSC + /* GCC and Visual C can accept very long line */ + len = strlen( out); + if (len > NWORK - 1) + devide_line( out); /* Devide a too long line */ + else +#endif + put_a_line( out); +} + +#if COMPILER != GNUC && COMPILER != MSC + +static void devide_line( + char * out /* 'out' is 'output' in actual */ +) +/* + * Devide a too long line into output lines shorter than NWORK. + * This routine is called from putout(). + */ +{ + FILEINFO * file; + char * save; + char * wp; + int c; + + file = unget_string( out, NULL); /* To re-read the line */ + wp = out_ptr = out; + + while ((c = get_ch()), file == infile) { + if (char_type[ c] & HSP) { + if (keep_spaces || out == out_ptr + || (char_type[ *(out_ptr - 1) & UCHARMAX] & HSP)) { + *out_ptr++ = c; + wp++; + } + continue; + } + scan_token( c, &wp, out_wend); /* Read a token */ + if (NWORK-2 < wp - out_ptr) { /* Too long a token */ + cfatal( "Too long token %s", out_ptr, 0L, NULL); /* _F_ */ + } else if (out_end <= wp) { /* Too long line */ + if (mcpp_debug & MACRO_CALL) { /* -K option */ + /* Other than GCC or Visual C */ + /* scan_token() scans a comment as sequence of some */ + /* tokens such as '/', '*', ..., '*', '/', since it */ + /* does not expect comment. */ + save = out_ptr; + while ((save = strrchr( save, '/')) != NULL) { + if (*(save - 1) == '*') { /* '*' '/' sequence */ + out_ptr = save + 1; /* Devide at the end*/ + break; /* of a comment*/ + } + } + } + save = save_string( out_ptr); /* Save the token */ + *out_ptr++ = '\n'; /* Append newline */ + *out_ptr = EOS; + put_a_line( out); /* Putout the former tokens */ + wp = out_ptr = stpcpy( out, save); /* Restore the token */ + free( save); + } else { /* Still in size */ + out_ptr = wp; /* Advance the pointer */ + } + } + + unget_ch(); /* Push back the source character */ + put_a_line( out); /* Putout the last tokens */ + sharp( NULL, 0); /* Correct line number */ +} + +#endif + +static void put_a_line( + char * out +) +/* + * Finally put out the preprocessed line. + */ +{ + size_t len; + char * out_p; + char * tp; + + if (no_output) + return; + len = strlen( out); + tp = out_p = out + len - 2; /* Just before '\n' */ + while (char_type[ *out_p & UCHARMAX] & SPA) + out_p--; /* Remove trailing white spaces */ + if (out_p < tp) { + *++out_p = '\n'; + *++out_p = EOS; + } + if (mcpp_fputs( out, OUT) == EOF) + cfatal( "File write error", NULL, 0L, NULL); /* _F_ */ +} + + +/* + * Routines to P O S T - P R E P R O C E S S + * + * 1998/08 created kmatsui (revised 1998/09, 2004/02, 2006/07) + * Supplementary phase for the older compiler-propers. + * 1. Convert digraphs to usual tokens. + * 2. Double '\\' of the second byte of multi-byte characters. + * These conversions are done selectively according to the macros defined + * in system.H. + * 1. Digraphs are converted if ! HAVE_DIGRAPHS and digraph recoginition + * is enabled by DIGRAPHS_INIT and/or -2 option on execution. + * 2. '\\' of the second byte of SJIS (BIGFIVE or ISO2022_JP) is doubled + * if bsl_need_escape == TRUE. + */ + +#if HAVE_DIGRAPHS && MBCHAR_IS_ESCAPE_FREE + /* No post_preproc() */ +#else + +static int post_preproc( + char * out +) +/* + * Convert digraphs and double '\\' of the second byte of SJIS (BIGFIVE or + * ISO2022_JP). + * Note: Output of -K option embeds macro informations into comments. + * scan_token() does not recognize comment and parses it as '/', '*', etc. + */ +{ +#if ! HAVE_DIGRAPHS + int di_count = 0; +#endif + int token_type; + int c; + char * str; + char * cp = out; + + unget_string( out, NULL); + while ((c = get_ch()) != '\n') { /* Not to read over to next line */ + if (char_type[ c] & HSP) { + *cp++ = c; + continue; + } + str = cp; + token_type = scan_token( c, &cp, out_wend); + switch (token_type) { +#if ! MBCHAR_IS_ESCAPE_FREE + case WSTR : + case WCHR : + str++; /* Skip prefix 'L' */ + /* Fall through */ + case STR : + case CHR : + if (bsl_need_escape) + cp = esc_mbchar( str, cp); + break; +#endif /* ! MBCHAR_IS_ESCAPE_FREE */ +#if ! HAVE_DIGRAPHS + case OPE : + if (mcpp_mode == STD && (openum & OP_DIGRAPH)) { + cp = conv_a_digraph( cp); /* Convert a digraph */ + di_count++; + } + break; +#endif + } + } + *cp++ = '\n'; + *cp = EOS; +#if ! HAVE_DIGRAPHS + if (mcpp_mode == STD && di_count && (warn_level & 16)) + cwarn( "%.0s%ld digraph(s) converted" /* _W16_ */ + , NULL, (long) di_count, NULL); +#endif + return 0; +} + +#endif /* ! HAVE_DIGRAPHS || ! MBCHAR_IS_ESCAPE_FREE */ + +#if ! HAVE_DIGRAPHS +static char * conv_a_digraph( + char * cp /* The end of the digraph token */ +) +/* + * Convert a digraph to usual token in place. + * This routine is never called in POST_STD mode. + */ +{ + cp -= 2; + switch (openum) { + case OP_LBRACE_D : + *cp++ = '{'; + break; + case OP_RBRACE_D : + *cp++ = '}'; + break; + case OP_LBRCK_D : + *cp++ = '['; + break; + case OP_RBRCK_D : + *cp++ = ']'; + break; + case OP_SHARP_D : /* Error of source */ + *cp++ = '#'; + break; + case OP_DSHARP_D : /* Error of source */ + cp -= 2; + *cp++ = '#'; + *cp++ = '#'; + break; + } + return cp; +} +#endif /* ! HAVE_DIGRAPHS */ + +#if ! MBCHAR_IS_ESCAPE_FREE +static char * esc_mbchar( + char * str, /* String literal or character constant without 'L' */ + char * str_end /* The end of the token */ +) +/* + * Insert \ before the byte of 0x5c('\\') of the SJIS, BIGFIVE or ISO2022_JP + * multi-byte character code in string literal or character constant. + * Insert \ also before the byte of 0x22('"') and 0x27('\'') of ISO2022_JP. + * esc_mbchar() does in-place insertion. + */ +{ + char * cp; + int delim; + int c; + + if (! bsl_need_escape) + return str_end; + if ((delim = *str++) == 'L') + delim = *str++; /* The quote character */ + while ((c = *str++ & UCHARMAX) != delim) { + if (char_type[ c] & mbchk) { /* MBCHAR */ + cp = str; + mb_read( c, &str, (workp = work_buf, &workp)); + while (cp++ < str) { + c = *(cp - 1); + if (c == '\\' || c == '"' || c == '\'') { + /* Insert \ before 0x5c, 0x22, 0x27 */ + memmove( cp, cp - 1, (size_t) (str_end - cp) + 2); + *(cp++ - 1) = '\\'; + str++; + str_end++; + } + } + } else if (c == '\\' && ! (char_type[ *str & UCHARMAX] & mbchk)) { + str++; /* Escape sequence */ + } + } + return str_end; +} +#endif /* ! MBCHAR_IS_ESCAPE_FREE */ + diff --git a/lib/mcpp/main_libmcpp.c b/lib/mcpp/main_libmcpp.c new file mode 100644 index 000000000..71639259e --- /dev/null +++ b/lib/mcpp/main_libmcpp.c @@ -0,0 +1,9 @@ +/* most simple sample source to use libmcpp */ + +#include "mcpp_lib.h" + +int +main (int argc, char *argv[]) +{ + return mcpp_lib_main (argc, argv); +} diff --git a/lib/mcpp/mbchar.c b/lib/mcpp/mbchar.c new file mode 100644 index 000000000..c0afb850a --- /dev/null +++ b/lib/mcpp/mbchar.c @@ -0,0 +1,869 @@ +/*- + * Copyright (c) 1998, 2002-2008 Kiyoshi Matsui + * All rights reserved. + * + * Some parts of this code are derived from the public domain software + * DECUS cpp (1984,1985) written by Martin Minow. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * 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 AUTHOR 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. + */ + +/* + * M B C H A R . C + * C h a r a c t e r h a n d l i n g R o u t i n e s + * + * Character handling and multi-byte character handling routines are + * placed here. + */ + +#if PREPROCESSED +#include "mcpp.H" +#else +#include "system.H" +#include "internal.H" +#endif + +/* + * Tables of character types and multi-byte character types. + * + * Some of these character attributes will be overwritten by + * execution time option '-@post' or '-@old'. + * Warning on erroneous sequence will be issued from the caller routines: + * scan_quote(), scan_id() or scan_number(). + */ + +/* Non-ASCII characters are always checked by mb_read(). */ +#define NA 0x4000 /* Non-ASCII characters */ + +/* Horizontal spaces (' ', '\t' and TOK_SEP) */ +#define HSPA (SPA | HSP) + +short * char_type; /* Pointer to one of the following type_*[]. */ + +#define EJ1 0x100 /* 1st byte of EUC_JP */ +#define EJ2 0x200 /* 2nd byte of EUC_JP */ +#define GB1 0x400 /* 1st byte of GB2312 */ +#define GB2 0x800 /* 2nd byte of GB2312 */ +#define KS1 0x1000 /* 1st byte of KSC5601 */ +#define KS2 0x2000 /* 2nd byte of KSC5601 */ + +#define EJ12 (EJ1 | EJ2) /* 1st byte or 2nd byte of EUC_JP */ +#define GB12 (GB1 | GB2) +#define KS12 (KS1 | KS2) +#define EJ1N (NA | EJ1) +#define EU12N (NA | EJ12 | GB12 | KS12) + /* 1st or 2nd byte of EUC_JP, GB2312 or KSC5601, or any other non-ASCII */ + +static short type_euc[ UCHARMAX + 1] = { +/* + * For EUC_JP, GB2312, KSC5601 or other similar multi-byte char encodings. + */ + +/* Character type codes */ +/* 0, 1, 2, 3, 4, 5, 6, 7, */ +/* 8, 9, A, B, C, D, E, F, Hex */ + + 000, 000, 000, 000, 000, 000, 000, 000, /* 00 */ + 000, HSPA, SPA, SPA, SPA, SPA, 000, 000, /* 08 */ + 000, 000, 000, 000, 000, 000, 000, 000, /* 10 */ + /* 0x17-0x1A and 0x1F will be cleared in some modes by chk_opts() */ + 000, LET, LET, 000, 000, 000, 000, HSPA, /* 18 */ + HSPA, PUNC, QUO, PUNC, 000, PUNC, PUNC, QUO, /* 20 !"#$%&' */ + PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, DOT, PUNC, /* 28 ()*+,-./ */ + DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, /* 30 01234567 */ + DIG, DIG, PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, /* 38 89:;<=>? */ + + 000, LET, LET, LET, LET, LET, LET, LET, /* 40 @ABCDEFG */ + LET, LET, LET, LET, LET, LET, LET, LET, /* 48 HIJKLMNO */ + LET, LET, LET, LET, LET, LET, LET, LET, /* 50 PQRSTUVW */ + LET, LET, LET, PUNC, 000, PUNC, PUNC, LET, /* 58 XYZ[\]^_ */ + 000, LET, LET, LET, LET, LET, LET, LET, /* 60 `abcdefg */ + LET, LET, LET, LET, LET, LET, LET, LET, /* 68 hijklmno */ + LET, LET, LET, LET, LET, LET, LET, LET, /* 70 pqrstuvw */ + LET, LET, LET, PUNC, PUNC, PUNC, PUNC, 000, /* 78 xyz{|}~ */ + + NA, NA, NA, NA, NA, NA, NA, NA, /* 80 .. 87 */ + NA, NA, NA, NA, NA, NA, EJ1N, NA, /* 88 .. 8F */ + NA, NA, NA, NA, NA, NA, NA, NA, /* 90 .. 97 */ + NA, NA, NA, NA, NA, NA, NA, NA, /* 98 .. 9F */ + NA, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, /* A0 .. A7 */ + EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, /* A8 .. AF */ + EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, /* B0 .. B7 */ + EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, /* B8 .. BF */ + EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, /* C0 .. C7 */ + EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, /* C8 .. CF */ + EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, /* D0 .. D7 */ + EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, /* D8 .. DF */ + EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, /* E0 .. E7 */ + EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, /* E8 .. EF */ + EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, /* F0 .. F7 */ + EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, EU12N, NA, /* F8 .. FF */ +}; + +static short type_bsl[ UCHARMAX + 1] = { +/* + * For SJIS, BIGFIVE or other similar encodings which may have '\\' value as + * the second byte of multi-byte character. + */ + +#define SJ1 0x100 /* 1st byte of SJIS */ +#define SJ2 0x200 /* 2nd byte of SJIS */ +#define BF1 0x400 /* 1st byte of BIGFIVE */ +#define BF2 0x800 /* 2nd byte of BIGFIVE */ + +#define SB2 (SJ2 | BF2) +#define SJ2N (NA | SJ2) +#define SB2N (NA | SJ2 | BF2) +#define SJ12N (NA | SJ1 | SJ2) +#define BF12N (NA | BF1 | BF2) +#define SB12N (NA | SJ1 | SJ2 | BF1 | BF2) +#define S2B12N (NA | SJ2 | BF1 | BF2) + +#define LSB2 (LET | SB2) +#define PSB2 (PUNC| SB2) + +/* Character type codes */ +/* 0, 1, 2, 3, 4, 5, 6, 7, */ +/* 8, 9, A, B, C, D, E, F, Hex */ + + 000, 000, 000, 000, 000, 000, 000, 000, /* 00 */ + 000, HSPA, SPA, SPA, SPA, SPA, 000, 000, /* 08 */ + 000, 000, 000, 000, 000, 000, 000, 000, /* 10 */ + /* 0x17-0x1A and 0x1F will be cleared in some modes by chk_opts() */ + 000, LET, LET, 000, 000, 000, 000, HSPA, /* 18 */ + HSPA, PUNC, QUO, PUNC, 000, PUNC, PUNC, QUO, /* 20 !"#$%&' */ + PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, DOT, PUNC, /* 28 ()*+,-./ */ + DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, /* 30 01234567 */ + DIG, DIG, PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, /* 38 89:;<=>? */ + + SB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, /* 40 @ABCDEFG */ + LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, /* 48 HIJKLMNO */ + LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, /* 50 PQRSTUVW */ + LSB2, LSB2, LSB2, PSB2, SB2, PSB2, PSB2, LSB2, /* 58 XYZ[\]^_ */ + SB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, /* 60 `abcdefg */ + LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, /* 68 hijklmno */ + LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, LSB2, /* 70 pqrstuvw */ + LSB2, LSB2, LSB2, PSB2, PSB2, PSB2, PSB2, 000, /* 78 xyz{|}~ */ + + SB2N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, /* 80 .. 87 */ + SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, /* 88 .. 8F */ + SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, /* 90 .. 97 */ + SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, SJ12N, /* 98 .. 9F */ + SJ2N, S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N, /* A0 .. A7 */ + S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N, /* A8 .. AF */ + S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N, /* B0 .. B7 */ + S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N, /* B8 .. BF */ + S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N, /* C0 .. C7 */ + S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N, /* C8 .. CF */ + S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N, /* D0 .. D7 */ + S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N,S2B12N, /* D8 .. DF */ + SB12N, SB12N, SB12N, SB12N, SB12N, SB12N, SB12N, SB12N, /* E0 .. E7 */ + SB12N, SB12N, SB12N, SB12N, SB12N, SB12N, SB12N, SB12N, /* E8 .. EF */ + SB12N, SB12N, SB12N, SB12N, SB12N, SB12N, SB12N, SB12N, /* F0 .. F7 */ + SB12N, SB12N, SB12N, SB12N, SB12N, BF12N, BF12N, NA, /* F8 .. FF */ +}; + +/* + * For ISO2022_JP multi-byte character encoding. + */ + +#define IS1 0x100 /* 1st byte of shift-sequence */ +#define IS2 0x200 /* 2nd byte of shift-sequence */ +#define IS3 0x400 /* 3rd byte of shift-sequence */ +#define IS4 0x800 /* 4th byte of shift-sequence */ +#define IJP 0x1000 /* 1st or 2nd byte of ISO-2022-JP (ISO-2022-JP1) */ + +#define PIJP (PUNC | IJP) +#define QIJP (QUO | IJP) +#define DTJP (DOT | IJP) +#define DGJP (DIG | IJP) +#define LIJP (LET | IJP) + +#define JPS2 (IJP | IS2) +#define PJPS23 (PIJP | IS2 | IS3) +#define LJPS3 (LIJP | IS3) +#define LJPS4 (LIJP | IS4) + +static short type_iso2022_jp[ UCHARMAX + 1] = { + +/* Character type codes */ +/* 0, 1, 2, 3, 4, 5, 6, 7, */ +/* 8, 9, A, B, C, D, E, F, Hex */ + + 000, 000, 000, 000, 000, 000, 000, 000, /* 00 */ + 000, HSPA, SPA, SPA, SPA, SPA, 000, 000, /* 08 */ + 000, 000, 000, 000, 000, 000, 000, 000, /* 10 */ + /* 0x17-0x1A and 0x1F will be cleared in some modes by chk_opts() */ + 000, LET, LET, IS1, 000, 000, 000, HSPA, /* 18 */ + HSPA, PIJP, QIJP, PIJP, JPS2, PIJP, PIJP, QIJP, /* 20 !"#$%&' */ + PJPS23,PIJP, PIJP, PIJP, PIJP, PIJP, DTJP, PIJP, /* 28 ()*+,-./ */ + DGJP, DGJP, DGJP, DGJP, DGJP, DGJP, DGJP, DGJP, /* 30 01234567 */ + DGJP, DGJP, PIJP, PIJP, PIJP, PIJP, PIJP, PIJP, /* 38 89:;<=>? */ + + IJP, LIJP, LJPS3, LIJP, LJPS4, LIJP, LIJP, LIJP, /* 40 @ABCDEFG */ + LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, /* 48 HIJKLMNO */ + LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, /* 50 PQRSTUVW */ + LIJP, LIJP, LIJP, PIJP, IJP, PIJP, PIJP, LIJP, /* 58 XYZ[\]^_ */ + IJP, LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, /* 60 `abcdefg */ + LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, /* 68 hijklmno */ + LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, LIJP, /* 70 pqrstuvw */ + LIJP, LIJP, LIJP, PIJP, PIJP, PIJP, PIJP, 000, /* 78 xyz{|}~ */ + + NA, NA, NA, NA, NA, NA, NA, NA, /* 80 .. 87 */ + NA, NA, NA, NA, NA, NA, NA, NA, /* 88 .. 8F */ + NA, NA, NA, NA, NA, NA, NA, NA, /* 90 .. 97 */ + NA, NA, NA, NA, NA, NA, NA, NA, /* 98 .. 9F */ + NA, NA, NA, NA, NA, NA, NA, NA, /* A0 .. A7 */ + NA, NA, NA, NA, NA, NA, NA, NA, /* A8 .. AF */ + NA, NA, NA, NA, NA, NA, NA, NA, /* B0 .. B7 */ + NA, NA, NA, NA, NA, NA, NA, NA, /* B8 .. BF */ + NA, NA, NA, NA, NA, NA, NA, NA, /* C0 .. C7 */ + NA, NA, NA, NA, NA, NA, NA, NA, /* C8 .. CF */ + NA, NA, NA, NA, NA, NA, NA, NA, /* D0 .. D7 */ + NA, NA, NA, NA, NA, NA, NA, NA, /* D8 .. DF */ + NA, NA, NA, NA, NA, NA, NA, NA, /* E0 .. E7 */ + NA, NA, NA, NA, NA, NA, NA, NA, /* E8 .. EF */ + NA, NA, NA, NA, NA, NA, NA, NA, /* F0 .. F7 */ + NA, NA, NA, NA, NA, NA, NA, NA, /* F8 .. FF */ +}; + +/* + * For UTF8 multi-byte character encoding. + */ + +#define U2_1 0x100 /* 1st byte of 2-byte encoding of UTF8 */ +#define U3_1 0x200 /* 1st byte of 3-byte encoding of UTF8 */ +#define U4_1 0x400 /* 1st byte of 4-byte encoding of UTF8 */ +#define UCONT 0x800 /* Continuation of a 2, 3, or 4 byte UTF8 sequence */ +#define U2_1N (NA | U2_1) +#define U3_1N (NA | U3_1) +#define U4_1N (NA | U4_1) +#define UCONTN (NA | UCONT) + +static short type_utf8[ UCHARMAX + 1] = { + +/* Character type codes */ +/* 0, 1, 2, 3, 4, 5, 6, 7, */ +/* 8, 9, A, B, C, D, E, F, Hex */ + + 000, 000, 000, 000, 000, 000, 000, 000, /* 00 */ + 000, HSPA, SPA, SPA, SPA, SPA, 000, 000, /* 08 */ + 000, 000, 000, 000, 000, 000, 000, 000, /* 10 */ + /* 0x17-0x1A and 0x1F will be cleared in some modes by chk_opts() */ + 000, LET, LET, 000, 000, 000, 000, HSPA, /* 18 */ + HSPA, PUNC, QUO, PUNC, 000, PUNC, PUNC, QUO, /* 20 !"#$%&' */ + PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, DOT, PUNC, /* 28 ()*+,-./ */ + DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, /* 30 01234567 */ + DIG, DIG, PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, /* 38 89:;<=>? */ + + 000, LET, LET, LET, LET, LET, LET, LET, /* 40 @ABCDEFG */ + LET, LET, LET, LET, LET, LET, LET, LET, /* 48 HIJKLMNO */ + LET, LET, LET, LET, LET, LET, LET, LET, /* 50 PQRSTUVW */ + LET, LET, LET, PUNC, 000, PUNC, PUNC, LET, /* 58 XYZ[\]^_ */ + 000, LET, LET, LET, LET, LET, LET, LET, /* 60 `abcdefg */ + LET, LET, LET, LET, LET, LET, LET, LET, /* 68 hijklmno */ + LET, LET, LET, LET, LET, LET, LET, LET, /* 70 pqrstuvw */ + LET, LET, LET, PUNC, PUNC, PUNC, PUNC, 000, /* 78 xyz{|}~ */ + + UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN, /* 80 .. 87 */ + UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN, /* 88 .. 8F */ + UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN, /* 90 .. 97 */ + UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN, /* 98 .. 9F */ + UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN, /* A0 .. A7 */ + UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN, /* A8 .. AF */ + UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN, /* B0 .. B7 */ + UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN,UCONTN, /* B8 .. BF */ + NA, NA, U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, /* C0 .. C7 */ + U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, /* C8 .. CF */ + U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, /* D0 .. D7 */ + U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, U2_1N, /* D8 .. DF */ + U3_1N, U3_1N, U3_1N, U3_1N, U3_1N, U3_1N, U3_1N, U3_1N, /* E0 .. E7 */ + U3_1N, U3_1N, U3_1N, U3_1N, U3_1N, U3_1N, U3_1N, U3_1N, /* E8 .. EF */ + U4_1N, U4_1N, U4_1N, U4_1N, U4_1N, NA, NA, NA, /* F0 .. F7 */ + NA, NA, NA, NA, NA, NA, NA, NA, /* F8 .. FF */ +}; + +#define SETLOCALE 2 /* #pragma setlocale (not __setlocale) */ + +#define NUM_ENCODING 8 +#define NUM_ALIAS 6 + +/* Names of encoding recognized. Table for search_encoding(). */ +static const char * const encoding_name[ NUM_ENCODING][ NUM_ALIAS] = { + /* Visual C full, Visual C short + , 4 miscellaneous */ + { "english", "c" + , "c", "en", "latin", "iso8859"}, + { "", "" + , "eucjp", "euc", "ujis", ""}, + { "chinesesimplified", "chs" + , "gb2312", "cngb", "euccn", ""}, + { "korean", "kor" + , "ksc5601", "ksx1001", "wansung", "euckr"}, + { "japanese", "jpn" + , "sjis", "shiftjis", "mskanji", ""}, + { "chinesetraditional", "cht" + , "bigfive", "big5", "cnbig5", "euctw"}, + { "", "" + , "iso2022jp", "iso2022jp1", "jis", ""}, + { "", "" + , "utf8", "utf", "", ""}, +}; + +static int mbstart; +static int mb2; + +static size_t mb_read_2byte( int c1, char ** in_pp, char ** out_pp); + /* For 2-byte encodings of mbchar */ +static const char * search_encoding( char * norm, int alias); + /* Search encoding_name[][] table */ +static void strip_bar( char * string); + /* Remove '_', '-' or '.' in the string */ +static void conv_case( char * name, char * lim, int upper); + /* Convert to upper/lower case */ +static size_t mb_read_iso2022_jp( int c1, char ** in_pp, char ** out_pp); + /* For ISO2022_JP encoding */ +static size_t mb_read_utf8( int c1, char ** in_pp, char ** out_pp); + /* For UTF8 mbchar encoding */ + +#define NAMLEN 20 +#define UPPER 1 /* To upper */ +#define LOWER 0 /* To lower */ + + +const char * set_encoding( + char * name, /* Name of encoding specified */ + char * env, /* Name of environment variable */ + int pragma + /* 2: #pragma setlocale, 1: #pragma __setlocale, 0: not #pragma */ +) +/* + * Search the encoding specified and re-initialize mbchar settings. + */ +{ + const char * unknown_encoding + = "Unknown encoding: %s%.0ld%.0s"; /* _W1_ */ + const char * too_long + = "Too long encoding name: %s%.0ld%.0s"; /* _E_ */ + const char * loc = ""; + int alias; + char norm[ NAMLEN]; + /* + * Normalized name (removed 'xxxxx.', stripped '_', '-', '.' + * and lowered. + */ + + if (strlen( name) >= NAMLEN) { + if ((env || pragma) && (warn_level & 1)) { + cwarn( too_long, name, 0L, NULL); + } else { + mcpp_fprintf( ERR, too_long, name); + mcpp_fputc( '\n', ERR); + } + } + strcpy( norm, name); + if (norm[ 5] == '.') + memmove( norm, norm + 5, strlen( norm + 5) + 1); + /* Remove initial 'xxxxx.' as 'ja_JP.', 'en_US.' or any other */ + conv_case( norm, norm + strlen( norm), LOWER); + strip_bar( norm); + + if (strlen( name) == 0) { /* "" */ + mbchar = MBCHAR; /* Restore to the default encoding */ + } else if (memcmp( norm, "iso8859", 7) == 0 /* iso8859* */ + || memcmp( norm, "latin", 5) == 0 /* latin* */ + || memcmp( norm, "en", 2) == 0) { /* en* */ + mbchar = 0; /* No multi-byte character */ + } else { + alias = 2; +#if COMPILER == MSC + if (pragma == SETLOCALE) /* #pragma setlocale */ + alias = 0; +#endif + loc = search_encoding( norm, alias); /* Search the name */ + } + if (loc == NULL) { + if ((env || pragma) && (warn_level & 1)) { + cwarn( unknown_encoding, name, 0L, NULL); + } else { /* -m option */ + mcpp_fprintf( ERR, unknown_encoding, name); + mcpp_fputc( '\n', ERR); + } + } else { + mb_init(); /* Re-initialize */ + } + return loc; +} + +static const char * search_encoding( + char * norm, /* The name of encoding specified */ + int alias /* The number of alias to start searching */ +) +{ + const char * loc; + int lo, al; + + for (lo = 0; lo < NUM_ENCODING; lo++) { + for (al = alias ; al < NUM_ALIAS; al++) { + loc = encoding_name[ lo][ al]; + if (str_eq( loc, norm)) { + switch (lo) { + case 0 : mbchar = 0; break; + case 1 : mbchar = EUC_JP; break; + case 2 : mbchar = GB2312; break; + case 3 : mbchar = KSC5601; break; + case 4 : mbchar = SJIS; break; + case 5 : mbchar = BIGFIVE; break; + case 6 : mbchar = ISO2022_JP; break; + case 7 : mbchar = UTF8; break; + } + return loc; + } + } + } + return NULL; +} + +static void strip_bar( + char * string +) +/* + * Strip '_', '-' or '.' in the string. + */ +{ + char * cp = string; + + while (*cp != EOS) { + if (*cp == '_' || *cp == '-' || *cp == '.') + memmove( cp, cp + 1, strlen( cp)); + else + cp++; + } +} + +static void conv_case( + char * name, /* (diretory) Name */ + char * lim, /* End of (directory) name */ + int upper /* TRUE if to upper */ +) +/* Convert a string to upper-case letters or lower-case letters in-place */ +{ + int c; + char * sp; + + for (sp = name; sp < lim; sp++) { + c = *sp & UCHARMAX; +#if MBCHAR + if ((char_type[ c] & mbstart)) { + char tmp[ PATHMAX+1]; + char * tp = tmp; + *tp++ = *sp++; + mb_read( c, &sp, &tp); + } else +#endif + { + if (upper) + *sp = toupper( c); + else + *sp = tolower( c); + } + } +} + +void mb_init( void) +/* + * Initialize multi-byte character settings. + * First called prior to setting the 'mcpp_mode'. + * Will be called again each time the multibyte character encoding is changed. + */ +{ + /* + * Select the character classification table, select the multi-byte + * character reading routine and decide whether multi-byte character + * may contain the byte of value 0x5c. + */ + switch (mbchar) { + case 0 : + case EUC_JP : + case GB2312 : + case KSC5601 : + char_type = type_euc; + bsl_in_mbchar = FALSE; + mb_read = mb_read_2byte; + break; + case SJIS : + case BIGFIVE : + char_type = type_bsl; + bsl_in_mbchar = TRUE; + mb_read = mb_read_2byte; + break; + case ISO2022_JP : + char_type = type_iso2022_jp; + bsl_in_mbchar = TRUE; + mb_read = mb_read_iso2022_jp; + break; + case UTF8 : + char_type = type_utf8; + bsl_in_mbchar = FALSE; + mb_read = mb_read_utf8; + break; + } + + /* Set the bit patterns for character classification. */ + switch (mbchar) { + case 0 : + mbstart = 0; + break; + case EUC_JP : + mbstart = EJ1; + mb2 = EJ2; + break; + case GB2312 : + mbstart = GB1; + mb2 = GB2; + break; + case KSC5601: + mbstart = KS1; + mb2 = KS2; + break; + case SJIS : + mbstart = SJ1; + mb2 = SJ2; + break; + case BIGFIVE: + mbstart = BF1; + mb2 = BF2; + break; + case ISO2022_JP : + mbstart = IS1; + break; + case UTF8 : + mbstart = (U2_1 | U3_1 | U4_1); + break; + } + switch (mbchar) { + case 0 : + mbchk = 0; + break; + case EUC_JP : + case GB2312 : + case KSC5601: + case SJIS : + case BIGFIVE: + case UTF8 : + mbchk = NA; + break; + case ISO2022_JP : + mbchk = (IS1 | NA); + break; + } + + /* + * Set special handling for some encodings to supplement some compiler's + * deficiency. + */ + switch (mbchar) { + case SJIS : +#if ! SJIS_IS_ESCAPE_FREE + bsl_need_escape = TRUE; +#endif + break; + case BIGFIVE: +#if ! BIGFIVE_IS_ESCAPE_FREE + bsl_need_escape = TRUE; +#endif + break; + case ISO2022_JP : +#if ! ISO2022_JP_IS_ESCAPE_FREE + bsl_need_escape = TRUE; +#endif + break; + default : + bsl_need_escape = FALSE; + break; + } + + /* + * Modify magic characters in character type table. + * char_type[] table should be rewritten in accordance with the 'mcpp_mode' + * whenever the encoding is changed. + */ + if (mcpp_mode) { /* If mcpp_mode is already set */ + char_type[ DEF_MAGIC] = standard ? LET : 0; + char_type[ IN_SRC] = (mcpp_mode == STD) ? LET : 0; + char_type[ TOK_SEP] = (mcpp_mode == STD || mcpp_mode == OLD_PREP) + ? HSPA: 0; /* TOK_SEP equals to COM_SEP */ + } +} + +static size_t mb_read_2byte( + int c1, /* The 1st byte of mbchar sequence (already read) */ + char ** in_pp, /* Pointer to input */ + char ** out_pp /* Pointer to output */ +) +/* + * Multi-byte character reading routine for 2-byte encodings. + */ +{ + int error = FALSE; + size_t len = 0; /* Number of multi-byte characters read. */ + char * in_p = *in_pp; + char * out_p = *out_pp; + + if (! (char_type[ c1 & UCHARMAX] & mbstart)) + return MB_ERROR; /* Not a multi-byte character */ + + do { + if (! (char_type[ (*out_p++ = *in_p++) & UCHARMAX] & mb2)) { + error = TRUE; + break; + } + len++; + } while (char_type[ (*out_p++ = *in_p++) & UCHARMAX] & mbstart); + *in_pp = --in_p; + *(--out_p) = EOS; + *out_pp = out_p; + return error ? (len | MB_ERROR) : len; +} + +static size_t mb_read_iso2022_jp( + int c1, /* The 1st byte of the sequence already read (always 0x1b). */ + char ** in_pp, + char ** out_pp +) +/* + * Multi-byte character reading routine for ISO2022_JP. + */ +{ + int error = FALSE; + size_t len = 0; + char * in_p = *in_pp; + char * out_p = *out_pp; + int c2, c3, c4; + + if (! (char_type[ c1 & UCHARMAX] & mbstart)) + return MB_ERROR; + + do { + + *out_p++ = c2 = *in_p++; + if (! (char_type[ c2 & UCHARMAX] & IS2)) { + error = TRUE; + break; + } + *out_p++ = c3 = *in_p++; + if (! (char_type[ c3 & UCHARMAX] & IS3)) { + error = TRUE; + break; + } + + switch (c2) { + case 0x24 : + switch (c3) { + case 0x42 : /* 0x1b 0x24 0x42: JIS X 0208-1983 */ + break; + case 0x28 : + *out_p++ = c4 = *in_p++; + if (! (char_type[ c4 & UCHARMAX] & IS4)) + error = TRUE; + /* else: 0x1b 0x24 0x28 0x44: JIS X 0212 */ + break; + default : + error = TRUE; + } + break; + case 0x28 : + switch (c3) { + case 0x42 : /* 0x1b 0x28 0x42: ASCII */ + c1 = *out_p++ = *in_p++ & UCHARMAX; + continue; + default : + error = TRUE; + } + break; + } + if (error) + break; + + while (char_type[ c1 = *out_p++ = (*in_p++ & UCHARMAX)] & IJP) { + if (! (char_type[ *out_p++ = (*in_p++ & UCHARMAX)] & IJP)) { + error = TRUE; + break; + } + len++; /* String of multi-byte characters */ + } + if (error) + break; + + } while (char_type[ c1] & IS1); /* 0x1b: start of shift-sequence */ + + *in_pp = --in_p; + *(--out_p) = EOS; + *out_pp = out_p; + return error ? (len | MB_ERROR) : len; +} + +static size_t mb_read_utf8( + int c1, + char ** in_pp, + char ** out_pp +) +/* + * Multi-byte character reading routine for UTF8. + */ +{ + int error = FALSE; + size_t len = 0; + char * in_p = *in_pp; + char * out_p = *out_pp; + + if (! (char_type[ c1 & UCHARMAX] & mbstart)) + return MB_ERROR; + + do { + unsigned int codepoint; + int i, bytes; + + if ((char_type[ c1 & UCHARMAX] & U4_1) == U4_1) + bytes = 4; /* 4-byte character */ + else if ((char_type[ c1 & UCHARMAX] & U3_1) == U3_1) + bytes = 3; /* 3-byte character */ + else if ((char_type[ c1 & UCHARMAX] & U2_1) == U2_1) + bytes = 2; /* 2-byte character */ + + /* Must ensure that the sequence is not reserved as a surrogate */ + codepoint = ((2 << (6-bytes)) - 1) & c1; /* mask off top bits */ + + /* All bytes left in the sequence must be in 0x80 - 0xBF */ + for (i = bytes - 1; i && !error; i--) { + codepoint = (codepoint << 6) + ((*in_p) & 0x3fU); + if (! (char_type[ (*out_p++ = *in_p++) & UCHARMAX] & UCONT)) + error = TRUE; + } + + /* Check for overlong/underlong sequences */ + if ((bytes == 2 && (codepoint < 0x80 || codepoint > 0x7FF)) + || (bytes == 3 && (codepoint < 0x800 || codepoint > 0xFFFF)) + || (bytes == 4 && (codepoint < 0x10000 || codepoint > 0x10FFFF))) + error = TRUE; + if ((codepoint >= 0xD800 && codepoint <= 0xDFFF) + /* Check for reserved surrogate codepoints */ + || (codepoint >= 0xFFFE && codepoint <= 0xFFFF)) + /* Illegal */ + error = TRUE; +#if 0 + printf( "codepoint:0x%x\n", codepoint); +#endif + if (error) + break; + len++; + } while (char_type[ (*out_p++ = c1 = *in_p++) & UCHARMAX] & mbstart); + /* Start of the next multi-byte character */ + *in_pp = --in_p; + *(--out_p) = EOS; + *out_pp = out_p; + return error ? (len | MB_ERROR) : len; +} + +uexpr_t mb_eval( + char ** seq_pp +) +/* + * Evaluate the value of a multi-byte character. + * This routine does not check the legality of the sequence. + * This routine is called from eval_char(). + * This routine is never called in POST_STD mode. + */ +{ + char * seq = *seq_pp; + uexpr_t val = 0; + int c, c1; + + if (! (char_type[ c = *seq++ & UCHARMAX] & mbstart)) { + *seq_pp = seq; + return c; /* Not a multi-byte character */ + } + + switch (mbchar) { + case EUC_JP : + case GB2312 : + case KSC5601: + case SJIS : + case BIGFIVE: + val = (c << 8) + (*seq++ & UCHARMAX); + /* Evaluate the 2-byte sequence */ + break; + case ISO2022_JP : + if (char_type[ c & UCHARMAX] & IS1) { /* Skip shift-sequence */ + if (char_type[ c = *seq++ & UCHARMAX] & IS2) { + if (char_type[ c1 = *seq++ & UCHARMAX] & IS3) { + if (c1 == 0x28) + seq++; + if (c == 0x28 && c1 == 0x42) { /* Shift-out sequence */ + val = 0; + break; + } + c = *seq++ & UCHARMAX; + } + } + } + val = (c << 8) + (*seq++ & UCHARMAX); /* Evaluate the 2-bytes */ + break; + case UTF8 : /* Evaluate the sequence of 2, 3 or 4 bytes as it is */ + val = (c << 8) + (*seq++ & UCHARMAX); + if (char_type[ c & UCHARMAX] & U3_1) { + val = (val << 8) + (*seq++ & UCHARMAX); + } else if (char_type[ c & UCHARMAX] & U4_1) { + val = (val << 8) + (*seq++ & UCHARMAX); + val = (val << 8) + (*seq++ & UCHARMAX); + } + break; + } + + *seq_pp = seq; + return val; +} + +int last_is_mbchar( + const char * in, /* Input physical line */ + int len /* Length of the line minus 2 */ +) +/* + * Return 2, if the last char of the line is second byte of SJIS or BIGFIVE, + * else return 0. + */ +{ + const char * cp = in + len; + const char * const endp = in + len; /* -> the char befor '\n' */ + + if ((mbchar & (SJIS | BIGFIVE)) == 0) + return 0; + while (in <= --cp) { /* Search backwardly */ + if ((char_type[ *cp & UCHARMAX] & mbstart) == 0) + break; /* Not the first byte of MBCHAR */ + } + if ((endp - cp) & 1) + return 0; + else + return 2; +} + diff --git a/lib/mcpp/mcpp_lib.h b/lib/mcpp/mcpp_lib.h new file mode 100644 index 000000000..4827f0aaf --- /dev/null +++ b/lib/mcpp/mcpp_lib.h @@ -0,0 +1,31 @@ +/* mcpp_lib.h: declarations of libmcpp exported (visible) functions */ +#ifndef _MCPP_LIB_H +#define _MCPP_LIB_H + +#ifndef _MCPP_OUT_H +#include "mcpp_out.h" /* declaration of OUTDEST */ +#endif + +#if _WIN32 || _WIN64 || __CYGWIN__ || __CYGWIN64__ || __MINGW32__ \ + || __MINGW64__ +#if DLL_EXPORT || (__CYGWIN__ && PIC) +#define DLL_DECL __declspec( dllexport) +#elif DLL_IMPORT +#define DLL_DECL __declspec( dllimport) +#else +#define DLL_DECL +#endif +#else +#define DLL_DECL +#endif + +extern DLL_DECL int mcpp_lib_main( int argc, char ** argv); +extern DLL_DECL void mcpp_reset_def_out_func( void); +extern DLL_DECL void mcpp_set_out_func( + int (* func_fputc) ( int c, OUTDEST od), + int (* func_fputs) ( const char * s, OUTDEST od), + int (* func_fprintf)( OUTDEST od, const char * format, ...) + ); +extern DLL_DECL void mcpp_use_mem_buffers( int tf); +extern DLL_DECL char * mcpp_get_mem_buffer( OUTDEST od); +#endif /* _MCPP_LIB_H */ diff --git a/lib/mcpp/mcpp_out.h b/lib/mcpp/mcpp_out.h new file mode 100644 index 000000000..02ba2aae1 --- /dev/null +++ b/lib/mcpp/mcpp_out.h @@ -0,0 +1,13 @@ +/* mcpp_out.h: declarations of OUTDEST data types for MCPP */ +#ifndef _MCPP_OUT_H +#define _MCPP_OUT_H + +/* Choices for output destination */ +typedef enum { + OUT, /* ~= fp_out */ + ERR, /* ~= fp_err */ + DBG, /* ~= fp_debug */ + NUM_OUTDEST +} OUTDEST; + +#endif /* _MCPP_OUT_H */ diff --git a/lib/mcpp/preproc.c b/lib/mcpp/preproc.c new file mode 100644 index 000000000..33d66f5aa --- /dev/null +++ b/lib/mcpp/preproc.c @@ -0,0 +1,9 @@ +/* preproc.c: to "pre-preprocess" header files. */ + +#pragma MCPP preprocess + +#include "system.H" +#include "internal.H" + +#pragma MCPP put_defines + diff --git a/lib/mcpp/support.c b/lib/mcpp/support.c new file mode 100644 index 000000000..e3357e461 --- /dev/null +++ b/lib/mcpp/support.c @@ -0,0 +1,2811 @@ +/*- + * Copyright (c) 1998, 2002-2008 Kiyoshi Matsui + * All rights reserved. + * + * Some parts of this code are derived from the public domain software + * DECUS cpp (1984,1985) written by Martin Minow. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * 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 AUTHOR 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. + */ + +/* + * S U P P O R T . C + * S u p p o r t R o u t i n e s + * + * The common routines used by several source files are placed here. + */ + +/* + * The following are global functions. + * + * get_unexpandable() Gets the next unexpandable token in the line, expanding + * macros. + * Called from #if, #line and #include processing routines. + * skip_nl() Skips over a line. + * skip_ws() Skips over white spaces but not skip over the end of the line. + * skip_ws() skips also COM_SEP and TOK_SEP. + * scan_token() Reads the next token of any type into the specified output + * pointer, advances the pointer, returns the type of token. + * scan_quote() Reads a string literal, character constant or header-name from + * the input stream, writes out to the specified buffer and + * returns the advanced output pointer. + * get_ch() Reads the next byte from the current input stream, handling + * end of (macro/file) input and embedded comments appropriately. + * cnv_trigraph() Maps trigraph sequence to C character. + * cnv_digraph() Maps digraph sequence to C character. + * id_operator() See whether the identifier is an operator in C++. + * unget_ch() Pushs last gotten character back on the input stream. + * unget_string() Pushs sequence on the input stream. + * save_string() Saves a string in malloc() memory. + * get_file() Initializes a new FILEINFO structure, called when #include + * opens a new file, or from unget_string(). + * xmalloc() Gets a specified number of bytes from heap memory. + * If malloc() returns NULL, exits with a message. + * xrealloc() realloc(). If it fails, exits with a message. + * get_src_location() Trace back line-column datum into pre-line-splicing + * phase. A function for -K option. + * cfatal(), cerror(), cwarn() + * These routines format print messages to the user. + * mcpp_fputc(), mcpp_fputs(), mcpp_fprintf() + * Wrap library functions to support alternate output to memory + * buffer. + */ + +#if PREPROCESSED +#include "mcpp.H" +#else +#include "system.H" +#include "internal.H" +#endif + +static void scan_id( int c); + /* Scan an identifier */ +static char * scan_number( int c, char * out, char * out_end); + /* Scan a preprocessing number */ +static char * scan_number_prestd( int c, char * out, char * out_end); + /* scan_number() for pre-Standard mode */ +#if OK_UCN +static char * scan_ucn( int cnt, char * out); + /* Scan an UCN sequence */ +#endif +static char * scan_op( int c, char * out); + /* Scan an operator or a punctuator */ +static char * parse_line( void); + /* Parse a logical line and convert comments */ +static char * read_a_comment( char * sp, size_t * sizp); + /* Read over a comment */ +static char * get_line( int in_comment); + /* Get a logical line from file, handle line-splicing */ +static char * at_eof( int in_comment); + /* Check erroneous end of file */ +static void do_msg( const char * severity, const char * format + , const char * arg1, long arg2, const char * arg3); + /* Putout diagnostic message */ +static char * cat_line( int del_bsl); + /* Splice the line */ +static void put_line( char * out, FILE * fp); + /* Put out a logical line */ +static void dump_token( int token_type, const char * cp); + /* Dump a token and its type */ + +#define EXP_MAC_IND_MAX 16 +/* Information of current expanding macros for diagnostic */ +static struct { + const char * name; /* Name of the macro just expanded */ + int to_be_freed; /* Name should be freed later */ +} expanding_macro[ EXP_MAC_IND_MAX]; +static int exp_mac_ind = 0; /* Index into expanding_macro[] */ + +static int in_token = FALSE; /* For token scanning functions */ +static int in_string = FALSE; /* For get_ch() and parse_line()*/ +static int squeezews = FALSE; + +#define MAX_CAT_LINE 256 +/* Information on line catenated by */ +/* and by line-crossing comment. This is for -K option. */ +typedef struct catenated_line { + long start_line; /* Starting line of catenation */ + long last_line; /* Ending line of catanation */ + size_t len[ MAX_CAT_LINE + 1]; + /* Length of successively catenated lines */ +} CAT_LINE; +static CAT_LINE bsl_cat_line; + /* Datum on the last catenated line by */ +static CAT_LINE com_cat_line; + /* Datum on the last catenated line by a line-crossing comment */ + +#if MCPP_LIB +static int use_mem_buffers = FALSE; + +void init_support( void) +{ + in_token = in_string = squeezews = FALSE; + bsl_cat_line.len[ 0] = com_cat_line.len[ 0] = 0; + clear_exp_mac(); +} + +typedef struct mem_buf { + char * buffer; + char * entry_pt; + size_t size; + size_t bytes_avail; +} MEMBUF; + +static MEMBUF mem_buffers[ NUM_OUTDEST]; + +void mcpp_use_mem_buffers( + int tf +) +{ + int i; + + use_mem_buffers = tf ? TRUE : FALSE; + + for (i = 0; i < NUM_OUTDEST; ++i) { + if (mem_buffers[ i].buffer) + /* Free previously allocated memory buffer */ + free( mem_buffers[ i].buffer); + if (use_mem_buffers) { + /* Output to memory buffers instead of files */ + mem_buffers[ i].buffer = NULL; + mem_buffers[ i].entry_pt = NULL; + mem_buffers[ i].size = 0; + mem_buffers[ i].bytes_avail = 0; + } + } +} + +int using_mem_buffers( void) +{ + return use_mem_buffers; +} + +#define BUF_INCR_SIZE (NWORK * 2) +#define MAX( a, b) (((a) > (b)) ? (a) : (b)) + +static char * append_to_buffer( + MEMBUF * mem_buf_p, + const char * string, + size_t length +) +{ + if (mem_buf_p->bytes_avail < length + 1) { /* Need to allocate more memory */ + size_t size = MAX( BUF_INCR_SIZE, length); + + if (mem_buf_p->buffer == NULL) { /* 1st append */ + mem_buf_p->size = size; + mem_buf_p->bytes_avail = size; + mem_buf_p->buffer = xmalloc( mem_buf_p->size); + mem_buf_p->entry_pt = mem_buf_p->buffer; + } else { + mem_buf_p->size += size; + mem_buf_p->bytes_avail += size; + mem_buf_p->buffer = xrealloc( mem_buf_p->buffer, mem_buf_p->size); + mem_buf_p->entry_pt = mem_buf_p->buffer + mem_buf_p->size + - mem_buf_p->bytes_avail; + } + } + + /* Append the string to the tail of the buffer */ + memcpy( mem_buf_p->entry_pt, string, length); + mem_buf_p->entry_pt += length; + mem_buf_p->entry_pt[ 0] = '\0'; /* Terminate the string buffer */ + mem_buf_p->bytes_avail -= length; + + return mem_buf_p->buffer; +} + +static int mem_putc( + int c, + OUTDEST od +) +{ + char string[ 1]; + + string[ 0] = (char) c; + + if (append_to_buffer( &(mem_buffers[ od]), string, 1) != NULL) + return 0; + else + return !0; +} + +static int mem_puts( + const char * s, + OUTDEST od +) +{ + if (append_to_buffer( &(mem_buffers[od]), s, strlen(s)) != NULL) + return 0; + else + return !0; +} + +char * mcpp_get_mem_buffer( + OUTDEST od +) +{ + return mem_buffers[ od].buffer; +} + +#endif /* MCPP_LIB */ + +#define DEST2FP(od) \ + (od == OUT) ? fp_out : \ + ((od == ERR) ? fp_err : \ + ((od == DBG) ? fp_debug : \ + (NULL))) + +/* + * The following mcpp_*() wrapper functions are intended to centralize + * the output generated by MCPP. They support memory buffer alternates to + * each of the primary output streams: out, err, debug. The memory buffer + * output option would be used in a setup where MCPP has been built as a + * function call - i.e. mcpp_lib_main(). + */ + +int mcpp_lib_fputc( + int c, + OUTDEST od +) +{ +#if MCPP_LIB + if (use_mem_buffers) { + return mem_putc( c, od); + } else { +#endif + FILE * stream = DEST2FP( od); + + return (stream != NULL) ? fputc( c, stream) : EOF; +#if MCPP_LIB + } +#endif +} + +int (* mcpp_fputc)( int c, OUTDEST od) = mcpp_lib_fputc; + +int mcpp_lib_fputs( + const char * s, + OUTDEST od +) +{ +#if MCPP_LIB + if (use_mem_buffers) { + return mem_puts( s, od); + } else { +#endif + FILE * stream = DEST2FP( od); + + return (stream != NULL) ? fputs( s, stream) : EOF; +#if MCPP_LIB + } +#endif +} + +int (* mcpp_fputs)( const char * s, OUTDEST od) = mcpp_lib_fputs; + +#include + +int mcpp_lib_fprintf( + OUTDEST od, + const char * format, + ... +) +{ + va_list ap; + FILE * stream = DEST2FP( od); + + if (stream != NULL) { + int rc; + + va_start( ap, format); +#if MCPP_LIB + if (use_mem_buffers) { + static char mem_buffer[ NWORK]; + + rc = vsprintf( mem_buffer, format, ap); + + if (rc != 0) { + rc = mem_puts( mem_buffer, od); + } + } else { +#endif + rc = vfprintf( stream, format, ap); +#if MCPP_LIB + } +#endif + va_end( ap); + + return rc; + + } else { + return EOF; + } +} + +int (* mcpp_fprintf)( OUTDEST od, const char * format, ...) = mcpp_lib_fprintf; + +#if MCPP_LIB +void mcpp_reset_def_out_func( void) +{ + mcpp_fputc = mcpp_lib_fputc; + mcpp_fputs = mcpp_lib_fputs; + mcpp_fprintf = mcpp_lib_fprintf; +} + +void mcpp_set_out_func( + int (* func_fputc)( int c, OUTDEST od), + int (* func_fputs)( const char * s, OUTDEST od), + int (* func_fprintf)( OUTDEST od, const char * format, ...) +) +{ + mcpp_fputc = func_fputc; + mcpp_fputs = func_fputs; + mcpp_fprintf = func_fprintf; +} +#endif + +int get_unexpandable( + int c, /* First char of token */ + int diag /* Flag of diagnosis */ +) +/* + * Get the next unexpandable token in the line, expanding macros. + * Return the token type. The token is written in work_buf[]. + * The once expanded macro is never expanded again. + * Called only from the routines processing #if (#elif, #assert), #line and + * #include directives in order to diagnose some subtle macro expansions. + */ +{ + DEFBUF * defp = NULL; + FILEINFO * file; + FILE * fp = NULL; + LINE_COL line_col = { 0L, 0}; + int token_type = NO_TOKEN; + int has_pragma; + + while (c != EOS && c != '\n' /* In a line */ + && (fp = infile->fp /* Preserve current state */ + , (token_type + = scan_token( c, (workp = work_buf, &workp), work_end)) + == NAM) /* Identifier */ + && fp != NULL /* In source ! */ + && (defp = is_macro( NULL)) != NULL) { /* Macro */ + expand_macro( defp, work_buf, work_end, line_col, & has_pragma); + /* Expand macro */ + if (has_pragma) + cerror( "_Pragma operator found in directive line" /* _E_ */ + , NULL, 0L, NULL); + file = unget_string( work_buf, defp->name); /* Stack to re-read */ + c = skip_ws(); /* Skip TOK_SEP */ + if (file != infile && macro_line != MACRO_ERROR && (warn_level & 1)) { + /* This diagnostic is issued even if "diag" is FALSE. */ + cwarn( "Macro \"%s\" is expanded to 0 token" /* _W1_ */ + , defp->name, 0L, NULL); + if (! option_flags.no_source_line) + dump_a_def( " macro", defp, FALSE, TRUE, fp_err); + } + } + + if (c == '\n' || c == EOS) { + unget_ch(); + return NO_TOKEN; + } + + if (diag && fp == NULL && defp && (warn_level & 1)) { + char tmp[ NWORK + 16]; + char * tmp_end = tmp + NWORK; + char * tmp_p; + file = unget_string( infile->buffer, defp->name); /* To diagnose */ + c = get_ch(); + while (file == infile) { /* Search the expanded macro */ + if (scan_token( c, (tmp_p = tmp, &tmp_p), tmp_end) != NAM) { + c = get_ch(); + continue; + } + if (standard && str_eq( identifier, "defined")) { + cwarn( "Macro \"%s\" is expanded to \"defined\"" /* _W1_ */ + , defp->name, 0L, NULL); + break; + } + if (! standard && str_eq( identifier, "sizeof")) { + cwarn( "Macro \"%s\" is expanded to \"sizeof\"" /* _W1_ */ + , defp->name, 0L, NULL); + break; + } + c = get_ch(); + } + if (file == infile) { + infile->bptr += strlen( infile->bptr); + get_ch(); + } + unget_ch(); + if (token_type == OPE) { + unget_string( work_buf, NULL); /* Set again 'openum' */ + scan_token( get_ch(), (workp = work_buf, &workp), work_end); + } + } + + return token_type; +} + +void skip_nl( void) +/* + * Skip to the end of the current input line. + */ +{ + insert_sep = NO_SEP; + while (infile && infile->fp == NULL) { /* Stacked text */ + infile->bptr += strlen( infile->bptr); + get_ch(); /* To the parent */ + } + if (infile) + infile->bptr += strlen( infile->bptr); /* Source line */ +} + +int skip_ws( void) +/* + * Skip over horizontal whitespaces. + */ +{ + int c; + + do { + c = get_ch(); + } while (char_type[ c] & HSP); + + return c; +} + +#define MBMASK 0xFF /* Mask to hide multibyte char */ + +int scan_token( + int c, /* The first character of the token */ + char ** out_pp, /* Pointer to pointer to output buf */ + char * out_end /* End of output buffer */ +) +/* + * Scan the next token of any type. + * The token is written out to the specified buffer and the output pointer + * is advanced. Token is terminated by EOS. Return the type of token. + * If the token is an identifier, the token is also in identifier[]. + * If the token is a operator or punctuator, return OPE. + * If 'c' is token separator, then return SEP. + * If 'c' is not the first character of any known token and not a token + * separator, return SPE. + * In POST_STD mode, inserts token separator (a space) between any tokens of + * source. + */ +{ + char * out = *out_pp; /* Output pointer */ + int ch_type; /* Type of character */ + int token_type = 0; /* Type of token */ + int ch; + + if (standard) + in_token = TRUE; /* While a token is scanned */ + c = c & UCHARMAX; + ch_type = char_type[ c] & MBMASK; + + switch (ch_type) { + case LET: /* Probably an identifier */ + switch (c) { + case 'L': + if (! standard) + goto ident; + ch = get_ch(); + if (char_type[ ch] & QUO) { /* char_type[ ch] == QUO */ + if (ch == '"') + token_type = WSTR; /* Wide-char string literal */ + else + token_type = WCHR; /* Wide-char constant */ + c = ch; + *out++ = 'L'; + break; /* Fall down to "case QUO:" */ + } else { + unget_ch(); + } /* Fall through */ + default: /* An identifier */ +ident: + scan_id( c); + out = stpcpy( out, identifier); + token_type = NAM; + break; + } + if (token_type == NAM) + break; + /* Else fall through -- i.e. WSTR, WCHR */ + case QUO: /* String or character constant */ + out = scan_quote( c, out, out_end, FALSE); + if (token_type == 0) { /* Without prefix L */ + if (c == '"') + token_type = STR; + else + token_type = CHR; + } /* Else WSTR or WCHR */ + break; + case DOT: + ch = get_ch(); + unget_ch(); + if ((char_type[ ch] & DIG) == 0) /* Operator '.' or '...' */ + goto operat; + /* Else fall through */ + case DIG: /* Preprocessing number */ + out = (standard ? scan_number( c, out, out_end) + : scan_number_prestd( c, out, out_end)); + token_type = NUM; + break; + case PUNC: +operat: out = scan_op( c, out); /* Operator or punctuator */ + token_type = OPE; /* Number is set in global "openum" */ + break; + default: /* Special tokens or special characters */ +#if OK_UCN + if (mcpp_mode == STD && c == '\\' && stdc2) { + ch = get_ch(); + unget_ch(); + if (ch == 'U' || ch == 'u') + goto ident; /* Universal-Characte-Name */ + } +#endif +#if OK_MBIDENT + if (mcpp_mode == STD && (char_type[ c] & mbchk) && stdc3) { + char * bptr = infile->bptr; + mb_read( c, &infile->bptr, &out); + infile->bptr = bptr; + out = *out_pp; + goto ident; /* An identifier with multi-byte characters */ + /* Mbchar cheking has been done in scan_quote() and others. */ + } +#endif + if ((standard && (c == CAT || c == ST_QUOTE)) || (char_type[ c] & SPA)) + token_type = SEP; /* Token separator or magic char*/ + else + token_type = SPE; + /* Unkown token ($, @, multi-byte character or Latin */ + *out++ = c; + *out = EOS; + break; + } + + if (out_end < out) + cfatal( "Buffer overflow scanning token \"%s\"" /* _F_ */ + , *out_pp, 0L, NULL); + if (mcpp_debug & TOKEN) + dump_token( token_type, *out_pp); + if (mcpp_mode == POST_STD && token_type != SEP && infile->fp != NULL + && (char_type[ *infile->bptr & UCHARMAX] & SPA) == 0) + insert_sep = INSERT_SEP; /* Insert token separator */ + *out_pp = out; + + in_token = FALSE; /* Token scanning has been done */ + return token_type; +} + +static void scan_id( + int c /* First char of id */ +) +/* + * Reads the next identifier and put it into identifier[]. + * The caller has already read the first character of the identifier. + */ +{ + static char * const limit = &identifier[ IDMAX]; + static int dollar_diagnosed = FALSE; /* Flag of diagnosing '$' */ +#if OK_UCN + int uc2 = 0, uc4 = 0; /* Count of UCN16, UCN32 */ +#endif +#if OK_MBIDENT + int mb = 0; /* Count of MBCHAR */ +#endif + size_t len; /* Length of identifier */ + char * bp = identifier; + + if (c == IN_SRC) { /* Magic character */ + *bp++ = c; + if ((mcpp_debug & MACRO_CALL) && ! in_directive) { + *bp++ = get_ch(); /* Its 2-bytes */ + *bp++ = get_ch(); /* argument */ + } + c = get_ch(); + } + + do { + if (bp < limit) + *bp++ = c; +#if OK_UCN + if (mcpp_mode == STD && c == '\\' && stdc2) { + int cnt; + char * tp = bp; + + if ((c = get_ch()) == 'u') { + cnt = 4; + } else if (c == 'U') { + cnt = 8; + } else { + unget_ch(); + bp--; + break; + } + *bp++ = c; + if ((bp = scan_ucn( cnt, bp)) == NULL) /* Error */ + return; + if (cnt == 4) + uc2++; + else if (cnt == 8) + uc4++; + if (limit <= tp) /* Too long identifier */ + bp = tp; /* Back the pointer */ + goto next_c; + } +#endif /* OK_UCN */ +#if OK_MBIDENT + if (mcpp_mode == STD && (char_type[ c] & mbchk) && stdc3) { + len = mb_read( c, &infile->bptr, &bp); + if (len & MB_ERROR) { + if (infile->fp) + cerror( + "Illegal multi-byte character sequence." /* _E_ */ + , NULL, 0L, NULL); + } else { + mb += len; + } + } +#endif /* OK_MBIDENT */ +#if OK_UCN +next_c: +#endif + c = get_ch(); + } while ((char_type[ c] & (LET | DIG)) /* Letter or digit */ +#if OK_UCN + || (mcpp_mode == STD && c == '\\' && stdc2) +#endif +#if OK_MBIDENT + || (mcpp_mode == STD && (char_type[ c] & mbchk) && stdc3) +#endif + ); + + unget_ch(); + *bp = EOS; + + if (bp >= limit && (warn_level & 1)) /* Limit of token */ + cwarn( "Too long identifier truncated to \"%s\"" /* _W1_ */ + , identifier, 0L, NULL); + + len = bp - identifier; +#if IDMAX > IDLEN90MIN + /* UCN16, UCN32, MBCHAR are counted as one character for each. */ +#if OK_UCN + if (mcpp_mode == STD) + len -= (uc2 * 5) - (uc4 * 9); +#endif +#if OK_MBIDENT + if (mcpp_mode == STD) + len -= mb; +#endif + if (standard && infile->fp && len > std_limits.id_len && (warn_level & 4)) + cwarn( "Identifier longer than %.0s%ld characters \"%s\"" /* _W4_ */ + , NULL, (long) std_limits.id_len, identifier); +#endif /* IDMAX > IDLEN90MIN */ + + if (option_flags.dollar_in_name && dollar_diagnosed == FALSE + && (warn_level & 2) && strchr( identifier, '$') != NULL) { + cwarn( "'$' in identifier \"%s\"", identifier, 0L, NULL); /* _W2_ */ + dollar_diagnosed = TRUE; /* Diagnose only once */ + } +} + +char * scan_quote( + int delim, /* ', " or < (header-name) */ + char * out, /* Output buffer */ + char * out_end, /* End of output buffer */ + int diag /* Diagnostic should be output */ +) +/* + * Scan off a string literal or character constant to the output buffer. + * Report diagnosis if the quotation is terminated by newline or character + * constant is empty (provided 'diag' is TRUE). + * Return the next output pointer or NULL (on error). + */ +{ + const char * const skip_line = ", skipped the line"; /* _E_ */ + const char * const unterm_string + = "Unterminated string literal%s"; + const char * const unterm_char + = "Unterminated character constant %s%.0ld%s"; + const char * const empty_const + = "Empty character constant %s%.0ld%s"; + const char * skip; + size_t len; + int c; + char * out_p = out; + + /* Set again in case of called from routines other than scan_token(). */ + if (standard) + in_token = TRUE; + *out_p++ = delim; + if (delim == '<') + delim = '>'; + +scan: + while ((c = get_ch()) != EOS) { + +#if MBCHAR + if (char_type[ c] & mbchk) { + /* First of multi-byte character (or shift-sequence) */ + char * bptr = infile->bptr; + len = mb_read( c, &infile->bptr, (*out_p++ = c, &out_p)); + if (len & MB_ERROR) { + if (infile->fp != NULL && compiling && diag) { + if (warn_level & 1) { + char * buf; + size_t chlen; + buf = xmalloc( chlen = infile->bptr - bptr + 2); + memcpy( buf, bptr, chlen - 1); + buf[ chlen - 1] = EOS; + cwarn( + "Illegal multi-byte character sequence \"%s\" in quotation", /* _W1_ */ + buf, 0L, NULL); + free( buf); + } + } + continue; + } else { /* Valid multi-byte character (or sequence) */ + goto chk_limit; + } + } +#endif + if (c == delim) { + break; + } else if (c == '\\' && delim != '>') { /* In string literal */ +#if OK_UCN + if (mcpp_mode == STD && stdc2) { + int cnt; + char * tp; + + *out_p++ = c; + if ((c = get_ch()) == 'u') { + cnt = 4; + } else if (c == 'U') { + cnt = 8; + } else { + goto escape; + } + *out_p++ = c; + if ((tp = scan_ucn( cnt, out_p)) != NULL) + out_p = tp; + /* Else error */ + continue; /* Error or not, anyway continue */ + } +#endif /* OK_UCN */ + *out_p++ = c; /* Escape sequence */ + c = get_ch(); +escape: +#if MBCHAR + if (char_type[ c] & mbchk) { + /* '\\' followed by multi-byte char */ + unget_ch(); + continue; + } +#endif + if (! standard && c == '\n') { /* */ + out_p--; /* Splice the lines */ + if (cat_line( TRUE) == NULL) /* End of file */ + break; + c = get_ch(); + } + } else if (mcpp_mode == POST_STD && c == ' ' && delim == '>' + && infile->fp == NULL) { + continue; /* Skip space possibly inserted by macro expansion */ + } else if (c == '\n') { + break; + } + if (diag && iscntrl( c) && ((char_type[ c] & SPA) == 0) + && (warn_level & 1)) + cwarn( + "Illegal control character %.0s0lx%02x in quotation" /* _W1_ */ + , NULL, (long) c, NULL); + *out_p++ = c; +chk_limit: + if (out_end < out_p) { + *out_end = EOS; + cfatal( "Too long quotation", NULL, 0L, NULL); /* _F_ */ + } + } + + if (c == '\n' || c == EOS) + unget_ch(); + if (c == delim) + *out_p++ = delim; + *out_p = EOS; + if (diag) { /* At translation phase 3 */ + skip = (infile->fp == NULL) ? NULL : skip_line; + if (c != delim) { + if (mcpp_mode == OLD_PREP /* Implicit closing of quote*/ + && (delim == '"' || delim == '\'')) + goto done; + if (delim == '"') { + if (mcpp_mode != POST_STD && option_flags.lang_asm) { + /* STD, KR */ + /* Concatenate the unterminated string to the next line */ + if (warn_level & 1) + cwarn( unterm_string + , ", catenated to the next line" /* _W1_ */ + , 0L, NULL); + if (cat_line( FALSE) != NULL) + goto scan; /* Splice the lines */ + /* Else end of file */ + } else { + cerror( unterm_string, skip, 0L, NULL); /* _E_ */ + } + } else if (delim == '\'') { + if (mcpp_mode != POST_STD && option_flags.lang_asm) { + /* STD, KR */ + if (warn_level & 1) + cwarn( unterm_char, out, 0L, NULL); /* _W1_ */ + goto done; + } else { + cerror( unterm_char, out, 0L, skip); /* _E_ */ + } + } else { + cerror( "Unterminated header name %s%.0ld%s" /* _E_ */ + , out, 0L, skip); + } + out_p = NULL; + } else if (delim == '\'' && out_p - out <= 2) { + if (mcpp_mode != POST_STD && option_flags.lang_asm) { + /* STD, KR */ + if (warn_level & 1) + cwarn( empty_const, out, 0L, skip); /* _W1_ */ + } else { + cerror( empty_const, out, 0L, skip); /* _E_ */ + out_p = NULL; + goto done; + } + } else if (mcpp_mode == POST_STD && delim == '>' && (warn_level & 2)) { + cwarn( + "Header-name enclosed by <, > is an obsolescent feature %s" /* _W2_ */ + , out, 0L, skip); + } +#if NWORK-2 > SLEN90MIN + if (standard && out_p - out > std_limits.str_len && (warn_level & 4)) + cwarn( "Quotation longer than %.0s%ld bytes" /* _W4_ */ + , NULL, std_limits.str_len, NULL); +#endif + } + +done: + in_token = FALSE; + return out_p; +} + +static char * cat_line( + int del_bsl /* Delete the ? */ +) +/* + * If del_bsl == TRUE: + * Delete sequence in string literal. + * FALSE: Overwrite the with 'n'. + * Return NULL on end of file. Called only from scan_quote(). + * This routine is never called in POST_STD mode. + */ +{ + size_t len; + char * save1, * save2; + + if (del_bsl) { /* Delete the */ + infile->bptr -= 2; + len = infile->bptr - infile->buffer; + } else { /* Overwrite the with 'n' */ + strcpy( infile->bptr, "\\n"); + len = strlen( infile->buffer); + } + save1 = save_string( infile->buffer); + save2 = get_line( FALSE); /* infile->buffer is overwritten */ + if (save2 == NULL) { + free( save1); + return NULL; + } + save2 = save_string( infile->buffer); + memcpy( infile->buffer, save1, len); + strcpy( infile->buffer + len, save2); /* Catenate */ + free( save1); + free( save2); + if (! del_bsl) + len -= 2; + infile->bptr = infile->buffer + len; + return infile->bptr; +} + +static char * scan_number( + int c, /* First char of number */ + char * out, /* Output buffer */ + char * out_end /* Limit of output buffer */ +) +/* + * Read a preprocessing number. + * By scan_token() we know already that the first c is from 0 to 9 or dot, + * and if c is dot then the second character is digit. + * Returns the advanced output pointer. + * Note: preprocessing number permits non-numeric forms such as 3E+xy, + * which are used in stringization or token-concatenation. + */ +{ + char * out_p = out; /* Current output pointer */ + + do { + *out_p++ = c; + if (c == 'E' || c == 'e' /* Sign should follow 'E', 'e', */ + || (stdc3 && (c == 'P' || c == 'p')) + /* 'P' or 'p'. */ + ) { + c = get_ch(); + if (c == '+' || c == '-') { + *out_p++ = c; + c = get_ch(); + } +#if OK_UCN + } else if (mcpp_mode == STD && c == '\\' && stdc3) { + int cnt; + char * tp; + + if ((c = get_ch()) == 'u') { + cnt = 4; + } else if (c == 'U') { + cnt = 8; + } else { + unget_ch(); + out_p--; + break; + } + *out_p++ = c; + if ((tp = scan_ucn( cnt, out_p)) == NULL) /* Error */ + break; + else + out_p = tp; + c = get_ch(); +#endif /* OK_UCN */ +#if OK_MBIDENT + } else if (mcpp_mode == STD && (char_type[ c] & mbchk) && stdc3) { + len = mb_read( c, &infile->bptr, &out_p); + if (len & MB_ERROR) { + if (infile->fp) + cerror( + "Illegal multi-byte character sequence." /* _E_ */ + , NULL, 0L, NULL); + } +#endif /* OK_MBIDENT */ + } else { + c = get_ch(); + } + } while ((char_type[ c] & (DIG | DOT | LET)) /* Digit, dot or letter */ +#if OK_UCN + || (mcpp_mode == STD && c == '\\' && stdc3) +#endif +#if OK_MBIDENT + || (mcpp_mode == STD && (char_type[ c] & mbchk) && stdc3) +#endif + ); + + *out_p = EOS; + if (out_end < out_p) + cfatal( "Too long pp-number token \"%s\"" /* _F_ */ + , out, 0L, NULL); + unget_ch(); + return out_p; +} + +/* Original version of DECUS CPP with slight modifications, */ +/* too exact for Standard preprocessing. */ +static char * scan_number_prestd( + int c, /* First char of number */ + char * out, /* Output buffer */ + char * out_end /* Limit of output buffer */ +) +/* + * Process a number. We know that c is from 0 to 9 or dot. + * Algorithm from Dave Conroy's Decus C. + * Returns the advanced output pointer. + */ +{ + char * const out_s = out; /* For diagnostics */ + int radix; /* 8, 10, or 16 */ + int expseen; /* 'e' seen in floater */ + int octal89; /* For bad octal test */ + int dotflag; /* TRUE if '.' was seen */ + + expseen = FALSE; /* No exponent seen yet */ + octal89 = FALSE; /* No bad octal yet */ + radix = 10; /* Assume decimal */ + if ((dotflag = (c == '.')) != FALSE) { /* . something? */ + *out++ = '.'; /* Always out the dot */ + if ((char_type[(c = get_ch())] & DIG) == 0) { + /* If not a float numb, */ + goto nomore; /* All done for now */ + } + } /* End of float test */ + else if (c == '0') { /* Octal or hex? */ + *out++ = c; /* Stuff initial zero */ + radix = 8; /* Assume it's octal */ + c = get_ch(); /* Look for an 'x' */ + if (c == 'x' || c == 'X') { /* Did we get one? */ + radix = 16; /* Remember new radix */ + *out++ = c; /* Stuff the 'x' */ + c = get_ch(); /* Get next character */ + } + } + while (1) { /* Process curr. char. */ + /* + * Note that this algorithm accepts "012e4" and "03.4" + * as legitimate floating-point numbers. + */ + if (radix != 16 && (c == 'e' || c == 'E')) { + if (expseen) /* Already saw 'E'? */ + break; /* Exit loop, bad nbr. */ + expseen = TRUE; /* Set exponent seen */ + radix = 10; /* Decimal exponent */ + *out++ = c; /* Output the 'e' */ + if ((c = get_ch()) != '+' && c != '-') + continue; + } + else if (radix != 16 && c == '.') { + if (dotflag) /* Saw dot already? */ + break; /* Exit loop, two dots */ + dotflag = TRUE; /* Remember the dot */ + radix = 10; /* Decimal fraction */ + } + else { /* Check the digit */ + switch (c) { + case '8': case '9': /* Sometimes wrong */ + octal89 = TRUE; /* Do check later */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + break; /* Always ok */ + + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + if (radix == 16) /* Alpha's are ok only */ + break; /* if reading hex. */ + default: /* At number end */ + goto done; /* Break from for loop */ + } /* End of switch */ + } /* End general case */ + *out++ = c; /* Accept the character */ + c = get_ch(); /* Read another char */ + } /* End of scan loop */ + + if (out_end < out) /* Buffer overflow */ + goto nomore; + /* + * When we break out of the scan loop, c contains the first + * character (maybe) not in the number. If the number is an + * integer, allow a trailing 'L' for long. If not those, push + * the trailing character back on the input stream. + * Floating point numbers accept a trailing 'L' for "long double". + */ +done: + if (! (dotflag || expseen)) { /* Not floating point */ + /* + * We know that dotflag and expseen are both zero, now: + * dotflag signals "saw 'L'". + */ + for (;;) { + switch (c) { + case 'l': + case 'L': + if (dotflag) + goto nomore; + dotflag = TRUE; + break; + default: + goto nomore; + } + *out++ = c; /* Got 'L' . */ + c = get_ch(); /* Look at next, too. */ + } + } + +nomore: *out = EOS; + if (out_end < out) + goto overflow; + unget_ch(); /* Not part of a number */ + if (octal89 && radix == 8 && (warn_level & 1)) + cwarn( "Illegal digit in octal number \"%s\"" /* _W1_ */ + , out_s, 0L, NULL); + return out; + +overflow: + cfatal( "Too long number token \"%s\"", out_s, 0L, NULL); /* _F_ */ + return out; +} + +#if OK_UCN +static char * scan_ucn( + int cnt, /* Bytes of sequence */ + char * out /* Output buffer */ +) +/* + * Scan an UCN sequence and put the sequence to 'out'. + * Return the advanced pointer or NULL on failure. + * This routine is never called in POST_STD mode. + */ +{ + uexpr_t value; /* Value of UCN */ + int i, c; + + value = 0L; + for (i = 0; i < cnt; i++) { + c = get_ch(); + if (! isxdigit( c)) { + if (infile->fp) + cerror( "Illegal UCN sequence" /* _E_ */ + , NULL, 0L, NULL); + *out = EOS; + unget_ch(); + return NULL; + } + c = tolower( c); + *out++ = c; + c = (isdigit( c) ? (c - '0') : (c - 'a' + 10)); + value = (value << 4) | c; + } + if (infile->fp /* In source */ + && ((value >= 0L && value <= 0x9FL + && value != 0x24L && value != 0x40L && value != 0x60L) + /* Basic source character */ + || (stdc3 && (value >= 0xD800L && value <= 0xDFFFL)))) + /* Reserved for special chars */ + cerror( "UCN cannot specify the value %.0s\"%08lx\"" /* _E_ */ + , NULL, (long) value, NULL); + return out; +} +#endif /* OK_UCN */ + +static char * scan_op( + int c, /* First char of the token */ + char * out /* Output buffer */ +) +/* + * Scan C operator or punctuator into the specified buffer. + * Return the advanced output pointer. + * The code-number of the operator is stored to global variable 'openum'. + * Note: '#' is not an operator nor a punctuator in other than directive line, + * nevertheless is handled as a punctuator in this cpp for convenience. + */ +{ + int c2, c3, c4; + + *out++ = c; + + switch (c) { + case '~': openum = OP_COM; break; + case '(': openum = OP_LPA; break; + case ')': openum = OP_RPA; break; + case '?': openum = OP_QUE; break; + case ';': case '[': case ']': case '{': + case '}': case ',': + openum = OP_1; + break; + default: + openum = OP_2; /* Tentative guess */ + } + + if (openum != OP_2) { /* Single byte operators */ + *out = EOS; + return out; + } + + c2 = get_ch(); /* Possibly two bytes ops */ + *out++ = c2; + + switch (c) { + case '=': + openum = ((c2 == '=') ? OP_EQ : OP_1); /* ==, = */ + break; + case '!': + openum = ((c2 == '=') ? OP_NE : OP_NOT); /* !=, ! */ + break; + case '&': + switch (c2) { + case '&': openum = OP_ANA; break; /* && */ + case '=': /* openum = OP_2; */ break; /* &= */ + default : openum = OP_AND; break; /* & */ + } + break; + case '|': + switch (c2) { + case '|': openum = OP_ORO; break; /* || */ + case '=': /* openum = OP_2; */ break; /* |= */ + default : openum = OP_OR; break; /* | */ + } + break; + case '<': + switch (c2) { + case '<': c3 = get_ch(); + if (c3 == '=') { + openum = OP_3; /* <<= */ + *out++ = c3; + } else { + openum = OP_SL; /* << */ + unget_ch(); + } + break; + case '=': openum = OP_LE; break; /* <= */ + case ':': /* <: i.e. [ */ + if (mcpp_mode == STD && option_flags.dig) + openum = OP_LBRCK_D; + else + openum = OP_LT; + break; + case '%': /* <% i.e. { */ + if (mcpp_mode == STD && option_flags.dig) + openum = OP_LBRACE_D; + else + openum = OP_LT; + break; + default : openum = OP_LT; break; /* < */ + } + break; + case '>': + switch (c2) { + case '>': c3 = get_ch(); + if (c3 == '=') { + openum = OP_3; /* >>= */ + *out++ = c3; + } else { + openum = OP_SR; /* >> */ + unget_ch(); + } + break; + case '=': openum = OP_GE; break; /* >= */ + default : openum = OP_GT; break; /* > */ + } + break; + case '#': + if (standard && (in_define || macro_line)) /* in #define or macro */ + openum = ((c2 == '#') ? OP_CAT : OP_STR); /* ##, # */ + else + openum = OP_1; /* # */ + break; + case '+': + switch (c2) { + case '+': /* ++ */ + case '=': /* openum = OP_2; */ break; /* += */ + default : openum = OP_ADD; break; /* + */ + } + break; + case '-': + switch (c2) { + case '-': /* -- */ + case '=': /* -= */ + /* openum = OP_2; */ + break; + case '>': + if (cplus_val) { + if ((c3 = get_ch()) == '*') { /* ->* */ + openum = OP_3; + *out++ = c3; + } else { + /* openum = OP_2; */ + unget_ch(); + } + } /* else openum = OP_2; */ /* -> */ + /* else openum = OP_2; */ + break; + default : openum = OP_SUB; break; /* - */ + } + break; + case '%': + switch (c2) { + case '=': break; /* %= */ + case '>': /* %> i.e. } */ + if (mcpp_mode == STD && option_flags.dig) + openum = OP_RBRACE_D; + else + openum = OP_MOD; + break; + case ':': + if (mcpp_mode == STD && option_flags.dig) { + if ((c3 = get_ch()) == '%') { + if ((c4 = get_ch()) == ':') { /* %:%: i.e. ## */ + openum = OP_DSHARP_D; + *out++ = c3; + *out++ = c4; + } else { + unget_ch(); + unget_ch(); + openum = OP_SHARP_D; /* %: i.e. # */ + } + } else { + unget_ch(); + openum = OP_SHARP_D; /* %: i.e. # */ + } + if (in_define) { /* in #define */ + if (openum == OP_DSHARP_D) + openum = OP_CAT; + else + openum = OP_STR; + } + } else { + openum = OP_MOD; + } + break; + default : openum = OP_MOD; break; /* % */ + } + break; + case '*': + if (c2 != '=') /* * */ + openum = OP_MUL; + /* else openum = OP_2; */ /* *= */ + break; + case '/': + if (c2 != '=') /* / */ + openum = OP_DIV; + /* else openum = OP_2; */ /* /= */ + break; + case '^': + if (c2 != '=') /* ^ */ + openum = OP_XOR; + /* else openum = OP_2; */ /* ^= */ + break; + case '.': + if (standard) { + if (c2 == '.') { + c3 = get_ch(); + if (c3 == '.') { + openum = OP_ELL; /* ... */ + *out++ = c3; + break; + } else { + unget_ch(); + openum = OP_1; + } + } else if (cplus_val && c2 == '*') { /* .* */ + /* openum = OP_2 */ ; + } else { /* . */ + openum = OP_1; + } + } else { + openum = OP_1; + } + break; + case ':': + if (cplus_val && c2 == ':') /* :: */ + /* openum = OP_2 */ ; + else if (mcpp_mode == STD && c2 == '>' && option_flags.dig) + openum = OP_RBRCK_D; /* :> i.e. ] */ + else /* : */ + openum = OP_COL; + break; + default: /* Never reach here */ + cfatal( "Bug: Punctuator is mis-implemented %.0s0lx%x" /* _F_ */ + , NULL, (long) c, NULL); + openum = OP_1; + break; + } + + switch (openum) { + case OP_STR: + if (mcpp_mode == STD && c == '%') break; /* %: */ + case OP_1: + case OP_NOT: case OP_AND: case OP_OR: case OP_LT: + case OP_GT: case OP_ADD: case OP_SUB: case OP_MOD: + case OP_MUL: case OP_DIV: case OP_XOR: case OP_COM: + case OP_COL: /* Any single byte operator or punctuator */ + unget_ch(); + out--; + break; + default: /* Two or more bytes operators or punctuators */ + break; + } + + *out = EOS; + return out; +} + +int id_operator( + const char * name +) +/* + * Check whether the name is identifier-like operator in C++. + * Return the operator number if matched, return 0 if not matched. + * Note: these identifiers are defined as macros in in C95. + * This routine is never called in POST_STD mode. + */ +{ + typedef struct id_op { + const char * name; + int op_num; + } ID_OP; + + ID_OP id_ops[] = { + { "and", OP_ANA}, + { "and_eq", OP_2}, + { "bitand", OP_AND}, + { "bitor", OP_OR}, + { "compl", OP_COM}, + { "not", OP_NOT}, + { "not_eq", OP_NE}, + { "or", OP_ORO}, + { "or_eq", OP_2}, + { "xor", OP_XOR}, + { "xor_eq", OP_2}, + { NULL, 0}, + }; + + ID_OP * id_p = id_ops; + + while (id_p->name != NULL) { + if (str_eq( name, id_p->name)) + return id_p->op_num; + id_p++; + } + return 0; +} + +void expanding( + const char * name, /* The name of (nested) macro just expanded. */ + int to_be_freed /* The name should be freed later. */ +) +/* + * Remember used macro name for diagnostic. + */ +{ + if (exp_mac_ind < EXP_MAC_IND_MAX - 1) { + exp_mac_ind++; + } else { + clear_exp_mac(); + exp_mac_ind++; + } + expanding_macro[ exp_mac_ind].name = name; + expanding_macro[ exp_mac_ind].to_be_freed = to_be_freed; +} + +void clear_exp_mac( void) +/* + * Initialize expanding_macro[] freeing names registered in + * name_to_be_freed[]. + */ +{ + int i; + + for (i = 1; i < EXP_MAC_IND_MAX; i++) { + if (expanding_macro[ i].to_be_freed) { + free( (void *) expanding_macro[ i].name); + expanding_macro[ i].to_be_freed = FALSE; + } + } + exp_mac_ind = 0; +} + +int get_ch( void) +/* + * Return the next character from a macro or the current file. + * Always return the value representable by unsigned char. + */ +{ + int len; + int c; + FILEINFO * file; + + /* + * 'in_token' is set to TRUE while scan_token() is executed (and + * scan_id(), scan_quote(), scan_number(), scan_ucn() and scan_op() + * via scan_token()) in Standard mode to simplify tokenization. + * Any token cannot cross "file"s. + */ + if (in_token) + return (*infile->bptr++ & UCHARMAX); + + if ((file = infile) == NULL) + return CHAR_EOF; /* End of all input */ + + if (mcpp_mode == POST_STD && file->fp) { /* In a source file */ + switch (insert_sep) { + case NO_SEP: + break; + case INSERT_SEP: /* Insert a token separator */ + insert_sep = INSERTED_SEP; /* Remember this fact */ + return ' '; /* for unget_ch(). */ + case INSERTED_SEP: /* Has just inserted */ + insert_sep = NO_SEP; /* Clear the flag */ + break; + } + } + if (! standard && squeezews) { + if (*file->bptr == ' ') + file->bptr++; /* Squeeze white spaces */ + squeezews = FALSE; + } + + if (mcpp_debug & GETC) { + mcpp_fprintf( DBG, "get_ch(%s) '%c' line %ld, bptr = %d, buffer" + , file->fp ? cur_fullname : file->real_fname ? file->real_fname + : file->filename ? file->filename : "NULL" + , *file->bptr & UCHARMAX + , src_line, (int) (file->bptr - file->buffer)); + dump_string( NULL, file->buffer); + dump_unget( "get entrance"); + } + + /* + * Read a character from the current input logical line or macro. + * At EOS, either finish the current macro (freeing temporary storage) + * or get another logical line by parse_line(). + * At EOF, exit the current file (#included) or, at EOF from the MCPP input + * file, return CHAR_EOF to finish processing. + * The character is converted to int with no sign-extension. + */ + if ((c = (*file->bptr++ & UCHARMAX)) != EOS) { + if (standard) + return c; /* Just a character */ + if (! in_string && c == '\\' && *file->bptr == '\n' + && in_define /* '\\''\n' is deleted in #define line, */ + /* provided the '\\' is not the 2nd byte of mbchar. */ + && ! last_is_mbchar( file->buffer, strlen( file->buffer) - 2 + && ! keep_spaces) + ) { + if (*(file->bptr - 2) == ' ') + squeezews = TRUE; + } else { + return c; + } + } + + /* + * Nothing in current line or macro. Get next line (if input from a + * file), or do end of file/macro processing, and reenter get_ch() to + * restart from the top. + */ + if (file->fp && /* In source file */ + parse_line() != NULL) /* Get line from file */ + return get_ch(); + /* + * Free up space used by the (finished) file or macro and restart + * input from the parent file/macro, if any. + */ + infile = file->parent; /* Unwind file chain */ + free( file->buffer); /* Free buffer */ + if (infile == NULL) { /* If at end of input */ + free( file->filename); + free( file->src_dir); + free( file); /* full_fname is the same with filename for main file*/ + return CHAR_EOF; /* Return end of file */ + } + if (file->fp) { /* Source file included */ + free( file->filename); /* Free filename */ + free( file->src_dir); /* Free src_dir */ + fclose( file->fp); /* Close finished file */ + /* Do not free file->real_fname and file->full_fname */ + cur_fullname = infile->full_fname; + cur_fname = infile->real_fname; /* Restore current fname*/ + if (infile->pos != 0L) { /* Includer was closed */ + infile->fp = fopen( cur_fullname, "r"); + fseek( infile->fp, infile->pos, SEEK_SET); + } /* Re-open the includer and restore the file-position */ + len = (int) (infile->bptr - infile->buffer); + infile->buffer = xrealloc( infile->buffer, NBUFF); + /* Restore full size buffer to get the next line */ + infile->bptr = infile->buffer + len; + src_line = infile->line; /* Reset line number */ + inc_dirp = infile->dirp; /* Includer's directory */ +#if MCPP_LIB + mcpp_set_out_func( infile->last_fputc, infile->last_fputs, + infile->last_fprintf); +#endif + include_nest--; + src_line++; /* Next line to #include*/ + sharp( NULL, infile->include_opt ? 1 : (file->include_opt ? 0 : 2)); + /* Need a #line now. Marker depends on include_opt. */ + /* The file of include_opt should be marked as 1. */ + /* Else if returned from include_opt file, it is the */ + /* main input file, and should not be marked. */ + /* Else, it is normal includer file, and marked as 2. */ + src_line--; + newlines = 0; /* Clear the blank lines*/ + if (mcpp_debug & MACRO_CALL) /* Should be re-initialized */ + com_cat_line.last_line = bsl_cat_line.last_line = 0L; + } else if (file->filename) { /* Expanding macro */ + if (macro_name) /* file->filename should be freed later */ + expanding( file->filename, TRUE); + else + free( file->filename); + } + free( file); /* Free file space */ + return get_ch(); /* Get from the parent */ +} + +static char * parse_line( void) +/* + * ANSI (ISO) C: translation phase 3. + * Parse a logical line. + * Check illegal control characters. + * Check unterminated string literal, character constant or comment. + * Convert each comment to one space (or spaces of the comment length on + * 'keep_spaces' mode).. + * Squeeze succeding white spaces other than (including comments) to + * one space (unless keep_spaces == TRUE). + * The lines might be spliced by comments which cross the lines. + */ +{ + char * temp; /* Temporary buffer */ + char * limit; /* Buffer end */ + char * tp; /* Current pointer into temporary buffer */ + char * sp; /* Pointer into input buffer */ + size_t com_size; + int c; + + if ((sp = get_line( FALSE)) == NULL) /* Next logical line */ + return NULL; /* End of a file */ + if (in_asm) { /* In #asm block */ + while (char_type[ *sp++ & UCHARMAX] & SPA) + ; + if (*--sp == '#') /* Directive line */ + infile->bptr = sp; + return infile->bptr; /* Don't tokenize */ + } + tp = temp = xmalloc( (size_t) NBUFF); + limit = temp + NBUFF - 2; + + while (char_type[ c = *sp++ & UCHARMAX] & HSP) { + if (mcpp_mode != POST_STD) + /* Preserve line top horizontal white spaces */ + /* as they are for human-readability */ + *tp++ = c; + /* Else skip the line top spaces */ + } + sp--; + + while ((c = *sp++ & UCHARMAX) != '\n') { + + switch (c) { + case '/': + switch (*sp++) { + case '*': /* Start of a comment */ +com_start: + if ((sp = read_a_comment( sp, &com_size)) == NULL) { + free( temp); /* End of file with un- */ + return NULL; /* terminated comment */ + } + if (keep_spaces && mcpp_mode != OLD_PREP) { + if (tp + com_size >= limit - 1) /* Too long comment */ + com_size = limit - tp - 1; /* Truncate */ + while (com_size--) + *tp++ = ' '; /* Spaces of the comment length */ + break; + } + switch (mcpp_mode) { + case POST_STD: + if (temp < tp && *(tp - 1) != ' ') + *tp++ = ' '; /* Squeeze white spaces */ + break; + case OLD_PREP: + if (temp == tp + || ! (char_type[ *(tp - 1) & UCHARMAX] & HSP)) + *tp++ = COM_SEP; /* Convert to magic character */ + break; + default: + if (temp == tp || + ! (char_type[ *(tp - 1) & UCHARMAX] & HSP)) + *tp++ = ' '; /* Squeeze white spaces */ + break; + } + break; + case '/': /* // */ + if (! standard) + goto not_comment; + /* Comment when C++ or __STDC_VERSION__ >= 199901L */ + /* Need not to convert to a space because '\n' follows */ + if (! stdc2 && (warn_level & 2)) + cwarn( "Parsed \"//\" as comment" /* _W2_ */ + , NULL, 0L, NULL); + if (keep_comments) { + sp -= 2; + while (*sp != '\n') /* Until end of line */ + mcpp_fputc( *sp++, OUT); + mcpp_fputc('\n', OUT); + wrong_line = TRUE; + } + goto end_line; + default: /* Not a comment */ +not_comment: + *tp++ = '/'; + sp--; /* To re-read */ + break; + } + break; + case '\r': /* Vertical white spaces*/ + /* Note that [CR+LF] is already converted to [LF]. */ + case '\f': + case '\v': + if (warn_level & 4) + cwarn( "Converted %.0s0x%02lx to a space" /* _W4_ */ + , NULL, (long) c, NULL); + case '\t': /* Horizontal space */ + case ' ': + if (keep_spaces) { + if (c == '\t') + *tp++ = '\t'; + else + *tp++ = ' '; /* Convert to ' ' */ + } else if (! (char_type[ *(tp - 1) & UCHARMAX] & HSP)) { + *tp++ = ' '; /* Squeeze white spaces */ + } else if (mcpp_mode == OLD_PREP && *(tp - 1) == COM_SEP) { + *(tp - 1) = ' '; /* Replace COM_SEP with ' ' */ + } + break; + case '"': /* String literal */ + case '\'': /* Character constant */ + infile->bptr = sp; + if (standard) { + tp = scan_quote( c, tp, limit, TRUE); + } else { + in_string = TRUE; /* Enable line splicing by scan_quote() */ + tp = scan_quote( c, tp, limit, TRUE); /* (not by get_ch())*/ + in_string = FALSE; + } + if (tp == NULL) { + free( temp); /* Unbalanced quotation */ + return parse_line(); /* Skip the line */ + } + sp = infile->bptr; + break; + default: + if (iscntrl( c)) { + cerror( /* Skip the control character */ + "Illegal control character %.0s0x%lx, skipped the character" /* _E_ */ + , NULL, (long) c, NULL); + } else { /* Any valid character */ + *tp++ = c; + } + break; + } + + if (limit < tp) { + *tp = EOS; + cfatal( "Too long line spliced by comments" /* _F_ */ + , NULL, 0L, NULL); + } + } + +end_line: + if (temp < tp && (char_type[ *(tp - 1) & UCHARMAX] & HSP)) + tp--; /* Remove trailing white space */ + *tp++ = '\n'; + *tp = EOS; + infile->bptr = strcpy( infile->buffer, temp); /* Write back to buffer */ + free( temp); + if (macro_line != 0 && macro_line != MACRO_ERROR) { /* Expanding macro */ + temp = infile->buffer; + while (char_type[ *temp & UCHARMAX] & HSP) + temp++; + if (*temp == '#' /* This line starts with # token */ + || (mcpp_mode == STD && *temp == '%' && *(temp + 1) == ':')) + if (warn_level & 1) + cwarn( + "Macro started at line %.0s%ld swallowed directive-like line" /* _W1_ */ + , NULL, macro_line, NULL); + } + return infile->buffer; +} + +static char * read_a_comment( + char * sp, /* Source */ + size_t * sizp /* Size of the comment */ +) +/* + * Read over a comment (which may cross the lines). + */ +{ + int c; + char * saved_sp; + int cat_line = 0; /* Number of catenated lines */ + + if (keep_spaces) { + saved_sp = sp - 2; /* '-2' for beginning / and * */ + *sizp = 0; + } + if (keep_comments) /* If writing comments */ + mcpp_fputs( "/*", OUT); /* Write the initializer*/ + c = *sp++; + + while (1) { /* Eat a comment */ + if (keep_comments) + mcpp_fputc( c, OUT); + + switch (c) { + case '/': + if ((c = *sp++) != '*') /* Don't let comments */ + continue; /* nest. */ + if (warn_level & 1) + cwarn( "\"/*\" within comment", NULL, 0L, NULL); /* _W1_ */ + if (keep_comments) + mcpp_fputc( c, OUT); + /* Fall into * stuff */ + case '*': + if ((c = *sp++) != '/') /* If comment doesn't */ + continue; /* end, look at next. */ + if (keep_comments) { /* Put out comment */ + mcpp_fputc( c, OUT); /* terminator, too. */ + mcpp_fputc( '\n', OUT); /* Append '\n' to avoid */ + /* trouble on some other tools such as rpcgen. */ + wrong_line = TRUE; + } + if (keep_spaces) /* Save the length */ + *sizp = *sizp + (sp - saved_sp); + if ((mcpp_debug & MACRO_CALL) && compiling) { + if (cat_line) { + cat_line++; + com_cat_line.len[ cat_line] /* Catenated length */ + = com_cat_line.len[ cat_line - 1] + + strlen( infile->buffer) - 1; + /* '-1' for '\n' */ + com_cat_line.last_line = src_line; + } + } + return sp; /* End of comment */ + case '\n': /* Line-crossing comment*/ + if (keep_spaces) /* Save the length */ + *sizp = *sizp + (sp - saved_sp) - 1; /* '-1' for '\n' */ + if ((mcpp_debug & MACRO_CALL) && compiling) { + /* Save location informations */ + if (cat_line == 0) /* First line of catenation */ + com_cat_line.start_line = src_line; + if (cat_line >= MAX_CAT_LINE - 1) { + *sizp = 0; /* Discard the too long comment */ + cat_line = 0; + if (warn_level & 4) + cwarn( + "Too long comment, discarded up to here" /* _W4_ */ + , NULL, 0L, NULL); + } + cat_line++; + com_cat_line.len[ cat_line] + = com_cat_line.len[ cat_line - 1] + + strlen( infile->buffer) - 1; + } + if ((saved_sp = sp = get_line( TRUE)) == NULL) + return NULL; /* End of file within comment */ + /* Never happen, because at_eof() supplement closing*/ + wrong_line = TRUE; /* We'll need a #line later */ + break; + default: /* Anything else is */ + break; /* just a character */ + } /* End switch */ + + c = *sp++; + } /* End comment loop */ + + return sp; /* Never reach here */ +} + +static char * mcpp_fgets( + char * s, + int size, + FILE * stream +) +{ + return fgets( s, size, stream); +} + +static char * get_line( + int in_comment +) +/* + * ANSI (ISO) C: translation phase 1, 2. + * Get the next logical line from source file. + * Convert [CR+LF] to [LF]. + */ +{ +#if COMPILER == INDEPENDENT +#define cr_warn_level 1 +#else +#define cr_warn_level 2 +#endif + static int cr_converted; + int converted = FALSE; + int len; /* Line length - alpha */ + char * ptr; + int cat_line = 0; /* Number of catenated lines */ + + if (infile == NULL) /* End of a source file */ + return NULL; + ptr = infile->bptr = infile->buffer; + if ((mcpp_debug & MACRO_CALL) && src_line == 0) /* Initialize */ + com_cat_line.last_line = bsl_cat_line.last_line = 0L; + + while (mcpp_fgets( ptr, (int) (infile->buffer + NBUFF - ptr), infile->fp) + != NULL) { + /* Translation phase 1 */ + src_line++; /* Gotten next physical line */ + if (standard && src_line == std_limits.line_num + 1 + && (warn_level & 1)) + cwarn( "Line number %.0s\"%ld\" got beyond range" /* _W1_ */ + , NULL, src_line, NULL); + if (mcpp_debug & (TOKEN | GETC)) { /* Dump it to DBG */ + mcpp_fprintf( DBG, "\n#line %ld (%s)", src_line, cur_fullname); + dump_string( NULL, ptr); + } + len = strlen( ptr); + if (NBUFF - 1 <= ptr - infile->buffer + len + && *(ptr + len - 1) != '\n') { + /* The line does not yet end, though the buffer is full. */ + if (NBUFF - 1 <= len) + cfatal( "Too long source line" /* _F_ */ + , NULL, 0L, NULL); + else + cfatal( "Too long logical line" /* _F_ */ + , NULL, 0L, NULL); + } + if (*(ptr + len - 1) != '\n') /* Unterminated source line */ + break; + if (len >= 2 && *(ptr + len - 2) == '\r') { /* [CR+LF] */ + *(ptr + len - 2) = '\n'; + *(ptr + --len) = EOS; + if (! cr_converted && (warn_level & cr_warn_level)) { + cwarn( "Converted [CR+LF] to [LF]" /* _W1_ _W2_ */ + , NULL, 0L, NULL); + cr_converted = TRUE; + } + } + if (standard) { + if (option_flags.trig) + converted = cnv_trigraph( ptr); + if (mcpp_mode == POST_STD && option_flags.dig) + converted += cnv_digraph( ptr); + if (converted) + len = strlen( ptr); + /* Translation phase 2 */ + len -= 2; + if (len >= 0) { + if ((*(ptr + len) == '\\') && ! last_is_mbchar( ptr, len)) { + /* (not MBCHAR) */ + ptr = infile->bptr += len; /* Splice the lines */ + wrong_line = TRUE; + if ((mcpp_debug & MACRO_CALL) && compiling) { + /* Save location informations */ + if (cat_line == 0) /* First line of catenation */ + bsl_cat_line.start_line = src_line; + if (cat_line < MAX_CAT_LINE) + /* Record the catenated length */ + bsl_cat_line.len[ ++cat_line] + = strlen( infile->buffer) - 2; + /* Else ignore */ + } + continue; + } + } +#if NBUFF-2 > SLEN90MIN + if (ptr - infile->buffer + len + 2 > std_limits.str_len + 1 + && (warn_level & 4)) /* +1 for '\n' */ + cwarn( "Logical source line longer than %.0s%ld bytes" /* _W4_ */ + , NULL, std_limits.str_len, NULL); +#endif + } + if ((mcpp_debug & MACRO_CALL) && compiling) { + if (cat_line && cat_line < MAX_CAT_LINE) { + bsl_cat_line.len[ ++cat_line] = strlen( infile->buffer) - 1; + /* Catenated length: '-1' for '\n' */ + bsl_cat_line.last_line = src_line; + } + } + return infile->bptr = infile->buffer; /* Logical line */ + } + + /* End of a (possibly included) source file */ + if (ferror( infile->fp)) + cfatal( "File read error", NULL, 0L, NULL); /* _F_ */ + if ((ptr = at_eof( in_comment)) != NULL) /* Check at end of file */ + return ptr; /* Partial line supplemented */ + if (option_flags.z) { + no_output--; /* End of included file */ + keep_comments = option_flags.c && compiling && !no_output; + } + return NULL; +} + +#define TRIOFFSET 10 + +int cnv_trigraph( + char * in +) +/* + * Perform in-place trigraph replacement on a physical line. This was added + * to the C90. In an input text line, the sequence ??[something] is + * transformed to a character (which might not appear on the input keyboard). + */ +{ + const char * const tritext = "=(/)'-\0#[\\]^{|}~"; + /* ^ ^ + * +----------+ + * this becomes this + */ + int count = 0; + const char * tp; + + while ((in = strchr( in, '?')) != NULL) { + if (*++in != '?') + continue; + while (*++in == '?') + ; + if ((tp = strchr( tritext, *in)) == NULL) + continue; + *(in - 2) = *(tp + TRIOFFSET); + in--; + memmove( in, in + 2, strlen( in + 1)); + count++; + } + + if (count && (warn_level & 16)) + cwarn( "%.0s%ld trigraph(s) converted" /* _W16_ */ + , NULL, (long) count, NULL); + return count; +} + +int cnv_digraph( + char * in +) +/* + * Perform in-place digraph replacement on a physical line. + * Called only in POST_STD mode. + */ +{ + int count = 0; + int i; + int c1, c2; + + while ((i = strcspn( in, "%:<")), (c1 = *(in + i)) != '\0') { + in += i + 1; + c2 = *in; + switch (c1) { + case '%' : + switch (c2) { + case ':' : *(in - 1) = '#'; break; + case '>' : *(in - 1) = '}'; break; + default : continue; + } + break; + case ':' : + switch (c2) { + case '>' : *(in - 1) = ']'; break; + default : continue; + } + break; + case '<' : + switch (c2) { + case '%' : *(in - 1) = '{'; break; + case ':' : *(in - 1) = '['; break; + default : continue; + } + break; + } + memmove( in, in + 1, strlen( in)); + count++; + } + + if (count && (warn_level & 16)) + cwarn( "%.0s%ld digraph(s) converted" /* _W16_ */ + , NULL, (long) count, NULL); + return count; +} + +static char * at_eof( + int in_comment +) +/* + * Check the partial line, unterminated comment, unbalanced #if block, + * uncompleted macro call at end of a file or at end of input. + * Supplement the line terminator, if possible. + * Return the supplemented line or NULL on unrecoverable error. + */ +{ + const char * const format + = "End of %s with %.0ld%s"; /* _E_ _W1_ */ + const char * const unterm_if_format += "End of %s within #if (#ifdef) section started at line %ld"; /* _E_ _W1_ */ + const char * const unterm_macro_format + = "End of %s within macro call started at line %ld";/* _E_ _W1_ */ + const char * const input + = infile->parent ? "file" : "input"; /* _E_ _W1_ */ + const char * const no_newline + = "no newline, supplemented newline"; /* _W1_ */ + const char * const unterm_com + = "unterminated comment, terminated the comment"; /* _W1_ */ + const char * const backsl = "\\, deleted the \\"; /* _W1_ */ + const char * const unterm_asm_format += "End of %s with unterminated #asm block started at line %ld"; /* _E_ _W1_ */ + size_t len; + char * cp; + + cp = infile->buffer; + len = strlen( cp); + if (len && *(cp += (len - 1)) != '\n') { + *++cp = '\n'; /* Supplement */ + *++cp = EOS; + if (mcpp_mode != OLD_PREP && (warn_level & 1)) + cwarn( format, input, 0L, no_newline); + return infile->bptr = infile->buffer; + } + if (standard && infile->buffer < infile->bptr) { + /* No line after */ + cp = infile->bptr; + *cp++ = '\n'; /* Delete the \\ */ + *cp = EOS; + if (warn_level & 1) + cwarn( format, input, 0L, backsl); + return infile->bptr = infile->buffer; + } + if (in_comment) { /* End of file within a comment */ + if (mcpp_mode != OLD_PREP && (warn_level & 1)) + cwarn( format, input, 0L, unterm_com); + /* The partial comment line has been already read by */ + /* read_a_comment(), so supplement the next line. */ + strcpy( infile->buffer, "*/\n"); + return infile->bptr = infile->buffer; + } + + if (infile->initif < ifptr) { + IFINFO * ifp = infile->initif + 1; + if (standard) { + cerror( unterm_if_format, input, ifp->ifline, NULL); + ifptr = infile->initif; /* Clear information of */ + compiling = ifptr->stat; /* erroneous grouping */ + } else if (mcpp_mode == KR && (warn_level & 1)) { + cwarn( unterm_if_format, input, ifp->ifline, NULL); + } + } + + if (macro_line != 0 && macro_line != MACRO_ERROR + && ((mcpp_mode == STD && in_getarg) || ! standard)) { + if (standard) { + cerror( unterm_macro_format, input, macro_line, NULL); + macro_line = MACRO_ERROR; + } else if (warn_level & 1) { + cwarn( unterm_macro_format, input, macro_line, NULL); + } + } + + if (in_asm && mcpp_mode == KR && (warn_level & 1)) + cwarn( unterm_asm_format, input, in_asm, NULL); + + return NULL; +} + +void unget_ch( void) +/* + * Back the pointer to reread the last character. Fatal error (code bug) + * if we back too far. unget_ch() may be called, without problems, at end of + * file. Only one character may be ungotten. If you need to unget more, + * call unget_string(). + */ +{ + if (in_token) { + infile->bptr--; + return; + } + + if (infile != NULL) { + if (mcpp_mode == POST_STD && infile->fp) { + switch (insert_sep) { + case INSERTED_SEP: /* Have just read an inserted separator */ + insert_sep = INSERT_SEP; + return; + case INSERT_SEP: + cfatal( "Bug: unget_ch() just after scan_token()" /* _F_ */ + , NULL, 0L, NULL); + break; + default: + break; + } + } + --infile->bptr; + if (infile->bptr < infile->buffer) /* Shouldn't happen */ + cfatal( "Bug: Too much pushback", NULL, 0L, NULL); /* _F_ */ + } + + if (mcpp_debug & GETC) + dump_unget( "after unget"); +} + +FILEINFO * unget_string( + const char * text, /* Text to unget */ + const char * name /* Name of the macro, if any*/ +) +/* + * Push a string back on the input stream. This is done by treating + * the text as if it were a macro or a file. + */ +{ + FILEINFO * file; + size_t size; + + if (text) + size = strlen( text) + 1; + else + size = 1; + file = get_file( name, NULL, NULL, size, FALSE); + if (text) + memcpy( file->buffer, text, size); + else + *file->buffer = EOS; + return file; +} + +char * save_string( + const char * text +) +/* + * Store a string into free memory. + */ +{ + char * result; + size_t size; + + size = strlen( text) + 1; + result = xmalloc( size); + memcpy( result, text, size); + return result; +} + +FILEINFO * get_file( + const char * name, /* File or macro name */ + const char * src_dir, /* Source file directory*/ + const char * fullname, /* Full path list */ + size_t bufsize, /* Line buffer size */ + int include_opt /* Specified by -include opt (for GCC) */ +) +/* + * Common FILEINFO buffer initialization for a new file or macro. + */ +{ + FILEINFO * file; + + file = (FILEINFO *) xmalloc( sizeof (FILEINFO)); + file->buffer = xmalloc( bufsize); + file->bptr = file->buffer; /* Initialize line ptr */ + file->buffer[ 0] = EOS; /* Force first read */ + file->line = 0L; /* (Not used just yet) */ + file->fp = NULL; /* No file yet */ + file->pos = 0L; /* No pos to remember */ + file->parent = infile; /* Chain files together */ + file->initif = ifptr; /* Initial ifstack */ + file->include_opt = include_opt; /* Specified by -include*/ + file->dirp = NULL; /* No include dir yet */ + file->real_fname = name; /* Save file/macro name */ + file->full_fname = fullname; /* Full path list */ + if (name) { + file->filename = xmalloc( strlen( name) + 1); + strcpy( file->filename, name); /* Copy for #line */ + } else { + file->filename = NULL; + } + if (src_dir) { + file->src_dir = xmalloc( strlen( src_dir) + 1); + strcpy( file->src_dir, src_dir); + } else { + file->src_dir = NULL; + } +#if MCPP_LIB + file->last_fputc = mcpp_lib_fputc; + file->last_fputs = mcpp_lib_fputs; + file->last_fprintf = mcpp_lib_fprintf; +#endif + if (infile != NULL) { /* If #include file */ + infile->line = src_line; /* Save current line */ +#if MCPP_LIB + infile->last_fputc = mcpp_fputc; + infile->last_fputs = mcpp_fputs; + infile->last_fprintf = mcpp_fprintf; +#endif + } + infile = file; /* New current file */ + + return file; /* All done. */ +} + +static const char * const out_of_memory + = "Out of memory (required size is %.0s0x%lx bytes)"; /* _F_ */ + +char * +(xmalloc)( + size_t size +) +/* + * Get a block of free memory. + */ +{ + char * result; + + if ((result = (char *) malloc( size)) == NULL) { + if (mcpp_debug & MEMORY) + print_heap(); + cfatal( out_of_memory, NULL, (long) size, NULL); + } + return result; +} + +char * (xrealloc)( + char * ptr, + size_t size +) +/* + * Reallocate malloc()ed memory. + */ +{ + char * result; + + if ((result = (char *) realloc( ptr, size)) == NULL && size != 0) { + /* 'size != 0' is necessary to cope with some */ + /* implementation of realloc( ptr, 0) which returns NULL. */ + if (mcpp_debug & MEMORY) + print_heap(); + cfatal( out_of_memory, NULL, (long) size, NULL); + } + return result; +} + +LINE_COL * get_src_location( + LINE_COL * p_line_col /* Line and column on phase 4 */ +) +/* + * Convert line-column datum of just after translation phase 3 into that of + * phase 2, tracing back line splicing by a comment and . + * Note: This conversion does not give correct datum on a line catenated by + * both of and line-crossing-comment at the same time. + * + * com_cat_line and bsl_cat_line have data only on last catenated line. + * com_cat_line.len[] and bsl_cat_line.len[] have the length of catenated + * line, and len[ 0] is always 0, followed by len[ 1], len[ 2], ..., as + * accumulated length of successively catenated lines. + */ +{ + long line; + size_t col; + size_t * cols; + CAT_LINE * l_col_p; + int i; + + line = p_line_col->line; + col = p_line_col->col; + + for (i = 0; i <= 1; i++) { + l_col_p = i ? & bsl_cat_line : & com_cat_line; + if (l_col_p->last_line != line) + continue; + /* Else just catenated line */ + cols = l_col_p->len + 1; + while (*cols < col) + cols++; + if (col <= *cols) { + cols--; + col -= *cols; + } + line = l_col_p->start_line + (cols - l_col_p->len); + } + + p_line_col->line = line; + p_line_col->col = col + 1; + /* col internally start at 0, output start at 1 */ + + return p_line_col; +} + +static void put_line( + char * out, + FILE * fp +) +/* + * Put out a logical source line. + * This routine is called only in OLD_PREP mode. + */ +{ + int c; + + while ((c = *out++) != EOS) { + if (c != COM_SEP) /* Skip 0-length comment */ + mcpp_fputc( c, FP2DEST( fp)); + } +} + +static void do_msg( + const char * severity, /* "fatal", "error", "warning" */ + const char * format, /* Format for the error message */ + const char * arg1, /* String arg. for the message */ + long arg2, /* Integer argument */ + const char * arg3 /* Second string argument */ +) +/* + * Print filenames, macro names, line numbers and error messages. + * Also print macro definitions on macro expansion problems. + */ +{ + FILEINFO * file; + DEFBUF * defp; + int i; + size_t slen; + const char * arg_s[ 2]; + char * arg_t[ 2]; + char * tp; + const char * sp; + int c; + int ind; + + fflush( fp_out); /* Synchronize output and diagnostics */ + arg_s[ 0] = arg1; arg_s[ 1] = arg3; + + for (i = 0; i < 2; i++) { /* Convert special characters to visible */ + sp = arg_s[ i]; + if (sp != NULL) + slen = strlen( sp) + 1; + else + slen = 1; + tp = arg_t[ i] = (char *) malloc( slen); + /* Don't use xmalloc() so as not to cause infinite recursion */ + if (sp == NULL || *sp == EOS) { + *tp = EOS; + continue; + } + + while ((c = *sp++) != EOS) { + switch (c) { + case TOK_SEP: + if (mcpp_mode == OLD_PREP) /* COM_SEP */ + break; /* Skip magic characters */ + /* Else fall through */ + case RT_END: + case CAT: + case ST_QUOTE: + case DEF_MAGIC: + if (! standard) + *tp++ = ' '; + break; /* Skip the magic characters*/ + case IN_SRC: + if (! standard) + *tp++ = ' '; + if ((mcpp_debug & MACRO_CALL) && ! in_directive) + sp += 2; /* Skip two more bytes */ + break; + case MAC_INF: + if (mcpp_mode != STD) { + *tp++ = ' '; + /* Illegal control character, convert to a space*/ + } else { + switch (*sp++) { /* Skip the magic characters*/ + case MAC_ARG_START : + sp++; + /* Fall through */ + case MAC_CALL_START : + sp += 2; + break; + case MAC_ARG_END : + if (! option_flags.v) + break; + else + sp++; + /* Fall through */ + case MAC_CALL_END : + if (option_flags.v) + sp += 2; + break; + } + } + break; + case '\n': + *tp++ = ' '; /* Convert '\n' to a space */ + break; + default: + *tp++ = c; + break; + } + } + + if (*(sp - 2) == '\n') + tp--; + *tp = EOS; + } + + /* Print source location and diagnostic */ + file = infile; + while (file != NULL && (file->fp == NULL || file->fp == (FILE *)-1)) + file = file->parent; /* Skip macro */ + if (file != NULL) { + file->line = src_line; + mcpp_fprintf( ERR, "%s:%ld: %s: ", cur_fullname, src_line, severity); + } + mcpp_fprintf( ERR, format, arg_t[ 0], arg2, arg_t[ 1]); + mcpp_fputc( '\n', ERR); + if (option_flags.no_source_line) + goto free_arg; + + /* Print source line, includers and expanding macros */ + file = infile; + if (file != NULL && file->fp != NULL) { + if (mcpp_mode == OLD_PREP) { + mcpp_fputs( " ", ERR); + put_line( file->buffer, fp_err); + } else { + mcpp_fprintf( ERR, " %s", file->buffer); + /* Current source line */ + } + file = file->parent; + } + while (file != NULL) { /* Print #includes, too */ + if (file->fp == NULL) { /* Macro */ + if (file->filename) { + defp = look_id( file->filename); + if ((defp->nargs > DEF_NOARGS_STANDARD) + && ! (file->parent && file->parent->filename + && str_eq( file->filename, file->parent->filename))) + /* If the name is not duplicate of parent */ + dump_a_def( " macro", defp, FALSE, TRUE, fp_err); + } + } else { /* Source file */ + if (file->buffer[ 0] == '\0') + strcpy( file->buffer, "\n"); + if (mcpp_mode != OLD_PREP) { + mcpp_fprintf( ERR, " from %s: %ld: %s", + file->line ? file->full_fname /* Full-path-list */ + : "", /* Included by -include */ + file->line, /* Current line number */ + file->buffer); /* The source line */ + } else { + mcpp_fprintf( ERR, " from %s: %ld: ", file->full_fname + , file->line); + put_line( file->buffer, fp_err); + } + } + file = file->parent; + } + + if (! macro_name) + goto free_arg; + /* Additional information of macro definitions */ + expanding_macro[ 0].name = macro_name; + for (ind = 0; ind <= exp_mac_ind; ind++) { + int ind_done; + + for (ind_done = 0; ind_done < ind; ind_done++) + if (str_eq( expanding_macro[ ind].name + , expanding_macro[ ind_done].name)) + break; /* Already reported */ + if (ind_done < ind) + continue; + for (file = infile; file; file = file->parent) + if (file->fp == NULL && file->filename + && str_eq( expanding_macro[ ind].name, file->filename)) + break; /* Already reported */ + if (file) + continue; + if ((defp = look_id( expanding_macro[ ind].name)) != NULL) { + if (defp->nargs <= DEF_NOARGS_STANDARD) + continue; /* Standard predefined */ + dump_a_def( " macro", defp, FALSE, TRUE, fp_err); + /* Macro already read over */ + } + } + +free_arg: + for (i = 0; i < 2; i++) + free( arg_t[ i]); +} + +void cfatal( + const char * format, + const char * arg1, + long arg2, + const char * arg3 +) +/* + * A real disaster. + */ +{ + do_msg( "fatal error", format, arg1, arg2, arg3); + longjmp( error_exit, -1); +} + +void cerror( + const char * format, + const char * arg1, + long arg2, + const char * arg3 +) +/* + * Print a error message. + */ +{ + do_msg( "error", format, arg1, arg2, arg3); + errors++; +} + +void cwarn( + const char * format, + const char * arg1, + long arg2, + const char * arg3 +) +/* + * Maybe an error. + */ +{ + do_msg( "warning", format, arg1, arg2, arg3); +} + +void dump_string( + const char * why, + const char * text +) +/* + * Dump text readably. + * Bug: macro argument number may be putout as a control character or any + * other character, just after MAC_PARM has been read away. + */ +{ + const char * cp; + const char * chr; + int c, c1, c2; + + if (why != NULL) + mcpp_fprintf( DBG, " (%s)", why); + mcpp_fputs( " => ", DBG); + + if (text == NULL) { + mcpp_fputs( "NULL", DBG); + return; + } + + for (cp = text; (c = *cp++ & UCHARMAX) != EOS; ) { + chr = NULL; + + switch (c) { + case MAC_PARM: + c = *cp++ & UCHARMAX; /* Macro parameter number */ + mcpp_fprintf( DBG, "<%d>", c); + break; + case MAC_INF: + if (! (mcpp_mode == STD && (mcpp_debug & MACRO_CALL))) + goto no_magic; + /* Macro informations inserted by -K option */ + c2 = *cp++ & UCHARMAX; + if (option_flags.v || c2 == MAC_CALL_START + || c2 == MAC_ARG_START) { + c = ((*cp++ & UCHARMAX) - 1) * UCHARMAX; + c += (*cp++ & UCHARMAX) - 1; + } + switch (c2) { + case MAC_CALL_START: + mcpp_fprintf( DBG, "", c); + break; + case MAC_CALL_END: + if (option_flags.v) + mcpp_fprintf( DBG, "", c); + else + chr = ""; + break; + case MAC_ARG_START: + c1 = *cp++ & UCHARMAX; + mcpp_fprintf( DBG, "", c, c1 - 1); + break; + case MAC_ARG_END: + if (option_flags.v) { + c1 = *cp++ & UCHARMAX; + mcpp_fprintf( DBG, "", c, c1 - 1); + } else { + chr = ""; + } + break; + } + break; + case DEF_MAGIC: + if (standard) { + chr = ""; + break; + } /* Else fall through */ + case CAT: + if (standard) { + chr = "##"; + break; + } /* Else fall through */ + case ST_QUOTE: + if (standard) { + chr = "#"; + break; + } /* Else fall through */ + case RT_END: + if (standard) { + chr = ""; + break; + } /* Else fall through */ + case IN_SRC: + if (standard) { + if ((mcpp_debug & MACRO_CALL) && ! in_directive) { + int num; + num = ((*cp++ & UCHARMAX) - 1) * UCHARMAX; + num += (*cp++ & UCHARMAX) - 1; + mcpp_fprintf( DBG, "", num); + } else { + chr = ""; + } + } else { /* Control character */ + mcpp_fprintf( DBG, "<^%c>", c + '@'); + } + break; + case TOK_SEP: + if (mcpp_mode == STD) { + chr = ""; + break; + } else if (mcpp_mode == OLD_PREP) { /* COM_SEP */ + chr = ""; + break; + } /* Else fall through */ + default: +no_magic: + if (c < ' ') + mcpp_fprintf( DBG, "<^%c>", c + '@'); + else + mcpp_fputc( c, DBG); + break; + } + + if (chr) + mcpp_fputs( chr, DBG); + } + + mcpp_fputc( '\n', DBG); +} + +void dump_unget( + const char * why +) +/* + * Dump all ungotten junk (pending macros and current input lines). + */ +{ + const FILEINFO * file; + + mcpp_fputs( "dump of pending input text", DBG); + if (why != NULL) { + mcpp_fputs( "-- ", DBG); + mcpp_fputs( why, DBG); + } + mcpp_fputc( '\n', DBG); + + for (file = infile; file != NULL; file = file->parent) + dump_string( file->real_fname ? file->real_fname + : file->filename ? file->filename : "NULL", file->bptr); +} + +static void dump_token( + int token_type, + const char * cp /* Token */ +) +/* + * Dump a token. + */ +{ + static const char * const t_type[] + = { "NAM", "NUM", "STR", "WSTR", "CHR", "WCHR", "OPE", "SPE" + , "SEP", }; + + mcpp_fputs( "token", DBG); + dump_string( t_type[ token_type - NAM], cp); +} + diff --git a/lib/mcpp/system.H b/lib/mcpp/system.H new file mode 100644 index 000000000..bd55770a0 --- /dev/null +++ b/lib/mcpp/system.H @@ -0,0 +1,396 @@ +/*- + * Copyright (c) 1998, 2002-2008 Kiyoshi Matsui + * All rights reserved. + * + * Some parts of this code are derived from the public domain software + * DECUS cpp (1984,1985) written by Martin Minow. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * 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 AUTHOR 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. + */ + +/* + * S Y S T E M . H + * S y s t e m D e p e n d e n t + * D e f i n i t i o n s f o r M C P P + * + * Definitions in this file may be edited to configure MCPP for particular + * operating systems and compiler configurations. + * + * Note: MCPP assumes the system implement the Ascii character set. + * If this is not the case, you will have to do some editing here and there. + */ + +#define SYSTEM_H + +#if HAVE_CONFIG_H +#include "configed.H" +#else +#include "noconfig.H" +#endif + +/* Some system has a wrong definition of UCHAR_MAX. See cpp-test.html#5.1.3 */ +#if UCHARMAX < -255 +/* The definition of UCHARMAX (possibly UCHAR_MAX too) is wrong. */ +/* Define it as a signed int value, not as an unsigned value. */ +#undef UCHARMAX +#define UCHARMAX ((1 << CHAR_BIT) - 1) +#endif + +/* + * PART 1 and PART 2 are defined in the above header files. + */ + +/* + * P A R T 1 Configurations for target-operating-system + * and target-compiler. + */ + +/* + * P A R T 2 Configurations for host-compiler. + */ + +/* + * P A R T 3 Configurations for default settings, typedefs and + * translation limits. + */ + +/* + * The variable 'mcpp_mode' specifies the mode of preprocessing as one of + * OLD_PREP, KR, STD, or POST_STD. + * Those modes have many differences each other -- + * i.e. handling of translation phases; handling of some + * preprocessing tokens; availability of some directives; way of + * macro expansion; + * -- as follows. + * + * KR Actual arguments of a macro are expanded (after substitution) + * with rest of the replacement text and the subsequent source text. + * ## in macro definition has no significance to cpp. The surrounding + * tokens are macro-expanded separately. Also, # has no significance + * to cpp. The following token is expanded. + * Directly or intermediately recursive macro call causes an error. + * sequence is deleted only in string literals + * and in #define directive lines. + * sizeof (type) can be used in #if line. + * KR corresponds to the "K&R 1st." + * + * OLD_PREP In addition to the KR specifications, this mode has the + * following characteristics (and some others). + * Converts comment to 0 space instead of 1 space. + * Expands the parameter like spelling in string literal as a macro. + * Does not check unmatched pair of '"' or '\''. + * OLD_PREP corresponts to "Reiser model" cpp. + * + * STD Standard conforming mode. + * sequence is always deleted after trigraph + * conversion and before tokenization. + * Digraph sequences are recognized as tokens. + * Actual arguments of a macro are expanded separately prior to + * re-scanning of the replacement text. + * The name in the replacement text once expanded is not re-expanded, + * thus preventing recursive death. + * ## in macro definition concatenates tokens. The tokens are not + * expanded. The concatenated token is expanded by rescanning. + * # in macro definition stringizes the following argument. The argument + * is not expanded. \ is inserted before " and \ in or surrounding + * the string literal or character constant. + * An expanded macro is surrounded by spaces to prevent unintended + * token merging. + * + * POST_STD This mode simplifies the behavior of STD mode as follows. + * 1. Digraph sequences are converted in translation phase 1, as + * alternate characters rather than as tokens. + * 2. A space is inserted as a token separator between any tokens in + * a source (except a macro name and the next '(' in macro + * definition): thus simplifying tokenization, test of macro + * redefinition and macro expansion, especially "stringization". + * 3. Test of macro redefinition ignores difference of parameter names, + * test of which has little utility and not a little overhead. + * 4. #if expression forbids character constants, which have little + * portability, little utility and not a little overhead. + * 5. Rescanning of a macro expansion is limited in the replacement + * text, rest of the source file is not scanned, thus making the + * syntax of "function-like" macro call more similar to that of + * function call. + * 6. Argument of #include directive in form is an obsolescent + * feature. + * 7. '$' or so are not treated specially in #define directive. + * 8. Trigraphs, UCN (universal-character name) are not recognized. + * 9. Multi-byte characters in an identifier are not recognized. + * + * The following specifications are available when mode is STD or POST_STD. + * preprocessing number token, digraphs, + * #pragma (#pragma MCPP put_defines, #pragma MCPP warning + * , #pragma MCPP debug) directive, + * #error directive, + * #if defined operator, #elif directive, + * predefined macros __FILE__, __LINE__, __DATE__, __TIME__ + * , __STDC__, __STDC_VERSION__, __STDC_HOSTED__, + * wide character constant, wide character string literal, + * _Pragma() operator, variable-arguments macro, + * macro as an argument of #include, #line directives, + * escape sequences \x[hex-digits], \a, \v, + * '+' option (C++ preprocessing), + * 'S' option (re-defines __STDC__ as , unpredefine some macros), + * 'V' option (re-defines __STDC_VERSION__ or __cplusplus as ), + * 'h' option (re-defines __STDC_HOSTED__ as ). + * The following specifications are available only in STD mode. + * Trigraphs and UCN, + * Multi-byte characters in an identifier. + * The following specifications are available only in KR and OLD_PREP modes. + * #assert, #asm, #endasm, #put_defines, #debug and some other older + * directives, + * argument of #line directive other than decimal-digits. + */ + +/* The values of 'mcpp_mode'. */ +#define OLD_PREP 1 /* "Reiser" cpp mode */ +#define KR 2 /* K&R 1st mode */ +#define STD 3 /* Standard moce */ +#define POST_STD 9 /* Special mode of MCPP */ + +/* + * TRIGRAPHS_INIT Initial value for the -3 option. If TRUE -3 + * disables trigraphs, if FALSE -3 enables them. + * DIGRAPHS_INIT Initial value for the -2 option. If TRUE -2 + * disables digraphs, if FALSE -2 enables them. + * OK_UCN Enable recognition of Universal-Character-Name sequence + * by -V199901L option. + * OK_MBIDENT Enable multi-byte characters in identifier by -V199901L + * option. + * EXPAND_PRAGMA Enable macro expansion of #pragma line (even in modes + * other than C99). + * expr_t, uexpr_t Type of maximum integer: + * long long (unsigned long long) or longer. + * EXPR_MAX should be defined to the maximum value of uexpr_t. + */ +#define TRIGRAPHS_INIT FALSE +#define DIGRAPHS_INIT FALSE +#ifndef EXPAND_PRAGMA +#define EXPAND_PRAGMA FALSE +#endif +#define OK_UCN TRUE +#define OK_MBIDENT FALSE + +#if HAVE_INTMAX_T +#if HAVE_STDINT_H +#include "stdint.h" +#elif HAVE_INTTYPES_H +#include "inttypes.h" +#endif +typedef intmax_t expr_t; +typedef uintmax_t uexpr_t; +#else +#if HAVE_LONG_LONG +#if (HOST_COMPILER == MSC && _MSC_VER < 1500) || HOST_COMPILER == BORLANDC +typedef __int64 expr_t; +typedef unsigned __int64 uexpr_t; +#else +typedef long long expr_t; +typedef unsigned long long uexpr_t; +#endif +#else /* !HAVE_LONG_LONG */ +typedef unsigned long uexpr_t; +typedef long expr_t; +#endif /* HAVE_LONG_LONG */ +#endif /* HAVE_INTMAX_T */ + +#if HAVE_INTMAX_T +#define EXPR_MAX UINTMAX_MAX +#elif HAVE_LONG_LONG +#if (HOST_COMPILER == MSC && _MSC_VER < 1400) || HOST_COMPILER == BORLANDC +#define EXPR_MAX 0xFFFFFFFFFFFFFFFFui64 +#else +#define EXPR_MAX 0xFFFFFFFFFFFFFFFFULL +#endif +#else +#define EXPR_MAX 4294967295UL +#endif + +/* + * Translation limits. + * The following definitions are used to allocate memory for work buffers. + * + * NWORK Output buffer size. Set this size according to your compiler- + * proper. Length of string literal should be less than NWORK + * - 1. + * Nevertheless, when COMPILER == GNUC || COMPILER == MSC, mcpp + * uses NMACWORK as output buffer size because GNUC and Visual C + * can accept very long line. + * NBUFF Input buffer size after line concatenation by + * . + * NMACWORK Internal work buffer size for macro definition and expansion. + * IDMAX The longest identifier length. + * NMACPARS The maximum number of #define parameters. + * NOTE: Must be NMACPARS <= UCHARMAX. + * NEXP The maximum nesting depth of #if expressions. + * BLK_NEST The number of nested #if's permitted. + * INCLUDE_NEST The maximum nesting depth of #include. This is needed to + * prevent infinite recursive inclusion. + * RESCAN_LIMIT The maximum rescan times of macro expansion in STD or POST_STD + * modes. + * PRESTD_RESCAN_LIMIT The maximum rescan times of macro expansion in KR or + * OLD_PREP modes.. + * + * NBUFF should not be smaller than NWORK. + * NMACWORK should not be smaller than NWORK * 2. + * + * SBSIZE defines the number of hash-table slots for the macro symbol table. + * It must be a power of 2. + * + * MKDEP_INIT The initial maximum number of filenames in a dependency line + * of output of -M* option. The maximum number is dynamically + * enlarged in execution. + */ + +#ifndef IDMAX +#define IDMAX 0x400 +#endif +#ifndef NMACPARS +#define NMACPARS 0xFF +#endif +#ifndef NEXP +#define NEXP 0x100 +#endif +#ifndef BLK_NEST +#define BLK_NEST 0x100 +#endif +#ifndef INCLUDE_NEST +#define INCLUDE_NEST 0x100 +#endif +#ifndef RESCAN_LIMIT +#define RESCAN_LIMIT 0x40 +#endif +#ifndef PRESTD_RESCAN_LIMIT +#define PRESTD_RESCAN_LIMIT 0x100 +#endif +#ifndef NBUFF +#define NBUFF 0x10000 /* Must be NWORK <= NBUFF */ +#endif +#ifndef NWORK +#define NWORK NBUFF /* 0x1000, 0x4000, 0x10000, .. */ +#endif +#ifndef NMACWORK +#define NMACWORK (NWORK * 4) /* Must be NWORK * 2 <= NMACWORK */ +#endif +#ifndef SBSIZE +#define SBSIZE 0x400 +#endif +#ifndef MKDEP_INIT +#define MKDEP_INIT 0x100 +#endif + +#if UCHARMAX < NMACPARS + #error "NMACPARS should not be greater than UCHARMAX" +#endif + +#if NBUFF < NWORK + #error "NBUFF must be same or greater than NWORK" +#endif +#if NMACWORK < NWORK * 2 + #error "NMACWORK must be same or greater than NWORK * 2" +#endif + +#define SBMASK (SBSIZE - 1) +#if (SBSIZE ^ SBMASK) != ((SBSIZE * 2) - 1) + #error "SBSIZE must be a power of 2 !" +#endif + +/* + * Translation limits required by the Standard. + * + * *90MIN limits specified by C90. + * *99MIN limits specified by C99. + * *_CPLUS_MIN limits recommended by C++ (ISO 1998/07 Standard). + * + * SLEN*MIN Characters in a logical source line + * and characters in a string literal or wide string literal + * (after concatenation). + * IDLEN*MIN Significant initial characters in an internal identifier + * or a macro name. + * NMACPARS*MIN Parameters in one macro definition. + * Arguments in one macro invocation. + * EXP_NEST*MIN Nesting levels of parenthesized expressions in a full + * expression. + * BLK_NEST*MIN Nesting levels of conditional inclusion. + * INCLUDE_NEST*MIN Nesting levels for #include files. + * NMACRO*MIN Macro identifiers simultaneously defined in one translation + * unit. + */ +#define SLEN90MIN 0x1FD +#define IDLEN90MIN 0x1F +#define NMACPARS90MIN 0x1F +#define EXP_NEST90MIN 0x20 +#define BLK_NEST90MIN 8 +#define INCLUDE_NEST90MIN 8 +#define NMACRO90MIN 0x400 + +#define SLEN99MIN 0xFFF +#define IDLEN99MIN 0x3F +#define NMACPARS99MIN 0x7F +#define EXP_NEST99MIN 0x3F +#define BLK_NEST99MIN 0x3F +#define INCLUDE_NEST99MIN 0xF +#define NMACRO99MIN 0xFFF + +#define SLEN_CPLUS_MIN 0x10000 +#define IDLEN_CPLUS_MIN 0x400 +#define NMACPARS_CPLUS_MIN 0x100 +#define EXP_NEST_CPLUS_MIN 0x100 +#define BLK_NEST_CPLUS_MIN 0x100 +#define INCLUDE_NEST_CPLUS_MIN 0x100 +#define NMACRO_CPLUS_MIN 0x10000 + +/* LINE99LIMIT means the line number limit of C99 */ +#define LINE99LIMIT 0x7FFFFFFF + +/* + * STDC This macro is used for the predefined __STDC__. + * STDC_VERSION is used for the value of __STDC_VERSION__. + * STDC_HOSTED is used for the value of __STDC_HOSTED__. + */ +#if IDMAX < IDLEN90MIN || NBUFF < SLEN90MIN + 3 + || NWORK < SLEN90MIN + 2 || NMACPARS < NMACPARS90MIN + || NEXP < EXP_NEST90MIN || BLK_NEST < BLK_NEST90MIN +#define STDC 0 +#endif +#ifndef STDC +#define STDC 1 /* 1 : for ISO 9899:1990 or later */ +#endif + +#ifndef STDC_VERSION +#define STDC_VERSION 0L /* 199409L : For conforming + implementation to ISO 9899:1990 / Amendment 1:1995 + 199901L : For C99 */ +#endif +#ifndef STDC_HOSTED +#define STDC_HOSTED 1 /* 1 : for hosted implementation, + 0 : for free-standing implementation (C99 specification) */ +#endif + +/* + * CPLUS specifies the default value of the pre-defined macro __cplusplus + * for C++ processing. + * The value can be changed by -V option. + */ +#define CPLUS 1 /* 199711L for C++ Standard */ + diff --git a/lib/mcpp/system.c b/lib/mcpp/system.c new file mode 100644 index 000000000..b0fcce3da --- /dev/null +++ b/lib/mcpp/system.c @@ -0,0 +1,4940 @@ +/*- + * Copyright (c) 1998, 2002-2008 Kiyoshi Matsui + * All rights reserved. + * + * Some parts of this code are derived from the public domain software + * DECUS cpp (1984,1985) written by Martin Minow. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * 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 AUTHOR 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. + */ + +/* + * S Y S T E M . C + * S y s t e m D e p e n d e n t R o u t i n e s + * + * Routines dependent on O.S., compiler or compiler-driver. + * To port MCPP for the systems not yet ported, you must + * 1. specify the constants in "configed.H" or "noconfig.H", + * 2. append the system-dependent routines in this file. + */ +#if PREPROCESSED +#include "mcpp.H" +#else +#include "system.H" +#include "internal.H" +#endif + +#if HOST_SYS_FAMILY == SYS_UNIX +#include "unistd.h" /* For getcwd(), readlink() */ +#elif HOST_COMPILER == MSC || HOST_COMPILER == LCC +#include "direct.h" +#define getcwd( buf, size) _getcwd( buf, size) +#elif HOST_COMPILER == BORLANDC +#include "dir.h" +#endif + +#include "sys/types.h" +#include "sys/stat.h" /* For stat() */ +#if ! defined( S_ISREG) +#define S_ISREG( mode) (mode & S_IFREG) +#define S_ISDIR( mode) (mode & S_IFDIR) +#endif +#if HOST_COMPILER == MSC +#define S_IFREG _S_IFREG +#define S_IFDIR _S_IFDIR +#define stat( path, stbuf) _stat( path, stbuf) +#endif + +/* Function to compare path-list */ +#if FNAME_FOLD +#if HOST_COMPILER == GNUC /* CYGWIN, MINGW, MAC */ +#include /* POSIX 1, 2001 */ +#define str_case_eq( str1, str2) (strcasecmp( str1, str2) == 0) +#else /* MSC, BORLANDC, LCC */ +#if HOST_COMPILER == MSC +#define stricmp( str1, str2) _stricmp( str1, str2) +#endif +#define str_case_eq( str1, str2) (stricmp( str1, str2) == 0) +#endif +#else /* ! FNAME_FOLD */ +#define str_case_eq( str1, str2) (strcmp( str1, str2) == 0) +#endif + +/* + * PATH_DELIM is defined for the O.S. which has single byte path-delimiter. + * Note: '\\' or any other character identical to second byte of MBCHAR should + * not be used for PATH_DELIM for convenience of path-list parsing. + */ +#if SYS_FAMILY == SYS_UNIX || SYS_FAMILY == SYS_WIN || SYSTEM == SYS_UNKNOWN +#define PATH_DELIM '/' +#define SPECIAL_PATH_DELIM FALSE +#else /* Any other path-delimiter, define PATH_DELIM by yourself */ +#define SPECIAL_PATH_DELIM TRUE /* Any path-delimiter other than '/' */ +#endif + +/* + * OBJEXT is the suffix to denote "object" file. + */ +#ifndef OBJEXT +#if SYS_FAMILY == SYS_UNIX || HOST_COMPILER == GNUC +#define OBJEXT "o" +#elif SYS_FAMILY == SYS_WIN +#define OBJEXT "obj" +#elif 1 +/* Add here appropriate definitions for other systems. */ +#endif +#endif + +static void version( void); + /* Print version message */ +static void usage( int opt); + /* Putout usage of MCPP */ +static void set_opt_list( char * optlist); + /* Set list of legal option chars */ +static int parse_warn_level( const char * mcpp_optarg, int opt); + /* Parse warning level option */ +static void def_a_macro( int opt, char * def); + /* Do a -D option */ +static void chk_opts( int sflag, int trad); + /* Check consistency of options */ +#if COMPILER != GNUC +static void init_cpu_macro( int gval, int sse); + /* Predefine CPU-dependent macros */ +#endif +static void init_predefines( void); + /* Set and unset predefined macros */ +static void init_std_defines( void); + /* Predefine Standard macros */ +static void set_limit( void); + /* Set minimum translation limits */ +static void set_pragma_op( void); + /* Set the _Pragma() operator */ +static void put_info( FILEINFO * sharp_file); + /* Print compiler-specific-inf */ +static char * set_files( int argc, char ** argv, char ** in_pp + , char ** out_pp); + /* Set input, output, diagnostic */ +static void set_sys_dirs( int set_cplus_dir); + /* Set system-specific include dirs */ +static void set_env_dirs( void); + /* Set user-defined include dirs */ +static void parse_env( const char * env); + /* Parse environment variables */ +static void set_a_dir( const char * dirname); + /* Append an include directory */ +static char * norm_dir( const char * dirname, int framework); + /* Normalize include directory path */ +static char * norm_path( const char * dir, const char * fname, int inf + , int hmap); /* Normalize pathname to compare */ +#if SYS_FAMILY == SYS_UNIX +static void deref_syml( char * slbuf1, char * slbuf2, char * chk_start); + /* Dereference symbolic linked directory and file */ +#endif +#if COMPILER == GNUC +static void init_gcc_macro( void); + /* Predefine GCC-specific macros */ +static void chk_env( void); + /* Check the environment variables */ +#elif COMPILER == MSC +static void init_msc_macro( void); + /* Predefine Visual C-specific macros */ +#endif +static void def_macros( void); + /* Define macros specified by -D */ +static void undef_macros( void); + /* Undefine macros specified by -U */ +static char * md_init( const char * filename, char * output); + /* Initialize makefile dependency */ +static char * md_quote( char * output); + /* 'Quote' special characters */ +static int open_include( char * filename, int searchlocal, int next); + /* Open the file to include */ +static int has_directory( const char * source, char * directory); + /* Get directory part of fname */ +static int is_full_path( const char * path); + /* The path is absolute path list ? */ +static int search_dir( char * filename, int searchlocal, int next); + /* Search the include directories */ +static int open_file( const char ** dirp, const char * src_dir + , const char * filename, int local, int include_opt, int sys_frame); + /* Open a source file */ +static const char * set_fname( const char * filename); + /* Remember the source filename */ +#if SYSTEM == SYS_MAC +#if COMPILER == GNUC +static char * search_header_map( const char * hmap_file + , const char * filename, char * pathlist); + /* Search header map file for a header */ +static unsigned hmap_hash( const char * fname); + /* Get hash value for the fname */ +#endif +static void init_framework( void); + /* Initialize framework[] */ +static int search_framework( char * filename); + /* Search "Framework" directories */ +static int search_subdir( char * fullname, char * cp, char * frame + , char * fname, int sys_frame); + /* Search "Headers" and other dirs */ +#endif /* SYSTEM == SYS_MAC */ +#if 0 /* This function is only for debugging use */ +static int chk_dirp( const char ** dirp); + /* Check validity of dirp arg for open_file() */ +#endif +static void cur_file( FILEINFO * file, FILEINFO * sharp_file, int marker); + /* Output current source file name */ +#if SYS_FAMILY == SYS_WIN +static char * bsl2sl( char * filename); + /* Convert \ to / in path-list */ +#endif +static int is_junk( void); + /* The directive has trailing junk? */ +static void do_once( const char * fullname); + /* Process #pragma once */ +static int included( const char * fullname); + /* The file has been once included? */ +static void push_or_pop( int direction); + /* Push or pop a macro definition */ +static int do_prestd_directive( void); + /* Process pre-Standard directives */ +static void do_preprocessed( void); + /* Process preprocessed file */ +static int do_debug( int set); + /* #pragma MCPP debug, #debug */ +static void dump_path( void); + /* Print include search path */ +static void do_asm( int asm_start); + /* Process #asm, #endasm */ +static int mcpp_getopt( int argc, char * const * argv, const char * opts); + /* getopt() to prevent linking of glibc getopt */ + +/* for mcpp_getopt() */ +static int mcpp_optind = 1; +static int mcpp_opterr = 1; +static int mcpp_optopt; +static char * mcpp_optarg; + +static int mb_changed = FALSE; /* Flag of -e option */ +static char cur_work_dir[ PATHMAX + 1]; /* Current working directory*/ + +/* + * incdir[] stores the -I directories (and the system-specific #include <...> + * directories). This is set by set_a_dir(). A trailing PATH_DELIM is + * appended if absent. + */ +static const char ** incdir; /* Include directories */ +static const char ** incend; /* -> active end of incdir */ +static int max_inc; /* Number of incdir[] */ + +typedef struct inc_list { /* List of directories or files */ + char * name; /* Filename or directory-name */ + size_t len; /* Length of 'name' */ +} INC_LIST; + +/* + * fnamelist[] stores the souce file names opened by #include directive for + * debugging information. + */ +static INC_LIST * fnamelist; /* Source file names */ +static INC_LIST * fname_end; /* -> active end of fnamelist */ +static int max_fnamelist; /* Number of fnamelist[] */ + +/* once_list[] stores the #pragma once file names. */ +static INC_LIST * once_list; /* Once opened file */ +static INC_LIST * once_end; /* -> active end of once_list */ +static int max_once; /* Number of once_list[] */ + +#define INIT_NUM_INCLUDE 32 /* Initial number of incdir[] */ +#define INIT_NUM_FNAMELIST 256 /* Initial number of fnamelist[] */ +#define INIT_NUM_ONCE 64 /* Initial number of once_list[] */ + +/* + * 'search_rule' holds searching rule of #include "header.h" to search first + * before searching user specified or system-specific include directories. + * 'search_rule' is initialized to SEARCH_INIT. It can be changed by -I1, -I2 + * or -I3 option. -I1 specifies CURRENT, -I2 SOURCE and -I3 both. + */ + +static int search_rule = SEARCH_INIT; /* Rule to search include file */ + +static int nflag = FALSE; /* Flag of -N (-undef) option */ +static long std_val = -1L; /* Value of __STDC_VERSION__ or __cplusplus */ + +#define MAX_DEF 256 +#define MAX_UNDEF (MAX_DEF/4) +static char * def_list[ MAX_DEF]; /* Macros to be defined */ +static char * undef_list[ MAX_UNDEF]; /* Macros to be undefined */ +static int def_cnt; /* Count of def_list */ +static int undef_cnt; /* Count of undef_list */ + +/* Values of mkdep. */ +#define MD_MKDEP 1 /* Output source file dependency line */ +#define MD_SYSHEADER 2 /* Print also system-header names */ +#define MD_FILE 4 /* Output to the file named *.d */ +#define MD_PHONY 8 /* Print also phony targets for each header */ +#define MD_QUOTE 16 /* 'Quote' $ and space in target name */ + +static FILE * mkdep_fp; /* For -Mx option */ +static char * mkdep_target; + /* For -MT TARGET option and for GCC's queer environment variables. */ +static char * mkdep_mf; /* Argument of -MF option */ +static char * mkdep_md; /* Argument of -MD option */ +static char * mkdep_mq; /* Argument of -MQ option */ +static char * mkdep_mt; /* Argument of -MT option */ + +/* sharp_filename is filename for #line line, used only in cur_file() */ +static char * sharp_filename = NULL; +static char * argv0; /* argv[ 0] for usage() and version() */ +static int ansi; /* __STRICT_ANSI__ flag for GNUC */ +static int compat_mode; + /* "Compatible" mode of recursive macro expansion */ +#define MAX_ARCH_LEN 16 +static char arch[ MAX_ARCH_LEN]; /* -arch or -m64, -m32 options */ + +#if COMPILER == GNUC +#define N_QUOTE_DIR 8 +/* quote_dir[]: Include directories for "header" specified by -iquote */ +/* quote_dir_end: Active end of quote_dir */ +static const char * quote_dir[ N_QUOTE_DIR]; +static const char ** quote_dir_end = quote_dir; +/* sys_dirp indicates the first directory to search for system headers. */ +static const char ** sys_dirp = NULL; /* System header directory */ +static const char * sysroot = NULL; /* Logical root directory of header */ +static int i_split = FALSE; /* For -I- option */ +static int gcc_work_dir = FALSE; /* For -fworking-directory */ +static int gcc_maj_ver; /* __GNUC__ */ +static int gcc_min_ver; /* __GNUC_MINOR__ */ +static int dDflag = FALSE; /* Flag of -dD option */ +static int dMflag = FALSE; /* Flag of -dM option */ +#endif + +#if COMPILER == GNUC || COMPILER == MSC +/* + * preinclude points to the file specified by -include (-Fl for MSC) option, + * which is included prior to the main input file. + */ +#define NPREINCLUDE 8 +static char * preinclude[ NPREINCLUDE]; /* File to pre-include */ +static char ** preinc_end = preinclude; /* -> active end of preinclude */ +#endif + +#if COMPILER == MSC +static int wchar_t_modified = FALSE; /* -Zc:wchar_t flag */ +#endif + +#if COMPILER == LCC +static const char * optim_name = "__LCCOPTIMLEVEL"; +#endif + +#if SYSTEM == SYS_CYGWIN +static int no_cygwin = FALSE; /* -mno-cygwin */ + +#elif SYSTEM == SYS_MAC +#define MAX_FRAMEWORK 8 +static char * framework[ MAX_FRAMEWORK]; /* Framework directories*/ +static int num_framework; /* Current number of framework[] */ +static int sys_framework; /* System framework dir */ +static const char ** to_search_framework; + /* Search framework[] next to the directory */ +static int in_import; /* #import rather than #include */ +#endif + +#define NO_DIR FALSE +#if NO_DIR +/* Unofficial feature to strip directory part of include file */ +static int no_dir; +#endif + +#if MCPP_LIB +void init_system( void) +/* Initialize static variables */ +{ + if (sharp_filename) + free( sharp_filename); + sharp_filename = NULL; + incend = incdir = NULL; + fnamelist = once_list = NULL; + search_rule = SEARCH_INIT; + mb_changed = nflag = ansi = compat_mode = FALSE; + mkdep_fp = NULL; + mkdep_target = mkdep_mf = mkdep_md = mkdep_mq = mkdep_mt = NULL; + std_val = -1L; + def_cnt = undef_cnt = 0; + mcpp_optind = mcpp_opterr = 1; +#if COMPILER == GNUC + sys_dirp = NULL; + sysroot = NULL; + gcc_work_dir = i_split = FALSE; + quote_dir_end = quote_dir; + dDflag = dMflag = FALSE; +#endif +#if COMPILER == MSC + wchar_t_modified = FALSE; +#endif +#if COMPILER == GNUC || COMPILER == MSC + preinc_end = preinclude; +#endif +#if SYSTEM == SYS_CYGWIN + no_cygwin = FALSE; +#elif SYSTEM == SYS_MAC + num_framework = sys_framework = 0; + to_search_framework = NULL; +#endif +#if NO_DIR + no_dir = FALSE; +#endif +} + +#endif + +#define OPTLISTLEN 80 + +void do_options( + int argc, + char ** argv, + char ** in_pp, /* Input file name */ + char ** out_pp /* Output file name */ +) +/* + * Process command line arguments, called only at MCPP startup. + */ +{ + char optlist[ OPTLISTLEN]; /* List of option letter*/ + const char * warning = "warning: -%c%s option is ignored\n"; + int opt; + int unset_sys_dirs; + /* Unset system-specific and site-specific include directories ? */ + int set_cplus_dir; /* Set C++ include directory ? (for GCC)*/ + int show_path; /* Show include directory list */ + DEFBUF * defp; + VAL_SIGN * valp; + int sflag; /* -S option or similar */ + int trad; /* -traditional */ + int old_mode; /* backup of 'mcpp_mode'*/ + int gval, sse; + char * cp; + int i; +#if COMPILER == GNUC +#define NSYSDIR 8 + /* System include directory specified by -isystem */ + char * sysdir[ NSYSDIR] = { NULL, }; + char ** sysdir_end = sysdir; + int integrated_cpp; /* Flag of cc1 which integrates cpp in it */ +#elif COMPILER == LCC + const char * debug_name = "__LCCDEBUGLEVEL"; +#endif + + argv0 = argv[ 0]; + nflag = unset_sys_dirs = show_path = sflag = trad = FALSE; + arch[ 0] = 0; + gval = sse = 0; + set_cplus_dir = TRUE; + + /* Get current directory for -I option and #pragma once */ + getcwd( cur_work_dir, PATHMAX); +#if SYS_FAMILY == SYS_WIN + bsl2sl( cur_work_dir); +#endif + sprintf( cur_work_dir + strlen( cur_work_dir), "%c%c", PATH_DELIM, EOS); + /* Append trailing path-delimiter */ + +#if COMPILER == GNUC + defp = look_id( "__GNUC__"); /* Already defined by init_defines() */ + gcc_maj_ver = atoi( defp->repl); + defp = look_id( "__GNUC_MINOR__"); + gcc_min_ver = atoi( defp->repl); + integrated_cpp = ((gcc_maj_ver == 3 && gcc_min_ver >= 3) + || gcc_maj_ver == 4); +#endif +#if COMPILER == GNUC || COMPILER == MSC + option_flags.dollar_in_name = TRUE; + /* GCC and Visual C allows '$' in name by default */ +#endif + + set_opt_list( optlist); + +opt_search: ; + while (mcpp_optind < argc + && (opt = mcpp_getopt( argc, argv, optlist)) != EOF) { + + switch (opt) { /* Command line option character */ + +#if COMPILER == GNUC + case '$': /* Forbid '$' in identifier */ + option_flags.dollar_in_name = FALSE; + break; +#endif + + case '+': +#if COMPILER == GNUC +plus: +#endif + if (cplus_val || sflag) { + mcpp_fputs( "warning: -+ option is ignored\n", ERR); + break; + } + cplus_val = CPLUS; + break; +#if COMPILER == GNUC + case '-': + if (memcmp( mcpp_optarg, "sysroot", 7) == 0) { + if (mcpp_optarg[ 7] == '=') /* --sysroot=DIR */ + sysroot = mcpp_optarg + 8; + else if (mcpp_optarg[ 7] == EOS) /* --sysroot DIR */ + sysroot = argv[ mcpp_optind++]; + else + usage( opt); + break; + } else { + usage( opt); + } +#endif + case '2': /* Reverse digraphs recognition */ + option_flags.dig = ! option_flags.dig; + break; + case '3': /* Reverse trigraph recogniion */ + option_flags.trig = ! option_flags.trig; + break; + + case '@': /* Special preprocessing mode */ + old_mode = mcpp_mode; + if (str_eq( mcpp_optarg, "post") + || str_eq( mcpp_optarg, "poststd")) + mcpp_mode = POST_STD; /* 'post-Standard' mode */ + else if (str_eq( mcpp_optarg, "old") + || str_eq( mcpp_optarg, "oldprep")) + mcpp_mode = OLD_PREP; /* 'old-Preprocessor' mode */ + else if (str_eq( mcpp_optarg, "kr")) + mcpp_mode = KR; /* 'K&R 1st' mode */ + else if (str_eq( mcpp_optarg, "std")) + mcpp_mode = STD; /* 'Standard' mode (default)*/ + else if (str_eq( mcpp_optarg, "compat")) { + compat_mode = TRUE; /* 'compatible' mode */ + mcpp_mode = STD; + } + else + usage( opt); + standard = (mcpp_mode == STD || mcpp_mode == POST_STD); + if (old_mode != STD && old_mode != mcpp_mode) + mcpp_fprintf( ERR, "Mode is redefined to: %s\n", mcpp_optarg); + break; + +#if COMPILER == GNUC + case 'A': /* Ignore -A system(gnu), -A cpu(vax) or so */ + break; + case 'a': + if (str_eq( mcpp_optarg, "nsi")) { /* -ansi */ + look_and_install( "__STRICT_ANSI__", DEF_NOARGS_PREDEF, null + , "1"); + ansi = TRUE; + break; + } else if (memcmp( mcpp_optarg, "uxbase", 6) == 0) { + mcpp_optind++; + break; /* Ignore '-auxbase some' or such nonsence */ +#if SYSTEM == SYS_MAC + } else if (str_eq( mcpp_optarg, "rch")) { /* -arch */ + strcpy( arch, argv[ mcpp_optind++]); + if (str_eq( arch, "ppc") || str_eq( arch, "ppc7400") + || str_eq( arch, "ppc64") + || str_eq( arch, "i386") || str_eq( arch, "i686") + || str_eq( arch, "x86_64") || str_eq( arch, "amd64")) { + if (str_eq( arch, "i686")) + strcpy( arch, "i386"); + else if (str_eq( arch, "amd64")) + strcpy( arch, "x86_64"); + else if (str_eq( arch, "ppc7400")) + strcpy( arch, "ppc"); + break; + } /* Else usage() */ +#endif + } + usage( opt); +#elif COMPILER == MSC + case 'a': + if (memcmp( mcpp_optarg, "rch", 3) == 0) { + if (str_eq( mcpp_optarg + 3, ":SSE") /* -arch:SSE */ + || str_eq( mcpp_optarg + 3, ":sse")) + sse = 1; + else if (str_eq( mcpp_optarg + 3, ":SSE2") /* -arch:SSE2 */ + || str_eq( mcpp_optarg + 3, ":sse2")) + sse = 2; + /* Else ignore */ + } else { + usage( opt); + } + break; + + case 'A': + option_flags.lang_asm = TRUE; /* "assembler" source */ + break; +#else + case 'a': + option_flags.lang_asm = TRUE; /* "assembler" source */ + break; +#endif + +#if ! STD_LINE_PREFIX + case 'b': + std_line_prefix = TRUE; /* Putout line and file infor- */ + break; /* mation in C source style. */ +#endif + + case 'C': /* Keep comments */ + option_flags.c = TRUE; + break; + +#if COMPILER == GNUC + case 'c': + if (! integrated_cpp) + usage( opt); + break; /* Else ignore this option */ + case 'd': + if (str_eq( mcpp_optarg, "M")) { /* -dM */ + dMflag = TRUE; + no_output++; + } else if (str_eq( mcpp_optarg, "D")) { /* -dD */ + dDflag = TRUE; + } else if (str_eq( mcpp_optarg, "igraphs")) { /* -digraphs */ + option_flags.dig = TRUE; + } else if (str_eq( mcpp_optarg, "umpbase")) { /* -dumpbase */ + ; /* Ignore */ + } else { + usage( opt); + } + break; +#endif /* COMPILER == GNUC */ + + case 'D': /* Define symbol */ + if (def_cnt >= MAX_DEF) { + mcpp_fputs( "Too many -D options.\n", ERR); + longjmp( error_exit, -1); + } + def_list[ def_cnt++] = mcpp_optarg; + break; + + case 'e': + /* Change the default MBCHAR encoding */ + if (set_encoding( mcpp_optarg, FALSE, 0) == NULL) + usage( opt); + mb_changed = TRUE; + break; + +#if COMPILER == GNUC + case 'E': + if (! integrated_cpp) + usage( opt); + break; /* Ignore this option */ + case 'f': + if (memcmp( mcpp_optarg, "input-charset=", 14) == 0) { + /* Treat -finput-charset= as the same option as -e */ + if (set_encoding( mcpp_optarg + 14, FALSE, 0) == NULL) + usage( opt); + mb_changed = TRUE; + } else if (str_eq( mcpp_optarg, "working-directory")) { + gcc_work_dir = TRUE; + } else if (str_eq( mcpp_optarg, "no-working-directory")) { + gcc_work_dir = FALSE; + } else if (str_eq( mcpp_optarg, "stack-protector")) { + look_and_install( "__SSP__", DEF_NOARGS_PREDEF, null, "1"); + } else if (str_eq( mcpp_optarg, "stack-protector-all")) { + look_and_install( "__SSP_ALL__", DEF_NOARGS_PREDEF, null, "2"); + } else if (str_eq( mcpp_optarg, "exceptions")) { + look_and_install( "__EXCEPTIONS", DEF_NOARGS_PREDEF, null + , "1"); + } else if (str_eq( mcpp_optarg, "no-exceptions")) { + undef_list[ undef_cnt++] = "__EXCEPTIONS"; + } else if (str_eq( mcpp_optarg, "PIC") + || str_eq( mcpp_optarg, "pic") + || str_eq( mcpp_optarg, "PIE") + || str_eq( mcpp_optarg, "pie")) { + look_and_install( "__PIC__", DEF_NOARGS_PREDEF, null, "1"); + look_and_install( "__pic__", DEF_NOARGS_PREDEF, null, "1"); + } else if (str_eq( mcpp_optarg, "no-dollars-in-identifiers")) { + option_flags.dollar_in_name = FALSE; + } else if (str_eq( mcpp_optarg, "no-show-column")) { + ; /* Ignore this option */ + } else if (! integrated_cpp) { + usage( opt); + } + break; + + case 'g': + if (!isdigit( *mcpp_optarg) + && str_eq( argv[ mcpp_optind - 2], "-g")) + /* Neither '-g 0' nor '-ggdb' -- No argument */ + mcpp_optind--; + break; /* Ignore the option */ +#elif COMPILER == LCC + case 'g': /* Define __LCCDEBUGLEVEL as */ + if (*(mcpp_optarg + 1) == EOS && isdigit( *mcpp_optarg)) { + defp = look_id( debug_name); + strcpy( defp->repl, mcpp_optarg); + } else { + usage( opt); + } + break; +#elif COMPILER == MSC + case 'G': + if (*(mcpp_optarg + 1) == EOS) { /* -Gx */ + switch (*mcpp_optarg) { + case '3': case '4': case '5': case '6': + gval = *mcpp_optarg; + break; + case 'B': /* -GB */ + gval = '6'; + break; + case 'R': + look_and_install( "_CPPRTTI", DEF_NOARGS_PREDEF, null + , "1"); + break; + case 'X': + look_and_install( "_CPPUNWIND", DEF_NOARGS_PREDEF, null + , "1"); + break; + case 'Z': + look_and_install( "__MSVC_RUNTIME_CHECKS" + , DEF_NOARGS_PREDEF, null, "1"); + break; + default : + mcpp_fprintf( ERR, warning, opt, mcpp_optarg); + } + } else { + usage( opt); + } + break; +#endif + +#if SYSTEM == SYS_MAC + case 'F': + framework[ num_framework++] = mcpp_optarg; + break; +#endif + + case 'h': + if (*(mcpp_optarg + 1) == EOS && isdigit( *mcpp_optarg)) + /* a digit */ + look_and_install( "__STDC_HOSTED__", DEF_NOARGS_PREDEF, null + , mcpp_optarg); + else + usage( opt); + break; + +#if COMPILER == MSC + case 'X': + unset_sys_dirs = TRUE; + break; +#endif + case 'I': /* Include directory */ + if (str_eq( mcpp_optarg, "-")) { /* -I- */ +#if COMPILER == GNUC + sys_dirp = incend; /* Split include directories */ + i_split = TRUE; +#else + unset_sys_dirs = TRUE; + /* Unset pre-specified include directories */ +#endif + } else if (*(mcpp_optarg + 1) == EOS && isdigit( *mcpp_optarg) + && (i = *mcpp_optarg - '0') != 0 + && (i & ~(CURRENT | SOURCE)) == 0) { + search_rule = i; /* -I1, -I2 or -I3 */ + } else { /* Not '-' nor a digit */ + set_a_dir( mcpp_optarg); /* User-defined dir */ + } + break; + +#if COMPILER == MSC + case 'F': + if (str_eq( mcpp_optarg, "l")) { /* -Fl */ + if (preinc_end >= &preinclude[ NPREINCLUDE]) { + mcpp_fputs( "Too many -Fl options.\n", ERR); + longjmp( error_exit, -1); + } + *preinc_end++ = argv[ mcpp_optind++]; + } else { + usage( opt); + } + break; +#endif + +#if COMPILER == GNUC + case 'i': + if (str_eq( mcpp_optarg, "nclude")) { /* -include */ + if (preinc_end >= &preinclude[ NPREINCLUDE]) { + mcpp_fputs( "Too many -include options.\n", ERR); + longjmp( error_exit, -1); + } + *preinc_end++ = argv[ mcpp_optind++]; + } else if (str_eq( mcpp_optarg, "system")) { /* -isystem */ + if (sysdir_end >= &sysdir[ NSYSDIR]) { + mcpp_fputs( "Too many -isystem options.\n", ERR); + longjmp( error_exit, -1); + } + *sysdir_end++ = argv[ mcpp_optind++]; + /* Add the directory before system include directory*/ + } else if (str_eq( mcpp_optarg, "quote")) { /* -iquote */ + if (quote_dir_end >= "e_dir[ N_QUOTE_DIR]) { + mcpp_fputs( "Too many -iquote options.\n", ERR); + longjmp( error_exit, -1); + } + *quote_dir_end++ = argv[ mcpp_optind++]; + /* Add the directory for #include "header" */ + } else if (memcmp( mcpp_optarg, "sysroot", 7) == 0) { + if (mcpp_optarg[ 7] == '=') /* -isysroot=DIR */ + sysroot = mcpp_optarg + 8; + else if (mcpp_optarg[ 7] == EOS) /* -isysroot DIR */ + sysroot = argv[ mcpp_optind++]; + else + usage( opt); + } else if (str_eq( mcpp_optarg, "prefix") /* -iprefix */ + || str_eq( mcpp_optarg, "withprefix") /* -iwithprefix */ + || str_eq( mcpp_optarg, "withprefixbefore") + /* -iwithprefixbefore */ + || str_eq( mcpp_optarg, "dirafter") /* -idirafter */ + || str_eq( mcpp_optarg, "multilib")) { /* -imultilib */ + mcpp_optind++; /* Skip the argument */ + /* Ignore these options */ + } else { + usage( opt); + } + break; +#endif + + case 'j': + option_flags.no_source_line = TRUE; + break; /* Do not output the source line in diagnostics */ + +#if COMPILER == MSC + case 'J': + look_and_install( "_CHAR_UNSIGNED", DEF_NOARGS_PREDEF, null, "1"); + break; +#endif + + case 'K': + mcpp_debug |= MACRO_CALL; + /* + * Putout macro expansion informations embedded in comments. + * Same with '#pragma MCPP debug macro_call'. + */ + /* Enable white spaces preservation, too */ + /* Fall through */ + case 'k': + option_flags.k = TRUE; + /* Keep white spaces of input lines as they are */ + break; + +#if COMPILER == GNUC + case 'l': + if (memcmp( mcpp_optarg, "ang-", 4) != 0) { + usage( opt); + } else if (str_eq( mcpp_optarg + 4, "c")) { /* -lang-c */ + ; /* Ignore this option */ + } else if (str_eq( mcpp_optarg + 4, "c99") /* -lang-c99*/ + || str_eq( mcpp_optarg + 4, "c9x")) { /* -lang-c9x*/ + if (! sflag) { + stdc_val = 1; /* Define __STDC__ to 1 */ + std_val = 199901L; + sflag = TRUE; + } + } else if (str_eq( mcpp_optarg + 4, "c89")) { /* -lang-c89*/ + if (! sflag) { + stdc_val = 1; /* Define __STDC__ to 1 */ + sflag = TRUE; + } + } else if (str_eq( mcpp_optarg + 4, "c++")) { /* -lang-c++*/ + goto plus; + } else if (str_eq( mcpp_optarg + 4, "asm")) { /* -lang-asm*/ + option_flags.lang_asm = TRUE; + } else { + usage( opt); + } + break; +#endif /* COMPILER == GNUC */ + + case 'M': /* Output source file dependency line */ + if (str_eq( mcpp_optarg, "M")) { /* -MM */ + ; + } else if (str_eq( mcpp_optarg, "D")) { /* -MD */ + mkdep |= (MD_SYSHEADER | MD_FILE); + } else if (str_eq( mcpp_optarg, "MD")) { /* -MMD */ + mkdep |= MD_FILE; + } else if (str_eq( mcpp_optarg, "P")) { /* -MP */ + mkdep |= MD_PHONY; + } else if (str_eq( mcpp_optarg, "Q")) { /* -MQ target */ + mkdep |= MD_QUOTE; + mkdep_mq = argv[ mcpp_optind++]; + } else if (str_eq( mcpp_optarg, "T")) { /* -MT target */ + mkdep_mt = argv[ mcpp_optind++]; + } else if (str_eq( mcpp_optarg, "F")) { /* -MF file */ + mkdep_mf = argv[ mcpp_optind++]; + } else if (argv[ mcpp_optind - 1] == mcpp_optarg) { /* -M */ + mkdep |= MD_SYSHEADER; + mcpp_optind--; + } else { + usage( opt); + } + if (str_eq( mcpp_optarg, "D") || str_eq( mcpp_optarg, "MD")) { + cp = argv[ mcpp_optind]; + if (cp && *cp != '-') /* -MD (-MMD) file */ + mkdep_md = argv[ mcpp_optind++]; + } + mkdep |= MD_MKDEP; + break; + +#if SYS_FAMILY == SYS_UNIX + case 'm': + if (str_eq( mcpp_optarg, "64")) { /* -m64 */ + if (str_eq( CPU, "i386")) + strcpy( arch, "x86_64"); + else if (str_eq( CPU, "ppc")) + strcpy( arch, "ppc64"); + /* Else ignore */ + break; + } else if (str_eq( mcpp_optarg, "32")) { /* -m32 */ + if (str_eq( CPU, "x86_64")) + strcpy( arch, "i386"); + else if (str_eq( CPU, "ppc64")) + strcpy( arch, "ppc"); + /* Else ignore */ + break; + } else if (str_eq( mcpp_optarg, "mmx")) { /* -mmmx */ + look_and_install( "__MMX__", DEF_NOARGS_PREDEF, null, "1"); + break; + } else if (str_eq( mcpp_optarg, "no-mmx")) { /* -mno-mmx */ + undef_list[ undef_cnt++] = "__MMX__"; + break; + } +#endif /* SYS_FAMILY == UNIX */ +#if COMPILER == GNUC +#if SYSTEM == SYS_CYGWIN + if (str_eq( mcpp_optarg, "no-cygwin")) { /* -mno-cygwin */ + no_cygwin = TRUE; + break; + } +#endif + if (! integrated_cpp) + usage( opt); + break; + + case 'u': + if (! str_eq( mcpp_optarg, "ndef")) /* -undef */ + usage( opt); /* Else fall through */ +#endif /* COMPILER == GNUC */ + +#if COMPILER == MSC + case 'u': +#endif + case 'N': + /* No predefines: remove "unix", "__unix__" and friends. */ + nflag = TRUE; + break; + +#if COMPILER == GNUC || NO_DIR + case 'n': +#if NO_DIR + if (str_eq( mcpp_optarg, "odir")) { /* -nodir */ + no_dir = TRUE; + } +#endif +#if COMPILER == GNUC + if (str_eq( mcpp_optarg, "ostdinc")) { /* -nostdinc */ + unset_sys_dirs = TRUE; /* Unset pre-specified directories */ + } else if (str_eq( mcpp_optarg, "ostdinc++")) { /* -nostdinc++ */ + set_cplus_dir = FALSE; /* Unset C++-specific directories */ + } else if (str_eq( mcpp_optarg, "oprecomp")) { /* -noprecomp */ + mcpp_fprintf( ERR, warning, opt, mcpp_optarg); + break; + } +#endif + else { + usage( opt); + } + break; +#endif + +#if COMPILER == GNUC + case 'O': + if (integrated_cpp) { + if (*mcpp_optarg == '-') /* No argument */ + mcpp_optind--; + else if ((isdigit( *mcpp_optarg) && *mcpp_optarg != '0') + || *mcpp_optarg == 's' || *mcpp_optarg == 'z') + /* -O1, -O2 -Os, -Oz */ + look_and_install( "__OPTIMIZE__", DEF_NOARGS_PREDEF, null + , "1"); + else if (! isdigit( *mcpp_optarg)) + usage( opt); + /* Else -O0: ignore */ + } else { + usage( opt); + } + break; /* Else ignore -Ox option */ +#elif COMPILER == LCC + case 'O': /* Define __LCCOPTIMLEVEL as 1 */ + defp = look_id( optim_name); + strcpy( defp->repl, "1"); + break; +#endif + + case 'o': + *out_pp = mcpp_optarg; /* Output file name */ + break; + + case 'P': /* No #line output */ + option_flags.p = TRUE; + break; + +#if COMPILER == GNUC + case 'p': + if (str_eq( mcpp_optarg, "edantic") /* -pedantic */ + || str_eq( mcpp_optarg, "edantic-errors")) { + /* -pedantic-errors */ + /* This option does not imply -ansi */ + if (warn_level == -1) + warn_level = 0; + warn_level |= (1 | 2 | 4); + if (! sflag && ! cplus_val) { + stdc_val = 1; + sflag = TRUE; + } + } else { + usage( opt); + } + break; + case 'q': + if (str_eq( mcpp_optarg, "uiet")) + /* -quiet: GCC's undocumented, yet frequently specified opt */ + break; /* Ignore the option */ + else + usage( opt); + break; +#endif /* COMPILER == GNUC */ + + case 'Q': + option_flags.q = TRUE; + break; + +#if COMPILER == MSC + case 'R': /* -RTC1, -RTCc, -RTCs, -RTCu, etc. */ + if (memcmp( mcpp_optarg, "TC", 2) == 0 + && *(mcpp_optarg + 2) != EOS) + look_and_install( "__MSVC_RUNTIME_CHECKS", DEF_NOARGS_PREDEF + , null, "1"); + else + usage( opt); + break; +#endif + + case 'S': + if (cplus_val || sflag) { /* C++ or the second time */ + mcpp_fprintf( ERR, warning, opt, mcpp_optarg); + break; + } + i = *mcpp_optarg; + if (! isdigit( i) || *(mcpp_optarg + 1) != EOS) + usage( opt); + stdc_val = i - '0'; + sflag = TRUE; + break; + +#if COMPILER == GNUC + case 'r': + if (str_eq( mcpp_optarg, "emap")) + mcpp_fprintf( ERR, warning, opt, mcpp_optarg); + /* Ignore -remap option */ + else + usage( opt); + break; + + case 's': + if (memcmp( mcpp_optarg, "td=", 3) == 0 + && strlen( mcpp_optarg) > 3) { /* -std=STANDARD*/ + cp = mcpp_optarg + 3; + if (str_eq( cp, "c89") /* std=c89 */ + || str_eq( cp, "c90") /* std=c90 */ + || str_eq( cp, "gnu89") /* std=gnu89 */ + || str_eq( cp, "iso9899:1990")) { + std_val = 0L; /* C90 + extensions */ + } else if (str_eq( cp, "c99") /* std=c99 */ + || str_eq( cp, "c9x") /* std=c9x */ + || str_eq( cp, "gnu99") /* std=gnu99 */ + || str_eq( cp, "gnu9x") /* std=gnu9x */ + || str_eq( cp, "iso9899:1999") + || str_eq( cp, "iso9899:199x")) { + std_val = 199901L; + } else if (str_eq( cp, "c++98")) { /* std=c++98 */ + cplus_val = std_val = 199711L; + } else if (memcmp( cp, "iso9899:", 8) == 0 + && strlen( cp) >= 14) { /* std=iso9899:199409, etc. */ + mcpp_optarg = cp + 8; + look_and_install( "__STRICT_ANSI__", DEF_NOARGS_PREDEF + , null, "1"); + ansi = TRUE; + goto Version; + } else if (memcmp( cp, "iso14882", 8) == 0) { + cp += 8; + ansi = TRUE; + if (cp && *cp == ':' && strlen( cp) >= 7) { + /* std=iso14882:199711, etc. */ + cplus_val = CPLUS; + mcpp_optarg = cp + 1; + goto Version; + } else { + goto plus; + } + } else { + usage( opt); + } + if (! cplus_val && memcmp( cp, "gnu", 3) != 0) { + /* 'std=gnu*' does not imply -ansi */ + look_and_install( "__STRICT_ANSI__", DEF_NOARGS_PREDEF + , null, "1"); + ansi = TRUE; + } + stdc_val = 1; + sflag = TRUE; + } else { + usage( opt); + } + break; + + case 't': + if (str_eq( mcpp_optarg, "raditional") + || str_eq( mcpp_optarg, "raditional-cpp")) { + /* -traditional, -traditional-cpp */ + trad = TRUE; + mcpp_mode = OLD_PREP; + } else if (str_eq( mcpp_optarg, "rigraphs")) { + option_flags.trig = TRUE; /* -trigraphs */ + } else { + usage( opt); + } + break; +#endif /* COMPILER == GNUC */ + +#if COMPILER == MSC + case 'T': + if (strlen( mcpp_optarg) > 1) + usage( opt); + i = tolower( *mcpp_optarg); /* Fold case */ + if (i == 'c') { + ; /* Ignore this option */ + } else if (i == 'p') { + cplus_val = CPLUS; + } else { + usage( opt); + } + break; +#endif + + case 'U': /* Undefine macro */ + if (undef_cnt >= MAX_UNDEF) { + mcpp_fputs( "Too many -U options.\n", ERR); + longjmp( error_exit, -1); + } + undef_list[ undef_cnt++] = mcpp_optarg; + break; + + case 'V': +#if COMPILER == GNUC +Version: +#endif + valp = eval_num( mcpp_optarg); + if (valp->sign == VAL_ERROR) + usage( opt); + std_val = (long) valp->val; + break; + + case 'v': + option_flags.v = TRUE; + show_path = TRUE; + break; + + case 'W': /* warning level */ + if (warn_level == -1) /* Have to initialize */ + warn_level = 0; +#if COMPILER == GNUC + if (argv[ mcpp_optind - 1] == mcpp_optarg) { /* No argument */ + /* + * Note: -W without argument is not officially supported. + * It may cause an error. + */ + warn_level |= (1 | 2 | 4 | 16); + mcpp_optind--; + } else if (str_eq( mcpp_optarg, "comment") + || str_eq( mcpp_optarg, "comments") + || str_eq( mcpp_optarg, "sign-compare")) { + warn_level |= 1; + } else if (str_eq( mcpp_optarg, "undef")) { + warn_level |= 4; + } else if (str_eq( mcpp_optarg, "all")) { + warn_level |= (1 | 16); /* Convert -Wall to -W17*/ + } else if (str_eq( mcpp_optarg, "trigraphs")) { + warn_level |= 16; + } +#endif /* COMPILER == GNUC */ +#if COMPILER == MSC + if (str_eq( mcpp_optarg, "all")) { + warn_level |= (1 | 16); /* Convert -Wall to -W17*/ + } else if (str_eq( mcpp_optarg, "L")) { + option_flags.no_source_line = TRUE; + /* Single-line diagnostic */ + } +#endif + if (isdigit( *mcpp_optarg)) { + warn_level |= parse_warn_level( mcpp_optarg, opt); + if (warn_level > 31 || warn_level < 0) + usage( opt); + } + if (warn_level == 0) + warn_level = 0xFF; /* Remember this option */ + /* Else ignore the option */ + break; + +#if COMPILER == GNUC || COMPILER == MSC + case 'w': /* Same as -W0 */ + warn_level = 0xFF; /* Remenber this option */ + break; +#endif + +#if COMPILER == GNUC + case 'x': + if (str_eq( mcpp_optarg, "c")) { + break; /* -x c -- ignore this */ + } else if (str_eq( mcpp_optarg, "c++")) { + goto plus; + } else if (str_eq( mcpp_optarg, "assembler-with-cpp")) { + option_flags.lang_asm = TRUE; + break; + } else { + usage( opt); + } + break; +#endif + +#if COMPILER == MSC + case 'Z': + if (str_eq( mcpp_optarg, "c:wchar_t")) { /* -Zc:wchar_t */ + look_and_install( "_NATIVE_WCHAR_T_DEFINED", DEF_NOARGS_PREDEF + , null, "1"); + look_and_install( "_WCHAR_T_DEFINED", DEF_NOARGS_PREDEF, null + , "1"); + wchar_t_modified = TRUE; + } else if (str_eq( mcpp_optarg, "c:wchar_t-")) {/* -Zc:wchar_t- */ + wchar_t_modified = TRUE; /* Do not define the macros */ + } else if (str_eq( mcpp_optarg, "l")) { + look_and_install( "_VC_NODEFAULTLIB", DEF_NOARGS_PREDEF, null + , "1"); + } else if (str_eq( mcpp_optarg, "a")) { /* -Za */ + undefine( "_MSC_EXTENSIONS"); + option_flags.dollar_in_name = FALSE; + } else if (str_eq( mcpp_optarg, "e")) { + /* Ignore -Ze silently */ + break; + } else if (*(mcpp_optarg + 1) == EOS) { + /* -Z followed by one char */ + mcpp_fprintf( ERR, warning, opt, mcpp_optarg); + /* Ignore the option with warning */ + } else { + usage( opt); + } + break; +#endif + + case 'z': + option_flags.z = TRUE; /* No output of included file */ + break; + + default: /* What is this one? */ + usage( opt); + break; + } /* Switch on all options */ + + } /* For all arguments */ + + if (mcpp_optind < argc && set_files( argc, argv, in_pp, out_pp) != NULL) + goto opt_search; /* More options after the filename */ + + /* Check consistency of specified options, set some variables */ + chk_opts( sflag, trad); + + if (warn_level == -1) /* No -W option */ + warn_level = 1; /* Default warning level */ + else if (warn_level == 0xFF) + warn_level = 0; /* -W0 has high precedence */ + +#if SYSTEM == SYS_MAC + set_a_dir( NULL); /* Initialize incdir[] */ + to_search_framework = incend; + /* Search framework[] next to the directory */ +#endif + +#if COMPILER == GNUC && SYSTEM == SYS_MAC + if (arch[ 0]) { /* -arch option has been specified */ + if (((str_eq( CPU, "i386") || str_eq( CPU, "x86_64")) + && (! str_eq( arch, "i386") && ! str_eq( arch, "x86_64"))) + || ((str_eq( CPU, "ppc") || str_eq( CPU, "ppc64")) + && (! str_eq( arch, "ppc") && ! str_eq( arch, "ppc64")))) { + mcpp_fprintf( ERR, "Wrong argument of -arch option: %s\n", arch); + longjmp( error_exit, -1); + } + } +#endif + if (! arch[ 0]) { + /* None of -arch, -m32 or -m64 options has been specified. */ + /* The CPU-specific-macros will be defined in init_cpu_macro(). */ + strcpy( arch, CPU); + } +#if COMPILER != GNUC + init_cpu_macro( gval, sse); +#endif + +#if COMPILER == GNUC + if (sysdir < sysdir_end) { + char ** dp = sysdir; + if (! sys_dirp || sys_dirp == incdir) + sys_dirp = dp; + while (dp < sysdir_end) + set_a_dir( *dp++); + } + if (*in_pp && str_eq( (*in_pp) + strlen( *in_pp) - 2, ".S")) + option_flags.lang_asm = TRUE; /* Input file name is *.S */ + if (option_flags.lang_asm) { + look_and_install( "__ASSEMBLER__", DEF_NOARGS_PREDEF, null, "1"); + option_flags.dollar_in_name = FALSE; /* Disable '$' in name */ + } + if (! sys_dirp || sys_dirp == incdir) + sys_dirp = incend; +#endif +#if SYSTEM == SYS_MAC + init_framework(); /* After setting sys_dirp */ +#endif + set_env_dirs(); + if (! unset_sys_dirs) + set_sys_dirs( set_cplus_dir); + + if (mkdep_mf) { /* -MF overrides -MD */ + mkdep_fp = fopen( mkdep_mf, "w"); + } else if (mkdep_md) { + mkdep_fp = fopen( mkdep_md, "w"); + } + if (mkdep_mq) /* -MQ overrides -MT */ + mkdep_target = mkdep_mq; + else if (mkdep_mt) + mkdep_target = mkdep_mt; + + /* Normalize the path-list */ + if (*in_pp && ! str_eq( *in_pp, "-")) { + char * tmp = norm_path( null, *in_pp, FALSE, FALSE); + if (tmp) /* The file exists */ + *in_pp = tmp; + /* Else mcpp_main() will diagnose *in_pp and exit */ + } + if (! (mcpp_debug & MACRO_CALL)) { + /* -K option alters behavior of -v option */ + if (option_flags.v) + version(); + if (show_path) { + fp_debug = stderr; + dump_path(); + fp_debug = stdout; + } + } +} + +static void version( void) +/* + * Print version message. + */ +{ + const char * mes[] = { + +#if MCPP_LIB +/* Write messages here, for example, "MySomeTool with ". */ +#endif + +#ifdef VERSION_MSG + "MCPP V.2.7.2-ned (2017/02) " +#else + "MCPP V.", VERSION, "-ned (", DATE, ") " +#endif +#if COMPILER == INDEPENDENT + , "compiler-independent-build " +#else +#ifdef CMP_NAME + , "for ", CMP_NAME, " " +#endif +#endif + , "compiled by " +#ifdef VERSION_MSG + , VERSION_MSG +#else +#ifdef HOST_CMP_NAME + , HOST_CMP_NAME +#if HOST_COMPILER == GNUC + , " V.", GCC_MAJOR_VERSION, ".", GCC_MINOR_VERSION +#endif +#endif +#endif + , "\n", + NULL + }; + + const char ** mpp = mes; + while (*mpp) + mcpp_fputs( *mpp++, ERR); +} + +static void usage( + int opt +) +/* + * Print usage. + */ +{ + const char * mes[] = { + +"Usage: ", +"mcpp", +" [- [-]] [ [-] [] [-]]\n", +" defaults to stdin and defaults to stdout.\n", + +"\nCommonly used options:\n", + +"-@MODE Specify preprocessing mode. MODE should be one of these 4:\n", +" -@std Standard conforming mode. (default)\n", +" -@poststd, -@post special 'post-Standard' mode.\n", +" -@kr K&R 1st mode.\n", +" -@oldprep, -@old 'old_preprocessor' mode (i.e. 'Reiser model' cpp).\n", + +#if COMPILER == MSC +"-arch:SSE, -arch:SSE2 Define the macro _M_IX86_FP as 1, 2 respectively.\n", +#endif +#if SYSTEM == SYS_MAC && COMPILER == GNUC +"-arch Change the target to (i386, x86_64, ppc, ppc64).\n", +#endif + +#if ! STD_LINE_PREFIX +"-b Output #line lines in C source style.\n", +#endif + +"-C Output also comments.\n", +"-D [=] Define as (default:1).\n", +"-D [=] Define as .\n", +"-e Change the default multi-byte character encoding to one of:\n", +" euc_jp, gb2312, ksc5601, big5, sjis, iso2022_jp, utf8.\n", + +#if SYSTEM == SYS_MAC +"-F Add to top of framework directory list.\n", +#endif +#if COMPILER == GNUC +"-finput-charset= Same as -e .\n", +" (Don't insert spaces around '=').\n", +#endif +#if COMPILER == MSC +"-Fl Include the prior to the main input file.\n", +"-G Define the macro _M_IX86 according to .\n", +#endif +#if COMPILER == LCC +"-g Define the macro __LCCDEBUGLEVEL as .\n", +#endif + +"-I Add to the #include search list.\n", + +#if COMPILER == GNUC +"-isysroot Change root of system header directory to .\n", +"-include Include the prior to the main input file.\n", +#else +"-I- Unset system or site specific include directories.\n", +#endif +#if COMPILER == MSC +"-J Define the macro _CHAR_UNSIGNED as 1.\n", +#endif + +"-j Don't output the source line in diagnostics.\n", +"-M, -MM, -MD, -MMD, -MP, -MQ target, -MT target, -MF file\n", +" Output source file dependency line for makefile.\n", +#if SYS_FAMILY == SYS_UNIX +"-m32 Change target CPU from x86_64, ppc64 to i386, ppc, respectively.\n", +"-m64 Change target CPU from i386, ppc to x86_64, ppc64, respectively.\n", +#endif +"-N Don't predefine any non-standard macros.\n", + +#if COMPILER == GNUC +"-nostdinc Unset system or site specific include directories.\n", +#endif +#if COMPILER == LCC +"-O Define the macro __LCCOPTIMLEVEL as 1.\n", +#endif + +"-o Output to .\n", +"-P Don't output #line lines.\n", +"-Q Output diagnostics to \"mcpp.err\" (default:stderr).\n", +#if COMPILER == MSC +"-RTC* Define the macro __MSVC_RUNTIME_CHECKS as 1.\n", +#endif +#if COMPILER == GNUC +"-traditional, -traditional-cpp Same as -@oldprep.\n", +#endif +"-U Undefine .\n", + +#if COMPILER == GNUC +"-undef Same as -N.\n", +#endif +#if COMPILER == MSC +"-u Same as -N.\n", +#endif + +"-v Show version and include directories of mcpp.\n", +"-W Set warning level to (OR of {0,1,2,4,8,16}, default:1).\n", + +#if COMPILER == MSC +"-WL Same as -j.\n", +#endif +#if COMPILER == MSC || COMPILER == GNUC +"-w Same as -W0.\n", +#endif +#if COMPILER == MSC +"-X Same as -I-.\n", +"-Zc:wchar_t Define _NATIVE_WCHAR_T_DEFINED and _WCHAR_T_DEFINED as 1.\n", +"-Zl Define the macro _VC_NODEFAULTLIB as 1.\n", +#endif + +"-z Don't output the included file, only defining macros.\n", + +"\nOptions available with -@std (default) or -@poststd options:\n", + +"-+ Process C++ source.\n", + +#if DIGRAPHS_INIT +"-2 Disable digraphs.\n", +#else +"-2 Enable digraphs.\n", +#endif +#if COMPILER == GNUC +"-digraphs Enable digraphs.\n", +#endif + +"-h Re-define the pre-defined macro __STDC_HOSTED__ as .\n", + +#if COMPILER == GNUC +"-lang-c89 Same as -S1.\n", +"-lang-c++ Same as -+.\n", +"-pedantic, -pedantic-errors Same as -W7.\n", +"-S Redefine __STDC__ to .\n", +#else +"-S Redefine __STDC__ to , undefine old style macros.\n", +#endif + +#if COMPILER == GNUC +"-std= Specify the standard to which the code should conform.\n", +" may be one of: c90, c99, iso9899:1990, iso14882, etc.\n", +" iso9899:, iso14882: : Same as -V (long in decimals).\n", +#endif +#if COMPILER == MSC +"-Tp Same as -+.\n", +#endif + +"-V Redefine __STDC_VERSION__ or __cplusplus to .\n", +" C with -V199901L specifies C99 specs.\n", +" C++ with -V199901L specifies C99 compatible specs.\n", + +#if COMPILER == GNUC +"-x c++ Same as -+.\n", +#endif + +"\nOptions available with only -@std (default) option:\n", + +"-@compat Expand recursive macro more than Standard.\n", +#if TRIGRAPHS_INIT +"-3 Disable trigraphs.\n", +#else +"-3 Enable trigraphs.\n", +#endif +"-K Output macro annotations embedding in comments.\n", +#if COMPILER == GNUC +"-trigraphs Enable trigraphs.\n", +#endif + +"\nOptions available with -@std (default), -@kr or -@oldprep options:\n", + +#if COMPILER == GNUC +"-lang-asm Same as -x assembler-with-cpp.\n", +"-x assembler-with-cpp Process \"assembler\" source.\n", +#elif COMPILER == MSC +"-A Process \"assembler\" source.\n", +#else +"-a Process \"assembler\" source.\n", +#endif + +"-k Keep white spaces of input lines as they are.\n", + +"\nFor further details see mcpp-manual.html.\n", + NULL, + }; + + const char * illegopt = "Incorrect option -%c%s\n"; + const char * const * mpp = mes; + + if (opt != '?') + mcpp_fprintf( ERR, illegopt, opt, mcpp_optarg ? mcpp_optarg : null); + version(); +#if MCPP_LIB + mes[ 1] = argv0; +#endif + while (*mpp) + mcpp_fputs( *mpp++, ERR); + longjmp( error_exit, -1); +} + +static void set_opt_list( + char * optlist +) +/* + * Set list of legal option characters. + */ +{ + const char * list[] = { + +#if ! STD_LINE_PREFIX + "b", +#endif + +#if SYS_FAMILY == SYS_UNIX + "m:", +#endif + +#if COMPILER == GNUC + "$A:a:cd:Ef:g:i:l:r:s:t:u:O:p:q:wx:", +#elif COMPILER == MSC + "Aa:F:G:JR:T:XZ:uw", +#elif COMPILER == LCC + "g:O", +#endif + +#if COMPILER != GNUC && COMPILER != MSC + "a", +#endif +#if SYSTEM == SYS_MAC + "F:-:", +#endif + + NULL + }; + + const char * const * lp = & list[ 0]; + + strcpy( optlist, "23+@:e:h:jkn:o:vzCD:I:KM:NPQS:U:V:W:"); + /* Default options */ + while (*lp) + strcat( optlist, *lp++); + if (strlen( optlist) >= OPTLISTLEN) + cfatal( "Bug: Too long option list", NULL, 0L, NULL); /* _F_ */ +} + +static int parse_warn_level( + const char * mcpp_optarg, + int opt +) +/* + * Parse warn level option. + * Warning level option is specified as '19' or '1|2|16' or even '3|16'. + * Even spaces are allowed as ' 1 | 2|16 '. + */ +{ + const char * cp = mcpp_optarg; + int w, i; + + w = i = 0; + while( *cp != EOS) { + while( *cp == ' ') + cp++; /* Skip spaces */ + if (! isdigit( *cp)) + break; /* Error */ + while (isdigit( *cp)) { + i *= 10; + i += (*cp++ - '0'); + } + while (*cp == ' ') + cp++; + if (*cp == '|') { /* Only digits or '|' are allowed */ + w |= i; /* Take OR of the args */ + i = 0; + cp++; + } + } + if (*cp != EOS) { /* Not ending with digit */ + mcpp_fprintf( ERR, "Illegal warning level option \"%s\"\n" + , mcpp_optarg); + usage( opt); + } + w |= i; /* Take the last arg */ + return w; +} + +static void def_a_macro( + int opt, /* 'D' */ + char * def /* Argument of -D option */ +) +/* + * Define a macro specified by -D option. + * The macro maybe either object-like or function-like (with parameter). + */ +{ + DEFBUF * defp; + char * definition; /* Argument of -D option */ + char * cp; + int i; + + /* Convert trigraphs for the environment which need trigraphs */ + if (mcpp_mode == STD && option_flags.trig) + cnv_trigraph( def); + if (mcpp_mode == POST_STD && option_flags.dig) + cnv_digraph( def); /* Convert prior to installing macro */ + definition = xmalloc( strlen( def) + 4); + strcpy( definition, def); + if ((cp = strchr( definition, '=')) != NULL) { + *cp = ' '; /* Remove the '=' */ + cp = "\n"; /* Append */ + } else { + cp = " 1\n"; /* With definition "1" */ + } + strcat( definition, cp); + cp = definition; + while ((char_type[ *cp & UCHARMAX] & SPA) == 0) + cp++; + i = *cp; + *cp = EOS; + if ((defp = look_id( definition)) != NULL) /* Pre-defined */ + undefine( definition); + *cp = i; + /* Now, save the definition. */ + unget_string( definition, NULL); + if (do_define( FALSE, 0) == NULL) /* Define a macro */ + usage( opt); + *cp = EOS; + if (str_eq( definition, "__STDC__")) { + defp = look_id( definition); + defp->nargs = DEF_NOARGS_STANDARD; + /* Restore Standard-predefinedness */ + } + free( definition); + skip_nl(); /* Clear the appended */ +} + +static void chk_opts( + int sflag, /* Flag of Standard or post-Standard mode */ + int trad /* -traditional (GCC only) */ +) +/* + * Check consistency between the specified options. + * Set default value of some variables for each 'mcpp_mode'. + */ +{ + int incompat = FALSE; + + switch (mcpp_mode) { + case STD : + case POST_STD : + if (trad) + incompat = TRUE; + if (! stdc_val) + stdc_val = STDC; + break; + case KR : + case OLD_PREP : +#if COMPILER == GNUC + if (sflag || cplus_val || ansi || std_val != -1L) +#else + if (sflag || cplus_val || std_val != -1L) +#endif + incompat = TRUE; + if (option_flags.dig) { + if (option_flags.dig != DIGRAPHS_INIT) + incompat = TRUE; + else + option_flags.dig = 0; + } + break; + } + + if (mcpp_mode == POST_STD + && (option_flags.lang_asm || compat_mode || option_flags.k)) + incompat = TRUE; + if (mcpp_mode != STD && option_flags.trig) { + if (option_flags.trig != TRIGRAPHS_INIT) + incompat = TRUE; + else + option_flags.trig = FALSE; + } + if (mcpp_mode != STD && (mcpp_debug & MACRO_CALL)) + incompat = TRUE; + if ((mcpp_debug & MACRO_CALL) + && (option_flags.lang_asm || option_flags.c)) { + mcpp_fputs( "Disabled -K option.\n", ERR); + mcpp_debug &= ~MACRO_CALL; + /* -a and -C options do not co-exist with -K */ + } + if (incompat) { + mcpp_fputs( "Incompatible options are specified.\n", ERR); + usage( '?'); + } + + standard = (mcpp_mode == STD || mcpp_mode == POST_STD); + /* Modify magic characters in character type table. */ + if (! standard) + char_type[ DEF_MAGIC] = 0; + if (mcpp_mode != STD) + char_type[ IN_SRC] = 0; + if (mcpp_mode == POST_STD || mcpp_mode == KR) + char_type[ TOK_SEP] = 0; /* TOK_SEP equals to COM_SEP */ + if (mcpp_mode != STD) + char_type[ MAC_INF] = 0; + + expand_init( compat_mode, ansi); + /* Set function pointer to macro expansion routine */ +} + +#if COMPILER != GNUC + +static void init_cpu_macro ( + int gval, /* Argument of -G option for MSC */ + int sse /* Argument of -sse: option for MSC */ +) +/* + * Set predefined macros for CPU. + * This routine is called from do_options() only once. + * GCC-specific-build defines these macro by init_gcc_macro(). + */ +{ + const char * cpu_macro[][ 7] = { +#if SYS_FAMILY == SYS_UNIX + { "__i386__" +#if SYSTEM == SYS_CYGWIN + , "_X86_" +#endif + , NULL,}, + { "__x86_64__", "__amd64__", NULL,}, + { "__ppc__", "__powerpc__", NULL,}, + { "__ppc64__", "__powerpc64__", NULL,} +#elif SYS_FAMILY == SYS_WIN + { "_WIN32", "__WIN32__", "_X86_" +#if SYSTEM == SYS_MINGW + , "__i386__" +#endif + , NULL,}, + { "_WIN32", "_WIN64", "__WIN64__", "_M_AMD64", "_AMD64_", "_X64_" + , NULL,}, /* "_WIN32" is defined even on Windows 64 */ + { NULL,}, /* Not supported */ + { NULL,} /* Not supported */ +#endif + }; + const char ** macro; + int index; + + if (str_eq( arch, "i386")) + index = 0; + else if (str_eq( arch, "x86_64")) + index = 1; + else if (str_eq( arch, "ppc")) + index = 2; + else if (str_eq( arch, "ppc64")) + index = 3; + else + index = 9; + + if (index == 9) { /* Unknown CPU */ + look_and_install( "__" CPU "__", DEF_NOARGS_PREDEF, null, "1"); + return; + } + macro = cpu_macro[ index]; + while (*macro) + look_and_install( *macro++, DEF_NOARGS_PREDEF, null, "1"); +#if SYS_FAMILY == SYS_WIN + if (index == 0) { + char val[] = "600"; + if (gval) + val[ 0] = gval; + look_and_install( "_M_IX86", DEF_NOARGS_PREDEF, null, val); + val[ 0] = '0' + sse; + val[ 1] = '\0'; + look_and_install( "_M_IX86_FP", DEF_NOARGS_PREDEF, null, val); + } +#endif +} +#endif /* COMPILER != GNUC */ + +static void init_predefines( void) +/* + * Set or unset predefined macros. + */ +{ + char tmp[ 16]; + + if (std_val != -1L) { /* Version is specified */ + if (cplus_val) + cplus_val = std_val; /* Value of __cplusplus */ + else + stdc_ver = std_val; /* Value of __STDC_VERSION__ */ + } else { + if (! cplus_val) + stdc_ver = stdc_val ? STDC_VERSION : 0L; + } + + if (nflag) { + un_predefine( TRUE); + } else if (stdc_val || cplus_val) { +#if COMPILER != GNUC + un_predefine( FALSE); /* Undefine "unix" or so */ +#endif + } + sprintf( tmp, "%ldL", cplus_val ? cplus_val : stdc_ver); + if (cplus_val) { + look_and_install( "__cplusplus", DEF_NOARGS_STANDARD, null, tmp); + } else { + if (stdc_ver) + look_and_install( "__STDC_VERSION__", DEF_NOARGS_STANDARD, null + , tmp); +#ifdef COMPILER_CPLUS + if (! nflag) /* Undefine pre-defined macro for C++ */ + undefine( COMPILER_CPLUS); +#endif + } + set_limit(); + stdc2 = cplus_val || stdc_ver >= 199901L; + stdc3 = (cplus_val >= 199901L) || (stdc_ver >= 199901L); + /* (cplus_val >= 199901L) makes C++ C99-compatible specs */ + if (standard) + init_std_defines(); + if (stdc3) + set_pragma_op(); +} + +static void init_std_defines( void) +/* + * For STD and POST_STD modes. + * The magic pre-defines are initialized with magic argument counts. + * expand_macro() notices this and calls the appropriate routine. + * DEF_NOARGS is one greater than the first "magic" definition. + * 'DEF_NOARGS - n' are reserved for pre-defined macros. + * __STDC_VERSION__ and __cplusplus are defined by chk_opts() and set_cplus(). + */ +{ + char tmp[ 16]; + char timestr[ 14]; + time_t tvec; + char * tstring; + + look_and_install( "__LINE__", DEF_NOARGS_DYNAMIC - 1, null, "-1234567890"); + /* Room for 11 chars (10 for long and 1 for '-' in case of wrap round. */ + look_and_install( "__FILE__", DEF_NOARGS_DYNAMIC - 2, null, null); + /* Should be stuffed */ + + /* Define __DATE__, __TIME__ as present date and time. */ + time( &tvec); + tstring = ctime( &tvec); + sprintf( timestr, "\"%.3s %c%c %.4s\"", + tstring + 4, + *(tstring + 8) == '0' ? ' ' : *(tstring + 8), + *(tstring + 9), + tstring + 20); + look_and_install( "__DATE__", DEF_NOARGS_DYNAMIC, null, timestr); + sprintf( timestr, "\"%.8s\"", tstring + 11); + look_and_install( "__TIME__", DEF_NOARGS_DYNAMIC, null, timestr); + + if (! look_id( "__STDC_HOSTED__")) { + /* + * Some compilers, e.g. GCC older than 3.3, define this macro by + * -D option. + */ + sprintf( tmp, "%d", STDC_HOSTED); + look_and_install( "__STDC_HOSTED__", DEF_NOARGS_PREDEF, null, tmp); + } +#if COMPILER != GNUC /* GCC do not undefine __STDC__ on C++ */ + if (cplus_val) + return; +#endif + /* Define __STDC__ as 1 or such for Standard conforming compiler. */ + if (! look_id( "__STDC__")) { + sprintf( tmp, "%d", stdc_val); + look_and_install( "__STDC__", DEF_NOARGS_STANDARD, null, tmp); + } +} + +#define LINE90LIMIT 32767 +#define LINE_CPLUS_LIMIT 32767 + +static void set_limit( void) +/* + * Set the minimum translation limits specified by the Standards. + */ +{ + if (cplus_val) { /* Specified by C++ 1998 Standard */ + std_limits.str_len = SLEN_CPLUS_MIN; + std_limits.id_len = IDLEN_CPLUS_MIN; + std_limits.n_mac_pars = NMACPARS_CPLUS_MIN; + std_limits.exp_nest = EXP_NEST_CPLUS_MIN; + std_limits.blk_nest = BLK_NEST_CPLUS_MIN; + std_limits.inc_nest = INCLUDE_NEST_CPLUS_MIN; + std_limits.n_macro = NMACRO_CPLUS_MIN; + std_limits.line_num = LINE_CPLUS_LIMIT; + } else if (stdc_ver >= 199901L) { /* Specified by C 1999 Standard */ + std_limits.str_len = SLEN99MIN; + std_limits.id_len = IDLEN99MIN; + std_limits.n_mac_pars = NMACPARS99MIN; + std_limits.exp_nest = EXP_NEST99MIN; + std_limits.blk_nest = BLK_NEST99MIN; + std_limits.inc_nest = INCLUDE_NEST99MIN; + std_limits.n_macro = NMACRO99MIN; + std_limits.line_num = LINE99LIMIT; + } else if (standard) { /* Specified by C 1990 Standard */ + std_limits.str_len = SLEN90MIN; + std_limits.id_len = IDLEN90MIN; + std_limits.n_mac_pars = NMACPARS90MIN; + std_limits.exp_nest = EXP_NEST90MIN; + std_limits.blk_nest = BLK_NEST90MIN; + std_limits.inc_nest = INCLUDE_NEST90MIN; + std_limits.n_macro = NMACRO90MIN; + std_limits.line_num = LINE90LIMIT; + } + /* Else pre-Standard mode */ +} + +static void set_pragma_op( void) +/* + * #define _Pragma(a) _Pragma ( a ) + * Define _Pragma() operator as a special macro so as to be searched + * easily. The unusual 'DEF_PRAGMA' is a marker of this psuedo + * macro. + */ +{ + char * name = "_Pragma"; + char tmp[ 16]; + + sprintf( tmp, "%c%s ( %c%c )", DEF_MAGIC, name, MAC_PARM, 1); + /* Replacement text */ + look_and_install( name, DEF_PRAGMA, "a", tmp); +} + +void init_sys_macro( void) +/* + * Define system-specific macros and some Standard required macros + * and undefine macros specified by -U options. + */ +{ + /* This order is important. */ + def_macros(); /* Define macros specified by -D */ +#if COMPILER == GNUC + chk_env(); +#endif + init_predefines(); /* Define predefined macros */ +#if COMPILER == GNUC + init_gcc_macro(); +#elif COMPILER == MSC + init_msc_macro(); +#endif + undef_macros(); /* Undefine macros specified by -U */ + if (mcpp_debug & MACRO_CALL) + dump_def( FALSE, TRUE); /* Finally putout current macro names */ +} + +void at_start( void) +/* + * Do the commands prior to processing main source file after do_options(). + */ +{ + char * env; + FILEINFO * file_saved = infile; + + /* + * Set multi-byte character encoding according to environment variables + * LC_ALL, LC_CTYPE and LANG -- with preference in this order. + */ + if (! mb_changed) { /* -m option precedes */ + if ((env = getenv( "LC_ALL")) != NULL) + set_encoding( env, "LC_ALL", 0); + else if ((env = getenv( "LC_CTYPE")) != NULL) + set_encoding( env, "LC_CTYPE", 0); + else if ((env = getenv( "LANG")) != NULL) + set_encoding( env, "LANG", 0); + } + +#if COMPILER == GNUC || COMPILER == MSC + if (option_flags.dollar_in_name) + char_type[ 0x24] |= LET; /* Enable '$' in identifiers */ + /* + * Do the -include (-Fl for MSC) options in the specified order. + * Note: This functionality is implemented as nested #includes + * which results the same effect as sequential #includes. + */ + { + char ** preinc; + /* + * Note: Here, 'infile' is the main input file, which is pseudo- + * parent file of the files to pre-include. So, we must + * temporarily set the infile's directory to the current directory + * in order to preinclude the files relative to it. + */ + preinc = preinc_end; + while (preinclude <= --preinc && *preinc != NULL) + open_file( &null, NULL, *preinc, TRUE, TRUE, FALSE); + } +#endif + + put_info( file_saved); +} + +static void put_info( + FILEINFO * sharp_file +) +/* + * Putout compiler-specific information. + */ +{ + if (no_output || option_flags.p) + return; + sharp_file->line++; + sharp( sharp_file, 0); + sharp_file->line--; +#if COMPILER == GNUC + if (gcc_work_dir) + mcpp_fprintf( OUT, "%s%ld \"%s%c\"\n" + , std_line_prefix ? "#line " : LINE_PREFIX + , 1, cur_work_dir, '/'); + /* Putout the current directory as a #line line as: */ + /* '# 1 "/abs-path/cur_dir//"'. */ + mcpp_fprintf( OUT, "%s%ld \"\"\n" + , std_line_prefix ? "#line " : LINE_PREFIX , 1); + mcpp_fprintf( OUT, "%s%ld \"\"\n" + , std_line_prefix ? "#line " : LINE_PREFIX , 1); + mcpp_fprintf( OUT, "%s%ld \"%s\"%s\n" + , std_line_prefix ? "#line " : LINE_PREFIX, 1, cur_fullname + , ! str_eq( cur_fullname, sharp_file->full_fname) ? " 1" : null); + /* Suffix " 1" for the file specified by -include */ +#endif +} + +static char * set_files( + int argc, + char ** argv, + char ** in_pp, + char ** out_pp +) +/* + * Set input and/or output files. + */ +{ + char * cp; + + if (*in_pp == NULL) { /* Input file */ + cp = argv[ mcpp_optind++]; +#if SYS_FAMILY == SYS_WIN + cp = bsl2sl( cp); +#endif + *in_pp = cp; + } + if (mcpp_optind < argc && argv[ mcpp_optind][ 0] != '-' + && *out_pp == NULL) { + cp = argv[ mcpp_optind++]; +#if SYS_FAMILY == SYS_WIN + cp = bsl2sl( cp); +#endif + *out_pp = cp; /* Output file */ + } + if (mcpp_optind >= argc) + return NULL; /* Exhausted command line arguments */ + if (argv[ mcpp_optind][ 0] == '-') + return argv[ mcpp_optind]; /* More options */ + cfatal( "Excessive file argument \"%s\"", argv[ mcpp_optind], 0L , NULL); + return NULL; +} + +static void set_env_dirs( void) +/* + * Add to include path those specified by environment variables. + */ +{ + const char * env; + + if (cplus_val) { + if ((env = getenv( ENV_CPLUS_INCLUDE_DIR)) != NULL) + parse_env( env); + } + if ((env = getenv( ENV_C_INCLUDE_DIR)) != NULL) + parse_env( env); +} + +static void parse_env( + const char * env +) +/* + * Parse environmental variable and append the path to include-dir-list. + */ +{ + char * save; + char * save_start; + char * p; + int sep; + + save = save_start = save_string( env); + while (*save) { + p = save; + while (*p && *p != ENV_SEP) + p++; + if (p != save) { /* Variable separator */ + sep = *p; + *p = EOS; + set_a_dir( save); + if (sep == EOS) + break; + save = ++p; + } + while (*save == ENV_SEP) + ++save; + } + free( save_start); +} + +static void set_sys_dirs( + int set_cplus_dir /* Set C++ include-directory too */ +) +/* + * Set site-specific and system-specific directories to the include directory + * list. + */ +{ + if (cplus_val && set_cplus_dir) { +#ifdef CPLUS_INCLUDE_DIR1 + set_a_dir( CPLUS_INCLUDE_DIR1); +#endif +#ifdef CPLUS_INCLUDE_DIR2 + set_a_dir( CPLUS_INCLUDE_DIR2); +#endif +#ifdef CPLUS_INCLUDE_DIR3 + set_a_dir( CPLUS_INCLUDE_DIR3); +#endif +#ifdef CPLUS_INCLUDE_DIR4 + set_a_dir( CPLUS_INCLUDE_DIR4); +#endif + } + +#if SYS_FAMILY == SYS_UNIX + set_a_dir( "/usr/local/include"); +#endif + +#ifdef C_INCLUDE_DIR1 + set_a_dir( C_INCLUDE_DIR1); +#endif +#ifdef C_INCLUDE_DIR2 + set_a_dir( C_INCLUDE_DIR2); +#endif + +#if SYS_FAMILY == SYS_UNIX +#if SYSTEM == SYS_CYGWIN + if (no_cygwin) /* -mno-cygwin */ + set_a_dir( "/usr/include/mingw"); + else + set_a_dir( "/usr/include"); +#else + set_a_dir( "/usr/include"); /* Should be placed after C_INCLUDE_DIR? */ +#endif +#endif +} + +static void set_a_dir( + const char * dirname /* The path-name */ +) +/* + * Append an include directory. + * This routine is called from the following routines (in this order). + * 1. do_options() by -I option. + * 2. do_options() by -isystem option (for GNUC). + * 3. set_env_dirs() by environment variables. + * 4. set_sys_dirs() by CPLUS_INCLUDE_DIR?, C_INCLUDE_DIR? and system- + * specifics (unless -I- or -nostdinc option is specified). + * Ignore non-existent directory. + * Note that this routine should be called only in initializing steps, + * because increase of include dirs causes reallocation of incdir[]. + * Note: a trailing PATH-DELIM is appended by norm_path(). + */ +{ + char * norm_name; + const char ** ip; + + if (incdir == NULL) { /* Should be initialized */ + max_inc = INIT_NUM_INCLUDE; + incdir = (const char **) xmalloc( sizeof (char *) * max_inc); + incend = &incdir[ 0]; + } else if (incend - incdir >= max_inc) { /* Buffer full */ +#if SYSTEM == SYS_MAC + size_t framework_pos = to_search_framework - incdir; +#endif +#if COMPILER == GNUC + size_t sys_pos = 0; + if (sys_dirp) + sys_pos = sys_dirp - incdir; +#endif + incdir = (const char **) xrealloc( (void *) incdir + , sizeof (char *) * max_inc * 2); + incend = &incdir[ max_inc]; +#if COMPILER == GNUC + if (sys_pos) + sys_dirp = &incdir[ sys_pos]; +#endif +#if SYSTEM == SYS_MAC + to_search_framework = &incdir[ framework_pos]; +#endif + max_inc *= 2; + } + + if (dirname == NULL) + return; /* Only to initialize incdir[] */ + norm_name = norm_dir( dirname, FALSE); + if (! norm_name) /* Non-existent */ + return; + for (ip = incdir; ip < incend; ip++) { + if (str_case_eq( *ip, norm_name)) { + if (option_flags.v && ! (mcpp_debug & MACRO_CALL)) + mcpp_fprintf( ERR, "Duplicate directory \"%s\" is ignored\n" + , norm_name); + free( norm_name); /* Already registered */ + return; + } + } + /* Register new directory */ + *incend++ = norm_name; +} + +#if SYSTEM == SYS_MAC && COMPILER == GNUC +/* Definitions for "header map" file of Xcode / Apple-GCC. */ +/* These definitions were taken from cpplib.h of Apple-GCC-4.0.1 */ + +#define HMAP_SAME_ENDIANNESS_MAGIC \ + (((((('h' << 8) | 'm') << 8) | 'a') << 8) | 'p') + +typedef unsigned short uint16; +typedef unsigned int uint32; + +struct hmap_bucket +{ + uint32 key; /* Offset (into strings) of key */ + struct { + uint32 prefix; /* Offset (into strings) of value prefix */ + uint32 suffix; /* Offset (into strings) of value suffix */ + } value; /* Value (prefix- and suffix-strings) */ +}; + +struct hmap_header_map +{ + uint32 magic; /* Magic word, also indicates byte order */ + uint16 version; /* Version number -- currently 1 */ + uint16 _reserved; /* Reserved for future use -- zero for now */ + uint32 strings_offset; /* Offset to start of string pool */ + uint32 count; /* Number of entries in the string table */ + uint32 capacity; /* Number of buckets (always a power of 2) */ + uint32 max_value_length; + /* Length of longest result path (excl. '\0') */ + struct hmap_bucket buckets[1]; + /* Inline array of 'capacity' maptable buckets */ + /* Strings follow the buckets, at strings_offset. */ +}; +#endif + +static char * norm_dir( + const char * dirname, /* Directory path to normalize */ + int framework /* Setting a framework directory*/ +) +/* + * Normalize include directory path. + * Handle -isysroot option for GCC, including framework directory for SYS_MAC. + */ +{ + char * norm_name; + +#if COMPILER == GNUC + if (sysroot && sys_dirp) { + /* Logical system root specified and dirname is system header dir */ + char delim[ 2] = { EOS, EOS}; + char * dir; +#if SYSTEM == SYS_MAC + if (! framework && memcmp( dirname, "/usr/", 5) != 0) + return NULL; /* /Developer/usr/lib/gcc/ */ +#endif + if (dirname[ 0] != PATH_DELIM) + delim[ 0] = PATH_DELIM; + dir = xmalloc( strlen( sysroot) + strlen( dirname) + 2); + sprintf( dir, "%s%s%s", sysroot, delim, dirname); + dirname = dir; + } +#endif +#if SYSTEM == SYS_MAC && COMPILER == GNUC + if (strlen( dirname) > 5 + && str_case_eq( dirname + strlen( dirname) - 5, ".hmap")) { + /* "header map" file (not an include directory) */ + norm_name = norm_path( null, dirname, FALSE, TRUE); + if (! norm_name && option_flags.v) + mcpp_fprintf( ERR, "Invalid header map file \"%s\" is ignored\n" + , dirname); + } else +#endif + { + norm_name = norm_path( dirname, NULL, FALSE, FALSE); + /* Normalize the pathname to compare */ + if (! norm_name && option_flags.v && ! (mcpp_debug & MACRO_CALL)) + mcpp_fprintf( ERR, "Non-existent directory \"%s\" is ignored\n" + , dirname); + } +#if COMPILER == GNUC + if (sysroot && sys_dirp) + free( dirname); +#endif + + return norm_name; +} + +static char * norm_path( + const char * dir, /* Include directory (maybe "", never NULL) */ + const char * fname, + /* Filename (possibly has directory part, or maybe NULL) */ + int inf, /* If TRUE, output some infs when (mcpp_debug & PATH) */ + int hmap /* "header map" file of Apple-GCC */ +) +/* + * Normalize the pathname removing redundant components such as + * "foo/../", "./" and trailing "/.". + * Append trailing "/" if 'fname' is NULL. + * Change relative path to absolute path. + * Dereference a symbolic linked file (or directory) to a real directory/file. + * Return a malloc'ed buffer, if the directory/file exists. + * Return NULL, if the specified directory/file does not exist or 'dir' is + * not a directory or 'fname' is not a regular file. + * This routine is called from set_a_dir(), init_gcc_macro(), do_once() and + * open_file(). + */ +{ + char * norm_name; /* The path-list converted */ + char * start; + char * cp1; + char * cp2; + char * abs_path; + int len; /* Should not be size_t */ + size_t start_pos = 0; + char slbuf1[ PATHMAX+1]; /* Working buffer */ +#if SYS_FAMILY == SYS_UNIX + char slbuf2[ PATHMAX+1]; /* Working buffer for dereferencing */ +#endif +#if SYSTEM == SYS_CYGWIN || SYSTEM == SYS_MINGW + static char * root_dir; + /* System's root directory in Windows file system */ + static size_t root_dir_len; +#if SYSTEM == SYS_CYGWIN + static char * cygdrive = "/cygdrive/"; /* Prefix for drive letter */ +#else + static char * mingw_dir; /* "/mingw" dir in Windows */ + static size_t mingw_dir_len; +#endif +#endif +#if HOST_COMPILER == MSC + struct _stat st_buf; +#else + struct stat st_buf; +#endif + + if (! dir || (*dir && is_full_path( fname))) + cfatal( "Bug: Wrong argument to norm_path()" /* _F_ */ + , NULL, 0L, NULL); + inf = inf && (mcpp_debug & PATH); /* Output information */ + + strcpy( slbuf1, dir); /* Include directory */ + len = strlen( slbuf1); + if (fname && len && slbuf1[ len - 1] != PATH_DELIM) { + slbuf1[ len] = PATH_DELIM; /* Append PATH_DELIM */ + slbuf1[ ++len] = EOS; + } else if (! fname && len && slbuf1[ len - 1] == PATH_DELIM) { + /* stat() of some systems do not like trailing '/' */ + slbuf1[ --len] = EOS; + } + if (fname) + strcat( slbuf1, fname); + if (stat( slbuf1, & st_buf) != 0 /* Non-existent */ + || (! fname && ! S_ISDIR( st_buf.st_mode)) + /* Not a directory though 'fname' is not specified */ + || (fname && ! S_ISREG( st_buf.st_mode))) + /* Not a regular file though 'fname' is specified */ + return NULL; +#if SYSTEM == SYS_MAC && COMPILER == GNUC + if (hmap) { /* Dirty "header map" file */ + struct hmap_header_map hmap; + size_t cnt; + FILE * fp; + fp = fopen( fname, "r"); + cnt = fread( & hmap, sizeof (struct hmap_header_map), 1, fp); + fclose( fp); + if (cnt == 0 || hmap.magic != HMAP_SAME_ENDIANNESS_MAGIC) + return NULL; + } +#endif + if (! fname) { + slbuf1[ len] = PATH_DELIM; /* Append PATH_DELIM */ + slbuf1[ ++len] = EOS; + } +#if SYS_FAMILY == SYS_UNIX + /* Dereference symbolic linked directory or file, if any */ + slbuf1[ len] = EOS; /* Truncate PATH_DELIM and 'fname' part, if any */ + slbuf2[ 0] = EOS; + if (*dir && ! fname) { /* Registering include directory */ + /* Symbolic link check of directories are required */ + deref_syml( slbuf1, slbuf2, slbuf1); + } else if (fname) { /* Regular file */ + len = strlen( slbuf1); + strcat( slbuf1, fname); + deref_syml( slbuf1, slbuf2, slbuf1 + len); + /* Symbolic link check of directory */ + if ((len = readlink( slbuf1, slbuf2, PATHMAX)) > 0) { + /* Dereference symbolic linked file (not directory) */ + *(slbuf2 + len) = EOS; + cp1 = slbuf1; + if (slbuf2[ 0] != PATH_DELIM) { /* Relative path */ + cp2 = strrchr( slbuf1, PATH_DELIM); + if (cp2) /* Append to the source directory */ + cp1 = cp2 + 1; + } + strcpy( cp1, slbuf2); + } + } + if (inf) { + if (slbuf2[ 0]) + mcpp_fprintf( DBG, "Dereferenced \"%s%s\" to \"%s\"\n" + , dir, fname ? fname : null, slbuf1); + } +#endif + len = strlen( slbuf1); + start = norm_name = xmalloc( len + 1); /* Need a new buffer */ + strcpy( norm_name, slbuf1); +#if SYS_FAMILY == SYS_WIN + bsl2sl( norm_name); +#endif +#if SPECIAL_PATH_DELIM /* ':' ? */ + for (cp1 = norm_name; *cp1 != EOS; cp1++) { + if (*cp1 == PATH_DELIM) + *cp1 = '/'; + } +#endif + cp1 = norm_name; + +#if SYSTEM == SYS_CYGWIN + /* Convert to "/cygdirve/x/dir" style of absolute path-list */ + if (len >= 8 && (memcmp( cp1, "/usr/bin", 8) == 0 + || memcmp( cp1, "/usr/lib", 8) == 0)) { + memmove( cp1, cp1 + 4, len - 4 + 1); /* Remove "/usr" */ + len -= 4; + } + if (*cp1 == '/' && (len < 10 || memcmp( cp1, cygdrive, 10) != 0)) { + /* /dir, not /cygdrive/ */ + if (! root_dir_len) { /* Should be initialized */ + /* Convert "X:\DIR-list" to "/cygdrive/x/dir-list" */ + root_dir = xmalloc( strlen( CYGWIN_ROOT_DIRECTORY) + 1); + strcpy( root_dir, CYGWIN_ROOT_DIRECTORY); + *(root_dir + 1) = *root_dir; /* "x:/" to " x/" */ + cp1 = xmalloc( strlen( cygdrive) + strlen( root_dir)); + strcpy( cp1, cygdrive); + strcat( cp1, root_dir + 1); + free( root_dir); + root_dir = cp1; + root_dir_len = strlen( root_dir); + } + cp1 = xmalloc( root_dir_len + len + 1); + strcpy( cp1, root_dir); + strcat( cp1, norm_name); /* Convert to absolute path */ + free( norm_name); + norm_name = start = cp1; + len += root_dir_len; + } +#endif + +#if SYSTEM == SYS_MINGW + /* Handle the mess of MinGW's path-list */ + /* Convert to "x:/dir" style of absolute path-list */ + if (*cp1 == PATH_DELIM && isalpha( *(cp1 + 1)) + && *(cp1 + 2) == PATH_DELIM) { /* /c/, /d/, etc*/ + *cp1 = *(cp1 + 1); + *(cp1 + 1) = ':'; /* Convert to c:/, d:/, etc */ + } else if (memcmp( cp1, "/mingw", 6) == 0) { + if (! mingw_dir_len) { /* Should be initialized */ + mingw_dir_len = strlen( MINGW_DIRECTORY); + mingw_dir = xmalloc( mingw_dir_len + 1); + strcpy( mingw_dir, MINGW_DIRECTORY); + } + cp1 = xmalloc( mingw_dir_len + len + 1); + strcpy( cp1, mingw_dir); + strcat( cp1, norm_name + 6); /* Convert to absolute path */ + free( norm_name); + norm_name = start = cp1; + len += mingw_dir_len; + } else if (memcmp( cp1, "/usr", 4) == 0) { + memmove( cp1, cp1 + 4, len - 4 + 1); /* Remove "/usr" */ + len -= 4; + } + if (*cp1 == '/') { /* /dir or / */ + if (! root_dir_len) { /* Should be initialized */ + root_dir_len = strlen( MSYS_ROOT_DIRECTORY); + root_dir = xmalloc( root_dir_len + 1); + strcpy( root_dir, MSYS_ROOT_DIRECTORY); + } + cp1 = xmalloc( root_dir_len + len + 1); + strcpy( cp1, root_dir); + strcat( cp1, norm_name); /* Convert to absolute path */ + free( norm_name); + norm_name = start = cp1; + len += root_dir_len; + } +#endif + +#if SYS_FAMILY == SYS_WIN + if (*(cp1 + 1) == ':') + start = cp1 += 2; /* Next to the drive letter */ + start_pos = 2; +#endif + if (len == 1 && *norm_name == '/') /* Only "/" */ + return norm_name; + + if (strncmp( cp1, "./", 2) == 0) /* Remove beginning "./" */ + memmove( cp1, cp1 + 2, strlen( cp1 + 2) + 1); /* +1 for EOS */ + if (*start != '/') { /* Relative path to current directory */ + /* Make absolute path */ + abs_path = xmalloc( len + strlen( cur_work_dir) + 1); + cp1 = stpcpy( abs_path, cur_work_dir); + strcpy( cp1, start); + free( norm_name); + norm_name = abs_path; + start = cp1 = norm_name + start_pos; + } + + while ((cp1 = strstr( cp1, "/./")) != NULL) + memmove( cp1, cp1 + 2, strlen( cp1 + 2) + 1); + /* Remove "/." of "/./" */ + cp1 = start; + /* Remove redundant "foo/../" */ + while ((cp1 = strstr( cp1, "/../")) != NULL) { + *cp1 = EOS; + if ((cp2 = strrchr( start, '/')) != NULL) { + if (*(cp1 - 1) != '.') { + memmove( cp2 + 1, cp1 + 4, strlen( cp1 + 4) + 1); + /* Remove "foo/../" */ + cp1 = cp2; + } else { /* Impossible */ + break; + } + } else { /* Impossible */ + break; + } + } + +#if SPECIAL_PATH_DELIM + for (cp1 = start; *cp1 != EOS; cp1++) { + if (*cp1 == '/') + *cp1 = PATH_DELIM; + } +#endif + if (inf) { + char debug_buf[ PATHMAX+1]; + strcpy( debug_buf, dir); + strcat( debug_buf, fname ? fname : null); +#if SYS_FAMILY == SYS_WIN + bsl2sl( debug_buf); +#endif + if (! str_eq( debug_buf, norm_name)) + mcpp_fprintf( DBG, "Normalized the path \"%s\" to \"%s\"\n" + , debug_buf, norm_name); + } + + return norm_name; +} + +#if SYS_FAMILY == SYS_UNIX + +static void deref_syml( + char * slbuf1, /* Original path-list */ + char * slbuf2, /* Working buffer */ + char * chk_start /* Pointer into slbuf1 */ +) +/* Dereference symbolic linked directory */ +{ + char * cp2; + int len; /* Should be int, not size_t */ + + while ((chk_start = strchr( chk_start, PATH_DELIM)) != NULL) { + *chk_start = EOS; + if ((len = readlink( slbuf1, slbuf2, PATHMAX)) > 0) { + /* Dereference symbolic linked directory */ + cp2 = strrchr( slbuf1, PATH_DELIM); /* Previous delimiter */ + *chk_start = PATH_DELIM; + strcpy( slbuf2 + len, chk_start); + if (slbuf2[ 0] == PATH_DELIM) { /* Absolute path */ + strcpy( slbuf1, slbuf2); + chk_start = slbuf1 + len + 1; + } else { + if (cp2) + chk_start = cp2 + 1; + else + chk_start = slbuf1; + strcpy( chk_start, slbuf2); /* Rewrite the path */ + chk_start += len; + } + } else { + *chk_start++ = PATH_DELIM; + } + } +} +#endif + +#if COMPILER == GNUC + +static void init_gcc_macro( void) +/* + * Predefine GCC macros. + * This routine should be called after opening output file in order to putout + * macro informations by -K option into the file. + * Also this routine should be called before undef_macros() in order to + * permit undefining a macro by -U option. + */ +{ + char fname[ BUFSIZ]; + char lbuf[ BUFSIZ]; + char * include_dir; /* The version-specific include directory */ + char * tmp; + FILE * fp; + DEFBUF * defp; + const char * cp; + char * tp; + int i; + + if (nflag) /* -undef option */ + goto undef_special; + + tmp = xmalloc( strlen( INC_DIR) + strlen( "/mingw/mcpp-gcc-") + + strlen( arch) + 1); +#if SYSTEM == SYS_CYGWIN + if (no_cygwin) { + sprintf( tmp, "%s/mingw/mcpp-gcc-%s", INC_DIR, arch); + } else { + sprintf( tmp, "%s/mcpp-gcc-%s", INC_DIR, arch); + } +#else + sprintf( tmp, "%s/mcpp-gcc-%s", INC_DIR, arch); +#endif + include_dir = norm_path( tmp, NULL, TRUE, FALSE); + free( tmp); + + for (i = 0; i <= 1; i++) { + int nargs; + + if ((mcpp_mode == POST_STD || ansi) && i == 0) + continue; /* POST_STD or __STRICT_ANSI__ does not */ + /* predefine non-conforming macros */ + /* The predefined macro file */ + cp = i ? "std" : "old"; + sprintf( fname, "%sg%s%d%d_predef_%s.h" + , include_dir, cplus_val ? "xx" : "cc" + , gcc_maj_ver, gcc_min_ver, cp); + /* Note that norm_path() append a PATH_DELIM. */ + if ((fp = fopen( fname, "r")) == NULL) { + mcpp_fprintf( ERR, "The mode for %s has not been installed.\n" + , arch); + longjmp( error_exit, -1); + } + nargs = i ? 0 : DEF_NOARGS_PREDEF_OLD; + /* g*_predef_std.h has DEF_NOARGS_PREDEF or non-negative args */ + /* while g*_predef_old.h has only DEF_NOARGS_PREDEF_OLD args */ + while (fgets( lbuf, BUFSIZ, fp) != NULL) { + unget_string( lbuf, "gcc_predefine"); + if (skip_ws() == '#' + && scan_token( skip_ws(), (tp = work_buf, &tp), work_end) + == NAM + && str_eq( work_buf, "define")) { + defp = do_define( TRUE, nargs); /* Ignore re-definition */ + } + skip_nl(); + } + } + free( include_dir); + +undef_special: + if (look_id( "__OPTIMIZE__")) /* -O option is specified */ + undefine( "__NO_INLINE__"); +} + +static void chk_env( void) +/* + * Check the environment variables to specify output of dependency lines. + */ +{ + char * env; + char * cp; + + /* Output of dependency lines */ + if ((env = getenv( "DEPENDENCIES_OUTPUT")) == NULL) { + if ((env = getenv( "SUNPRO_DEPENDENCIES")) == NULL) + return; + else + mkdep |= MD_SYSHEADER; + } + mkdep |= MD_MKDEP; + if ((cp = strchr( env, ' ')) != NULL) { + *cp++ = EOS; + while (*cp == ' ') + cp++; + } + if (! mkdep_fp) /* Command line option precedes */ + mkdep_fp = fopen( env, "a"); + if (! mkdep_target) + mkdep_target = cp; +} + +#elif COMPILER == MSC + +static void init_msc_macro( void) +/* + * Define a few MSC-specific predefined macros. + */ +{ + DEFBUF * defp; + int i; + + defp = look_id( "_MSC_VER"); + i = atoi( defp->repl); + if (i >= 1400) { /* _MSC_VER >= 1400 */ + look_and_install( "_MT", DEF_NOARGS_PREDEF, null, "1"); + if (cplus_val && ! wchar_t_modified) { + /* -Zc:wchar_t- was not specified */ + look_and_install( "_NATIVE_WCHAR_T_DEFINED", DEF_NOARGS_PREDEF + , null, "1"); + look_and_install( "_WCHAR_T_DEFINED", DEF_NOARGS_PREDEF, null + , "1"); + } + } +} + +#endif + +static void def_macros( void) +/* + * Define macros specified by -D option. + * This routine should be called before undef_macros(). + */ +{ + int i; + + for (i = 0; i < def_cnt; i++) + def_a_macro( 'D', def_list[ i]); +} + +static void undef_macros( void) +/* + * Undefine macros specified by -U option. + * This routine should be called after init_predefine(). + */ +{ + char * name; + int i; + + for (i = 0; i < undef_cnt; i++) { + name = undef_list[ i]; + if (look_id( name) != NULL) + undefine( name); + else if (warn_level & 8) + mcpp_fprintf( ERR, "\"%s\" wasn't defined\n", name); + } +} + +void put_depend( + const char * filename +) +/* + * Append a header name to the source file dependency line. + */ +{ +#define MAX_OUT_LEN 76 /* Maximum length of output line */ +#define MKDEP_INITLEN (MKDEP_INIT * 0x100) +#define MKDEP_MAX (MKDEP_INIT * 0x10) +#define MKDEP_MAXLEN (MKDEP_INITLEN * 0x10) + + static char * output = NULL; /* File names */ + static size_t * pos = NULL; /* Offset to filenames */ + static int pos_num; /* Index of pos[] */ + static char * out_p; /* Pointer to output[] */ + static size_t mkdep_len; /* Size of output[] */ + static size_t pos_max; /* Size of pos[] */ + static FILE * fp; /* Path to output dependency line */ + static size_t llen; /* Length of current physical output line */ + size_t * pos_p; /* Index into pos[] */ + size_t fnamlen; /* Length of filename */ + + if (fp == NULL) { /* Main source file. Have to initialize. */ +#if MCPP_LIB + if (output != NULL) { + free( output); + free( pos); + } +#endif + output = xmalloc( mkdep_len = MKDEP_INITLEN); + pos = (size_t *) xmalloc( (pos_max = MKDEP_INIT) * sizeof (size_t)); + out_p = md_init( filename, output); + fp = mkdep_fp; + llen = strlen( output); + pos_num = 0; /* Initialize for MCPP_LIB build */ + } else if (filename == NULL) { /* End of input */ + out_p = stpcpy( out_p, "\n\n"); + if (mkdep & MD_PHONY) { + /* Output the phony target line for each recorded header files. */ + char * cp; + int c; + + if (strlen( output) * 2 + (pos_num * 2) >= MKDEP_MAXLEN) { + cerror( "Too long dependency line" /* _E_ */ + , NULL, 0L, NULL); + if (fp == fp_out) + mcpp_fputs( output, OUT); + else + fputs( output, fp); + return; + } else if (strlen( output) * 2 + (pos_num * 2) >= mkdep_len) { + /* Enlarge the buffer */ + size_t len = out_p - output; + output = xrealloc( output, mkdep_len *= 2); + out_p = output + len; + } + pos_num--; + for (pos_p = &pos[ 0]; pos_p <= &pos[ pos_num]; pos_p++) { + if (pos_p == &pos[ pos_num]) { /* End of output */ + for (cp = output + *pos_p; *cp != '\n'; cp++) + ; + c = '\n'; /* Append newline */ + } else { + cp = output + *(pos_p + 1) - 1; + while( *cp == ' ' || *cp == '\\' || *cp == '\n') + cp--; /* Remove trailing spaces */ + c = *(++cp); + } + *cp = EOS; + out_p = stpcpy( out_p, output + *pos_p); + out_p = stpcpy( out_p, ":\n\n"); + *cp = c; + } + } + if (fp == fp_out) { /* To the same path with normal preprocessing */ + mcpp_fputs( output, OUT); + } else { /* To the file specified by -MF, -MD, -MMD options */ + fputs( output, fp); + fclose( fp); + } + fp = NULL; /* Clear for the next call in MCPP_LIB build */ + return; + } + + fnamlen = strlen( filename); + /* Check the recorded filename */ + for (pos_p = pos; pos_p < &pos[ pos_num]; pos_p++) { + if (memcmp( output + *pos_p, filename, fnamlen) == 0) + return; /* Already recorded filename */ + } + /* Any new header. Append its name to output. */ + if (llen + fnamlen > MAX_OUT_LEN) { /* Line is long */ + out_p = stpcpy( out_p, " \\\n "); /* Fold it */ + llen = 1; + } + llen += fnamlen + 1; + if (pos_num >= MKDEP_MAX + || out_p + fnamlen + 1 >= output + MKDEP_MAXLEN) + cfatal( "Too long dependency line: %s", output, 0L, NULL); + /* Need to enlarge the buffer */ + if (pos_num >= pos_max) { + pos = (size_t *) xrealloc( (char *) pos + , (pos_max *= 2) * sizeof (size_t *)); + } + if (output + mkdep_len <= out_p + fnamlen + 1) { + size_t len = out_p - output; + output = xrealloc( output, mkdep_len *= 2); + out_p = output + len; + } + *out_p++ = ' '; + pos[ pos_num++] = out_p - output; /* Remember the offset */ + /* Don't use pointer, since 'output' may be reallocated later. */ + out_p = stpcpy( out_p, filename); +} + +static char * md_init( + const char * filename, /* The source file name */ + char * output /* Output to dependency file */ +) +/* + * Initialize output file and target. + */ +{ + char prefix[ PATHMAX]; + char * cp = NULL; + size_t len; + char * out_p; + const char * target = filename; + const char * cp0; + + if (! mkdep_target || ! mkdep_fp) { /* Make target name */ +#ifdef PATH_DELIM + if ((cp0 = strrchr( target, PATH_DELIM)) != NULL) + target = cp0 + 1; +#endif + if ((cp0 = strrchr( target, '.')) == NULL) + len = strlen( target); + else + len = (size_t) (cp0 - target); + memcpy( prefix, target, len); + cp = prefix + len; + *cp++ = '.'; + } + + if (! mkdep_fp) { /* Unless already opened by -MF, -MD, -MMD options */ + if (mkdep & MD_FILE) { + strcpy( cp, "d"); + mkdep_fp = fopen( prefix, "w"); + } else { + mkdep_fp = fp_out; /* Output dependency line to normal output */ + no_output++; /* Without normal output */ + } + } + + if (mkdep_target) { /* -MT or -MQ option is specified */ + if (mkdep & MD_QUOTE) { /* 'Quote' $, \t and space */ + out_p = md_quote( output); + } else { + out_p = stpcpy( output, mkdep_target); + } + } else { + strcpy( cp, OBJEXT); + out_p = stpcpy( output, prefix); + } + + *out_p++ = ':'; + *out_p = EOS; + return out_p; +} + +static char * md_quote( + char * output +) +/* + * 'Quote' $, tab and space. + * This function was written referring to GCC V.3.2 source. + */ +{ + char * p; + char * q; + + for (p = mkdep_target; *p; p++, output++) { + switch (*p) { + case ' ': + case '\t': + /* GNU-make treats backslash-space sequence peculiarly */ + for (q = p - 1; mkdep_target <= q && *q == '\\'; q--) + *output++ = '\\'; + *output++ = '\\'; + break; + case '$': + *output++ = '$'; + break; + default: + break; + } + *output = *p; + } + *output = EOS; + return output; +} + +static const char * toolong_fname = + "Too long header name \"%s%.0ld%s\""; /* _F_ */ +static const char * excess_token = + "Excessive token sequence \"%s\""; /* _E_, _W1_ */ + +int do_include( + int next /* TRUE if the directive is #include_next */ +) +/* + * Process the #include line. + * There are three variations: + * #include "file" search somewhere relative to the + * current (or source) directory, if not + * found, treat as #include . + * #include Search in an implementation-dependent + * list of places. + * #include macro-call Expand the macro call, it must be one of + * "file" or , process as such. + * On success : return TRUE; + * On failure of syntax : return FALSE; + * On failure of file opening : return FALSE. + * do_include() always absorbs the line (including the ). + */ +{ + const char * const no_name = "No header name"; /* _E_ */ + char header[ PATHMAX + 16]; + int token_type; + char * fname; + char * filename; + int delim; /* " or <, > */ + + if ((delim = skip_ws()) == '\n') { /* No argument */ + cerror( no_name, NULL, 0L, NULL); + return FALSE; + } + fname = infile->bptr - 1; /* Current token for diagnosis */ + + if (standard && (char_type[ delim] & LET)) { /* Maybe macro */ + int c; + char *hp; + + hp = header; + *hp = EOS; + c = delim; + while (get_unexpandable( c, FALSE) != NO_TOKEN) { + /* Expand any macros in the line */ + if (header + PATHMAX < hp + (int) (workp - work_buf)) + cfatal( toolong_fname, header, 0L, work_buf); + hp = stpcpy( hp, work_buf); + while (char_type[ c = get_ch()] & HSP) + *hp++ = c; + } + *hp = EOS; /* Ensure to terminate */ + if (macro_line == MACRO_ERROR) /* Unterminated macro */ + return FALSE; /* already diagnosed. */ + unget_string( header, NULL); /* To re-read */ + delim = skip_ws(); + if (delim == '\n') { + cerror( no_name, NULL, 0L, NULL); /* Expanded to */ + return FALSE; /* 0 token. */ + } + } + + token_type = scan_token( delim, (workp = work_buf, &workp) + , work_buf + PATHMAX); + if (token_type == STR) /* String literal form */ + goto found_name; + else if (token_type == OPE && openum == OP_LT) /* '<' */ + workp = scan_quote( delim, work_buf, work_buf + PATHMAX, TRUE); + /* Re-construct or diagnose */ + else /* Any other token in- */ + goto not_header; /* cluding <=, <<, <% */ + + if (workp == NULL) /* Missing closing '>' */ + goto syntax_error; + +found_name: + *--workp = EOS; /* Remove the closing and */ + fname = save_string( &work_buf[ 1]); /* the starting delimiter. */ + + if (skip_ws() != '\n') { + if (standard) { + cerror( excess_token, infile->bptr-1, 0L, NULL); + skip_nl(); + goto error; + } else if (mcpp_mode == OLD_PREP) { + skip_nl(); + } else { + if (warn_level & 1) + cwarn( excess_token, infile->bptr-1, 0L, NULL); + skip_nl(); + } + } + +#if SYS_FAMILY == SYS_WIN + bsl2sl( fname); +#endif + filename = fname; +#if NO_DIR /* Unofficial feature */ + if (no_dir) { /* Strip directory components */ + char src_dir[ PATHMAX] = { EOS, }; + if (has_directory( fname, src_dir)) + filename = fname + strlen( src_dir); + delim = '"'; /* Even a system header is handled as a local one */ + } +#endif + if (open_include( filename, (delim == '"'), next)) { + /* 'fname' should not be free()ed, it is used as file-> */ + /* real_fname and has been registered into fnamelist[] */ + return TRUE; + } + + cerror( "Can't open include file \"%s\"", filename, 0L, NULL); /* _E_ */ +error: + free( fname); + return FALSE; + +not_header: + cerror( "Not a header name \"%s\"", fname, 0L, NULL); /* _E_ */ +syntax_error: + skip_nl(); + return FALSE; +} + +static int open_include( + char * filename, /* File name to include */ + int searchlocal, /* TRUE if #include "file" */ + int next /* TRUE if #include_next */ +) +/* + * Open an include file. This routine is only called from do_include() above. + * It searches the list of directories via search_dir() and opens the file + * via open_file(), linking it into the list of active files. + * Returns TRUE if the file was opened, FALSE if it fails. + */ +{ + char src_dir[ PATHMAX] = { EOS, }; /* Directory part of includer */ + int full_path; /* Filename is full-path-list */ + int has_dir = FALSE; /* Includer has directory part */ + int has_dir_src = FALSE; + int has_dir_fname = FALSE; + + full_path = is_full_path( filename); + + if (!full_path && searchlocal && (search_rule & SOURCE)) { + has_dir_src = has_directory( infile->src_dir, src_dir); + has_dir_fname = has_directory( infile->real_fname + , src_dir + strlen( src_dir)); + /* Get directory part of the parent file of the file to include.*/ + /* Note that infile->dirp of main input file is set to "" and */ + /* remains the same even if -include options are processed. */ + has_dir = has_dir_src || has_dir_fname + || (**(infile->dirp) != EOS); + } + if (mcpp_debug & PATH) + mcpp_fprintf( DBG, "filename: %s\n", filename); + +#if COMPILER == GNUC + if (! full_path) { + if (i_split /* -I- option is specified */ + || next) /* or #include_next */ + goto search_dirs; + } +#endif + + if ((searchlocal && ((search_rule & CURRENT) || !has_dir)) || full_path) { + /* + * Look in local directory first. + * Try to open filename relative to the "current directory". + */ + if (open_file( &null, NULL, filename, searchlocal && !full_path + , FALSE, FALSE)) + return TRUE; + if (full_path) + return FALSE; + } + + if (searchlocal && (search_rule & SOURCE) && has_dir) { + /* + * Look in local directory of source file. + * Try to open filename relative to the "source directory". + */ + if (open_file( infile->dirp, src_dir, filename, TRUE, FALSE, FALSE)) + return TRUE; + } + +#if COMPILER == MSC + if (searchlocal) { + /* Visual C searches ancestor source's directory, too. */ + FILEINFO * file = infile; + while ((file = file->parent) != NULL) { + /* Search each parent includer's directory */ + if (open_file( file->dirp, src_dir, filename, TRUE, FALSE, FALSE)) + return TRUE; + } + } +#endif +#if COMPILER == GNUC +search_dirs: + if (searchlocal) { + /* Search the directories specified by -iquote option, if any. */ + const char ** qdir; + for (qdir = quote_dir; qdir < quote_dir_end; qdir++) { + if (open_file( qdir, NULL, filename, FALSE, FALSE, FALSE)) + return TRUE; + } + } +#endif + /* Search the include directories */ + if (search_dir( filename, searchlocal, next)) + return TRUE; + + return FALSE; +} + +static int has_directory( + const char * source, /* Filename to examine */ + char * directory /* Put directory stuff here */ +) +/* + * If a directory is found in the 'source' filename string (i.e. "includer"), + * the directory part of the string is copied to 'directory' and + * has_directory() returns TRUE. + * Else, nothing is copied and it returns FALSE. + */ +{ + const char * sp; + size_t len; + + if (! source) + return FALSE; + if ((sp = strrchr( source, PATH_DELIM)) == NULL) { + return FALSE; + } else { + len = (size_t)(sp - source) + 1; /* With path-delimiter */ + memcpy( directory, source, len); + directory[ len] = EOS; + return TRUE; + } +} + +static int is_full_path( + const char * path +) +/* + * Check whether the path is a full (absolute) path list or not. + */ +{ + if (! path) + return FALSE; +#if SYS_FAMILY == SYS_UNIX + if (path[0] == PATH_DELIM) +#elif SYS_FAMILY == SYS_WIN + if ((path[1] == ':' && path[2] == PATH_DELIM) /* "C:/path" */ + || path[0] == PATH_DELIM) /* Root dir of current drive */ +#elif 1 +/* For other systems you should write code here. */ + if (path[0] == PATH_DELIM) +#endif + return TRUE; + else + return FALSE; +} + +static int search_dir( + char * filename, /* File name to include */ + int searchlocal, /* #include "header.h" */ + int next /* TRUE if #include_next */ +) +/* + * Look in any directories specified by -I command line arguments, + * specified by environment variable, then in the builtin search list. + */ +{ + const char ** incptr; /* -> inlcude directory */ + + incptr = incdir; +#if COMPILER == GNUC + if (next && **inc_dirp != EOS) + incptr = inc_dirp + 1; + /* In case of include_next search after the includer's directory */ +#endif + + for ( ; incptr < incend; incptr++) { + if (strlen( *incptr) + strlen( filename) >= PATHMAX) + cfatal( toolong_fname, *incptr, 0L, filename); /* _F_ */ +#if SYSTEM == SYS_MAC + if (incptr == to_search_framework && ! searchlocal) { + /* Now search the framework dirs */ + if (search_framework( filename)) { /* Found */ + if (in_import) /* "#import"ed file is once only */ + do_once( infile->full_fname); + return TRUE; + } + /* Else continue to search incptr */ + } +#endif + if (open_file( incptr, NULL, filename, FALSE, FALSE, FALSE)) + /* Now infile has been renewed */ + return TRUE; + } + + return FALSE; +} + +static int open_file( + const char ** dirp, /* Pointer to include directory */ + const char * src_dir, /* Source directory of includer */ + const char * filename, /* Filename (possibly has directory) */ + int local, /* #include "file" */ + int include_opt, /* Specified by -include option */ + int sys_frame /* System framework header (for SYS_MAC)*/ +) +/* + * Open a file, add it to the linked list of open files, close the includer + * if nessesary and truncate the includer's buffer. + * This is called from open_include() and at_start(). + */ +{ + char dir_fname[ PATHMAX] = { EOS, }; +#if HOST_COMPILER == BORLANDC + /* Borland's fopen() does not set errno. */ + static int max_open = FOPEN_MAX - 5; +#else + static int max_open; +#endif + int len; + FILEINFO * file = infile; + FILE * fp; + char * fullname; + const char * fname; + + errno = 0; /* Clear errno possibly set by path searching */ +#if SYSTEM == SYS_MAC && COMPILER == GNUC + if (strlen( *dirp) > 5 + && str_case_eq( *dirp + strlen( *dirp) - 5, ".hmap")) { + /* Search header map file for a header */ + if (! search_header_map( *dirp, filename, dir_fname)) + return NULL; + fname = dir_fname; /* Found a path-list */ + dirp = &null; + goto search; + } else +#endif + { + if (mcpp_debug & PATH) + mcpp_fprintf( DBG, "Searching %s%s%s\n", *dirp + , src_dir ? src_dir : null, filename); + } + /* src_dir is usually NULL. This is specified to */ + /* search the source directory of the includer. */ + if (src_dir && *src_dir != EOS) { + strcpy( dir_fname, src_dir); + strcat( dir_fname, filename); + fname = dir_fname; + } else { + fname = filename; + } +search: + fullname = norm_path( *dirp, fname, TRUE, FALSE); + /* Convert to absolute path */ + if (! fullname) /* Non-existent or directory */ + return FALSE; + if (standard && included( fullname)) /* Once included */ + goto true; + + if ((max_open != 0 && max_open <= include_nest) + /* Exceed the known limit of open files */ + || ((fp = fopen( fullname, "r")) == NULL && errno == EMFILE)) { + /* Reached the limit for the first time */ + if (mcpp_debug & PATH) { +#if HOST_COMPILER == BORLANDC + if (include_nest == FOPEN_MAX - 5) +#else + if (max_open == 0) +#endif + mcpp_fprintf( DBG, + "#include nest reached at the maximum of system: %d, returned errno: %d\n" + , include_nest, errno); + } + /* + * Table of open files is full. + * Remember the file position and close the includer. + * The state will be restored by get_line() on end of the included. + */ + file->pos = ftell( file->fp); + fclose( file->fp); + /* In case of failure, re-open the includer */ + if ((fp = fopen( fullname, "r")) == NULL) { + file->fp = fopen( cur_fullname, "r"); + fseek( file->fp, file->pos, SEEK_SET); + goto false; + } + if (max_open == 0) /* Remember the limit of the system */ + max_open = include_nest; + } else if (fp == NULL) /* No read permission */ + goto false; + /* Truncate buffer of the includer to save memory */ + len = (int) (file->bptr - file->buffer); + if (len) { + file->buffer = xrealloc( file->buffer, len + 1); + file->bptr = file->buffer + len; + } + + if (! include_opt) + sharp( NULL, 0); /* Print includer's line num and fname */ + add_file( fp, src_dir, filename, fullname, include_opt); + /* Add file-info to the linked list. 'infile' has been just renewed */ + /* + * Remember the directory for #include_next. + * Note: inc_dirp is restored to the parent includer's directory + * by get_ch() when the current includer is finished. + */ + infile->dirp = inc_dirp = dirp; +#if 0 /* This part is only for debugging */ + chk_dirp( dirp); +#endif +#if COMPILER == GNUC + if ((**dirp != EOS && sys_dirp <= dirp && dirp <= incend) +#if SYSTEM == SYS_MAC + || sys_frame +#endif + ) + infile->sys_header = TRUE; /* Found in a system header dir */ + else + infile->sys_header = FALSE; +#endif + cur_fullname = fullname; + + if (option_flags.z) { + no_output++; /* Don't output the included file */ + if (include_nest == 2) + mcpp_fprintf( OUT, "#include \"%s\"\n", fullname); + /* Output #include line instead, if it is in main source file */ + } else if (! include_opt) { /* Do not sharp() on -include */ + src_line = 1; /* Working on line 1 now */ + sharp( NULL, 1); /* Print out the included file name */ + } + src_line = 0; /* To read the first line */ + + if (mkdep && ((mkdep & MD_SYSHEADER) || ! infile->sys_header)) + put_depend( fullname); /* Output dependency line */ + +true: + return TRUE; +false: + free( fullname); + return FALSE; +} + +void add_file( + FILE * fp, /* Open file pointer */ + const char * src_dir, /* Directory of source */ + const char * filename, /* Name of the file */ + const char * fullname, /* Full path list */ + int include_opt /* File specified by -include option */ +) +/* + * Initialize tables for this open file. This is called from open_file() + * (for #include files), and from the entry to MCPP to open the main input + * file. It calls a common routine get_file() to build the FILEINFO + * structure which is used to read characters. + */ +{ + FILEINFO * file; + const char * too_many_include_nest = + "More than %.0s%ld nesting of #include"; /* _F_ _W4_ */ + + // + // When encoding is UTF-8, skip BOM if present. + // + if(fp != NULL && ftell(fp) == 0) + { + const unsigned char UTF8_BOM[3] = {0xEF, 0xBB, 0xBF}; + unsigned char FILE_HEAD[3] = {0, 0, 0}; + int i; + for(i = 0; i < 3; ++i) + { + FILE_HEAD[i] = getc(fp); + if(FILE_HEAD[i] != UTF8_BOM[i]) + { + if(FILE_HEAD[i] == (unsigned char)EOF) + { + i--; + } + for(; i >= 0; --i) + { + ungetc(FILE_HEAD[i], fp); + } + break; + } + } + } + + filename = set_fname( filename); /* Search or append to fnamelist[] */ + fullname = set_fname( fullname); /* Search or append to fnamelist[] */ + file = get_file( filename, src_dir, fullname, (size_t) NBUFF, include_opt); + /* file == infile */ + file->fp = fp; /* Better remember FILE * */ + cur_fname = filename; + + if (include_nest >= INCLUDE_NEST) /* Probably recursive #include */ + cfatal( too_many_include_nest, NULL, (long) INCLUDE_NEST, NULL); + if (standard && (warn_level & 4) + && include_nest == std_limits.inc_nest + 1) + cwarn( too_many_include_nest, NULL, (long) std_limits.inc_nest, NULL); + include_nest++; +} + +static const char * set_fname( + const char * filename +) +/* + * Register the source filename to fnamelist[]. + * Search fnamelist[] for filename or append filename to fnamelist[]. + * Returns the pointer. + * file->real_fname and file->full_fname points into fnamelist[]. + */ +{ + INC_LIST * fnamep; + size_t fnamelen; + + if (fnamelist == NULL) { /* Should be initialized */ + max_fnamelist = INIT_NUM_FNAMELIST; + fnamelist = (INC_LIST *) xmalloc( sizeof (INC_LIST) * max_fnamelist); + fname_end = &fnamelist[ 0]; + } else if (fname_end - fnamelist >= max_fnamelist) { + /* Buffer full: double the elements */ + fnamelist = (INC_LIST *) xrealloc( (void *) fnamelist + , sizeof (INC_LIST) * max_fnamelist * 2); + fname_end = &fnamelist[ max_fnamelist]; + max_fnamelist *= 2; + } + + /* Register the filename in fnamelist[] */ + fnamelen = strlen( filename); + for (fnamep = fnamelist; fnamep < fname_end; fnamep++) { + if (fnamep->len == fnamelen && str_case_eq( fnamep->name, filename)) + return filename; /* Already registered */ + } + fname_end->name = xmalloc( fnamelen + 1); + filename = strcpy( fname_end->name, filename); + /* Global pointer for get_file() */ + fname_end->len = fnamelen; + fname_end++; + + return filename; +} + +#if SYSTEM == SYS_MAC +#if COMPILER == GNUC + +/* Routines to search "header map" file of Xcode / Apple-GCC. */ +/* search_header_map() and hmap_hash() were written referring to */ +/* c-incpath.c of Apple-GCC-4.0.1. */ + +static char * search_header_map( + const char * hmap_file, /* Header map file */ + const char * filename, /* Filename to search */ + char * pathlist /* Buffer for a found pathlist */ +) +/* + * Search a table in "header map" file for a header. + */ +{ + struct stat stat_buf; + FILE * fp; + size_t fsize; + const char * contents; + struct hmap_header_map * hmap; + struct hmap_bucket * buckets; + const char * strings; + uint32 mask; + uint32 key_offs; + uint32 i; + + stat( hmap_file, &stat_buf); /* Get size of the file */ + fsize = stat_buf.st_size; + contents = xmalloc( fsize + 1); + fp = fopen( hmap_file, "r"); + fread( contents, fsize, 1, fp); /* Read whole of the file at once */ + hmap = (struct hmap_header_map *) contents; + + strings = ((const char *) hmap) + hmap->strings_offset; + buckets = hmap->buckets; + mask = hmap->capacity - 1; + i = hmap_hash( filename) & mask; + while ((key_offs = buckets[ i].key) != 0) { + if (str_case_eq( filename, strings + key_offs)) { + /* The names match. Make path-list. */ + char * cp = stpcpy( pathlist, strings + buckets[ i].value.prefix); + strcpy( cp, strings + buckets[ i].value.suffix); + break; + } + i = ++i & mask; + } + free( contents); + return key_offs ? pathlist : NULL; +} + +static unsigned hmap_hash( + const char * fname /* header name */ +) +/* + * Get hash value for the fname. + */ +{ + const char * sp; + unsigned hash_code = 0; + + for (sp = fname; *sp; sp++) + hash_code += tolower( *sp & 0xFF) * 13; + return hash_code; +} +#endif /* COMPILER == GNUC */ + +static void init_framework( void) +/* + * Initialize framework[]. + */ +{ + char * framework_dir; + /* Some frameworks may have been already specified by -F option. */ + sys_framework = num_framework; /* These are system frameworks */ +#ifdef FRAMEWORK1 + framework_dir = norm_dir( FRAMEWORK1, TRUE); + if (framework_dir) + framework[ num_framework++] = framework_dir; +#endif +#ifdef FRAMEWORK2 + framework_dir = norm_dir( FRAMEWORK2, TRUE); + if (framework_dir) + framework[ num_framework++] = framework_dir; +#endif +#ifdef FRAMEWORK3 + framework_dir = norm_dir( FRAMEWORK3, TRUE); + if (framework_dir) + framework[ num_framework++] = framework_dir; +#endif + if (num_framework >= MAX_FRAMEWORK) { + mcpp_fputs( "Too many Framework directories.", ERR); + longjmp( error_exit, -1); + } +} + +static const char * dot_frame = ".framework"; + +static int search_framework( + char * filename +) +/* + * Search "Framework" directories. + * 'frame/header.h' is converted to + * '/System/Library/Frameworks/frame.framework/Headers/header.h', + * '/System/Library/Frameworks/frame.framework/PrivateHeaders/header.h', + * and so on. + */ +{ + char fullname[ PATHMAX + 1]; + FILEINFO * file; + char * frame, * fname, * cp1, * cp2; + int sys_frame = FALSE; + int i; + + cp1 = cp2 = strchr( filename, PATH_DELIM); + /* + * 'filename' should be format or sometimes + * . + * e.g.: , + * or . + */ + if (! cp1) + return FALSE; + *cp1 = EOS; + frame = filename; + fname = cp1 + 1; + + /* Search framework[] directories */ + for (i = 0; i < num_framework; i++) { + cp1 = stpcpy( fullname, framework[ i]); + /* 'fullname' e.g.: /System/Library/Frameworks/ */ + if (search_subdir( fullname, cp1, frame, fname, sys_framework <= i)) + return TRUE; + } + + /* + * Search subframework dirs searching its possible parent framework + * starting from current file's directory to its ancestors. + * Header file in subframework directories should be included only + * by its parent or sibling framework headers. + */ + for (i = sys_framework; i < num_framework; i++) { + size_t frame_len, fname_len; + frame_len = strlen( framework[ i]); + fname_len = strlen( infile->real_fname); + if (fname_len <= frame_len) + continue; + if (memcmp( framework[ i], infile->real_fname, frame_len) == 0) { + sys_frame = TRUE; + break; + } + } + for (file = infile; file; file = file->parent) { + const char * dot; + size_t len; + + if (! file->fp) + continue; + dot = strstr( file->real_fname, dot_frame); + if (! dot) + continue; + len = dot - file->real_fname + strlen( dot_frame) + 1; + memcpy( fullname, file->real_fname, len); + cp1 = fullname + len; + cp1 = stpcpy( cp1, "Frameworks/"); + /* 'fullname' e.g.: */ + /* /System/Library/Frameworks/Foundation.framework/Frameworks/ */ + if (search_subdir( fullname, cp1, frame, fname, sys_frame)) + return TRUE; + } + + *cp2 = PATH_DELIM; /* Restore original include file format */ + + return FALSE; +} + +static int search_subdir( + char * fullname, /* Buffer for path-list to open */ + char * cp, /* Latter half of 'fullname' */ + char * frame, /* 'frame' of */ + char * fname, /* 'header' of */ + /* or sometimes 'dir/header' of */ + int sys_frame /* System framework header ? */ +) +/* + * Make path-list and try to open. + */ +{ + static const char * subdir[] = { "Headers", "PrivateHeaders", NULL}; + int j, n; + + cp += sprintf( cp, "%s%s%c", frame, dot_frame, PATH_DELIM); + for (j = 0; subdir[ j] != NULL; j++) { + n = sprintf( cp, "%s%c%s", subdir[ j], PATH_DELIM, fname); + /* + * 'fullname' is for example: + * /System/Library/Frameworks/Foundation.framework/Headers/ + * Foundation.h, + * /System/Library/Frameworks/Foundation.framework/Frameworks/ + * CarbonCore.framework/Headers/OSUtils.h, + * or /System/Library/Frameworks/IOKit.framework/Headers/ + * pwr_mgt/IOPMLib.h. + * Pass this as one filename argument to open_file() rather than + * deviding to directory part and file part. The first argument to + * open_file() which is a pointer to the directory part is remembered + * by FILEINFO struct. But, 'fullname' is over-written each time, + * and the former path-list is lost soon. Therefore, it cannot be + * passed as the first argument. In addition, though the first + * argument to open_file() is needed for #include_next, this directive + * has no meaning in framework. + */ + if ((cp - fullname) + n > PATHMAX) + cfatal( "Too long framework path", NULL, 0L, NULL); /* _F_ */ + if (open_file( &null, NULL, fullname, FALSE, FALSE, sys_frame)) + return TRUE; + } + return FALSE; +} + +#endif /* SYSTEM == SYS_MAC */ + +#if 0 /* This part is only for debugging */ +static int chk_dirp( + const char ** dirp +) +/* + * Check the validity of include directory specified for open_file(). + * Called only from open_file(). + */ +{ + const char ** ip; + + if (dirp == &null) + return TRUE; + + for (ip = incdir; ip < incend; ip++) + if (dirp == ip) + break; + if (ip == incend) { +#if COMPILER == MSC + FILEINFO * pfile = infile->parent; + if (pfile) { + while ((pfile = pfile->parent) != NULL) { + /* Search each parent includer's directory */ + if (dirp == pfile->dirp) + break; + } + } + if (! pfile) +#endif +#if COMPILER == GNUC + const char ** qdir; + for (qdir = quote_dir; qdir < quote_dir_end; qdir++) { + if (dirp == qdir) + break; + } + if (qdir == quote_dir_end) +#endif + { + cfatal( "Bug: *dirp:%s is invalid", *dirp, 0L, NULL); + return FALSE; + } + } + return TRUE; +} +#endif + +FILEINFO* sh_file; +int sh_line; + +void sharp( + FILEINFO * sharp_file, + int flag /* Flag to append to the line for GCC */ +) +/* + * Output a line number line. + * 'file' is 'sharp_file' if specified, + * else (i.e. 'sharp_file' is NULL) 'infile'. + */ +{ + FILEINFO * file; + int line; + + file = sharp_file ? sharp_file : infile; + if (! file) + return; + while (! file->fp) + file = file->parent; + line = sharp_file ? sharp_file->line : src_line; + if (no_output || option_flags.p || file == NULL + || (file == sh_file && line == sh_line)) + goto sharp_exit; + sh_file = file; + sh_line = line; + if (keep_comments) + mcpp_fputc( '\n', OUT); /* Ensure to be on line top */ + if (std_line_prefix) + mcpp_fprintf( OUT, "#line %ld", line); + else + mcpp_fprintf( OUT, "%s%ld", LINE_PREFIX, line); + cur_file( file, sharp_file, flag); + mcpp_fputc( '\n', OUT); +sharp_exit: + wrong_line = FALSE; +} + +static void cur_file( + FILEINFO * file, /* infile or sharp_file */ + FILEINFO * sharp_file, /* The 'file' or NULL */ + int flag /* Flag to append for GCC */ +) +/* + * Output current source file name and line number. + * Called only from sharp() above. + */ +{ + const char * name; + + if (mcpp_debug & MACRO_CALL) { /* In macro notification mode */ + if (sharp_file) /* Main input file */ + name = file->filename; + else /* Output full-path-list, normalized */ + name = cur_fullname; + } else { /* Usually, the path not "normalized" */ + if (sharp_file) { /* Main input file */ + name = file->filename; + } else if (str_eq( file->filename, file->real_fname)) { + sprintf( work_buf, "%s%s", *(file->dirp), cur_fname); + name = work_buf; + } else { /* Changed by '#line fname' directive */ + name = file->filename; + } + } + if (sharp_filename == NULL || ! str_eq( name, sharp_filename)) { + if (sharp_filename != NULL) + free( sharp_filename); + sharp_filename = save_string( name); + } + mcpp_fprintf( OUT, " \"%s\"", name); +#if COMPILER == GNUC + if (! std_line_prefix) { + if (flag) { + mcpp_fputc( ' ', OUT); + mcpp_fputc( '0' + flag, OUT); + } + if (file->sys_header) + mcpp_fputs( " 3", OUT); + } +#endif +} + +#if SYS_FAMILY == SYS_WIN + +static char * bsl2sl( + char * filename +) +/* + * Convert '\\' in the path-list to '/'. + */ +{ + static int diagnosed = FALSE; + char * cp; + + cp = filename; + + while (*cp) { + if (bsl_in_mbchar) { + int c; + c = *cp & UCHARMAX; + if (char_type[ c] & mbchk) { /* First byte of MBCHAR */ + char tmp[ PATHMAX]; + char * tp = tmp; + *tp++ = *cp++; + mb_read( c, &cp, &tp); + /* Read over the multi-byte characters */ + continue; + } + } + if (*cp == '\\') { + *cp++ = PATH_DELIM; + if (!diagnosed && (warn_level & 2) && (warn_level != -1)) { + /* Backslash in source program */ + cwarn( "Converted \\ to %s", "/", 0L, NULL); /* _W2_ */ + diagnosed = TRUE; /* Diagnose only once */ + } + } else { + cp++; + } + } + + return filename; +} + +#endif /* SYS_FAMILY == SYS_WIN */ + +static const char * const unknown_arg = + "Unknown argument \"%s\""; /*_W1_*/ +static const char * const not_ident = + "Not an identifier \"%s\""; /*_W1_*/ + +static int is_junk( void) +/* + * Check the trailing junk in a directive line. + * This routine is never called in OLD_PREP mode. + */ +{ + int c; + + c = skip_ws(); + unget_ch(); + if (c != '\n') { /* Trailing junk */ + if (warn_level & 1) + cwarn( unknown_arg, infile->bptr, 0L, NULL); + return TRUE; + } else { + return FALSE; + } +} + +#define PUSH 1 +#define POP -1 + +#define __SETLOCALE 1 /* #pragma __setlocale( "encoding") */ +#define SETLOCALE 2 /* #pragma setlocale( "encoding") */ + +void do_pragma( void) +/* + * Process the #pragma lines. + * 1. Process the sub-directive for MCPP. + * 2. Pass the line to the compiler-proper. + * #pragma MCPP put_defines, #pragma MCPP preprocess, + * #pragma MCPP preprocessed and #pragma once are, however, not put + * out so as not to duplicate output when re-preprocessed. + * When EXPAND_PRAGMA == TRUE and (__STDC_VERSION__ >= 199901L or + * __cplusplus >= 199901L), the line is subject to macro expansion unless + * the next to 'pragma' token is one of 'STDC', 'GCC' or 'MCPP'. + */ +{ + int c; + int warn = FALSE; /* Necessity of warning */ + int token_type; + char * bp; /* Pointer to argument */ + char * tp; + FILEINFO * file; + + wrong_line = TRUE; /* In case of error */ + c = skip_ws(); + bp = infile->bptr - 1; /* Remember token to pass to compiler */ + if (c == '\n') { + if (warn_level & 1) + cwarn( "No sub-directive", NULL, 0L, NULL); /* _W1_ */ + unget_ch(); + return; + } + token_type = scan_token( c, (tp = work_buf, &tp), work_end); +#if EXPAND_PRAGMA +#if COMPILER == MSC + if (token_type == NAM + && !str_eq( identifier, "STDC") && !str_eq( identifier, "MCPP")) { +#else + if (stdc3 && token_type == NAM + && !str_eq( identifier, "STDC") && !str_eq( identifier, "MCPP")) { +#endif + DEFBUF * defp; + char * mp; + char * mp_end; + LINE_COL line_col = { 0L, 0}; + + bp = mp = xmalloc( (size_t)(NMACWORK + IDMAX)); + /* Buffer for macro expansion */ + mp_end = mp + NMACWORK; + tp = stpcpy( mp, identifier); + do { /* Expand all the macros in the line */ + int has_pragma; + if (token_type == NAM && (defp = is_macro( &tp)) != NULL) { + tp = expand_macro( defp, bp, mp_end, line_col, & has_pragma); + if (has_pragma) + cerror( "_Pragma operator found in #pragma line" /* _E_ */ + , NULL, 0L, NULL); + if (! stdc3 && (warn_level & 2)) + cwarn( + "\"%s\" is macro expanded in other than C99 mode" /* _W2_ */ + , identifier, 0L, NULL); + } + token_type = scan_token( c = get_ch(), (bp = tp, &tp), mp_end); + } while (c != '\n'); + unget_string( mp, NULL); /* To re-read */ + free( mp); + c = skip_ws(); + bp = infile->bptr - 1; + token_type = scan_token( c, (tp = work_buf, &tp), work_end); + } +#endif + if (token_type != NAM) { + if (warn_level & 1) + cwarn( not_ident, work_buf, 0L, NULL); + goto skip_nl; + } else if (str_eq( identifier, "once")) { /* #pragma once */ + if (! is_junk()) { + file = infile; + while (file->fp == NULL) + file = file->parent; + do_once( file->full_fname); + goto skip_nl; + } + } else if (str_eq( identifier, "MCPP")) { + if (scan_token( skip_ws(), (tp = work_buf, &tp), work_end) != NAM) { + if (warn_level & 1) + cwarn( not_ident, work_buf, 0L, NULL); + } + if (str_eq( identifier, "put_defines")) { + if (! is_junk()) + dump_def( TRUE, FALSE); /* #pragma MCPP put_defines */ + } else if (str_eq( identifier, "preprocess")) { + if (! is_junk()) /* #pragma MCPP preprocess */ + mcpp_fputs( "#pragma MCPP preprocessed\n", OUT); + /* Just putout the directive */ + } else if (str_eq( identifier, "preprocessed")) { + if (! is_junk()) { /* #pragma MCPP preprocessed*/ + skip_nl(); + do_preprocessed(); + return; + } + } else if (str_eq( identifier, "warning")) { + /* #pragma MCPP warning */ + cwarn( infile->buffer, NULL, 0L, NULL); + } else if (str_eq( identifier, "push_macro")) { + push_or_pop( PUSH); /* #pragma MCPP push_macro */ + } else if (str_eq( identifier, "pop_macro")) { + push_or_pop( POP); /* #pragma MCPP pop_macro */ + } else if (str_eq( identifier, "debug")) { + do_debug( TRUE); /* #pragma MCPP debug */ + } else if (str_eq( identifier, "end_debug")) { + do_debug( FALSE); /* #pragma MCPP end_debug */ + } else { + warn = TRUE; + } + if (warn && (warn_level & 1)) + cwarn( unknown_arg, identifier, 0L, NULL); + goto skip_nl; /* Do not putout the line */ +#if COMPILER == GNUC + /* The #pragma lines for GCC is skipped not to confuse cc1. */ + } else if (str_eq( identifier, "GCC")) { /* #pragma GCC * */ + if (scan_token( skip_ws(), (tp = work_buf, &tp), work_end) == NAM) { + if (str_eq( identifier, "poison") + || str_eq( identifier, "dependency")) { + if (warn_level & 2) + cwarn( "Skipped the #pragma line" /*_W2_ */ + , NULL, 0L, NULL); + goto skip_nl; + } else if (str_eq( identifier, "system_header")) { + infile->sys_header = TRUE; /* Mark as a system header */ + goto skip_nl; + } + } +#endif + +#if COMPILER == MSC + } else if (str_eq( identifier, "setlocale")) { + if (skip_ws() == '(' + && scan_token( skip_ws(), (tp = work_buf, &tp), work_end) + == STR + && skip_ws() == ')') { + if (! is_junk()) { + work_buf[ 0] = *(tp - 1) = '\0'; + set_encoding( work_buf + 1, NULL, SETLOCALE); + work_buf[ 0] = *(tp - 1) = '"'; + } /* else warned by is_junk() */ + } else { + warn = TRUE; + } +#else /* COMPILER != MSC */ + } else if (str_eq( identifier, "__setlocale")) { + if (skip_ws() == '(' + && scan_token( skip_ws(), (tp = work_buf, &tp), work_end) + == STR + && skip_ws() == ')') { + if (! is_junk()) { /* #pragma __setlocale */ + work_buf[ 0] = *(tp - 1) = '\0'; + set_encoding( work_buf + 1, NULL, __SETLOCALE); + work_buf[ 0] = *(tp - 1) = '"'; + } /* else warned by is_junk() */ + } else { + warn = TRUE; + } +#endif + +#if COMPILER == MSC + } else if (str_eq( identifier, "push_macro")) { + push_or_pop( PUSH); + goto skip_nl; + } else if (str_eq( identifier, "pop_macro")) { + push_or_pop( POP); + goto skip_nl; +#endif + +#if COMPILER == LCC + } else if (str_eq( identifier, "optimize") + && (skip_ws() == '(') + && (char_type[ (c = skip_ws()) & UCHARMAX] == DIG) + && (skip_ws() == ')')) { + char tmp[ 2]; + + tmp[ 0] = c; + tmp[ 1] = EOS; + look_and_install( optim_name, DEF_NOARGS_PREDEF, null, tmp); +#endif + +#if COMPILER == COMPILER_UNKNOWN + /* + * Write here any compiler-specific #pragma sub-directive which should + * be processed by preprocessor. + */ +#endif + } + + if (warn) { + if (warn_level & 1) + cwarn( unknown_arg, identifier, 0L, NULL); + goto skip_nl; /* Do not putout the line */ + } + + sharp( NULL, 0); /* Synchronize line number before output */ + if (! no_output) { + mcpp_fputs( "#pragma ", OUT); + mcpp_fputs( bp, OUT); /* Line is put out */ + } +skip_nl: /* Don't use skip_nl() which skips to the newline in source file */ + while (get_ch() != '\n') + ; +} + +static void do_once( + const char * fullname /* Full-path-list of the header */ +) +/* + * Process #pragma once so as not to re-include the file later. + * This directive has been imported from GCC V.1.* / cpp as an extension. + */ +{ + if (once_list == NULL) { /* Should initialize */ + max_once = INIT_NUM_ONCE; + once_list = (INC_LIST *) xmalloc( sizeof (INC_LIST) * max_once); + once_end = &once_list[ 0]; + } else if (once_end - once_list >= max_once) { + /* Double the elements */ + once_list = (INC_LIST *) xrealloc( (void *) once_list + , sizeof (INC_LIST) * max_once * 2); + once_end = &once_list[ max_once]; + max_once *= 2; + } + once_end->name = fullname; + once_end->len = strlen( fullname); + once_end++; +} + +static int included( + const char * fullname +) +/* + * Has the file been once included ? + * This routine is only called from open_file(). + */ +{ + INC_LIST * inc; + size_t fnamelen; + + if (once_list == NULL) /* No once file registered */ + return FALSE; + fnamelen = strlen( fullname); + for (inc = once_list; inc < once_end; inc++) { + if (inc->len == fnamelen && str_case_eq( inc->name, fullname)) { + /* Already included */ + if (mcpp_debug & PATH) + mcpp_fprintf( DBG, "Once included \"%s\"\n", fullname); + return TRUE; + } + } + return FALSE; /* Not yet included */ +} + +static void push_or_pop( + int direction +) +/* Process #pragma MCPP push_macro( "MACRO"), + * #pragma MCPP pop_macro( "MACRO") for other compilers than Visual C, + * and #pragma push_macro( "MACRO"), #pragma pop_macro( "MACRO") for Visual C. + * Note:1. "push" count is set in defp->push. + * 2. pushed definitions are inserted immediatly after the current + * definition of the same name. + * 3. the definitions of a same name macro can be pushed multiple times. + */ +{ + char * tp; + DEFBUF ** prevp; + DEFBUF * defp; + DEFBUF * dp; + int cmp; + size_t s_name, s_def; + + if (skip_ws() == '(' + && scan_token( skip_ws(), (tp = work_buf, &tp), work_end) == STR + && skip_ws() == ')') { /* Correct syntax */ + + if (is_junk()) + return; + s_name = strlen( work_buf) - 2; + *(work_buf + s_name + 1) = '\0'; + memcpy( identifier, work_buf + 1, s_name + 1); + /* Remove enclosing '"' */ + prevp = look_prev( identifier, &cmp); + if (cmp == 0) { /* Current definition or pushed definition exists */ + defp = *prevp; + if (direction == PUSH) {/* #pragma push_macro( "MACRO") */ + if (defp->push) { /* No current definition*/ + if (warn_level & 1) + cwarn( "\"%s\" is already pushed" /* _W1_ */ + , identifier, 0L, NULL); + return; + } + /* Else the current definition exists. Push it */ + s_def = sizeof (DEFBUF) + 3 + s_name + + strlen( defp->repl) + strlen( defp->fname); + if (mcpp_mode == STD) + s_def += strlen( defp->parmnames); + dp = (DEFBUF *) xmalloc( s_def); + memcpy( dp, defp, s_def); /* Copy the definition */ + dp->link = *prevp; /* Insert to linked-list*/ + *prevp = dp; /* the pushed def */ + prevp = &dp->link; /* Next link to search */ + } else { /* #pragma pop_macro( "MACRO") */ + if (defp->push == 0) { /* Current definition */ + if (defp->link == NULL + || ! str_eq( identifier, defp->link->name)) { + if (warn_level & 1) + cwarn( "\"%s\" has not been pushed" /* _W1_ */ + , identifier, 0L, NULL); + return; + } else { + *prevp = defp->link; + /* Link the previous and the next */ + free( defp); + /* Delete the definition to enable popped def */ + } + } /* Else no current definition exists */ + } + while ((defp = *prevp) != NULL) { + /* Increment or decrement "push" count of all pushed defs */ + if ((cmp = memcmp( defp->name, identifier, s_name)) > 0) + break; + defp->push += direction; /* Increment or decrement */ + prevp = &defp->link; + } + } else { /* No current definition nor pushed definition */ + if (warn_level & 1) + cwarn( "\"%s\" has not been defined" /* _W1_ */ + , identifier, 0L, NULL); + } + } else { /* Wrong syntax */ + if (warn_level & 1) + cwarn( "Bad %s syntax", direction == PUSH /* _W1_ */ + ? "push_macro" : "pop_macro", 0L, NULL); + } +} + +static void do_asm( + int asm_start /* #asm ? */ +) +/* + * #asm, #endasm + * Originally written for OS-9/09 Microware C. + */ +{ + if (! compiling) + return; + if (asm_start == (in_asm != 0L)) { + if (in_asm) + cerror( "In #asm block started at line %.0s%ld" /* _E_ */ + , NULL, in_asm, NULL); + else + cerror( "Without #asm", NULL, 0L, NULL); /* _E_ */ + skip_nl(); + unget_ch(); + return; + } + in_asm = asm_start ? src_line : 0L; +} + +void do_old( void) +/* + * Process the out-of-standard directives. + * GCC permits #include_next and #warning even in STANDARD mode. + */ +{ + static const char * const unknown + = "Unknown #directive \"%s\"%.0ld%s"; /* _E_ _W8_ */ + static const char * const ext + = "%s is not allowed by Standard%.0ld%s"; /* _W2_ _W8_*/ + +#if COMPILER == GNUC + if (str_eq( identifier, "include_next")) { + if ((compiling && (warn_level & 2)) + || (! compiling && (warn_level & 8))) + cwarn( ext, "#include_next", 0L + , compiling ? NULL : " (in skipped block)"); + if (! compiling) + return; + in_include = TRUE; + do_include( TRUE); + in_include = FALSE; + return; + } else if (str_eq( identifier, "warning")) { + if ((compiling && (warn_level & 2)) + || (! compiling && (warn_level & 8))) + cwarn( ext, "#warning", 0L + , compiling ? NULL : " (in skipped block)"); + if (! compiling) + return; + cwarn( infile->buffer, NULL, 0L, NULL); + /* Always output the warning */ + skip_nl(); + unget_ch(); + return; + } else if (str_eq( identifier, "ident") || str_eq( identifier, "sccs")) { + if ((compiling && (warn_level & 1)) + || (! compiling && (warn_level & 8))) { + if (str_eq( identifier, "ident")) + cwarn( + compiling ? "Ignored #ident" : "#ident (in skipped block)" /* _W1_ _W8_*/ + , NULL, 0L, NULL); + else + cwarn( + compiling ? "Ignored #sccs" : "#sccs (in skipped block)" /* _W1_ _W8_*/ + , NULL, 0L, NULL); + } + if (! compiling) + return; + skip_nl(); + unget_ch(); + return; + } +#endif /* COMPILER == GNUC */ + +#if COMPILER == MSC + if (str_eq( identifier, "using") || str_eq( identifier, "import")) { + /* #using or #import */ + if (! compiling) + return; + mcpp_fputs( infile->buffer, OUT); /* Putout the line as is*/ + skip_nl(); + unget_ch(); + return; + } +#endif + +#if SYSTEM == SYS_MAC + if (str_eq( identifier, "import")) { + if ((compiling && (warn_level & 2)) + || (! compiling && (warn_level & 8))) + cwarn( ext, "#import", 0L + , compiling ? NULL : " (in skipped block)"); + if (! compiling) + return; + in_import = in_include = TRUE; + do_include( FALSE); + in_import = in_include = FALSE; + return; + } +#endif + + if (! standard && do_prestd_directive()) + return; + + if (compiling) { + if (option_flags.lang_asm) { /* "Assembler" source */ + if (warn_level & 1) + cwarn( unknown, identifier, 0L, NULL); + mcpp_fputs( infile->buffer, OUT); /* Putout the line */ + } else { + cerror( unknown, identifier, 0L, NULL); + } + } else if (warn_level & 8) { + cwarn( unknown, identifier, 0L, " (in skipped block)"); + } + skip_nl(); + unget_ch(); + return; +} + +static int do_prestd_directive( void) +/* + * Process directives for pre-Standard mode. + */ +{ +#if COMPILER != GNUC + if (str_eq( identifier, "assert")) { /* #assert */ + if (! compiling) /* Only validity check */ + return TRUE; + if (eval_if() == 0L) { /* Assert expression */ + cerror( "Preprocessing assertion failed" /* _E_ */ + , NULL, 0L, NULL); + skip_nl(); + unget_ch(); + } + return TRUE; + } else +#endif + if (str_eq( identifier, "put_defines")) { + if (! compiling) /* Only validity check */ + return TRUE; + if (mcpp_mode != OLD_PREP && ! is_junk()) + dump_def( TRUE, FALSE); /* #put_defines */ + skip_nl(); + unget_ch(); + return TRUE; + } else if (str_eq( identifier, "preprocess")) { + if (! compiling) /* Only validity check */ + return TRUE; + if (mcpp_mode != OLD_PREP && ! is_junk()) + /* Just putout the directive for the succeding preprocessor */ + mcpp_fputs( "#preprocessed\n", OUT); + skip_nl(); + unget_ch(); + return TRUE; + } else if (str_eq( identifier, "preprocessed")) { + if (! compiling) /* Only validity check */ + return TRUE; + if (mcpp_mode != OLD_PREP && ! is_junk()) { + skip_nl(); + do_preprocessed(); /* #preprocessed */ + return TRUE; + } + skip_nl(); + unget_ch(); + return TRUE; + } + + if (str_eq( identifier, "debug")) { /* #debug */ + if (! compiling) /* Only validity check */ + return TRUE; + do_debug( TRUE); + return TRUE; + } else if (str_eq( identifier, "end_debug")) { + if (! compiling) + return TRUE; + do_debug( FALSE); /* #end_debug */ + return TRUE; + } + + if (str_eq( identifier, "asm")) { /* #asm */ + do_asm( TRUE); + return TRUE; + } + if (str_eq( identifier, "endasm")) { /* #endasm */ + do_asm( FALSE); + skip_nl(); /* Skip comments, etc. */ + unget_ch(); + return TRUE; + } + + return FALSE; /* Unknown directive */ +} + +static void do_preprocessed( void) +/* + * The source file has been already preprocessed. + * Copy the lines to output. + * Install macros according the #define directives. + */ +{ + const char * corrupted = + "This preprocessed file is corrupted"; /* _F_ */ + FILEINFO * file; + char * lbuf; + char * cp; + const char ** incptr; + char * comment = NULL; + char * colon = NULL; + const char * dir; +#if STD_LINE_PREFIX == FALSE + char conv[ NBUFF]; + char * arg; + + /* + * Compiler cannot accept C source style #line. + * Convert it to the compiler-specific format. + */ + strcpy( conv, LINE_PREFIX); + arg = conv + strlen( conv); +#endif + file = infile; + lbuf = file->bptr = file->buffer; /* Reset file->bptr */ + + /* Copy the input to output until a comment line appears. */ + while (fgets( lbuf, NBUFF, file->fp) != NULL + && memcmp( lbuf, "/*", 2) != 0) { +#if STD_LINE_PREFIX == FALSE + if (memcmp( lbuf, "#line ", 6) == 0) { + strcpy( arg, lbuf + 6); + mcpp_fputs( conv, OUT); + } else +#endif + { + mcpp_fputs( lbuf, OUT); + } + } + if (! str_eq( lbuf, "/* Currently defined macros. */\n")) + cfatal( "This is not a preprocessed source" /* _F_ */ + , NULL, 0L, NULL); + + /* Define macros according to the #define lines. */ + while (fgets( lbuf, NWORK, file->fp) != NULL) { + if (memcmp( lbuf, "/*", 2) == 0) { + /* Standard predefined macro */ + continue; + } + if (memcmp( lbuf, "#define ", 8) != 0) { + if (memcmp( lbuf, "#line", 5) == 0) + continue; + else + cfatal( corrupted, NULL, 0L, NULL); + } + /* Filename and line-number information in comment as: */ + /* dir/fname:1234\t*/ + cp = lbuf + strlen( lbuf); + if ((memcmp( cp - 4, "\t*/\n", 4) != 0) + || (*(cp - 4) = EOS + , (comment = strrchr( lbuf, '*')) == NULL) + || (memcmp( --comment, "/* ", 3) != 0) + || ((colon = strrchr( comment, ':')) == NULL)) + cfatal( corrupted, NULL, 0L, NULL); + src_line = atol( colon + 1); /* Pseudo line number */ + *colon = EOS; + dir = comment + 3; + inc_dirp = &null; + /* Search the include directory list */ + for (incptr = incdir ; incptr < incend; incptr++) { + if (memcmp( *incptr, dir, strlen( *incptr)) == 0) { + inc_dirp = incptr; + break; + } + } + /* Register the filename to fnamelist[] */ + /* inc_dirp may be NULL, and cur_fname may be "(predefined)" */ + cur_fname = set_fname( dir + strlen( *inc_dirp)); + strcpy( comment - 2, "\n"); /* Remove the comment */ + unget_string( lbuf + 8, NULL); + do_define( FALSE, 0); + get_ch(); /* '\n' */ + get_ch(); /* Clear the "file" */ + unget_ch(); /* infile == file */ + } + file->bptr = file->buffer + strlen( file->buffer); +} + +static int do_debug( + int set /* TRUE to set debugging */ +) +/* + * #pragma MCPP debug, #pragma MCPP end_debug, #debug, #end_debug + * Return TRUE when diagnostic is issued else return FALSE. + */ +{ + struct Debug_arg { + const char * arg_name; /* Name of option */ + int arg_num; /* Value of 'debug' */ + }; + static struct Debug_arg debug_args[] = { + { "path", PATH }, + { "token", TOKEN }, + { "expand", EXPAND }, + { "macro_call", MACRO_CALL }, /* Implemented only in STD mode */ + { "if", IF }, + { "expression", EXPRESSION }, + { "getc", GETC }, + { "memory", MEMORY }, + { NULL, 0 }, + }; + struct Debug_arg *argp; + int num; + int c; + + c = skip_ws(); + if (c == '\n') { + unget_ch(); + if (set) { + if (warn_level & 1) + cwarn( "No argument", NULL, 0L, NULL); /* _W1_ */ + return TRUE; + } else { + mcpp_debug = 0; /* Clear all the flags */ + return FALSE; + } + } + while (scan_token( c, (workp = work_buf, &workp), work_end) == NAM) { + argp = debug_args; + while (argp->arg_name) { + if (str_eq( argp->arg_name, work_buf)) + break; + argp++; + } + if (argp->arg_name == NULL) { + if (warn_level & 1) + cwarn( unknown_arg, work_buf, 0L, NULL); + goto diagnosed; + } else { + num = argp->arg_num; + if (set) { + mcpp_debug |= num; + if (num == PATH) + dump_path(); + else if (num == MEMORY) + print_heap(); + else if (num == MACRO_CALL) + option_flags.k = TRUE; /* This pragma needs this mode */ + } else { + mcpp_debug &= ~num; + } + } + c = skip_ws(); + } + if ((mcpp_mode != STD && (mcpp_debug & MACRO_CALL)) || c != '\n') { + if (warn_level & 1) { + if (c != '\n') { + cwarn( not_ident, work_buf, 0L, NULL); + } else { + cwarn( unknown_arg, work_buf, 0L, NULL); + mcpp_debug &= ~num; /* Disable */ + } + } + skip_nl(); + unget_ch(); + goto diagnosed; + } + unget_ch(); + return FALSE; +diagnosed: + return TRUE; +} + +void put_asm( void) +/* + * Put out source line as it is. + */ +{ +#if 0 + mcpp_fputs( "#2\n", OUT); + mcpp_fputs( infile->buffer, OUT); + skip_nl(); +#endif +} + +static void dump_path( void) +/* + * Show the include directories. + */ +{ + const char ** incptr; + const char * inc_dir; + const char * dir = "./"; + int i; + + mcpp_fputs( "Include paths are as follows --\n", DBG); + for (incptr = incdir; incptr < incend; incptr++) { + inc_dir = *incptr; + if (*inc_dir == '\0') + inc_dir = dir; + mcpp_fprintf( DBG, " %s\n", inc_dir); + } + mcpp_fputs( "End of include path list.\n", DBG); +#if SYSTEM == SYS_MAC + mcpp_fputs( "Framework paths are as follows --\n", DBG); + for (i = 0; i < num_framework; i++ ) + mcpp_fprintf( DBG, " %s\n", framework[ i]); + mcpp_fputs( "End of framework path list.\n", DBG); +#endif +} + +/* + * Note: The getopt() of glibc should not be used since the specification + * differs from the standard one. + * Use this mcpp_getopt() for mcpp. + */ + +/* Based on the public-domain-software released by AT&T in 1985. */ + +#define OPTERR( s, c) if (mcpp_opterr) { \ + mcpp_fputs( argv[0], ERR); \ + mcpp_fputs( s, ERR); \ + mcpp_fputc( c, ERR); \ + mcpp_fputc( '\n', ERR); \ + } + +static int mcpp_getopt( + int argc, + char * const * argv, + const char * opts +) +/* + * Get the next option (and it's argument) from the command line. + */ +{ + const char * const error1 = ": option requires an argument --"; + const char * const error2 = ": illegal option --"; + static int sp = 1; + int c; + const char * cp; + + if (sp == 1) { + if (argc <= mcpp_optind || + argv[ mcpp_optind][ 0] != '-' + || argv[ mcpp_optind][ 1] == '\0') { + return EOF; + } else if (strcmp( argv[ mcpp_optind], "--") == 0) { + mcpp_optind++; + return EOF; + } + } +/* mcpp_optopt = c = (unsigned char) argv[ mcpp_optind][ sp]; */ + mcpp_optopt = c = argv[ mcpp_optind][ sp] & UCHARMAX; + if (c == ':' || (cp = strchr( opts, c)) == NULL) { + OPTERR( error2, c) + if (argv[ mcpp_optind][ ++sp] == '\0') { + mcpp_optind++; + sp = 1; + } + return '?'; + } + if (*++cp == ':') { + if (argv[ mcpp_optind][ sp+1] != '\0') { + mcpp_optarg = &argv[ mcpp_optind++][ sp+1]; + } else if (argc <= ++mcpp_optind) { + OPTERR( error1, c) + sp = 1; + return '?'; + } else { + mcpp_optarg = argv[ mcpp_optind++]; + } + sp = 1; + } else { + if (argv[ mcpp_optind][ ++sp] == '\0') { + sp = 1; + mcpp_optind++; + } + mcpp_optarg = NULL; + } + return c; +} + +#if ! HOST_HAVE_STPCPY +char * stpcpy( + char * dest, + const char * src +) +/* + * Copy the string and return the advanced pointer. + */ +{ + const char * s; + char * d; + + for (s = src, d = dest; (*d++ = *s++) != '\0'; ) + ; + return d - 1; +} +#endif + +/* + * list_heap() is a function to print out information of heap-memory. + * See "kmmalloc-2.5.3.zip" by kmatsui. + */ +#if KMMALLOC + int list_heap( int); +#elif BSD_MALLOC + int list_heap( char *); +#elif DB_MALLOC || DMALLOC + int list_heap( FILE *); +#endif + +void print_heap( void) +{ +#if KMMALLOC + list_heap( 1); +#elif BSD_MALLOC + list_heap( ":cpp"); +#elif DB_MALLOC || DMALLOC || PHK_MALLOC || DLMALLOC + list_heap( fp_debug); +#endif +} + +void at_end( void) +/* + * Handle the commands to be executed at the end of processing. + */ +{ +#if COMPILER == GNUC + if (dMflag || dDflag) + dump_def( FALSE, FALSE); +#endif +} + +#if MCPP_LIB +void clear_filelist( void) +/* + * Free malloced memory for filename-list and directory-list. + */ +{ + const char ** incp; + INC_LIST * namep; + + for (incp = incdir; incp < incend; incp++) + free( (void *) *incp); + free( (void *) incdir); + for (namep = fnamelist; namep < fname_end; namep++) + free( (void *) namep->name); + free( (void *) fnamelist); + if (standard) + free( (void *) once_list); +} +#endif +