/* * Copyright (c) 2003 - 2006, Nils R. Weller * All rights reserved. * * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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. * * Preprocessor driver */ #include "preprocess.h" #include #include #include #include #include #include #include #include #include #include "token.h" #include "defs.h" #include "error.h" #include "expr.h" #include "numlimits.h" #include "type.h" #include "n_libc.h" #include "macros.h" static void print_token_list(struct token *list); static int complete_directive( FILE *out, struct include_file *incf, struct pp_directive *dir, struct token **toklist, int *has_data); static struct token * do_macro_subst(FILE *in, FILE *out, struct token *toklist, struct token **tailp, int dontoutput); const char *cur_inc; int cur_inc_is_std; int g_recording_tokens; char g_textbuf[2048]; struct include_dir *include_dirs; static int pre_directive = 1; int lineno = 1; char *curfile = NULL; int get_next_char(FILE *fd) { int ch = getc(fd); if (ch == '\n') { lex_line_ptr = lex_file_map + lex_chars_read; err_setlineptr(lex_line_ptr); ++lineno; } else if (ch == '\\') { if ((ch = getc(fd)) == '\n') { /* * Line continued - XXX wow this doesn't handle * multiple adjacent \\\n constructs */ ++lex_chars_read; ++lineno; if ((ch = getc(fd)) == '\n') { ++lex_chars_read; ++lineno; } return ch; } else { --lex_chars_read; ungetc(ch, fd); return '\\'; } } return ch; } int unget_char(int ch, FILE *fd) { if (ch == '\n') { --lineno; } return ungetc(ch, fd); } static int get_string(FILE *in, char *buf, size_t bufsiz, int *lastch) { char *p; int ch; /* Get directive */ while ((ch = FGETC(in)) != EOF && isspace(ch)) { if (ch == '\n') { UNGETC(ch, in); *buf = 0; return 0; } } for (p = buf;;) { if (ch == EOF) { break; } else if (!isalnum(ch) && ch != '_' && ch != '$') { UNGETC(ch, in); break; } else if (p == buf + bufsiz - 2) { return -1; } *p++ = ch; ch = FGETC(in); } *p = 0; *lastch = ch; return 0; } #define CMD_DEFINE 1 #define CMD_UNDEF 2 #define CMD_IF 3 #define CMD_ELSE 4 #define CMD_ELIF 5 #define CMD_ENDIF 6 #define CMD_ERROR 7 #define CMD_WARNING 8 #define CMD_LINE 9 #define CMD_INCLUDE 10 #define CMD_IFDEF 11 #define CMD_IFNDEF 12 #define CMD_INCLUDE_NEXT 13 /* GNU C... */ #define CMD_IDENT 14 /* common extension */ #define CMD_PRAGMA 15 static struct include_dir *lastdir; /* for #include_next */ struct pp_directive { const char *name; int code; int takes_arg; int determined; int significant; void *data; struct pp_directive *next; struct pp_directive *prev; }; static void set_compiler_line(FILE *out, int line, const char *file) { int old_g_ignore_text = g_ignore_text; g_ignore_text = 0; x_fprintf(out, "# %d \"%s\"\n", line, file); g_ignore_text = old_g_ignore_text; } static int check_comment(FILE *in) { int ch; if ((ch = FGETC(in)) == EOF) { return -1; } else if (ch == '*') { int sline = lineno; /* C style comment */ for (;;) { ch = FGETC(in); if (ch == EOF) { lineno = sline; lexerror("Unterminated comment " "(started in line %d)", sline); return -1; } else if (ch == '\n') { /*x_fputc('\n', out);*/ } else if (ch == '*') { if ((ch = FGETC(in)) == '/') { /* comment complete */ break; } } } return 0; } else if (ch == '/' /* && standard != C89 */) { /* C99/C++ style comment */ while ((ch = FGETC(in)) != '\n' && ch != EOF) ; UNGETC('\n', in); return 0; } else { return 1; } } #define EAT_LINE(in, ch) \ do ch = FGETC(in); while (ch != EOF && ch != '\n') static void check_garbage(FILE *in, int ch, const char *dir) { if (ch == '\n') { (void) FGETC(in); return; } for (;;) { int rc; if (ch == '\n' || ch == EOF) { return; } else if (isspace(ch)) { do { if ((ch = FGETC(in)) == '\n') { return; } } while (ch != EOF && isspace(ch)); } else if (ch == '/') { if ((rc = check_comment(in)) == -1) { return; } else if (rc == 1) { break; } ch = FGETC(in); } else { break; } } if (ch != EOF) { lexwarning("Ignoring junk after preprocessor directive `%s'", dir); EAT_LINE(in, ch); } } void dump_toklist(struct token *t) { fprintf(stderr, "----HERE GOES THE TOKLIST--------"); for (; t; t = t->next) { fprintf(stderr, " LOL %d = %s (%p)\n", t->type, t->ascii, t); } } static unsigned long cur_directive_start; /* * do_directive() is called by preprocess() when a line begins with a * # as first non-whitespace char. For the #undef and #endif directives, * proecssing can be completed here already. For others, it is necessary * to record subsequent tokens on the same line and then to evaluate them. * To do so we just return to preprocess(), which will record the tokens and * call complete_directive() when the line ends */ static int do_directive(struct input_file *inf, struct pp_directive *dir) { char buf[128]; FILE *in = inf->fd; int ch; int i; static const struct pp_directive directives[] = { { "define", CMD_DEFINE, 1, 0, 0, NULL, 0,0 }, { "undef", CMD_UNDEF, 1, 0, 0, NULL, 0,0 }, { "include", CMD_INCLUDE, 1, 0, 0, NULL, 0,0 }, { "include_next", CMD_INCLUDE_NEXT, 1, 0, 0, NULL, 0,0 }, { "if", CMD_IF, 1, 0, 0, NULL, 0,0 }, { "else", CMD_ELSE, 0, 0, 0, NULL, 0,0 }, { "elif", CMD_ELIF, 1, 0, 0, NULL, 0,0 }, { "endif", CMD_ENDIF, 0, 0, 0, NULL, 0,0 }, { "ifdef", CMD_IFDEF, 0, 0, 0, NULL, 0,0 }, { "ifndef", CMD_IFNDEF, 0, 0, 0, NULL, 0,0 }, { "warning", CMD_WARNING, 0, 0, 0, NULL, 0,0 }, { "error", CMD_ERROR, 0, 0, 0, NULL, 0,0 }, { "line", CMD_LINE, 1, 0, 0, NULL, 0,0 }, { "ident", CMD_IDENT, 1, 0, 0, NULL, 0,0 }, { "pragma", CMD_PRAGMA, 0, 0, 0, NULL, 0,0 }, { NULL, 0, 0, 0, 0, NULL, 0,0 } }; dir->data = NULL; cur_directive_start = lex_chars_read; if (get_string(in, buf, sizeof buf, &ch) != 0 /*|| !isspace(ch)*/) { lexerror("Invalid preprocessor directive"); EAT_LINE(in, ch); return -1; } if (*buf == 0) { /* Hmm, GNU cpp just passes # through */ } for (i = 0; directives[i].name != NULL; ++i) { if (strcmp(directives[i].name, buf) == 0) { break; } } if (directives[i].name == NULL) { lexerror("Unknown preprocessor directive `%s'", buf); EAT_LINE(in, ch); return -1; } *dir = directives[i]; if (dir->takes_arg && ch == '\n') { lexerror("No argument given for preprocessor " "directive `%s'", buf); return -1; } if (dir->code == CMD_DEFINE) { struct macro *m = alloc_macro(); if (get_string(in, buf, sizeof buf, &ch) != -1) { if (g_ignore_text) { EAT_LINE(in, ch); return 1; } m->name = n_xstrdup(buf); if (ch == EOF || ch == '\n') { empty: /* #define EMPTY */ m->empty = 1; EAT_LINE(in, ch); } else if (ch == '(') { struct macro_arg *arglist = NULL; struct macro_arg *arglist_tail = NULL; struct macro_arg *ma; static struct macro_arg nullma; /* function-like macro */ (void) FGETC(in); /* eat ( */ m->functionlike = 1; /* Read parameters */ for (;;) { int trailing_last = 0; if (get_string(in, buf, sizeof buf, &ch) == -1) { EAT_LINE(in, ch); return -1; } switch (ch) { case ',': case ')': (void) FGETC(in); break; default: if (ch == '.') { (void) FGETC(in); if (FGETC(in) != '.' || FGETC(in) != '.') { goto garbage; } if (m->trailing_last) { lexerror("Macro " "already has a trailing last argument"); EAT_LINE(in,ch); return -1; } trailing_last = 1; } else if (!isspace(ch)) { garbage: lexerror("Garbage in " "macro parameter list" " - `%c'", ch); EAT_LINE(in, ch); return -1; } else if (ch == '\n') { lexerror("Incomplete macro" "parameter list"); EAT_LINE(in, ch); return -1; } } if (*buf == 0 && ch == ')') { break; } else if (*buf == 0 && arglist != NULL) { lexerror("Empty macro parameter"); EAT_LINE(in, ch); } ma = n_xmalloc(sizeof *ma); *ma = nullma; ma->name = n_xstrdup(buf); if (arglist == NULL) { arglist = arglist_tail = ma; } else { arglist_tail->next = ma; arglist_tail = arglist_tail->next; } if (trailing_last) { m->trailing_last = ma; } if (ch == ')') { /* done */ break; } } m->arglist = arglist; } else if (!isspace(ch)) { lexerror("Invalid character `%c' after macro " "name `%s'", ch, buf); return -1; } else { do { if ((ch = FGETC(in)) == EOF || ch == '\n') { if (ch == '\n') { UNGETC(ch, in); } goto empty; } } while (isspace(ch)); UNGETC(ch, in); } /* * Only empty macros can already be stored * because the macro body is needed to * check identical redefinitions */ if (m->empty && (m = put_macro(m)) == NULL) { return -1; } } dir->data = m; } else if (dir->code == CMD_UNDEF || dir->code == CMD_IFDEF || dir->code == CMD_IFNDEF) { /* These directives just take an identifier */ if (get_string(in, buf, sizeof buf, &ch) != -1) { check_garbage(in, ch, dir->name); } if (dir->code == CMD_UNDEF) { if (!g_ignore_text) { (void) drop_macro(buf); } } else { dir->data = n_xstrdup(buf); } return 0; } else if (dir->code == CMD_LINE) { ; } else if (dir->code == CMD_INCLUDE || dir->code == CMD_INCLUDE_NEXT) { if (isspace(ch) && ch != '\n') { do { ch = FGETC(in); } while (isspace(ch) && ch != EOF && ch != '\n'); } if (ch == '<' || ch == '"') { char *p = NULL; int lookingfor = ch == '<'? '>': '"'; store_char(NULL, 0); store_char(&p, ch); while ((ch = FGETC(in)) != EOF) { store_char(&p, ch); if (ch == '\n') { lexerror("Incomplete #include directive"); return -1; } else if (ch == lookingfor) { /* done! */ store_char(&p, 0); break; } } if (ch == EOF) { lexerror("Premature end of file"); return -1; } dir->data = p; if ((ch = FGETC(in)) != EOF && ch != '\n') { check_garbage(in, ch, dir->name); } } else if (ch == '\n' || ch == EOF) { lexerror("Empty #include directive"); return -1; } else { /* #include pp-tok */ UNGETC(ch, in); } return 0; } else if (dir->code == CMD_IF || dir->code == CMD_ELIF) { ; } else if (dir->code == CMD_ERROR || dir->code == CMD_WARNING) { if (ch == '\n') { /* Done! */ if (!g_ignore_text) { if (dir->code == CMD_ERROR) { lexerror("#error"); } else { lexwarning("#warning"); } } return 0; } else if (isspace(ch)) { do ch = FGETC(in); while (isspace(ch) && ch != '\n'); if (ch == '\n') { if (dir->code == CMD_ERROR) { lexerror("#error"); } else { lexwarning("#warning"); } return 0; } UNGETC(ch, in); } /* * If you think we can just dump ``a line of stuff'' * back to the user - think twice. The syntax is * ``#error pp-tokens newline'' * So we need to check token validity, e.g. for string * constants (ucpp doesn't, and accepts #error "foo). */ } else if (dir->code == CMD_ENDIF || dir->code == CMD_ELSE) { #if 0 check_garbage(in, ch, dir->name); (void) FGETC(in); #endif } else if (dir->code == CMD_IDENT) { char *p = NULL; store_char(NULL, 0); if (isspace(ch) && ch != '\n') { do { ch = FGETC(in); } while (isspace(ch) && ch != EOF && ch != '\n'); } if (ch != '"') { puts("invalid #ident directive"); exit(1); #if 0 /* irix doesn't seem to like this :( */ error("Invalid #ident directive"); return -1; #endif } while ((ch = FGETC(in)) != EOF) { store_char(&p, ch); if (ch == '\n') { lexerror("Incomplete #ident directive"); return -1; } else if (ch == '"') { /* done! */ store_char(&p, 0); break; } } if ((ch = FGETC(in)) != EOF && ch != '\n') { check_garbage(in, ch, dir->name); } return 0; } else if (dir->code == CMD_PRAGMA) { /* Ignore for now */ if (ch != '\n') { do { ch = FGETC(in); } while (ch != EOF && ch != '\n'); } } return 0; } /* * The overwhelming majority of include files uses include guards. But * if those files are included more than once, they still may have to * be read and processed to find the #endif belonging to the include * gaurd. In order to prevent this some programmers tend to use * ``redundant include guards''. (I haven't been using such guards * since 2003 because I think they suck.) In order to implement this * in the preprocessor, we record the position of the first #if/#ifdef/ * #ifndef and the corresponding #endif for every include file, and * whether that covers the entire header. The helps us skip a lot of * stuff, particularly in the system headers. */ static struct include_file *current_include; static struct include_dir current_working_directory; /* misnomer */ static struct include_file * lookup_include(struct include_dir *dir, const char *name) { struct include_file *inf; size_t len = strlen(name); for (inf = dir->inc_files; inf != NULL; inf = inf->next) { if (inf->namelen == len) { if (memcmp(inf->name, name, len) == 0) { return inf; } } } return NULL; } static void put_include(struct include_dir *dir, struct include_file *inc) { inc->namelen = strlen(inc->name); if (dir->inc_files == NULL) { dir->inc_files = dir->inc_files_tail = inc; } else { dir->inc_files_tail->next = inc; dir->inc_files_tail = inc; } } static int do_include(FILE *out, char *str, struct token *toklist, int type) { char *p; char *oldname; FILE *fd = NULL; int rc; int oldline; char *oldfile = curfile; struct macro *mp; static int nesting; struct input_file inf; struct include_file *old_current_include = NULL; static struct input_file nullf; struct include_dir *source_dir = NULL; struct include_file *cached_file; struct include_file *new_cached_file = NULL; size_t old_lex_chars_read; if (++nesting > 1000) { lexerror("Include file nesting way too deep!"); return -1; } inf = nullf; if (str == NULL) { /* First build include argument from token list */ toklist = do_macro_subst(NULL, NULL, toklist, NULL, 1); str = toklist_to_string(toklist); } p = strchr(str, 0); if (*str == 0 || --p == str+1) { lexerror("Empty include file name"); return -1; } else if ((*p != '"' && *p != '>') || (*p == '"' && *str != '"') || (*p == '>' && *str != '<')) { lexerror("#include sytnax is \"file\" or "); return -1; } *p = 0; /* cut " or < */ if (*str == '"') { /* * Try opening file in . first, then fall back to standard * directories (actually absolute paths are ok too.) */ if ((fd = fopen(str+1, "r")) != NULL) { /**p = '"';*/ inf.path = str+1; lastdir = NULL; source_dir = ¤t_working_directory; } } if (fd == NULL) { /* Try standard includes */ char *buf; struct include_dir *id; if (type == CMD_INCLUDE_NEXT && lastdir != NULL) { if ((id = lastdir->next) == NULL) { id = include_dirs; } } else { id = include_dirs; } for (;; id = id->next) { if (id == NULL) { /* End of directory list reached */ if (type == CMD_INCLUDE_NEXT) { if (lastdir != NULL) { /* * We started somewhere in the * middle of the directory * list; wrap around! */ id = include_dirs; lastdir = NULL; } else { break; } } else { break; } } buf = n_xmalloc(strlen(id->path) + sizeof "/" + strlen(str+1)); sprintf(buf, "%s/%s", id->path, str+1); if ((fd = fopen(buf, "r")) != NULL) { lastdir = id; inf.path = buf; source_dir = id; break; } else { free(buf); } } } if ((inf.fd = fd) == NULL) { lexerror("Cannot open include file `%s'", str+1);; return -1; } inf.is_header = 1; old_current_include = current_include; if ((cached_file = lookup_include(source_dir, str+1)) != NULL) { if (cached_file->has_guard) { if (cached_file->fully_guarded) { if (complete_directive(NULL, cached_file, NULL, NULL, NULL) == 0) { /* * Include guard condition evaluates * to false - as expected - so the * file need not be read */ (void) fclose(inf.fd); /* XXX */ goto out; } } } /* file is already known - don't record guard */ current_include = NULL; } else { /* Processing new file */ static struct include_file nullif; new_cached_file = n_xmalloc(sizeof *new_cached_file); *new_cached_file = nullif; new_cached_file->name = str+1; current_include = new_cached_file; } #if 0 fprintf(stderr, "processing %s\n", inf.path); #endif /* * __FILE__ and compiler line information must be updated! */ if ((mp = lookup_macro("__FILE__", 0)) != NULL) { oldname = mp->builtin; mp->builtin = inf.path; } old_lex_chars_read = lex_chars_read; lex_chars_read = 0; /* Inform the compiler that we're doing a new file */ set_compiler_line(out, 1, inf.path); oldline = lineno; lineno = 1; rc = preprocess(&inf, out); (void) fclose(inf.fd); /* free(inf.path); */ lex_chars_read = old_lex_chars_read; /* Restore old line number */ lineno = oldline; curfile = oldfile; set_compiler_line(out, lineno, curfile); if (mp != NULL) { mp->builtin = oldname; } if (new_cached_file != NULL) { put_include(source_dir, new_cached_file); } current_include = old_current_include; out: --nesting; return rc; } /* * cond_dir_list records the conditional preprocessor directives such as #if, * #elif, #ifdef, etc. It is used to match new occasions of such directives * with previous ones. The list should be read FIFO stack-like, where the * tail points to the top. When a directive is terminated by an #endif, all * belonging directives are removed from the tail. */ static struct pp_directive *cond_dir_list; static struct pp_directive *cond_dir_cur_start; static struct pp_directive *cond_dir_list_tail; int g_ignore_text = 0; /* XXX move to evalexpr.c */ int value_is_nonzero(struct tyval *cv) { int evaluates_true = 0; /* * The resulting expression must have integral * type */ #define EVALTRUE(cv, ty) *(ty *)cv->value != 0 switch (cv->type->code) { case TY_INT: evaluates_true = EVALTRUE(cv, int); break; case TY_UINT: evaluates_true = EVALTRUE(cv, unsigned int); break; case TY_LONG: evaluates_true = EVALTRUE(cv, long); break; case TY_ULONG: evaluates_true = EVALTRUE(cv, unsigned long); break; case TY_LLONG: case TY_ULLONG: { /* * Avoid relying on compiler support for * long long, but assume 64 bits */ unsigned char *p =cv->value; int i; for (i = 0; i < 8; ++i) { evaluates_true |= !!*p++; } } default: printf("BUG: preprocessor expression has " "type %d (not integral!)\n", cv->type->code); } return evaluates_true; } /* * This function is unfortunately overloaded to serve two distinct purposes: * * 1) complete a preprocessor directive. If it is an #if/#ifdef/#elif/#endif/etc * directive, g_ignore_text will be modified as necessary, and include guards * are also recorded if current_include is non-null. * All this is done if incf is null (and dir thusly non-null.) * * 2) check whether the include guard in include file ``incf'' (start_dir) * evaluates to true. Only the result of this evaluation is returned, and no * side effects take place. * This is done if incf is non-null * * XXX we should have an evaluates_true() instead! */ static int complete_directive( FILE *out, struct include_file *incf, struct pp_directive *dir, struct token **toklist, int *has_data) { struct macro *mp; struct expr *ex; struct token *t; struct token *last = NULL; struct token *ltoklist; int evaluates_true = 0; int recording_guard = 0; if (incf != NULL) { dir = incf->start_dir; ltoklist = incf->toklist; toklist = <oklist; } if (g_ignore_text) { if (dir->code == CMD_ERROR || dir->code == CMD_WARNING || dir->code == CMD_LINE || dir->code == CMD_DEFINE || dir->code == CMD_INCLUDE || dir->code == CMD_INCLUDE_NEXT) { return 0; } } if (dir->code == CMD_ERROR || dir->code == CMD_WARNING) { char *p = toklist_to_string(*toklist); if (dir->code == CMD_WARNING) { lexwarning("#warning: %s", p); } else { lexerror("#error: %s", p); } free(p); *toklist = NULL; return 0; } else if (dir->code == CMD_LINE) { struct token *t = *toklist; struct token *t2; if ((t = skip_ws(t)) == NULL) { lexerror("Empty #line directive"); return -1; } if (t->type != TY_INT) { lexerror("Invalid #line directive x"); return -1; } if ((t2 = skip_ws(t->next)) != NULL) { if (t2->type != TOK_STRING_LITERAL) { lexerror("Invalid #line directive"); return -1; } curfile = t2->data+1; (void) strtok(curfile, "\""); } lineno = *(int *)t->data; set_compiler_line(out, lineno, curfile); *toklist = NULL; return 0; } else if (dir->code == CMD_DEFINE) { mp = dir->data; mp->toklist = *toklist; *toklist = NULL; if (put_macro(mp) == NULL) { return -1; } return 0; } else if (dir->code == CMD_INCLUDE || dir->code == CMD_INCLUDE_NEXT) { if (!g_ignore_text) { int rc; rc = do_include(out, NULL, *toklist, dir->code); *toklist = NULL; return rc; } else { return 0; } } /* * At this point, we are only dealing with conditional directives * anymore, i.e. #if/#elif/#ifndef/#endif etc. Those need to be * stored, so a copy must be made */ if (dir->code != CMD_ENDIF && incf == NULL) { dir = n_xmemdup(dir, sizeof *dir); } if (incf == NULL) { /* * A newline has already been read - to ensure a correct __LINE__, * that must be undone */ --lineno; } if (dir->code == CMD_ELIF || dir->code == CMD_ELSE) { /* These can only continue an existent directive chain! */ if (cond_dir_cur_start == NULL) { lexerror("Use of #%s directive without preceding " "#if/#ifdef/#ifndef", dir->name); ++lineno; return -1; } else if (cond_dir_list_tail->code == CMD_ELSE && dir->code == CMD_ELIF) { lexerror("#else followed by #elif directive"); ++lineno; return -1; } } else if (dir->code == CMD_IF || dir->code == CMD_IFDEF || dir->code == CMD_IFNDEF) { /* These can only introduce a new directive chain! */ if (incf == NULL) { cond_dir_cur_start = dir; } } switch (dir->code) { case CMD_IFDEF: case CMD_IFNDEF: case CMD_IF: case CMD_ELIF: if (incf == NULL && current_include != NULL && !current_include->has_guard) { /* This may be the guard we are looking for */ if (!*has_data) { current_include->fully_guarded = 1; } recording_guard = 1; current_include->startp = dir; current_include->start_dir = dir; current_include->has_guard = 1; if (toklist != NULL) { current_include->toklist = *toklist; } current_include->start_guard = cur_directive_start; } if (dir->code == CMD_IFDEF || dir->code == CMD_IFNDEF) { if ((mp = lookup_macro(dir->data, 0)) != NULL) { /* Macro exists! */ if (dir->code == CMD_IFDEF) { evaluates_true = 1; } else { evaluates_true = 0; } } else { if (dir->code == CMD_IFDEF) { evaluates_true = 0; } else { evaluates_true = 1; } } if (incf == NULL && !recording_guard) { free(dir->data); } } else if (!g_ignore_text || cond_dir_cur_start->significant) { struct token *toklist_tail = NULL; /* * This is an #if or #elif - macro substituion was * disabled while reading tokens - process them * now! */ *toklist = do_macro_subst(NULL, NULL, *toklist, &toklist_tail, 1); /* * #if/#elif * * First cut all whitespace, then append a newline as * terminator for parse_expr() */ for (t = *toklist; t != NULL;) { struct token *next = t->next; if (t->type == TOK_WS) { if (t->prev) { t->prev->next = t->next; if (t->next) { t->next->prev = t->prev; } } else { *toklist = t->next; t->next->prev = NULL; } free(t); } else { last = t; } t = next; } if (last == NULL) { lexerror("Empty #%s directive", dir->name); ++lineno; return -1; } else { static struct token terminator; terminator.type = TOK_NEWLINE; last->next = &terminator; } ex = parse_expr(toklist, TOK_NEWLINE, 0, EXPR_CONST); if (ex == NULL) { ++lineno; return -1; } else if (ex->const_value == NULL) { puts("BUG: const_value = NULL?????"); abort(); } evaluates_true = value_is_nonzero(ex->const_value); if (dir->code == CMD_ELIF) { if (cond_dir_cur_start->determined) { /* Branch to take already determined */ evaluates_true = 0; } } } break; case CMD_ELSE: if (!cond_dir_cur_start->determined) { /* No branch determined yet, so #else wins */ evaluates_true = 1; } else { evaluates_true = 0; } break; case CMD_ENDIF: break; default: abort(); } if (incf != NULL) { return evaluates_true; } else if (dir->code != CMD_ENDIF) { /* * The result of the evaluation only matters if text * is not already being ignored */ if (!g_ignore_text) { if (!evaluates_true) { g_ignore_text = 1; dir->significant = 1; } else { cond_dir_cur_start->determined = 1; } } else { if (dir->code == CMD_ELSE) { if (cond_dir_list_tail->significant && evaluates_true) { /* * Previous directive ends - text is * no longer ignored */ g_ignore_text = 0; cond_dir_cur_start->determined = 1; } } else if (dir->code == CMD_ELIF) { if (cond_dir_list_tail->significant && evaluates_true) { g_ignore_text = 0; cond_dir_cur_start->determined = 1; } else if (cond_dir_list_tail->significant) { dir->significant = 1; } } } if (cond_dir_list == NULL) { cond_dir_list = cond_dir_list_tail = dir; } else { cond_dir_list_tail->next = dir; dir->prev = cond_dir_list_tail; cond_dir_list_tail = dir; } } else { /* Current chain is done! */ struct pp_directive *ppd; struct pp_directive *tmp; int code; for (ppd = cond_dir_list_tail; ppd != NULL;) { if (ppd->significant) { /* * An ignored (by ppd's controlling expression) * text passage ends here */ g_ignore_text = 0; } tmp = ppd; code = ppd->code; ppd = ppd->prev; if (code == CMD_IF || code == CMD_IFDEF || code == CMD_IFNDEF) { struct pp_directive *startp = tmp; /* * Start of chain reached, we are done. Now we * have to return to dealing with the previous, * outer chain (if any!) */ if ((cond_dir_list_tail = ppd) == NULL) { /* No outer one */ for (ppd = cond_dir_list /*->next*/; ppd;) { tmp = ppd; ppd = ppd->next; if (current_include != NULL && current_include->startp == tmp) { continue; } free(tmp); } cond_dir_list = NULL; } else { /* Yes, outer */ for (tmp = ppd; tmp != NULL; tmp = tmp->prev) { code = tmp->code; if (code == CMD_IF || code == CMD_IFDEF || code == CMD_IFNDEF) { cond_dir_cur_start = tmp; break; } } for (ppd = cond_dir_list_tail->next; ppd != NULL;) { tmp = ppd; ppd = ppd->next; if (current_include != NULL && current_include-> startp == tmp) { continue; } free(tmp); } cond_dir_list_tail->next = NULL; } if (current_include != NULL && current_include->has_guard && current_include->end_guard == 0 && startp == current_include->startp) { /* Record end of inc guard */ current_include->end_guard = cur_directive_start; current_include->end_dir = dir; *has_data = 0; } break; } } } if (toklist) *toklist = NULL; ++lineno; return 0; } extern int collect_parens; int preprocess(struct input_file *inf, FILE *out) { int ch; int tmpi; int compound = 0; int array = 0; int parentheses = 0; int ispragma = 0; int prevch = 0; int first_byte = 1; int *dummyptr = n_xmalloc(sizeof *dummyptr); int err; struct token *toklist = NULL; struct token *toklist_tail = NULL; struct token *t; struct pp_directive dir; struct macro *mp = NULL; static struct macro nullm; char *p; char *tmpc; int curfileid = 0; int firstch; int doing_funclike = 0; int substitute_macros = 1; int doing_pre = 0; int has_data = 0; int maybe_funclike = 0; FILE *in = inf->fd; if (/*options.showline*/ 1) { int fd = fileno(in); struct stat s; if (fstat(fd, &s) == -1) { perror("fstat"); exit(EXIT_FAILURE); } lex_file_map = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); if (lex_file_map == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); } lex_file_map_end = lex_file_map + s.st_size; lex_line_ptr = lex_file_map; } /* Initialize error message module */ err_setfile(curfile = inf->path); token_setfile(curfile); if (!inf->is_header && !inf->is_cmdline) { /* Processing new .c file */ lineno = 1; set_compiler_line(out, lineno, curfile); err_setline(&lineno); /* * Set predefined macros */ mp = n_xmalloc(sizeof *mp); *mp = nullm; mp->name = n_xstrdup("__LINE__"); mp->builtin = &lineno; (void) put_macro(mp); mp = n_xmalloc(sizeof *mp); *mp = nullm; mp->name = n_xstrdup("__FILE__"); mp->builtin = n_xmalloc(strlen(curfile) + 3); sprintf(mp->builtin, "\"%s\"", curfile); (void) put_macro(mp); } errors = 0; warnings = 0; /* * Initialize the digit limits of integral constants so * get_num_literal() can warn about overflow. */ init_max_digits(); while ((ch = FGETC(in)) != EOF) { lex_tok_ptr = lex_file_map + lex_chars_read; if (!isspace(ch) && ch != '#') { pre_directive = 0; /* * If this is a comment, has_data must remain zero - * check below */ if (ch != '/') { has_data = 1; } } if (g_ignore_text && !doing_pre && ch != '\n' && ch != '#' && ch != '/' && ch != '\'' && ch != '"') { /* * This is a blunt way of avoiding processing * data in an ignored (e.g. by ``#if 0'') * text passage. Comments, newlines, preprocessor * directives and string/character constants must * still be processed though. I hope I didn't * miss anything here ... */ continue; } if (maybe_funclike && !isspace(ch) && ch != '/' && ch != '(') { /* * Function-like macro identifier not followed by * opening parentheses! * (if this is a ``/'' it may be a comment - check * below.) */ output_token_list(out, toklist); free_token_list(toklist); toklist = NULL; maybe_funclike = 0; g_recording_tokens = 0; } switch (ch) { case '#': if (pre_directive || first_byte) { int done = 0; /* Preprocessor directive */ pre_directive = 0; /* XXX predef isn't an input_file */ if (do_directive(inf, &dir) != 0) { pre_directive = 1; break; } /* * Check whether subsequent tokens on same * line (including \-continued ones!) need * to be recorded for expression evaluation * or macro definitions */ switch (dir.code) { case CMD_UNDEF: case CMD_ENDIF: case CMD_IFDEF: case CMD_IFNDEF: case CMD_IDENT: case CMD_PRAGMA: g_recording_tokens = 0; doing_pre = 0; if (dir.code != CMD_UNDEF && dir.code != CMD_IDENT && dir.code != CMD_PRAGMA) { complete_directive(out, NULL, &dir, NULL, &has_data); } toklist = NULL; done = 1; break; case CMD_DEFINE: mp = dir.data; if (mp->empty) { /* No tokens needed */ done = 1; break; } doing_pre = 1; g_recording_tokens = 1; substitute_macros = 0; break; case CMD_INCLUDE: case CMD_INCLUDE_NEXT: if (dir.data != NULL) { if (!g_ignore_text) { (void) do_include( out, dir.data, NULL, dir.code); } doing_pre = 0; substitute_macros = 1; g_recording_tokens = 0; done = 1; } else { g_recording_tokens = 1; substitute_macros = 0; doing_pre = 1; } break; case CMD_LINE: case CMD_IF: case CMD_ELIF: if (dir.code == CMD_LINE) { substitute_macros = 1; } else { /* * Maco replacement is done * later */ substitute_macros = 0; } g_recording_tokens = 1; doing_pre = 1; break; case CMD_ERROR: case CMD_WARNING: /* Macro replacement is never done */ substitute_macros = 0; g_recording_tokens = 1; doing_pre = 1; break; default: if (dir.code != CMD_ERROR && dir.code != CMD_WARNING) { substitute_macros = 0; } if (dir.data != NULL) { done = 1; g_recording_tokens = 0; } else { doing_pre = 1; g_recording_tokens = 1; } break; } if (done) { /*UNGETC('\n', in);*/ pre_directive = 1; } } else { if (!g_recording_tokens) { x_fputc('#', out); } else { if ((ch = FGETC(in)) == '#') { store_token(&toklist, &toklist_tail, NULL, TOK_HASHHASH, lineno); } else { UNGETC(ch, in); store_token(&toklist, &toklist_tail, NULL, TOK_HASH, lineno); } } } break; case ' ': case '\f': case '\t': case '\r': firstch = ch; p = g_textbuf; *p++ = ch; while (isspace(ch = FGETC(in)) && ch != '\n') { *p++ = ch; /* XXX */ } *p = 0; UNGETC(ch, in); if (!g_recording_tokens) { if (pre_directive) { x_fprintf(out, g_textbuf); } else { x_fputc(' ', out); } } else { store_token(&toklist, &toklist_tail, n_xstrdup(" "), TOK_WS, lineno); } ispragma = 0; break; case '\n': if (g_recording_tokens && doing_pre) { g_recording_tokens = 0; substitute_macros = 1; doing_pre = 0; complete_directive(out, NULL, &dir, &toklist, &has_data); } else { mp = NULL; /* XXX hm?!? */ } pre_directive = 1; x_fputc('\n', out); ispragma = 0; break; case '/': if ((ch = FGETC(in)) == '*') { int sline = lineno; char *sfile = curfile; /* C style comment */ for (;;) { ch = FGETC(in); if (ch == EOF) { err_setfile(sfile); lineno = sline; lexerror("Unterminated comment " "(started in line %d," " file %s)", sline, sfile); return -1; #if 0 /* TODO: warn about nested comments */ } else if (ch == '/') { #endif } else if (ch == '\n') { x_fputc('\n', out); ++lineno; } else if (ch == '*') { if ((ch = FGETC(in)) == '/') { /* comment complete */ break; } else { UNGETC(ch, in); } } } UNGETC(' ', in); } else if (ch == '/' /* && standard != C89 */) { /* C99/C++ style comment */ while ((ch = FGETC(in)) != '\n' && ch != EOF) ; UNGETC(' ', in); if (ch != EOF) { UNGETC('\n', in); } } else { has_data = 1; /* Not a comment */ if (maybe_funclike) { maybe_funclike = 0; g_recording_tokens = 0; output_token_list(out, toklist); free_token_list(toklist); toklist = NULL; } UNGETC(ch, in); ch = '/'; goto do_operator; } break; case '\'': err = 0; tmpi = get_char_literal(in, &err, &tmpc); if (!err) { /* * Character literals are really treated * like integer constants */ int *tmpip = malloc(sizeof(int)); if (tmpip == NULL) { perror("malloc"); exit(EXIT_FAILURE); } *tmpip = tmpi; if (g_recording_tokens) { char *tmpc2; t = store_token(&toklist, &toklist_tail, tmpip, TY_INT, lineno); /* XXX .. */ tmpc2 = n_xmalloc(strlen(tmpc)+3); sprintf(tmpc2, "'%s'", tmpc); t->ascii = tmpc2; } else { x_fprintf(out, "'%s'", tmpc); } } ispragma = 0; break; case '"': { char *str; str = get_string_literal(in); if (str != NULL) { if (g_recording_tokens) { store_token(&toklist, &toklist_tail, str, TOK_STRING_LITERAL, lineno); } else { x_fprintf(out, "%s", str); } } ispragma = 0; break; } case '(': case ')': if (ch == '(') { if (maybe_funclike) { /* Is function-like macro! */ doing_funclike = 1; maybe_funclike = 0; parentheses = 1; substitute_macros = 0; g_recording_tokens = 1; /* * There may only be whitespace tokens * between the identifier and the * parentheses - cut those! */ if (toklist->next) { free_token_list(toklist->next); toklist->next = NULL; toklist_tail = toklist; } /* * fall through so that ( is appended * below */ } else { /* * Don't increment/decrement paren count * for things like * #define foo (lol */ if (!doing_pre) ++parentheses; } } else { if (!doing_pre) --parentheses; } if (g_recording_tokens) { store_token(&toklist, &toklist_tail, dummyptr, ch == '(' ? TOK_PAREN_OPEN : TOK_PAREN_CLOSE, lineno); } else { x_fputc(ch, out); } ispragma = 0; if (doing_funclike && parentheses == 0) { /* * XXX take g_recording_tokens into * account :-( */ toklist = do_macro_subst(in, out, toklist, &toklist_tail, 0); if (toklist == NULL) { /* done */ doing_funclike = 0; g_recording_tokens = 0; substitute_macros = 1; } else { parentheses = collect_parens; } } break; case '{': case '}': if (ch == '{') { ++compound; } else { if (compound == 0) { lexerror("No matching opening brace."); } --compound; } if (g_recording_tokens) { store_token(&toklist, &toklist_tail, dummyptr, ch == '{'? TOK_COMP_OPEN: TOK_COMP_CLOSE, lineno); } else { x_fputc(ch, out); } ispragma = 0; break; case '[': case ']': if (ch == '[') { ++array; } else { if (array == 0) { lexerror("Not a valid subscript."); ++array; } --array; } if (g_recording_tokens) { store_token(&toklist, &toklist_tail, dummyptr, ch == '[' ? TOK_ARRAY_OPEN : TOK_ARRAY_CLOSE, lineno); } else { x_fputc(ch, out); } ispragma = 0; break; case ';': if (g_recording_tokens) { store_token(&toklist, &toklist_tail, dummyptr, TOK_SEMICOLON, lineno); } else { x_fputc(';', out); } ispragma = 0; break; case '.': /* * This might be either a structure / union * indirection operator or a floating point * value like .5 (equivalent to 0.5). If the * latter is the case, call get_num_literal(), * else fall through */ if ((tmpi = FGETC(in)) == EOF) { lexerror("Unexpected end of file."); return 1; } UNGETC(tmpi, in); if (isdigit((unsigned char)tmpi)) { struct num *n = get_num_literal(ch, in); if (n != NULL) { if (g_recording_tokens) { struct token *t; t = store_token(&toklist, &toklist_tail, n->value, n->type, lineno); t->ascii = n->ascii; } else { } } break; } ispragma = 0; /* FALLTHRU */ default: if (ch == '?') { int trig; /* Might be trigraph */ if ((trig = get_trigraph(in)) == -1) { /* * Not a trigraph - LOOKUP_OP() * will catch the ``?'' */ ; } else if (trig == 0) { /* * The source file contained a ``??'' * that isn't isn't part of a trigraph - * this is a syntax error, since it * cannot be the conditional operator */ lexerror("Syntax error at ``?\?''"); } else { /* Valid trigraph! */ UNGETC(trig, in); ispragma = 0; break; } } do_operator: if (LOOKUP_OP(ch)) { int *ptri = malloc(sizeof(int)); struct operator *opp; if (ptri == NULL) { perror("malloc"); exit(EXIT_FAILURE); } tmpi = get_operator(ch, in, &opp); if (tmpi == -1) { lexerror("INVALID OPERATOR!!!"); break; } if (g_recording_tokens) { *ptri = tmpi; t = store_token(&toklist, &toklist_tail,ptri, TOK_OPERATOR, lineno); } else { x_fprintf(out, "%s", opp->name); } } else if (isdigit((unsigned char)ch)) { struct num *n = get_num_literal(ch, in); if (n != NULL) { if (g_recording_tokens) { t = store_token(&toklist, &toklist_tail, n->value, n->type, lineno); t->ascii = n->ascii; } else { x_fprintf(out, "%s", n->ascii); } } else { lexerror("Couldn't read numeric literal"); } } else if (isalpha((unsigned char)ch) || ch == '_') { struct macro *mp; struct macro_arg *ma = NULL; int is_defined; if (ch == 'L') { int tmpch; tmpch = FGETC(in); if (tmpch != EOF) { UNGETC(tmpch, in); if (tmpch == '\'' || tmpch == '"') { /* * Long constant - treat like * ordinary one */ break; } } } tmpc = get_identifier(ch, in); if (tmpc == NULL) { break; } if (g_recording_tokens && dir.code == CMD_DEFINE && ((struct macro *)dir.data)-> functionlike) { /* * Macro definition tokens are only * subject to macro substitution when * the macro is instantiated, thus; * #define x lol * #define foo() x * #undef x * foo() * ... must yield x rather than lol */ mp = dir.data; for (ma = mp->arglist; ma != NULL; ma = ma->next) { if (strcmp(ma->name, tmpc) == 0) { break; } } } else if (substitute_macros && (mp = lookup_macro(tmpc, 0)) != NULL /* && !mp->dontexpand maybe?!?!? */ ) { struct token *dum = 0; toklist = NULL; store_token(&toklist, &toklist_tail, tmpc, TOK_IDENTIFIER, lineno); if (mp->functionlike) { /* * We have to wait until a ( * comes along */ maybe_funclike = 1; g_recording_tokens = 1; } else { toklist = do_macro_subst(in, out, toklist, &toklist_tail, 0); if (toklist != NULL) { doing_funclike = 1; parentheses = collect_parens; } } if (doing_funclike) { /* * Don't process nested macros * just yet */ substitute_macros = 0; g_recording_tokens = 1; } break; } if (g_recording_tokens) { t = store_token(&toklist, &toklist_tail, tmpc, TOK_IDENTIFIER, lineno); if (ma != NULL) { t->maps_to_arg = ma; } } else { x_fprintf(out, "%s", tmpc); } } else { printf("LOOKUP_OP(%d) = %d\n", ch, LOOKUP_OP(ch)); lexerror("Unknown token - %c (code %d)\n", ch, ch); } ispragma = 0; } first_byte = 0; prevch = ch; /* * Check whether the file ends here in order to make last lines * without newline character work */ if ((ch = FGETC(in)) == EOF) { if (prevch != '\n') { UNGETC(ch, in); } } else { UNGETC(ch, in); } } if (has_data) { if (current_include != NULL) { current_include->fully_guarded = 0; } } #if 0 puts("nope, not fully guarded"); } else { puts("hahha lol"); } } else if (current_include && current_include->fully_guarded) { printf("%s IS FULLY GUARDED!!!!!!!!!!!!\n", current_include->name); } #endif #if 0 store_token(&toklist, NULL, 0, lineno); #endif return errors; } static void print_token_list(struct token *list) { (void)list; #ifdef DEBUG puts("-------------------------------------------------------------"); for (; list /*->data*/ != NULL; list = list->next) { if (list->type == TOK_OPERATOR) { int i; for (i = 0; operators[i].name != NULL; ++i) { if (*(int *)list->data == operators[i].value || *(int *)list->data == operators[i].is_ambig) { printf("%s", operators[i].name); break; } } if (operators[i].name == NULL) { (void) fprintf(stderr, "FATAL -- Unknown " "operator %d\n", *(int *)list->data); } } else if (IS_CONSTANT(list->type)) { rv_setrc_print(list->data, list->type, 0); } else if (IS_KEYWORD(list->type)) { printf(" %s ", (char *)list->data); } else if (list->type == TOK_IDENTIFIER) { printf(" %s ", (char *)list->data); } else if (list->type == TOK_STRING_LITERAL) { struct ty_string *ts = list->data; printf("\"%s\"", ts->str); } else if (list->type == TOK_PAREN_OPEN) { printf("("); } else if (list->type == TOK_PAREN_CLOSE){ printf(")"); } else if (list->type == TOK_ARRAY_OPEN) { printf("["); } else if (list->type == TOK_ARRAY_CLOSE) { printf("]"); } else if (list->type == TOK_COMP_OPEN) { printf("{\n"); } else if (list->type == TOK_COMP_CLOSE) { printf("}\n"); } else if (list->type == TOK_SEMICOLON) { printf(";\n"); } else { printf("Unknown - code %d\n", list->type); } } puts("-------------------------------------------------------------"); #endif }