/*- * 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 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 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 */