1129 lines
41 KiB
C
1129 lines
41 KiB
C
/*-
|
|
* Copyright (c) 1998, 2002-2008 Kiyoshi Matsui <kmatsui@t3.rim.or.jp>
|
|
* 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. <newline> 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 = "<stdin>";
|
|
|
|
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
|
|
clear_filelist();
|
|
clear_symtable();
|
|
#endif
|
|
if (stdin_name != in_file)
|
|
free(in_file);
|
|
|
|
if (fp_in && 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<n> 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 <newline> */
|
|
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 */
|
|
|