stk-code_catmod/lib/mcpp/main.c

1129 lines
41 KiB
C
Raw Normal View History

2020-01-02 23:46:35 -05:00
/*-
* 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);
2020-01-02 23:46:35 -05:00
2020-01-02 23:57:16 -05:00
if (fp_in && fp_in != stdin)
2020-01-02 23:46:35 -05:00
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 */