/* * C and T preprocessor, and integrated lexer * (c) Thomas Pornin 1999, 2000 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. The name of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /*vb*/ #ifdef HAVE_MISRA void misra(int,...); #endif #define VERS_MAJ 1 #define VERS_MIN 0 /* uncomment the following if you cannot set it with a compiler flag */ /* #define STAND_ALONE */ #include "tune.h" #ifdef UCPP_MMAP #ifndef _POSIX_SOURCE #define _POSIX_SOURCE 1 #endif #endif #include #include #include #include #include #include #include #include "ucppi.h" #include "mem.h" #include "hash.h" #ifdef UCPP_MMAP #include #include #include #include #endif /* * The standard path where includes are looked for. */ #ifdef STAND_ALONE static char *include_path_std[] = { STD_INCLUDE_PATH, 0 }; #endif static char **include_path; static size_t include_path_nb = 0; int no_special_macros = 0; int emit_dependencies = 0, emit_defines = 0, emit_assertions = 0; FILE *emit_output; #ifdef STAND_ALONE static char *system_macros_def[] = { STD_MACROS, 0 }; static char *system_assertions_def[] = { STD_ASSERT, 0 }; #endif char *current_filename, *current_long_filename = 0; static int current_incdir = -1; #ifndef NO_UCPP_ERROR_FUNCTIONS /* * "ouch" is the name for an internal ucpp error. If AUDIT is not defined, * no code calling this function will be generated; a "ouch" may still be * emitted by getmem() (in mem.c) if MEM_CHECK is defined, but this "ouch" * does not use this function. */ #endif /* NO_UCPP_ERROR_FUNCTIONS */ /* * Some memory allocations are manually garbage-collected; essentially, * strings duplicated in the process of macro replacement. Each such * string is referenced in the garbage_fifo, which is cleared when all * nested macros have been resolved. */ struct garbage_fifo { char **garbage; size_t ngarb, memgarb; }; /* * throw_away() marks a string to be collected later */ void throw_away(struct garbage_fifo *gf, char *n) { wan(gf->garbage, gf->ngarb, n, gf->memgarb); } /* * free marked strings */ void garbage_collect(struct garbage_fifo *gf) { size_t i; for (i = 0; i < gf->ngarb; i ++) freemem(gf->garbage[i]); gf->ngarb = 0; } static void init_garbage_fifo(struct garbage_fifo *gf) { gf->garbage = getmem((gf->memgarb = GARBAGE_LIST_MEMG) * sizeof(char *)); gf->ngarb = 0; } /* * order is important: it must match the token-constants declared as an * enum in the header file. */ char *operators_name[] = { " ", "\n", " ", "0000", "name", "bunch", "pragma", "context", "\"dummy string\"", "'dummy char'", "/", "/=", "-", "--", "-=", "->", "+", "++", "+=", "<", "<=", "<<", "<<=", ">", ">=", ">>", ">>=", "=", "==", #ifdef CAST_OP "=>", #endif "~", "!=", "&", "&&", "&=", "|", "||", "|=", "%", "%=", "*", "*=", "^", "^=", "!", "~=", "{", "}", "[", "]", "(", ")", ",", "?", ";", ":", ".", "...", "#", "##", " ", "ouch", "<:", ":>", "<%", "%>", "%:", "%:%:" }; /* the ascii representation of a token */ #ifdef SEMPER_FIDELIS #define tname(x) (ttWHI((x).type) ? " " : S_TOKEN((x).type) \ ? (x).name : operators_name[(x).type]) #else #define tname(x) (S_TOKEN((x).type) ? (x).name \ : operators_name[(x).type]) #endif char *token_name(struct token *t) { return tname(*t); } /* * To speed up deeply nested and repeated inclusions, we: * -- use a hash table to remember where we found each file * -- remember when the file is protected by a #ifndef/#define/#endif * construction; we can then avoid including several times a file * when this is not necessary. * -- remember in which directory, in the include path, the file was found. */ struct found_file { char *name; /* first field */ char *long_name; char *protect; int incdir; }; static struct HT *found_files = 0, *found_files_loc = 0; static struct found_file *new_found_file(void) { struct found_file *ff = getmem(sizeof(struct found_file)); ff->protect = 0; ff->incdir = -1; return ff; } static void del_found_file(void *m) { struct found_file *ff = (struct found_file *)m; freemem(ff->name); freemem(ff->long_name); if (ff->protect) freemem(ff->protect); } /* * To keep up with the #ifndef/#define/#endif protection mechanism * detection. */ struct protect protect_detect; static struct protect *protect_detect_stack = 0; /* * "current_filename" is used for error reporting, and the __FILE__ macro */ static void set_current_filename(char *x) { current_filename = x; } void set_init_filename(char *x, int real_file) { set_current_filename(sdup(x)); current_long_filename = 0; current_incdir = -1; if (real_file) { protect_detect.macro = 0; protect_detect.state = 1; protect_detect.ff = new_found_file(); protect_detect.ff->name = sdup(x); protect_detect.ff->long_name = sdup(x); putHT(found_files_loc, protect_detect.ff); } else { protect_detect.state = 0; } } static void init_found_files(void) { if (found_files) killHT(found_files); found_files = newHT(128, cmp_struct, hash_struct, del_found_file); if (found_files_loc) killHT(found_files_loc); found_files_loc = newHT(128, cmp_struct, hash_struct, del_found_file); } /* * convert a Unix-style path for the host architecture */ #if defined(MSDOS) || defined(ATARI) static char *convert_path(char *path) { /* on msdos systems, replace all / by \ */ char *c, *newp = sdup(path); for (c = newp; *c; c ++) if (*c == '/') *c = '\\'; return newp; } #elif defined AMIGA static char *convert_path(char *path) { char *newp = getmem(1 + strlen(path)); char *s = path, *d = newp; if (*s == '.') { /* convert "." to "" */ if (*(s+1) == '\0') s++; } while (*s) { if (*s == '.') { if (*(s+1) == '/') { s += 2; /* "./" is discarded */ continue; } else if (*(s+1) == '.') { if (*(s+2) == '/') s += 2; /* "../" is converted to "/" */ } } *d++ = *s++; } *d = '\0'; return newp; } #else /* no conversion */ static char *convert_path(char *path) { return sdup(path); } #endif /* * Set the lexer state at the beginning of a file. */ static void reinit_lexer_state(struct lexer_state *ls, int wb) { #ifndef NO_UCPP_BUF ls->input_buf = wb ? getmem(INPUT_BUF_MEMG) : 0; #ifdef UCPP_MMAP ls->from_mmap = 0; #endif #endif ls->ebuf = ls->pbuf = 0; ls->nlka = 0; ls->macfile = 0; ls->discard = 1; ls->last = 0; /* we suppose '\n' is not 0 */ ls->line = 1; ls->ltwnl = 1; ls->oline = 1; ls->pending_token = 0; ls->cli = 0; ls->copy_line[COPY_LINE_LENGTH - 1] = 0; ls->ifnest = 0; ls->condf[0] = ls->condf[1] = 0; } /* * Initialize the struct lexer_state, with optional input and output buffers. */ void init_buf_lexer_state(struct lexer_state *ls, int wb) { reinit_lexer_state(ls, wb); #ifndef NO_UCPP_BUF ls->output_buf = wb ? getmem(OUTPUT_BUF_MEMG) : 0; #endif ls->sbuf = 0; ls->ctok = getmem(sizeof(struct token)); ls->ctok->name = getmem(ls->tknl = TOKEN_NAME_MEMG); ls->pending_token = 0; ls->flags = 0; ls->count_trigraphs = 0; ls->gf = getmem(sizeof(struct garbage_fifo)); init_garbage_fifo(ls->gf); ls->condcomp = 1; ls->condnest = 0; #ifdef INMACRO_FLAG ls->inmacro = 0; ls->macro_count = 0; #endif } /* * Initialize the (complex) struct lexer_state. */ void init_lexer_state(struct lexer_state *ls) { init_buf_lexer_state(ls, 1); } /* * Restore what is needed from a lexer_state. This is used for #include. */ static void restore_lexer_state(struct lexer_state *ls, struct lexer_state *lsbak) { #ifndef NO_UCPP_BUF freemem(ls->input_buf); ls->input_buf = lsbak->input_buf; #ifdef UCPP_MMAP ls->from_mmap = lsbak->from_mmap; ls->input_buf_sav = lsbak->input_buf_sav; #endif #endif ls->input = lsbak->input; ls->ebuf = lsbak->ebuf; ls->pbuf = lsbak->pbuf; ls->nlka = lsbak->nlka; ls->macfile = lsbak->macfile; ls->discard = lsbak->discard; ls->line = lsbak->line; ls->oline = lsbak->oline; ls->ifnest = lsbak->ifnest; ls->condf[0] = lsbak->condf[0]; ls->condf[1] = lsbak->condf[1]; } /* * file_context (and the two functions push_ and pop_) are used to save * all that is needed when including a file. */ static struct file_context { struct lexer_state ls; char *name, *long_name; int incdir; } *ls_stack; static size_t ls_depth = 0; static void push_file_context(struct lexer_state *ls) { struct file_context fc; if (ls_depth > 1023) error(ls->line - 1, "too many nested includes"); fc.name = current_filename; fc.long_name = current_long_filename; fc.incdir = current_incdir; mmv(&(fc.ls), ls, sizeof(struct lexer_state)); aol(ls_stack, ls_depth, fc, LS_STACK_MEMG); ls_depth --; aol(protect_detect_stack, ls_depth, protect_detect, LS_STACK_MEMG); protect_detect.macro = 0; } static void pop_file_context(struct lexer_state *ls) { #ifdef AUDIT if (ls_depth <= 0) ouch("prepare to meet thy creator"); #endif restore_lexer_state(ls, &(ls_stack[-- ls_depth].ls)); if (protect_detect.macro) freemem(protect_detect.macro); protect_detect = protect_detect_stack[ls_depth]; set_current_filename(ls_stack[ls_depth].name); current_long_filename = ls_stack[ls_depth].long_name; current_incdir = ls_stack[ls_depth].incdir; } /* * report_context() returns the list of successive includers of the * current file, ending with a dummy entry with a negative line number. * The caller is responsible for freeing the returned pointer. */ struct stack_context *report_context(void) { struct stack_context *sc; size_t i; sc = getmem((ls_depth + 1) * sizeof(struct stack_context)); for (i = 0; i < ls_depth; i ++) { sc[i].name = ls_stack[ls_depth - i - 1].name; sc[i].long_name = ls_stack[ls_depth - i - 1].long_name; sc[i].line = ls_stack[ls_depth - i - 1].ls.line - 1; } sc[ls_depth].line = -1; return sc; } /* * init_lexer_mode() is used to end initialization of a struct lexer_state * if it must be used for a lexer */ void init_lexer_mode(struct lexer_state *ls) { ls->flags = DEFAULT_LEXER_FLAGS; ls->output_fifo = getmem(sizeof(struct token_fifo)); ls->output_fifo->art = ls->output_fifo->nt = 0; ls->toplevel_of = ls->output_fifo; ls->save_ctok = ls->ctok; } /* * release memory used by a struct lexer_state */ void free_lexer_state(struct lexer_state *ls) { #ifndef NO_UCPP_BUF if (ls->input_buf) freemem(ls->input_buf); if (ls->output_buf) freemem(ls->output_buf); #endif freemem(ls->ctok->name); freemem(ls->ctok); if (!(ls->flags & LEXER)) garbage_collect(ls->gf); freemem(ls->gf); } /* * Print line information. */ static void print_line_info(struct lexer_state *ls, unsigned long flags) { char *fn = current_long_filename ? current_long_filename : current_filename; char *b, *d; b = getmem(50 + strlen(fn)); if (flags & GCC_LINE_NUM) { sprintf(b, "# %ld \"%s\"\n", ls->line, fn); } else { sprintf(b, "#line %ld \"%s\"\n", ls->line, fn); } for (d = b; *d; d ++) put_char(ls, (unsigned char)(*d)); freemem(b); } /* * Enter a file; this implies the possible emission of a #line directive. * The flags used are passed as second parameter instead of being * extracted from the struct lexer_state. * * As a command-line option, gcc-like directives (with only a '#', * without 'line') may be produced. */ void enter_file(struct lexer_state *ls, unsigned long flags) { char *fn = current_long_filename ? current_long_filename : current_filename; if (!(flags & LINE_NUM)) return; if ((flags & LEXER) && !(flags & TEXT_OUTPUT)) { struct token t; t.type = CONTEXT; t.line = ls->line; t.name = fn; print_token(ls, &t, 0); return; } print_line_info(ls, flags); ls->oline --; /* emitted #line troubled oline */ } #ifdef UCPP_MMAP /* * We open() the file, then fdopen() it and fseek() to its end. If the * fseek() worked, we try to mmap() the file, up to the point where we * arrived. * On an architecture where end-of-lines are multibytes and translated * into single '\n', bad things could happen. We strongly hope that, if * we could fseek() to the end but could not mmap(), then we can get back. */ static void *find_file_map; static size_t map_length; FILE *fopen_mmap_file(char *name) { FILE *f; int fd; long l; find_file_map = 0; fd = open(name, O_RDONLY, 0); if (fd < 0) return 0; l = lseek(fd, 0, SEEK_END); f = fdopen(fd, "r"); if (!f) { close(fd); return 0; } if (l < 0) return f; /* not seekable */ map_length = l; if ((find_file_map = mmap(0, map_length, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { /* we could not mmap() the file; get back */ find_file_map = 0; if (fseek(f, 0, SEEK_SET)) { /* bwaah... can't get back. This file is cursed. */ fclose(f); return 0; } } return f; } void set_input_file(struct lexer_state *ls, FILE *f) { ls->input = f; if (find_file_map) { ls->from_mmap = 1; ls->input_buf_sav = ls->input_buf; ls->input_buf = find_file_map; ls->pbuf = 0; ls->ebuf = map_length; } else { ls->from_mmap = 0; } } #endif /* * Find a file by looking through the include path. * return value: a FILE * on the file, opened in "r" mode, or 0. * * find_file_error will contain: * FF_ERROR on error (file not found or impossible to read) * FF_PROTECT file is protected and therefore useless to read * FF_KNOWN file is already known * FF_UNKNOWN file was not already known */ static int find_file_error; enum { FF_ERROR, FF_PROTECT, FF_KNOWN, FF_UNKNOWN }; static FILE *find_file(char *name, int localdir) { FILE *f; int i, incdir = -1; size_t nl = strlen(name); char *s = 0; struct found_file *ff = 0; int lf = 0; find_file_error = FF_ERROR; protect_detect.state = -1; protect_detect.macro = 0; if (localdir) { int i; char *rfn = current_long_filename ? current_long_filename : current_filename; for (i = strlen(rfn) - 1; i >= 0; i --) #if defined(MSDOS) || defined(ATARI) if (rfn[i] == '\\') break; #elif defined AMIGA if (rfn[i] == '/' || rfn[i] == ':') break; #else if (rfn[i] == '/') break; #endif #if defined(MSDOS) || defined(ATARI) if (i >= 0 && *name != '\\' && (nl < 2 || name[1] != ':')) #elif defined AMIGA if (i >= 0 && strchr(name,':') == 0) #else if (i >= 0 && *name != '/') #endif { /* * current file is somewhere else, and the provided * file name is not absolute, so we must adjust the * base for looking for the file; besides, * found_files and found_files_loc are irrelevant * for this search. */ s = getmem(i + 2 + nl); mmv(s, rfn, i + 1); mmv(s + i + 1, name, nl); s[i + 1 + nl] = 0; ff = getHT(found_files_loc, &s); } else ff = getHT(found_files_loc, &name); if (!ff) { #ifdef UCPP_MMAP if (f = fopen_mmap_file(s ? s : name)) { #else if (f = fopen(s ? s : name, "r")) { #endif lf = 1; protect_detect.ff = new_found_file(); goto found_file; } } } if (!ff) ff = getHT(found_files, s ? &s : &name); if (ff) { if (ff->protect && get_macro(ff->protect)) { /* file is protected, do not include it */ find_file_error = FF_PROTECT; return 0; } protect_detect.ff = ff; #ifdef UCPP_MMAP f = fopen_mmap_file(ff->long_name); #else f = fopen(ff->long_name, "r"); #endif if (!f) return 0; goto found_file_2; } else { protect_detect.ff = new_found_file(); } /* * This is the first time we find the file, or it was not protected. */ if (s) freemem(s); for (i = 0; i < include_path_nb; i ++) { size_t ni = strlen(include_path[i]); s = getmem(ni + nl + 2); mmv(s, include_path[i], ni); #ifdef AMIGA /* contributed by Volker Barthelmann */ if (ni > 0 && s[ni - 1] != ':' && s[ni - 1] != '/') { s[ni] = '/'; mmv(s + ni + 1, name, nl + 1); } else { mmv(s + ni, name, nl + 1); } #else s[ni] = '/'; mmv(s + ni + 1, name, nl + 1); #endif incdir = i; #ifdef UCPP_MMAP f = fopen_mmap_file(s); #else f = fopen(s, "r"); #endif if (f) goto found_file; freemem(s); } return 0; found_file: if (f && ((emit_dependencies == 1 && lf) || emit_dependencies == 2)) { fprintf(emit_output, " %s", s ? s : name); } if (!ff) { struct found_file *nff = protect_detect.ff; nff->name = sdup(name); nff->long_name = s ? s : sdup(name); nff->incdir = incdir; putHT(lf ? found_files_loc : found_files, nff); s = 0; find_file_error = FF_UNKNOWN; ff = nff; } else find_file_error = FF_KNOWN; found_file_2: if (s) freemem(s); current_long_filename = ff->long_name; #ifdef NO_LIBC_BUF setbuf(f, 0); #endif current_incdir = ff->incdir; return f; } /* * Find the named file by looking through the end of the include path. * This one is not as optimized as find_file() since it is rarely used. */ static FILE *find_file_next(char *name) { int i; size_t nl = strlen(name); FILE *f; for (i = current_incdir + 1; i < include_path_nb; i ++) { char *s; size_t ni = strlen(include_path[i]); s = getmem(ni + nl + 2); mmv(s, include_path[i], ni); #ifdef AMIGA /* contributed by Volker Barthelmann */ if (ni > 0 && s[ni - 1] != ':' && s[ni - 1] != '/') { s[ni] = '/'; mmv(s + ni + 1, name, nl + 1); } else { mmv(s + ni, name, nl + 1); } #else s[ni] = '/'; mmv(s + ni + 1, name, nl + 1); #endif #ifdef UCPP_MMAP f = fopen_mmap_file(s); #else f = fopen(s, "r"); #endif if (f) { if (emit_dependencies == 2) { fprintf(emit_output, " %s", s ? s : name); } current_long_filename = s; current_incdir = i; return f; } freemem(s); } return 0; } /* * The #if directive. This function parse the expression, performs macro * expansion (and handles the "defined" operator), and call eval_expr. * return value: 1 if the expression is true, 0 if it is false, -1 on error. */ static int handle_if(struct lexer_state *ls) { struct token_fifo tf, tf1, tf2, tf3, *save_tf; long l = ls->line; unsigned long z; int ret = 0, ltww = 1; /* first, get the whole line */ tf.art = tf.nt = 0; while (!next_token(ls) && ls->ctok->type != NEWLINE) { struct token t; if (ltww && ttMWS(ls->ctok->type)) continue; ltww = ttMWS(ls->ctok->type); t.type = ls->ctok->type; t.line = l; if (S_TOKEN(ls->ctok->type)) { t.name = sdup(ls->ctok->name); throw_away(ls->gf, t.name); } aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG); } if (ltww && tf.nt) if ((-- tf.nt) == 0) freemem(tf.t); if (tf.nt == 0) { error(l, "void condition for a #if/#elif"); return -1; } /* handle the "defined" operator */ tf1.art = tf1.nt = 0; while (tf.art < tf.nt) { struct token *ct, rt; struct macro *m; size_t nidx, eidx; ct = tf.t + (tf.art ++); if (ct->type == NAME && !strcmp(ct->name, "defined")) { if (tf.art >= tf.nt) goto store_token; nidx = tf.art; if (ttMWS(tf.t[nidx].type)) if (++ nidx >= tf.nt) goto store_token; if (tf.t[nidx].type == NAME) { eidx = nidx; goto check_macro; } if (tf.t[nidx].type != LPAR) { #ifdef HAVE_MISRA misra_neu(100,19,14,0); #endif goto store_token;} if (++ nidx >= tf.nt) goto store_token; if (ttMWS(tf.t[nidx].type)) if (++ nidx >= tf.nt) goto store_token; if (tf.t[nidx].type != NAME) { #ifdef HAVE_MISRA misra_neu(100,19,14,0); #endif goto store_token;} eidx = nidx + 1; if (eidx >= tf.nt) goto store_token; if (ttMWS(tf.t[eidx].type)) if (++ eidx >= tf.nt) goto store_token; if (tf.t[eidx].type != RPAR) goto store_token; goto check_macro; } store_token: aol(tf1.t, tf1.nt, *ct, TOKEN_LIST_MEMG); continue; check_macro: m = get_macro(tf.t[nidx].name); rt.type = NUMBER; rt.name = m ? "1L" : "0L"; aol(tf1.t, tf1.nt, rt, TOKEN_LIST_MEMG); tf.art = eidx + 1; } freemem(tf.t); if (tf1.nt == 0) { error(l, "void condition (after expansion) for a #if/#elif"); return -1; } /* perform all macro substitutions */ tf2.art = tf2.nt = 0; save_tf = ls->output_fifo; ls->output_fifo = &tf2; while (tf1.art < tf1.nt) { struct token *ct; ct = tf1.t + (tf1.art ++); if (ct->type == NAME) { struct macro *m = get_macro(ct->name); if (m) { if (substitute_macro(ls, m, &tf1, 0, #ifdef NO_PRAGMA_IN_DIRECTIVE 1, #else 0, #endif ct->line)) { ls->output_fifo = save_tf; return -1; } continue; } } else if ((ct->type == SHARP || ct->type == DIG_SHARP) && (ls->flags & HANDLE_ASSERTIONS)) { /* we have an assertion; parse it */ int nnp, ltww = 1; size_t i = tf1.art; struct token_fifo atl; char *aname; struct assert *a; int av = 0; struct token rt; atl.art = atl.nt = 0; while (i < tf1.nt && ttMWS(tf1.t[i].type)) i ++; if (i >= tf1.nt) goto assert_error; if (tf1.t[i].type != NAME) goto assert_error; aname = tf1.t[i ++].name; while (i < tf1.nt && ttMWS(tf1.t[i].type)) i ++; if (i >= tf1.nt) goto assert_generic; if (tf1.t[i].type != LPAR) goto assert_generic; i ++; for (nnp = 1; nnp && i < tf1.nt; i ++) { if (ltww && ttMWS(tf1.t[i].type)) continue; if (tf1.t[i].type == LPAR) nnp ++; else if (tf1.t[i].type == RPAR && (-- nnp) == 0) { tf1.art = i + 1; break; } ltww = ttMWS(tf1.t[i].type); aol(atl.t, atl.nt, tf1.t[i], TOKEN_LIST_MEMG); } if (nnp) goto assert_error; if (ltww && atl.nt && (-- atl.nt) == 0) freemem(atl.t); if (atl.nt == 0) goto assert_error; /* the assertion is in aname and atl; check it */ a = get_assertion(aname); if (a) for (i = 0; i < a->nbval; i ++) if (!cmp_token_list(&atl, a->val + i)) { av = 1; break; } rt.type = NUMBER; rt.name = av ? "1" : "0"; aol(tf2.t, tf2.nt, rt, TOKEN_LIST_MEMG); continue; assert_generic: tf1.art = i; rt.type = NUMBER; rt.name = get_assertion(aname) ? "1" : "0"; aol(tf2.t, tf2.nt, rt, TOKEN_LIST_MEMG); continue; assert_error: error(l, "syntax error for assertion in #if"); ls->output_fifo = save_tf; return -1; } aol(tf2.t, tf2.nt, *ct, TOKEN_LIST_MEMG); } ls->output_fifo = save_tf; freemem(tf1.t); if (tf2.nt == 0) { error(l, "void condition (after expansion) for a #if/#elif"); return -1; } /* * suppress whitespace and replace rogue identifiers by 0 */ tf3.art = tf3.nt = 0; while (tf2.art < tf2.nt) { struct token *ct = tf2.t + (tf2.art ++); if (ttMWS(ct->type)) continue; if (ct->type == NAME) { /* * a rogue identifier; we replace it with "0". */ struct token rt; #ifdef HAVE_MISRA misra_neu(97,19,11,0); #endif rt.type = NUMBER; rt.name = "0"; aol(tf3.t, tf3.nt, rt, TOKEN_LIST_MEMG); continue; } aol(tf3.t, tf3.nt, *ct, TOKEN_LIST_MEMG); } freemem(tf2.t); if (tf3.nt == 0) { error(l, "void condition (after expansion) for a #if/#elif"); return -1; } eval_line = l; z = eval_expr(&tf3, &ret, (ls->flags & WARN_STANDARD) != 0); freemem(tf3.t); if (ret) return -1; return (z != 0); } /* * A #include was found; parse the end of line, replace macros if * necessary. * * If nex is set to non-zero, the directive is considered as a #include_next * (extension to C99) */ static int handle_include(struct lexer_state *ls, unsigned long flags, int nex) { int c, string_fname = 0; char *fname; size_t fname_ptr = 0; long l = ls->line; int x, y; FILE *f; struct token_fifo tf, tf2, *save_tf; size_t nl; int tgd; #define left_angle(t) ((t) == LT || (t) == LEQ || (t) == LSH \ || (t) == ASLSH || (t) == DIG_LBRK || (t) == LBRA) #define right_angle(t) ((t) == GT || (t) == RSH || (t) == ARROW \ || (t) == DIG_RBRK || (t) == DIG_RBRA) #ifdef HAVE_MISRA extern int misracheck,misratok; if(misracheck&&misratok) misra_neu(87,19,1,0); #endif tf.t=0; /*vb*/ while ((c = grap_char(ls)) >= 0 && c != '\n') { if (space_char(c)) { discard_char(ls); continue; } if (c == '<') { discard_char(ls); while ((c = grap_char(ls)) >= 0) { discard_char(ls); if (c == '\n') goto include_error; if (c == '>') break; aol(fname, fname_ptr, (char)c, FNAME_MEMG); } aol(fname, fname_ptr, (char)0, FNAME_MEMG); string_fname = 0; goto do_include; } else if (c == '"') { discard_char(ls); while ((c = grap_char(ls)) >= 0) { discard_char(ls); if (c == '\n') goto include_error; if (c == '"') break; aol(fname, fname_ptr, (char)c, FNAME_MEMG); } aol(fname, fname_ptr, (char)0, FNAME_MEMG); string_fname = 1; goto do_include; } goto include_macro; } include_error: error(l, "invalid '#include'"); return 1; include_macro: #ifdef HAVE_MISRA misra(90); #endif tf.art = tf.nt = 0; while (!next_token(ls) && ls->ctok->type != NEWLINE) { if (!ttMWS(ls->ctok->type)) { struct token t; t.type = ls->ctok->type; t.line = l; if (S_TOKEN(ls->ctok->type)) { t.name = sdup(ls->ctok->name); throw_away(ls->gf, t.name); } aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG); } } tf2.art = tf2.nt = 0; save_tf = ls->output_fifo; ls->output_fifo = &tf2; while (tf.art < tf.nt) { struct token *ct; ct = tf.t + (tf.art ++); if (ct->type == NAME) { struct macro *m = get_macro(ct->name); if (m) { if (substitute_macro(ls, m, &tf, 0, #ifdef NO_PRAGMA_IN_DIRECTIVE 1, #else 0, #endif ct->line)) { ls->output_fifo = save_tf; return -1; } continue; } } aol(tf2.t, tf2.nt, *ct, TOKEN_LIST_MEMG); } freemem(tf.t); ls->output_fifo = save_tf; for (x = 0; x < tf2.nt && ttWHI(tf2.t[x].type); x ++); for (y = tf2.nt - 1; y >= 0 && ttWHI(tf2.t[y].type); y --); if (x >= tf2.nt) goto include_macro_err; if (tf2.t[x].type == STRING) { if (y != x) goto include_macro_err; if (tf2.t[x].name[0] == 'L') { if (ls->flags & WARN_STANDARD) warning(l, "wide string for #include"); fname = sdup(tf2.t[x].name); nl = strlen(fname); *(fname + nl - 1) = 0; mmvwo(fname, fname + 2, nl - 2); } else { fname = sdup(tf2.t[x].name); nl = strlen(fname); *(fname + nl - 1) = 0; mmvwo(fname, fname + 1, nl - 1); } string_fname = 1; } else if (left_angle(tf2.t[x].type) && right_angle(tf2.t[y].type)) { int i, j; if (ls->flags & WARN_ANNOYING) warning(l, "reconstruction " "of in #include"); for (j = 0, i = x; i <= y; i ++) if (!ttWHI(tf2.t[i].type)) j += strlen(tname(tf2.t[i])); fname = getmem(j + 1); for (j = 0, i = x; i <= y; i ++) { if (ttWHI(tf2.t[i].type)) continue; strcpy(fname + j, tname(tf2.t[i])); j += strlen(tname(tf2.t[i])); } *(fname + j - 1) = 0; mmvwo(fname, fname + 1, j); string_fname = 0; } else goto include_macro_err; freemem(tf2.t); goto do_include_next; include_macro_err: error(l, "macro expansion did not produce a valid filename " "for #include"); if (tf2.nt) freemem(tf2.t); return 1; do_include: tgd = 1; while (!next_token(ls)) { if (tgd && !ttWHI(ls->ctok->type) && (ls->flags & WARN_STANDARD)) { warning(l, "trailing garbage in #include"); tgd = 0; } if (ls->ctok->type == NEWLINE) break; } /* the increment of ls->line is intended so that the line numbering is reported correctly in report_context() even if the #include is at the end of the line with no trailing newline */ if (ls->ctok->type != NEWLINE) ls->line ++; do_include_next: if (!(ls->flags & LEXER) && (ls->flags & KEEP_OUTPUT)) put_char(ls, '\n'); push_file_context(ls); reinit_lexer_state(ls, 1); #ifdef HAVE_MISRA if(misracheck){ char *p; for(p=fname;*p;p++) if(*p=='\''||*p=='\\'||*p=='\"'||(*p=='/'&&p[1]=='*')) misra_neu(88,19,2,-1); } #endif { char *conv_fname = convert_path(fname); freemem(fname); fname = conv_fname; } f = nex ? find_file_next(fname) : find_file(fname, string_fname); if (!f) { pop_file_context(ls); if (find_file_error == FF_ERROR) { error(l, "file '%s' not found", fname); return 1; } /* file was found, but it is useless to include it again */ return 0; } #ifdef UCPP_MMAP set_input_file(ls, f); #else ls->input = f; #endif #ifdef HAVE_MISRA if (!strcmp("signal.h",fname)) misra_neu(123,20,8,-1); if (!strcmp("stdio.h",fname)) misra_neu(124,20,9,-1); if (!strcmp("time.h",fname)) misra(127,20,12,-1); #endif set_current_filename(fname); enter_file(ls, flags); return 0; #undef left_angle #undef right_angle } /* * for #line directives */ static int handle_line(struct lexer_state *ls, unsigned long flags) { char *fname; long l = ls->line; struct token_fifo tf, tf2, *save_tf; size_t nl, j; unsigned long z; tf.art = tf.nt = 0; while (!next_token(ls) && ls->ctok->type != NEWLINE) { if (!ttMWS(ls->ctok->type)) { struct token t; t.type = ls->ctok->type; t.line = l; if (S_TOKEN(ls->ctok->type)) { t.name = sdup(ls->ctok->name); throw_away(ls->gf, t.name); } aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG); } } tf2.art = tf2.nt = 0; save_tf = ls->output_fifo; ls->output_fifo = &tf2; while (tf.art < tf.nt) { struct token *ct; ct = tf.t + (tf.art ++); if (ct->type == NAME) { struct macro *m = get_macro(ct->name); if (m) { if (substitute_macro(ls, m, &tf, 0, #ifdef NO_PRAGMA_IN_DIRECTIVE 1, #else 0, #endif ct->line)) { ls->output_fifo = save_tf; return -1; } continue; } } aol(tf2.t, tf2.nt, *ct, TOKEN_LIST_MEMG); } freemem(tf.t); for (tf2.art = 0; tf2.art < tf2.nt && ttWHI(tf2.t[tf2.art].type); tf2.art ++); ls->output_fifo = save_tf; if (tf2.art == tf2.nt || (tf2.t[tf2.art].type != NUMBER && tf2.t[tf2.art].type != CHAR)) { error(l, "not a valid number for #line"); goto line_macro_err; } for (j = 0; tf2.t[tf2.art].name[j]; j ++) if (tf2.t[tf2.art].name[j] < '0' || tf2.t[tf2.art].name[j] > '9') if (ls->flags & WARN_STANDARD) warning(l, "non-standard line number in #line"); if (catch(eval_exception)) goto line_macro_err; z = strtoconst(tf2.t[tf2.art].name); if (j > 10 || z > 2147483647U) { error(l, "out-of-bound line number for #line"); goto line_macro_err; } ls->oline = ls->line = z; if ((++ tf2.art) < tf2.nt) { int i; for (i = tf2.art; i < tf2.nt && ttMWS(tf2.t[i].type); i ++); if (i < tf2.nt) { if (tf2.t[i].type != STRING) { error(l, "not a valid filename for #line"); goto line_macro_err; } if (tf2.t[i].name[0] == 'L') { if (ls->flags & WARN_STANDARD) { warning(l, "wide string for #line"); } fname = sdup(tf2.t[i].name); nl = strlen(fname); *(fname + nl - 1) = 0; mmvwo(fname, fname + 2, nl - 2); } else { fname = sdup(tf2.t[i].name); nl = strlen(fname); *(fname + nl - 1) = 0; mmvwo(fname, fname + 1, nl - 1); } set_current_filename(fname); } for (i ++; i < tf2.nt && ttMWS(tf2.t[i].type); i ++); if (i < tf2.nt && (ls->flags & WARN_STANDARD)) { warning(l, "trailing garbage in #line"); } } freemem(tf2.t); enter_file(ls, flags); return 0; line_macro_err: if (tf2.nt) freemem(tf2.t); return 1; } /* * a #error or #warning directive: we emit the message without any * modification (except the usual backslash+newline and trigraphs) */ static void handle_error(struct lexer_state *ls,int warnflag) { int c; size_t p = 0, lp = 128; long l = ls->line; unsigned char *buf = getmem(lp); while ((c = grap_char(ls)) >= 0 && c != '\n') { discard_char(ls); wan(buf, p, (unsigned char)c, lp); } wan(buf, p, 0, lp); if (warnflag) warning(l, "#warning%s", buf); else error(l, "#error%s", buf); freemem(buf); } /* * convert digraph tokens to their standard equivalent. */ static int undig(int type) { static int ud[6] = { LBRK, RBRK, LBRA, RBRA, SHARP, DSHARP }; return ud[type - DIG_LBRK]; } #ifdef PRAGMA_TOKENIZE /* * Make a compressed representation of a token list; the contents of * the token_fifo are freed. Values equal to 0 are replaced by * PRAGMA_TOKEN_END (by default, (unsigned char)'\n') and the compressed * string is padded by a 0 (so that it may be * handled like a string). * Digraph tokens are replaced by their non-digraph equivalents. */ struct comp_token_fifo compress_token_list(struct token_fifo *tf) { struct comp_token_fifo ct; size_t l; for (l = 0, tf->art = 0; tf->art < tf->nt; tf->art ++) { l ++; if (S_TOKEN(tf->t[tf->art].type)) l += strlen(tf->t[tf->art].name) + 1; } ct.t = getmem((ct.length = l) + 1); for (l = 0, tf->art = 0; tf->art < tf->nt; tf->art ++) { int tt = tf->t[tf->art].type; if (tt == 0) tt = PRAGMA_TOKEN_END; if (tt > DIGRAPH_TOKENS && tt < DIGRAPH_TOKENS_END) tt = undig(tt); ct.t[l ++] = tt; if (S_TOKEN(tt)) { char *tn = tf->t[tf->art].name; size_t sl = strlen(tn); mmv(ct.t + l, tn, sl); l += sl; ct.t[l ++] = PRAGMA_TOKEN_END; freemem(tn); } } ct.t[l] = 0; if (tf->nt) freemem(tf->t); ct.rp = 0; return ct; } #endif /* * A #pragma directive: we make a PRAGMA token containing the rest of * the line. * * We strongly hope that we are called only in LEXER mode. */ static void handle_pragma(struct lexer_state *ls) { unsigned char *buf; struct token t; long l = ls->line; #ifdef PRAGMA_TOKENIZE struct token_fifo tf; tf.art = tf.nt = 0; while (!next_token(ls) && ls->ctok->type != NEWLINE) if (!ttMWS(ls->ctok->type)) break; if (ls->ctok->type != NEWLINE) { do { struct token t; t.type = ls->ctok->type; if (ttMWS(t.type)) continue; if (S_TOKEN(t.type)) t.name = sdup(ls->ctok->name); aol(tf.t, tf.nt, t, TOKEN_LIST_MEMG); } while (!next_token(ls) && ls->ctok->type != NEWLINE); } if (tf.nt == 0) { /* void pragma are silently ignored */ return; } buf = (compress_token_list(&tf)).t; #else int c, x = 1, y = 32; while ((c = grap_char(ls)) >= 0 && c != '\n') { discard_char(ls); if (!space_char(c)) break; } /* void #pragma are ignored */ if (c == '\n') return; buf = getmem(y); buf[0] = c; while ((c = grap_char(ls)) >= 0 && c != '\n') { discard_char(ls); wan(buf, x, c, y); } for (x --; x >= 0 && space_char(buf[x]); x --); x ++; wan(buf, x, 0, y); #endif t.type = PRAGMA; t.line = l; t.name = (char *)buf; aol(ls->output_fifo->t, ls->output_fifo->nt, t, TOKEN_LIST_MEMG); throw_away(ls->gf, (char *)buf); } /* * We saw a # at the beginning of a line (or preceeded only by whitespace). * We check the directive name and act accordingly. */ static int handle_cpp(struct lexer_state *ls, int sharp_type) { #define condfset(x) do { \ ls->condf[(x) / 32] |= 1UL << ((x) % 32); \ } while (0) #define condfclr(x) do { \ ls->condf[(x) / 32] &= ~(1UL << ((x) % 32)); \ } while (0) #define condfval(x) ((ls->condf[(x) / 32] & (1UL << ((x) % 32))) != 0) long l = ls->line; unsigned long save_flags = ls->flags; int ret = 0; save_flags = ls->flags; ls->flags |= LEXER; while (!next_token(ls)) { int t = ls->ctok->type; switch (t) { case COMMENT: if (ls->flags & WARN_ANNOYING) { warning(l, "comment in the middle of " "a cpp directive"); } /* fall through */ case NONE: continue; case NEWLINE: /* null directive */ if (ls->flags & WARN_ANNOYING) { /* truly an annoying warning; null directives are rare but may increase readability of some source files, and they are legal */ warning(l, "null cpp directive"); } if (!(ls->flags & LEXER)) put_char(ls, '\n'); goto handle_exit2; case NAME: break; default: if (ls->flags & FAIL_SHARP) { if (ls->condcomp) { error(l, "rogue '#'"); ret = 1; } else { if (ls->flags & WARN_STANDARD) { warning(l, "rogue '#' in code " "compiled out"); ret = 0; } } ls->flags = save_flags; goto handle_warp_ign; } else { struct token u; u.type = sharp_type; u.line = l; ls->flags = save_flags; print_token(ls, &u, 0); print_token(ls, ls->ctok, 0); if (ls->flags & WARN_ANNOYING) { warning(l, "rogue '#' dumped"); } goto handle_exit3; } } if (ls->condcomp) { if (!strcmp(ls->ctok->name, "define")) { ret = handle_define(ls); goto handle_exit; } else if (!strcmp(ls->ctok->name, "undef")) { ret = handle_undef(ls); goto handle_exit; } else if (!strcmp(ls->ctok->name, "if")) { if ((++ ls->ifnest) > 63) goto too_many_if; condfclr(ls->ifnest - 1); ret = handle_if(ls); if (ret > 0) ret = 0; else if (ret == 0) { ls->condcomp = 0; ls->condmet = 0; ls->condnest = ls->ifnest - 1; } else ret = 1; goto handle_exit; } else if (!strcmp(ls->ctok->name, "ifdef")) { if ((++ ls->ifnest) > 63) goto too_many_if; condfclr(ls->ifnest - 1); ret = handle_ifdef(ls); if (ret > 0) ret = 0; else if (ret == 0) { ls->condcomp = 0; ls->condmet = 0; ls->condnest = ls->ifnest - 1; } else ret = 1; goto handle_exit; } else if (!strcmp(ls->ctok->name, "ifndef")) { if ((++ ls->ifnest) > 63) goto too_many_if; condfclr(ls->ifnest - 1); ret = handle_ifndef(ls); if (ret > 0) ret = 0; else if (ret == 0) { ls->condcomp = 0; ls->condmet = 0; ls->condnest = ls->ifnest - 1; } else ret = 1; goto handle_exit; } else if (!strcmp(ls->ctok->name, "else")) { if (ls->ifnest == 0 || condfval(ls->ifnest - 1)) { error(l, "rogue #else"); ret = 1; goto handle_warp; } condfset(ls->ifnest - 1); if (ls->ifnest == 1) protect_detect.state = 0; ls->condcomp = 0; ls->condmet = 1; ls->condnest = ls->ifnest - 1; goto handle_warp; } else if (!strcmp(ls->ctok->name, "elif")) { if (ls->ifnest == 0 || condfval(ls->ifnest - 1)) { error(l, "rogue #elif"); ret = 1; goto handle_warp_ign; } if (ls->ifnest == 1) protect_detect.state = 0; ls->condcomp = 0; ls->condmet = 1; ls->condnest = ls->ifnest - 1; goto handle_warp_ign; } else if (!strcmp(ls->ctok->name, "endif")) { if (ls->ifnest == 0) { error(l, "unmatched #endif"); ret = 1; goto handle_warp; } if ((-- ls->ifnest) == 0 && protect_detect.state == 2) { protect_detect.state = 3; } goto handle_warp; } else if (!strcmp(ls->ctok->name, "include")) { ret = handle_include(ls, save_flags, 0); goto handle_exit3; } else if (!strcmp(ls->ctok->name, "include_next")) { ret = handle_include(ls, save_flags, 1); goto handle_exit3; } else if (!strcmp(ls->ctok->name, "pragma")) { if (!(save_flags & LEXER)) { #ifdef PRAGMA_DUMP /* dump #pragma in output */ struct token u; u.type = sharp_type; u.line = l; ls->flags = save_flags; print_token(ls, &u, 0); print_token(ls, ls->ctok, 0); while (ls->flags |= LEXER, !next_token(ls)) { long save_line; ls->flags &= ~LEXER; save_line = ls->line; ls->line = l; print_token(ls, ls->ctok, 0); ls->line = save_line; if (ls->ctok->type == NEWLINE) break; } goto handle_exit3; #else if (ls->flags & WARN_PRAGMA) warning(l, "#pragma ignored " "and not dumped"); goto handle_warp_ign; #endif } if (!(ls->flags & HANDLE_PRAGMA)) goto handle_warp_ign; handle_pragma(ls); goto handle_exit; } else if (!strcmp(ls->ctok->name, "error")) { ret = 1; handle_error(ls,0); goto handle_exit; } else if (!strcmp(ls->ctok->name, "warning")) { ret = 0; handle_error(ls,1); goto handle_exit; } else if (!strcmp(ls->ctok->name, "line")) { ret = handle_line(ls, save_flags); goto handle_exit; } else if ((ls->flags & HANDLE_ASSERTIONS) && !strcmp(ls->ctok->name, "assert")) { ret = handle_assert(ls); goto handle_exit; } else if ((ls->flags & HANDLE_ASSERTIONS) && !strcmp(ls->ctok->name, "unassert")) { ret = handle_unassert(ls); goto handle_exit; } } else { if (!strcmp(ls->ctok->name, "else")) { if (condfval(ls->ifnest - 1) && (ls->flags & WARN_STANDARD)) { warning(l, "rogue #else in code " "compiled out"); } if (ls->condnest == ls->ifnest - 1) { if (!ls->condmet) ls->condcomp = 1; } condfset(ls->ifnest - 1); if (ls->ifnest == 1) protect_detect.state = 0; goto handle_warp; } else if (!strcmp(ls->ctok->name, "elif")) { if (condfval(ls->ifnest - 1) && (ls->flags & WARN_STANDARD)) { warning(l, "rogue #elif in code " "compiled out"); } if (ls->condnest != ls->ifnest - 1 || ls->condmet) goto handle_warp_ign; if (ls->ifnest == 1) protect_detect.state = 0; ret = handle_if(ls); if (ret > 0) { ls->condcomp = 1; ls->condmet = 1; ret = 0; } else if (ret < 0) ret = 1; goto handle_exit; } else if (!strcmp(ls->ctok->name, "endif")) { if ((-- ls->ifnest) == ls->condnest) { if (ls->ifnest == 0 && protect_detect.state == 2) protect_detect.state = 3; ls->condcomp = 1; } goto handle_warp; } else if (!strcmp(ls->ctok->name, "if") || !strcmp(ls->ctok->name, "ifdef") || !strcmp(ls->ctok->name, "ifndef")) { if ((++ ls->ifnest) > 63) goto too_many_if; condfclr(ls->ifnest - 1); } goto handle_warp_ign; } /* * Unrecognized directive. We emit either an error or * an annoying warning, depending on a command-line switch. */ if (ls->flags & FAIL_SHARP) { error(l, "unknown cpp directive '#%s'", ls->ctok->name); goto handle_warp_ign; } else { struct token u; u.type = sharp_type; u.line = l; ls->flags = save_flags; print_token(ls, &u, 0); print_token(ls, ls->ctok, 0); if (ls->flags & WARN_ANNOYING) { warning(l, "rogue '#' dumped"); } } } return 1; handle_warp_ign: while (!next_token(ls)) if (ls->ctok->type == NEWLINE) break; goto handle_exit; handle_warp: while (!next_token(ls)) { if (!ttWHI(ls->ctok->type) && (ls->flags & WARN_STANDARD)) { warning(l, "trailing garbage in " "preprocessing directive"); } if (ls->ctok->type == NEWLINE) break; } handle_exit: if (!(ls->flags & LEXER)) put_char(ls, '\n'); handle_exit3: if (protect_detect.state == 1) { protect_detect.state = 0; } else if (protect_detect.state == -1) { /* just after the #include */ protect_detect.state = 1; } handle_exit2: ls->flags = save_flags; return ret; too_many_if: error(l, "too many levels of conditional inclusion (max 63)"); ret = 1; goto handle_warp; #undef condfset #undef condfclr #undef condfval } /* * This is the main entry function. It maintains count of #, and call the * appropriate functions when it encounters a cpp directive or a macro * name. * return value: positive on error; CPPERR_EOF means "end of input reached" */ int cpp(struct lexer_state *ls) { int r = 0; while (next_token(ls)) { if (protect_detect.state == 3) { /* Cool ! A protection mechanism has been detected */ protect_detect.ff->protect = protect_detect.macro; protect_detect.macro = 0; } if (ls->ifnest) { error(ls->line, "unterminated #if construction " "(depth %ld)", (long)ls->ifnest); r = CPPERR_NEST; } if (ls_depth == 0) return CPPERR_EOF; fclose(ls->input); freemem(current_filename); pop_file_context(ls); if (!(ls->flags & LEXER) && !ls->ltwnl) put_char(ls, '\n'); enter_file(ls, ls->flags); } if (!(ls->ltwnl && (ls->ctok->type == SHARP || ls->ctok->type == DIG_SHARP)) && protect_detect.state == 1 && !ttWHI(ls->ctok->type)) { /* the first non-whitespace token encountered is not a sharp introducing a cpp directive */ protect_detect.state = 0; } if (protect_detect.state == 3 && !ttWHI(ls->ctok->type)) { /* a non-whitespace token encountered after the #endif */ protect_detect.state = 0; } if (ls->condcomp) { if (ls->ltwnl && (ls->ctok->type == SHARP || ls->ctok->type == DIG_SHARP)) { int x = handle_cpp(ls, ls->ctok->type); ls->ltwnl = 1; return r ? r : x; } if (ls->ctok->type == NAME) { struct macro *m; if ((m = get_macro(ls->ctok->name)) != 0) { int x; x = substitute_macro(ls, m, 0, 1, 0, ls->ctok->line); if (!(ls->flags & LEXER)) garbage_collect(ls->gf); return r ? r : x; } if (!(ls->flags & LEXER)) print_token(ls, ls->ctok, 0); } } else { if (ls->ltwnl && (ls->ctok->type == SHARP || ls->ctok->type == DIG_SHARP)) { int x = handle_cpp(ls, ls->ctok->type); ls->ltwnl = 1; return r ? r : x; } } if (ls->ctok->type == NEWLINE) ls->ltwnl = 1; else if (!ttWHI(ls->ctok->type)) ls->ltwnl = 0; return r ? r : -1; } #ifndef STAND_ALONE /* * llex() and lex() are the lexing functions, when the preprocessor is * linked to another code. llex() should be called only by lex(). */ static int llex(struct lexer_state *ls) { struct token_fifo *tf = ls->output_fifo; int r; if (tf->nt != 0) { if (tf->art < tf->nt) { #ifdef INMACRO_FLAG if (!ls->inmacro) { ls->inmacro = 1; ls->macro_count ++; } #endif ls->ctok = tf->t + (tf->art ++); if (ls->ctok->type > DIGRAPH_TOKENS && ls->ctok->type < DIGRAPH_TOKENS_END) { ls->ctok->type = undig(ls->ctok->type); } return 0; } else { #ifdef INMACRO_FLAG ls->inmacro = 0; #endif if (tf->nt) freemem(tf->t); tf->art = tf->nt = 0; garbage_collect(ls->gf); ls->ctok = ls->save_ctok; } } r = cpp(ls); if (ls->ctok->type > DIGRAPH_TOKENS && ls->ctok->type < LAST_MEANINGFUL_TOKEN) { ls->ctok->type = undig(ls->ctok->type); } if (r > 0) return r; if (r < 0) return 0; return llex(ls); } /* * lex() reads the next token from the processed stream and stores it * into ls->ctok. * return value: non zero on error (including CPPERR_EOF, which is not * quite an error) */ int lex(struct lexer_state *ls) { int r; do { r = llex(ls); #ifdef SEMPER_FIDELIS } while (!r && !ls->condcomp); #else } while (!r && (!ls->condcomp || (ttWHI(ls->ctok->type) && (!(ls->flags & LINE_NUM) || ls->ctok->type != NEWLINE)))); #endif return r; } #endif /* * check_cpp_errors() must be called when the end of input is reached; * it checks pending errors due to truncated constructs (actually none, * this is reserved for future evolutions). */ int check_cpp_errors(struct lexer_state *ls) { return 0; } /* * init_cpp() initializes everything that should be initialized. * If standard_inc is non-zero, the standard include directories are * added to the search path. */ void init_cpp(void) { init_cppm(); init_buf_lexer_state(&dsharp_lexer, 0); #ifdef PRAGMA_TOKENIZE init_buf_lexer_state(&tokenize_lexer, 0); #endif } /* * (re)init the global tables. * If standard_assertions is non 0, init the assertions table. */ void init_tables(int with_assertions) { time_t t; struct tm *ct; time(&t); ct = localtime(&t); #ifdef MSDOS /* no function strftime() in TurboC 2.01 */ { char *c = asctime(ct); compile_time[0] = '"'; mmv(compile_time + 1, c + 11, 8); compile_time[9] = '"'; compile_time[10] = 0; compile_date[0] = '"'; mmv(compile_date + 1, c + 4, 7); mmv(compile_date + 8, c + 20, 4); compile_date[12] = '"'; compile_date[13] = 0; } #else strftime(compile_time, 12, "\"%H:%M:%S\"", ct); strftime(compile_date, 24, "\"%b %d %Y\"", ct); #endif sprintf(compile_adate,"\"(%02d.%02d.%02d)\"", ct->tm_mday,ct->tm_mon+1,ct->tm_year%100); init_macros(); if (with_assertions) init_assertions(); init_found_files(); } /* * Resets the include path. */ void init_include_path(char *incpath[]) { if (include_path_nb) { int i; for (i = 0; i < include_path_nb; i ++) freemem(include_path[i]); freemem(include_path); include_path_nb = 0; } if (incpath) { int i; for (i = 0; incpath[i]; i ++) aol(include_path, include_path_nb, convert_path(incpath[i]), INCPATH_MEMG); } } /* * add_incpath() adds "path" to the standard include path. */ void add_incpath(char *path) { aol(include_path, include_path_nb, convert_path(path), INCPATH_MEMG); } #ifdef STAND_ALONE /* * print some help */ static void usage(char *command_name) { fprintf(stderr, "Usage: %s [options] [file]\n" "language options:\n" " -C keep comments in output\n" " -s keep '#' when no cpp directive is recognized\n" " -l do not emit line numbers\n" " -lg emit gcc-like line numbers\n" " -CC disable C++-like comments\n" " -a, -na, -a0 handle (or not) assertions\n" " -V disable macros with extra arguments\n" " -u understand UTF-8 in source\n" " -X enable -a, -u and -Y\n" " -c90 mimic C90 behaviour\n" " -t disable trigraph support\n" "warning options:\n" " -wt emit a final warning when trigaphs are encountered\n" " -wtt emit warnings for each trigaph encountered\n" " -wa emit warnings that are usually useless\n" " -w0 disable standard warnings\n" "directory options:\n" " -I directory add 'directory' before the standard include path\n" " -J directory add 'directory' after the standard include path\n" " -zI do not use the standard include path\n" " -M emit Makefile-like dependencies instead of normal " "output\n" " -Ma emit also dependancies for system files\n" " -o file store output in file\n" "macro and assertion options:\n" " -Dmacro predefine 'macro'\n" " -Dmacro=def predefine 'macro' with 'def' content\n" " -Umacro undefine 'macro'\n" " -Afoo(bar) assert foo(bar)\n" " -Bfoo(bar) unassert foo(bar)\n" " -Y predefine system-dependant macros\n" " -Z do not predefine special macros\n" " -d emit defined macros\n" " -e emit assertions\n" "misc options:\n" " -v print version number and settings\n" " -h show this help\n", command_name); } /* * print version and compile-time settings */ static void version(void) { size_t i; fprintf(stderr, "ucpp version %d.%d\n", VERS_MAJ, VERS_MIN); fprintf(stderr, "search path:\n"); for (i = 0; i < include_path_nb; i ++) fprintf(stderr, " %s\n", include_path[i]); } /* * parse_opt() initializes many things according to the command-line * options. * Return values: * 0 on success * 1 on semantic error (redefinition of a special macro, for instance) * 2 on syntaxic error (unknown options for instance) */ static int parse_opt(int argc, char *argv[], struct lexer_state *ls) { int i, ret = 0; char *filename = 0; int with_std_incpath = 1; int print_version = 0, print_defs = 0, print_asserts = 0; int system_macros = 0, standard_assertions = 1; init_lexer_state(ls); ls->flags = DEFAULT_CPP_FLAGS; emit_output = ls->output = stdout; for (i = 1; i < argc; i ++) if (argv[i][0] == '-') { if (!strcmp(argv[i], "-h")) { return 2; } else if (!strcmp(argv[i], "-C")) { ls->flags &= ~DISCARD_COMMENTS; } else if (!strcmp(argv[i], "-CC")) { ls->flags &= ~CPLUSPLUS_COMMENTS; } else if (!strcmp(argv[i], "-a")) { ls->flags |= HANDLE_ASSERTIONS; } else if (!strcmp(argv[i], "-na")) { ls->flags |= HANDLE_ASSERTIONS; standard_assertions = 0; } else if (!strcmp(argv[i], "-a0")) { ls->flags &= ~HANDLE_ASSERTIONS; } else if (!strcmp(argv[i], "-V")) { ls->flags &= ~MACRO_VAARG; } else if (!strcmp(argv[i], "-u")) { ls->flags |= UTF8_SOURCE; } else if (!strcmp(argv[i], "-X")) { ls->flags |= HANDLE_ASSERTIONS; ls->flags |= UTF8_SOURCE; system_macros = 1; } else if (!strcmp(argv[i], "-c90")) { ls->flags &= ~MACRO_VAARG; ls->flags &= ~CPLUSPLUS_COMMENTS; c99_compliant = 0; c99_hosted = -1; } else if (!strcmp(argv[i], "-t")) { ls->flags &= ~HANDLE_TRIGRAPHS; } else if (!strcmp(argv[i], "-wt")) { ls->flags |= WARN_TRIGRAPHS; } else if (!strcmp(argv[i], "-wtt")) { ls->flags |= WARN_TRIGRAPHS_MORE; } else if (!strcmp(argv[i], "-wa")) { ls->flags |= WARN_ANNOYING; } else if (!strcmp(argv[i], "-w0")) { ls->flags &= ~WARN_STANDARD; ls->flags &= ~WARN_PRAGMA; } else if (!strcmp(argv[i], "-s")) { ls->flags &= ~FAIL_SHARP; } else if (!strcmp(argv[i], "-l")) { ls->flags &= ~LINE_NUM; } else if (!strcmp(argv[i], "-lg")) { ls->flags |= GCC_LINE_NUM; } else if (!strcmp(argv[i], "-M")) { ls->flags &= ~KEEP_OUTPUT; emit_dependencies = 1; } else if (!strcmp(argv[i], "-Ma")) { ls->flags &= ~KEEP_OUTPUT; emit_dependencies = 2; } else if (!strcmp(argv[i], "-Y")) { system_macros = 1; } else if (!strcmp(argv[i], "-Z")) { no_special_macros = 1; } else if (!strcmp(argv[i], "-d")) { ls->flags &= ~KEEP_OUTPUT; print_defs = 1; } else if (!strcmp(argv[i], "-e")) { ls->flags &= ~KEEP_OUTPUT; print_asserts = 1; } else if (!strcmp(argv[i], "-zI")) { with_std_incpath = 0; } else if (!strcmp(argv[i], "-I") || !strcmp(argv[i], "-J")) { i ++; } else if (!strcmp(argv[i], "-o")) { if ((++ i) >= argc) { error(-1, "missing filename after -o"); return 2; } if (argv[i][0] == '-' && argv[i][1] == 0) { emit_output = ls->output = stdout; } else { ls->output = fopen(argv[i], "w"); if (!ls->output) { error(-1, "failed to open for " "writing: %s", argv[i]); return 2; } emit_output = ls->output; } } else if (!strcmp(argv[i], "-v")) { print_version = 1; } else if (argv[i][1] != 'I' && argv[i][1] != 'J' && argv[i][1] != 'D' && argv[i][1] != 'U' && argv[i][1] != 'A' && argv[i][1] != 'B') warning(-1, "unknown option '%s'", argv[i]); } else { if (filename != 0) { error(-1, "spurious filename '%s'", argv[i]); return 2; } filename = argv[i]; } init_tables(ls->flags & HANDLE_ASSERTIONS); init_include_path(0); if (filename) { #ifdef UCPP_MMAP FILE *f = fopen_mmap_file(filename); ls->input = 0; if (f) set_input_file(ls, f); #else ls->input = fopen(filename, "r"); #endif if (!ls->input) { error(-1, "file '%s' not found", filename); return 1; } #ifdef NO_LIBC_BUF setbuf(ls->input, 0); #endif set_init_filename(filename, 1); } else { ls->input = stdin; set_init_filename("", 0); } for (i = 1; i < argc; i ++) if (argv[i][0] == '-' && argv[i][1] == 'I') add_incpath(argv[i][2] ? argv[i] + 2 : argv[i + 1]); if (system_macros) for (i = 0; system_macros_def[i]; i ++) ret = ret || define_macro(ls, system_macros_def[i]); for (i = 1; i < argc; i ++) if (argv[i][0] == '-' && argv[i][1] == 'D') ret = ret || define_macro(ls, argv[i] + 2); for (i = 1; i < argc; i ++) if (argv[i][0] == '-' && argv[i][1] == 'U') ret = ret || undef_macro(ls, argv[i] + 2); if (ls->flags & HANDLE_ASSERTIONS) { if (standard_assertions) for (i = 0; system_assertions_def[i]; i ++) make_assertion(system_assertions_def[i]); for (i = 1; i < argc; i ++) if (argv[i][0] == '-' && argv[i][1] == 'A') ret = ret || make_assertion(argv[i] + 2); for (i = 1; i < argc; i ++) if (argv[i][0] == '-' && argv[i][1] == 'B') ret = ret || destroy_assertion(argv[i] + 2); } else { for (i = 1; i < argc; i ++) if (argv[i][0] == '-' && (argv[i][1] == 'A' || argv[i][1] == 'B')) warning(-1, "assertions disabled"); } if (with_std_incpath) { for (i = 0; include_path_std[i]; i ++) add_incpath(include_path_std[i]); } for (i = 1; i < argc; i ++) if (argv[i][0] == '-' && argv[i][1] == 'J') add_incpath(argv[i][2] ? argv[i] + 2 : argv[i + 1]); if (print_version) { version(); return 1; } if (print_defs) { print_defines(); emit_defines = 1; } if (print_asserts && (ls->flags & HANDLE_ASSERTIONS)) { print_assertions(); emit_assertions = 1; } return ret; } int main(int argc, char *argv[]) { struct lexer_state ls; int r, fr = 0; init_cpp(); if ((r = parse_opt(argc, argv, &ls)) != 0) { if (r == 2) usage(argv[0]); return EXIT_FAILURE; } enter_file(&ls, ls.flags); while ((r = cpp(&ls)) < CPPERR_EOF) fr = fr || (r > 0); fr = fr || check_cpp_errors(&ls); if (ls.flags & KEEP_OUTPUT) { /* while (ls.line > ls.oline) put_char(&ls, '\n'); */ put_char(&ls, '\n'); } if (emit_dependencies) fputc('\n', emit_output); #ifndef NO_UCPP_BUF if (!(ls.flags & LEXER)) { flush_output(&ls); } #endif if (ls.flags & WARN_TRIGRAPHS && ls.count_trigraphs) warning(0, "%ld trigraphs encountered", ls.count_trigraphs); return fr ? EXIT_FAILURE : EXIT_SUCCESS; } #endif