/* CPP Library. * Copyright (C) 1986, 87, 89, 92, 93, 94, 1995 Free Software Foundation, Inc. * Written by Per Bothner, 1994-95. * Based on CCCP program by by Paul Rubin, June 1986 * Adapted to ANSI C, Richard Stallman, Jan 1987 * Copyright (C) 2003-2011 Kim Woelders * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * In other words, you are welcome to use, share and improve this program. * You are forbidden to forbid anyone else to use, share and improve * what you give them. Help stamp out software-hoarding! */ const char *version_string = "0.0.0"; #include "config.h" #ifndef STANDARD_INCLUDE_DIR #define STANDARD_INCLUDE_DIR "/usr/include" #endif #ifndef LOCAL_INCLUDE_DIR #define LOCAL_INCLUDE_DIR "/usr/local/include" #endif #include "cpplib.h" #include "cpphash.h" #ifndef STDC_VALUE #define STDC_VALUE 1 #endif /* By default, colon separates directories in a path. */ #ifndef PATH_SEPARATOR #define PATH_SEPARATOR ':' #endif #include #include #include #include #ifdef __STDC__ #include #endif #ifdef HAVE_ALLOCA_H #include #endif #include #include #include #include #include #ifndef VMS #ifndef USG #include #include /* for __DATE__ and __TIME__ */ #include #else #include /* CYGNUS LOCAL: shebs -noquiet */ #include #include #include #endif /* USG */ #endif /* not VMS */ /* This defines "errno" properly for VMS, and gives us EACCES. */ #include #ifndef O_RDONLY #define O_RDONLY 0 #endif #undef MIN #undef MAX #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) #define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) #ifndef S_ISREG #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif #ifndef S_ISDIR #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif /* Define a generic NULL if one hasn't already been defined. */ #ifndef GENERIC_PTR #if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__) #define GENERIC_PTR void * #else #define GENERIC_PTR char * #endif #endif #ifndef INCLUDE_LEN_FUDGE #define INCLUDE_LEN_FUDGE 0 #endif /* Symbols to predefine. */ #ifdef CPP_PREDEFINES static const char *predefs = CPP_PREDEFINES; #else static const char *predefs = ""; #endif /* We let tm.h override the types used here, to handle trivial differences * such as the choice of unsigned int or long unsigned int for size_t. * When machines start needing nontrivial differences in the size type, * it would be best to do something here to figure out automatically * from other information what type to use. */ /* The string value for __SIZE_TYPE__. */ #ifndef SIZE_TYPE #define SIZE_TYPE "long unsigned int" #endif /* The string value for __PTRDIFF_TYPE__. */ #ifndef PTRDIFF_TYPE #define PTRDIFF_TYPE "long int" #endif /* The string value for __WCHAR_TYPE__. */ #ifndef WCHAR_TYPE #define WCHAR_TYPE "int" #endif #define CPP_WCHAR_TYPE(PFILE) \ (CPP_OPTIONS (PFILE)->cplusplus ? "__wchar_t" : WCHAR_TYPE) /* The string value for __USER_LABEL_PREFIX__ */ #ifndef USER_LABEL_PREFIX #define USER_LABEL_PREFIX "" #endif /* The string value for __REGISTER_PREFIX__ */ #ifndef REGISTER_PREFIX #define REGISTER_PREFIX "" #endif struct directive { int length; int (*func)(cpp_reader * pfile, struct directive * keyword, unsigned char *buf, unsigned char *limit); const char *name; enum node_type type; char command_reads_line; char traditional_comments; char pass_thru; }; /* In the definition of a #assert name, this structure forms * a list of the individual values asserted. * Each value is itself a list of "tokens". * These are strings that are compared by name. */ struct tokenlist_list { struct tokenlist_list *next; struct arglist *tokens; }; struct assertion_hashnode { struct assertion_hashnode *next; /* double links for easy deletion */ struct assertion_hashnode *prev; /* also, a back pointer to this node's hash * chain is kept, in case the node is the head * of the chain and gets deleted. */ struct assertion_hashnode **bucket_hdr; int length; /* length of token, for quick comparison */ char *name; /* the actual name */ /* List of token-sequences. */ struct tokenlist_list *value; }; #define SKIP_WHITE_SPACE(p) do { while (is_hor_space[(unsigned char)(*p)]) p++; } while (0) #define SKIP_ALL_WHITE_SPACE(p) do { while (is_space[(unsigned char)(*p)]) p++; } while (0) #define PEEKN(N) (CPP_BUFFER (pfile)->rlimit - CPP_BUFFER (pfile)->cur >= (N) ? CPP_BUFFER (pfile)->cur[N] : EOF) #define FORWARD(N) CPP_FORWARD (CPP_BUFFER (pfile), (N)) #define GETC() CPP_BUF_GET (CPP_BUFFER (pfile)) #define PEEKC() CPP_BUF_PEEK (CPP_BUFFER (pfile)) /* CPP_IS_MACRO_BUFFER is true if the buffer contains macro expansion. * (Note that it is false while we're expanding marco *arguments*.) */ #define CPP_IS_MACRO_BUFFER(PBUF) ((PBUF)->cleanup == macro_cleanup) /* Move all backslash-newline pairs out of embarrassing places. * Exchange all such pairs following BP * with any potentially-embarrassing characters that follow them. * Potentially-embarrassing characters are / and * * (because a backslash-newline inside a comment delimiter * would cause it not to be recognized). */ #define NEWLINE_FIX \ do {while (PEEKC() == '\\' && PEEKN(1) == '\n') FORWARD(2); } while(0) /* Same, but assume we've already read the potential '\\' into C. */ #define NEWLINE_FIX1(C) do { \ while ((C) == '\\' && PEEKC() == '\n') { FORWARD(1); (C) = GETC(); }\ } while(0) /* Name under which this program was invoked. */ char *progname; struct cpp_pending { struct cpp_pending *next; const char *cmd; const char *arg; }; /* Structure returned by create_definition */ typedef struct { struct definition *defn; char *symnam; int symlen; } MACRODEF; /* Forward declarations. */ typedef struct file_name_list file_name_list; static void add_import(cpp_reader * pfile, int fd, char *fname); static int finclude(cpp_reader * pfile, int f, const char *fname, int system_header_p, file_name_list * dirptr); static void validate_else(cpp_reader * pfile, const char *directive); static int comp_def_part(int first, unsigned char *beg1, int len1, unsigned char *beg2, int len2, int last); static int lookup_import(cpp_reader * pfile, char *filename, file_name_list * searchptr); static int redundant_include_p(cpp_reader * pfile, char *name); static int is_system_include(cpp_reader * pfile, char *filename); static int open_include_file(cpp_reader * pfile, char *filename, file_name_list * searchptr); static int check_macro_name(cpp_reader * pfile, unsigned char *symname, const char *usage); static int compare_token_lists(struct arglist *l1, struct arglist *l2); static HOST_WIDE_INT eval_if_expression(cpp_reader * pfile, unsigned char *buf, int length); static int file_size_and_mode(int fd, int *mode_pointer, long int *size_pointer); static struct arglist *read_token_list(cpp_reader * pfile, int *error_flag); static void free_token_list(struct arglist *tokens); static int safe_read(int desc, char *ptr, int len); static void push_macro_expansion(cpp_reader * pfile, unsigned char *x, int xbuf_len, HASHNODE * hp); static struct cpp_pending *nreverse_pending(struct cpp_pending *list); static char *savestring(const char *input); static void conditional_skip(cpp_reader * pfile, int skip, enum node_type type, unsigned char *control_macro); static void skip_if_group(cpp_reader * pfile, int any); static void cpp_error_with_line(cpp_reader * pfile, int line, int column, const char *msg); static void cpp_pedwarn_with_line(cpp_reader * pfile, int line, int column, const char *msg); static void cpp_pedwarn_with_file_and_line(cpp_reader * pfile, const char *file, int line, const char *msg, const char *arg1, const char *arg2, const char *arg3); static void cpp_error_from_errno(cpp_reader * pfile, const char *name); static cpp_buffer *cpp_push_buffer(cpp_reader * pfile, unsigned char *buffer, long length); static cpp_buffer *cpp_pop_buffer(cpp_reader * pfile); /* Last arg to output_line_command. */ enum file_change_code { same_file, enter_file, leave_file }; /* These functions are declared to return int instead of void since they * are going to be placed in a table and some old compilers have trouble with * pointers to functions returning void. */ static int do_define(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_line(cpp_reader * pfile, struct directive *keyword, unsigned char *unused1, unsigned char *unused2); static int do_include(cpp_reader * pfile, struct directive *keyword, unsigned char *unused1, unsigned char *unused2); static int do_undef(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_error(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_pragma(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_ident(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_if(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_xifdef(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_else(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_elif(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_endif(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_assert(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_unassert(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); static int do_warning(cpp_reader * pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit); struct arglist *reverse_token_list(struct arglist *tokens); static int parse_name(cpp_reader * pfile, int c); static void parse_set_mark(struct parse_marker *pmark, cpp_reader * pfile); static void parse_clear_mark(struct parse_marker *pmark); static void parse_goto_mark(struct parse_marker *pmark, cpp_reader * pfile); static void parse_move_mark(struct parse_marker *pmark, cpp_reader * pfile); struct file_name_list { file_name_list *next; char *fname; /* If the following is nonzero, it is a macro name. * Don't include the file again if that macro is defined. */ unsigned char *control_macro; /* If the following is nonzero, it is a C-language system include * directory. */ int c_system_include_path; /* Mapping of file names for this directory. */ struct file_name_map *name_map; /* Non-zero if name_map is valid. */ int got_name_map; }; /* If a buffer's dir field is SELF_DIR_DUMMY, it means the file was found * via the same directory as the file that #included it. */ #define SELF_DIR_DUMMY ((file_name_list*)(~0)) /* #include "file" looks in source file dir, then stack. */ /* #include just looks in the stack. */ /* -I directories are added to the end, then the defaults are added. */ /* The */ static struct default_include { const char *fname; /* The name of the directory. */ int cplusplus; /* Only look here if we're compiling C++. */ int cxx_aware; /* Includes in this directory don't need to * be wrapped in extern "C" when compiling * C++. */ } include_defaults_array[] #ifdef INCLUDE_DEFAULTS = INCLUDE_DEFAULTS; #else = { /* Pick up GNU C++ specific include files. */ { GPLUSPLUS_INCLUDE_DIR, 1, 1 } , #ifdef CROSS_COMPILE /* This is the dir for fixincludes. Put it just before * the files that we fix. */ { GCC_INCLUDE_DIR, 0, 0 } , /* For cross-compilation, this dir name is generated * automatically in Makefile.in. */ { CROSS_INCLUDE_DIR, 0, 0 } , /* This is another place that the target system's headers might be. */ { TOOL_INCLUDE_DIR, 0, 1 } , #else /* not CROSS_COMPILE */ /* This should be /usr/local/include and should come before * the fixincludes-fixed header files. */ { LOCAL_INCLUDE_DIR, 0, 1 } , /* This is here ahead of GCC_INCLUDE_DIR because assert.h goes here. * Likewise, behind LOCAL_INCLUDE_DIR, where glibc puts its assert.h. */ { TOOL_INCLUDE_DIR, 0, 1 } , /* This is the dir for fixincludes. Put it just before * the files that we fix. */ { GCC_INCLUDE_DIR, 0, 0 } , /* Some systems have an extra dir of include files. */ #ifdef SYSTEM_INCLUDE_DIR { SYSTEM_INCLUDE_DIR, 0, 0 } , #endif { STANDARD_INCLUDE_DIR, 0, 0 } , #endif /* not CROSS_COMPILE */ { 0, 0, 0 } }; #endif /* no INCLUDE_DEFAULTS */ /* Here is the actual list of #-directives, most-often-used first. * The initialize_builtins function assumes #define is the very first. */ static struct directive directive_table[] = { { 6, do_define, "define", T_DEFINE, 0, 1, 0 }, { 5, do_xifdef, "ifdef", T_IFDEF, 1, 0, 0 }, { 6, do_xifdef, "ifndef", T_IFNDEF, 1, 0, 0 }, { 7, do_include, "include", T_INCLUDE, 1, 0, 0 }, { 12, do_include, "include_next", T_INCLUDE_NEXT, 1, 0, 0 }, { 6, do_include, "import", T_IMPORT, 1, 0, 0 }, { 5, do_endif, "endif", T_ENDIF, 1, 0, 0 }, { 4, do_else, "else", T_ELSE, 1, 0, 0 }, { 2, do_if, "if", T_IF, 1, 0, 0 }, { 4, do_elif, "elif", T_ELIF, 1, 0, 0 }, { 5, do_undef, "undef", T_UNDEF, 0, 0, 0 }, { 5, do_error, "error", T_ERROR, 0, 0, 0 }, { 7, do_warning, "warning", T_WARNING, 0, 0, 0 }, { 6, do_pragma, "pragma", T_PRAGMA, 0, 0, 1 }, { 4, do_line, "line", T_LINE, 1, 0, 0 }, { 5, do_ident, "ident", T_IDENT, 1, 0, 1 }, #ifdef SCCS_DIRECTIVE { 4, do_sccs, "sccs", T_SCCS, 0, 0, 0 }, #endif { 6, do_assert, "assert", T_ASSERT, 1, 0, 0 }, { 8, do_unassert, "unassert", T_UNASSERT, 1, 0, 0 }, { -1, 0, "", T_UNUSED, 0, 0, 0 }, }; /* table to tell if char can be part of a C identifier. */ unsigned char is_idchar[256]; /* table to tell if char can be first char of a c identifier. */ unsigned char is_idstart[256]; /* table to tell if c is horizontal space. */ unsigned char is_hor_space[256]; /* table to tell if c is horizontal or vertical space. */ static unsigned char is_space[256]; /* Initialize syntactic classifications of characters. */ static void initialize_char_syntax(struct cpp_options *opts) { int i; /* * Set up is_idchar and is_idstart tables. These should be * faster than saying (is_alpha (c) || c == '_'), etc. * Set up these things before calling any routines tthat * refer to them. */ for (i = 'a'; i <= 'z'; i++) { is_idchar[i - 'a' + 'A'] = 1; is_idchar[i] = 1; is_idstart[i - 'a' + 'A'] = 1; is_idstart[i] = 1; } for (i = '0'; i <= '9'; i++) is_idchar[i] = 1; is_idchar[(unsigned char)'_'] = 1; is_idstart[(unsigned char)'_'] = 1; is_idchar[(unsigned char)'$'] = opts->dollars_in_ident; is_idstart[(unsigned char)'$'] = opts->dollars_in_ident; /* horizontal space table */ is_hor_space[(unsigned char)' '] = 1; is_hor_space[(unsigned char)'\t'] = 1; is_hor_space[(unsigned char)'\v'] = 1; is_hor_space[(unsigned char)'\f'] = 1; is_hor_space[(unsigned char)'\r'] = 1; is_space[(unsigned char)' '] = 1; is_space[(unsigned char)'\t'] = 1; is_space[(unsigned char)'\v'] = 1; is_space[(unsigned char)'\f'] = 1; is_space[(unsigned char)'\n'] = 1; is_space[(unsigned char)'\r'] = 1; } /* Place into PFILE a quoted string representing the string SRC. * Caller must reserve enough space in pfile->token_buffer. */ static void quote_string(cpp_reader *pfile, const char *src) { unsigned char c; CPP_PUTC_Q(pfile, '\"'); for (;;) switch ((c = *src++)) { default: if (isprint(c)) CPP_PUTC_Q(pfile, c); else { sprintf((char *)CPP_PWRITTEN(pfile), "\\%03o", c); CPP_ADJUST_WRITTEN(pfile, 4); } break; case '\"': case '\\': CPP_PUTC_Q(pfile, '\\'); CPP_PUTC_Q(pfile, c); break; case '\0': CPP_PUTC_Q(pfile, '\"'); CPP_NUL_TERMINATE_Q(pfile); return; } } /* Make sure PFILE->token_buffer will hold at least N more chars. */ void cpp_grow_buffer(cpp_reader *pfile, long n) { long old_written = CPP_WRITTEN(pfile); pfile->token_buffer_size = n + 2 * pfile->token_buffer_size; pfile->token_buffer = (unsigned char *)xrealloc(pfile->token_buffer, pfile->token_buffer_size); CPP_SET_WRITTEN(pfile, old_written); } /* * process a given definition string, for initialization * If STR is just an identifier, define it with value 1. * If STR has anything after the identifier, then it should * be identifier=definition. */ void cpp_define(cpp_reader *pfile, unsigned char *str) { unsigned char *buf, *p; buf = str; p = str; if (!is_idstart[*p]) { cpp_error(pfile, "malformed option `-D %s'", str); return; } while (is_idchar[*++p]) ; if (*p == 0) { buf = (unsigned char *)alloca(p - buf + 4); strcpy((char *)buf, (const char *)str); strcat((char *)buf, " 1"); } else if (*p != '=') { cpp_error(pfile, "malformed option `-D %s'", str); return; } else { unsigned char *q; /* Copy the entire option so we can modify it. */ buf = (unsigned char *)alloca(2 * strlen((char *)str) + 1); strncpy((char *)buf, (const char *)str, p - str); /* Change the = to a space. */ buf[p - str] = ' '; /* Scan for any backslash-newline and remove it. */ p++; q = &buf[p - str]; while (*p) { if (*p == '\\' && p[1] == '\n') p += 2; else *q++ = *p++; } *q = 0; } do_define(pfile, NULL, buf, buf + strlen((char *)buf)); } /* Process the string STR as if it appeared as the body of a #assert. * OPTION is the option name for which STR was the argument. */ static void make_assertion(cpp_reader *pfile, const char *option, const char *str) { unsigned char *buf, *p, *q; /* Copy the entire option so we can modify it. */ buf = (unsigned char *)alloca(strlen((char *)str) + 1); strcpy((char *)buf, (const char *)str); /* Scan for any backslash-newline and remove it. */ p = q = buf; while (*p) { *q++ = *p++; } *q = 0; p = buf; if (!is_idstart[*p]) { cpp_error(pfile, "malformed option `%s %s'", option, str); return; } while (is_idchar[*++p]) ; while (*p == ' ' || *p == '\t') p++; if (!(*p == 0 || *p == '(')) { cpp_error(pfile, "malformed option `%s %s'", option, str); return; } cpp_push_buffer(pfile, buf, strlen((char *)buf)); do_assert(pfile, NULL, NULL, NULL); cpp_pop_buffer(pfile); } /* Append a chain of `file_name_list's * to the end of the main include chain. * FIRST is the beginning of the chain to append, and LAST is the end. */ static void append_include_chain(cpp_reader *pfile, file_name_list *first, file_name_list *last) { struct cpp_options *opts = CPP_OPTIONS(pfile); file_name_list *dir; if (!first || !last) return; if (!opts->include) opts->include = first; else opts->last_include->next = first; if (!opts->first_bracket_include) opts->first_bracket_include = first; for (dir = first;; dir = dir->next) { int len = strlen(dir->fname) + INCLUDE_LEN_FUDGE; if (len > pfile->max_include_len) pfile->max_include_len = len; if (dir == last) break; } last->next = NULL; opts->last_include = last; } /* Add output to `deps_buffer' for the -M switch. * STRING points to the text to be output. * SPACER is ':' for targets, ' ' for dependencies, zero for text * to be inserted literally. */ static void deps_output(cpp_reader *pfile, const char *string, int spacer) { int size = strlen(string); if (size == 0) return; #ifndef MAX_OUTPUT_COLUMNS #define MAX_OUTPUT_COLUMNS 72 #endif if (spacer && pfile->deps_column > 0 && (pfile->deps_column + size) > MAX_OUTPUT_COLUMNS) { deps_output(pfile, " \\\n ", 0); pfile->deps_column = 0; } if (pfile->deps_size + size + 8 > pfile->deps_allocated_size) { pfile->deps_allocated_size = (pfile->deps_size + size + 50) * 2; pfile->deps_buffer = (char *)xrealloc(pfile->deps_buffer, pfile->deps_allocated_size); } if (spacer == ' ' && pfile->deps_column > 0) pfile->deps_buffer[pfile->deps_size++] = ' '; memcpy(&pfile->deps_buffer[pfile->deps_size], string, size); pfile->deps_size += size; pfile->deps_column += size; if (spacer == ':') pfile->deps_buffer[pfile->deps_size++] = ':'; pfile->deps_buffer[pfile->deps_size] = 0; } /* Given a colon-separated list of file names PATH, * add all the names to the search path for include files. */ static void path_include(cpp_reader *pfile, char *path) { char *p; p = path; if (*p) while (1) { char *q = p; char *name; file_name_list *dirtmp; /* Find the end of this name. */ while (*q != 0 && *q != PATH_SEPARATOR) q++; if (p == q) { /* An empty name in the path stands for the current directory. */ name = (char *)xmalloc(2); name[0] = '.'; name[1] = 0; } else { /* Otherwise use the directory that is named. */ name = (char *)xmalloc(q - p + 1); memcpy(name, p, q - p); name[q - p] = 0; } dirtmp = (file_name_list *) xmalloc(sizeof(file_name_list)); dirtmp->next = 0; /* New one goes on the end */ dirtmp->control_macro = 0; dirtmp->c_system_include_path = 0; dirtmp->fname = name; dirtmp->got_name_map = 0; append_include_chain(pfile, dirtmp, dirtmp); /* Advance past this name. */ p = q; if (*p == 0) break; /* Skip the colon. */ p++; } } void init_parse_options(struct cpp_options *opts) { memset((char *)opts, 0, sizeof *opts); opts->in_fname = NULL; opts->out_fname = NULL; /* Initialize is_idchar to allow $. */ opts->dollars_in_ident = 1; initialize_char_syntax(opts); opts->dollars_in_ident = DOLLARS_IN_IDENTIFIERS > 0; opts->no_line_commands = 0; opts->no_trigraphs = 1; opts->put_out_comments = 0; opts->print_include_names = 0; opts->dump_macros = dump_none; opts->no_output = 0; opts->cplusplus = 0; opts->cplusplus_comments = 1; opts->verbose = 0; opts->objc = 0; opts->lang_asm = 0; opts->for_lint = 0; opts->chill = 0; opts->pedantic_errors = 0; opts->inhibit_warnings = 0; opts->warn_comments = 0; opts->warn_import = 1; opts->warnings_are_errors = 0; } static enum cpp_token null_underflow(cpp_reader *pfile __UNUSED__) { return CPP_EOF; } static int null_cleanup(cpp_buffer *pbuf __UNUSED__, cpp_reader *pfile __UNUSED__) { return 0; } static int macro_cleanup(cpp_buffer *pbuf, cpp_reader *pfile __UNUSED__) { HASHNODE *macro = (HASHNODE *) pbuf->data; if (macro->type == T_DISABLED) macro->type = T_MACRO; if (macro->type != T_MACRO || pbuf->buf != macro->value.defn->expansion) free(pbuf->buf); return 0; } static int file_cleanup(cpp_buffer *pbuf, cpp_reader *pfile __UNUSED__) { if (pbuf->buf) { free(pbuf->buf); pbuf->buf = 0; } return 0; } /* Assuming we have read '/'. * If this is the start of a comment (followed by '*' or '/'), * skip to the end of the comment, and return ' '. * Return EOF if we reached the end of file before the end of the comment. * If not the start of a comment, return '/'. */ static int skip_comment(cpp_reader *pfile, long *linep) { int c = 0; while (PEEKC() == '\\' && PEEKN(1) == '\n') { if (linep) (*linep)++; FORWARD(2); } if (PEEKC() == '*') { FORWARD(1); for (;;) { int prev_c = c; c = GETC(); if (c == EOF) return EOF; while (c == '\\' && PEEKC() == '\n') { if (linep) (*linep)++; FORWARD(1), c = GETC(); } if (prev_c == '*' && c == '/') return ' '; if (c == '\n' && linep) (*linep)++; } } else if (PEEKC() == '/' && CPP_OPTIONS(pfile)->cplusplus_comments) { FORWARD(1); for (;;) { c = GETC(); if (c == EOF) return ' '; /* Allow // to be terminated by EOF. */ while (c == '\\' && PEEKC() == '\n') { FORWARD(1); c = GETC(); if (linep) (*linep)++; } if (c == '\n') { /* Don't consider final '\n' to be part of comment. */ FORWARD(-1); return ' '; } } } else return '/'; } /* Skip whitespace \-newline and comments. Does not macro-expand. */ void cpp_skip_hspace(cpp_reader *pfile) { while (1) { int c = PEEKC(); if (c == EOF) return; /* FIXME */ if (is_hor_space[c]) { if ((c == '\f' || c == '\v') && CPP_PEDANTIC(pfile)) cpp_pedwarn(pfile, "%s in preprocessing directive", c == '\f' ? "formfeed" : "vertical tab"); FORWARD(1); } else if (c == '/') { FORWARD(1); c = skip_comment(pfile, NULL); if (c == '/') FORWARD(-1); if (c == EOF || c == '/') return; } else if (c == '\\' && PEEKN(1) == '\n') { FORWARD(2); } else if (c == '@' && CPP_BUFFER(pfile)->has_escapes && is_hor_space[PEEKN(1)]) FORWARD(2); else return; } } /* Read the rest of the current line. * The line is appended to PFILE's output buffer. */ static void copy_rest_of_line(cpp_reader *pfile) { struct cpp_options *opts = CPP_OPTIONS(pfile); for (;;) { int c = GETC(); int nextc; switch (c) { case EOF: goto end_directive; case '\\': if (PEEKC() == '\n') { FORWARD(1); continue; } case '\'': case '\"': goto scan_directive_token; break; case '/': nextc = PEEKC(); if (nextc == '*' || (opts->cplusplus_comments && nextc == '*')) goto scan_directive_token; break; case '\f': case '\v': if (CPP_PEDANTIC(pfile)) cpp_pedwarn(pfile, "%s in preprocessing directive", c == '\f' ? "formfeed" : "vertical tab"); break; case '\n': FORWARD(-1); goto end_directive; scan_directive_token: FORWARD(-1); cpp_get_token(pfile); continue; } CPP_PUTC(pfile, c); } end_directive:; CPP_NUL_TERMINATE(pfile); } void skip_rest_of_line(cpp_reader *pfile) { long old = CPP_WRITTEN(pfile); copy_rest_of_line(pfile); CPP_SET_WRITTEN(pfile, old); } /* Handle a possible # directive. * '#' has already been read. */ static int handle_directive(cpp_reader *pfile) { int c; struct directive *kt; int ident_length; long after_ident = 0; unsigned char *ident, *line_end; long old_written = CPP_WRITTEN(pfile); cpp_skip_hspace(pfile); c = PEEKC(); if (c >= '0' && c <= '9') { /* Handle # followed by a line number. */ if (CPP_PEDANTIC(pfile)) cpp_pedwarn(pfile, "`#' followed by integer"); do_line(pfile, NULL, NULL, NULL); goto done_a_directive; } /* Now find the directive name. */ CPP_PUTC(pfile, '#'); parse_name(pfile, GETC()); ident = pfile->token_buffer + old_written + 1; ident_length = CPP_PWRITTEN(pfile) - ident; if (ident_length == 0 && PEEKC() == '\n') { /* A line of just `#' becomes blank. */ goto done_a_directive; } /* * Decode the keyword and call the appropriate expansion * routine, after moving the input pointer up to the next line. */ for (kt = directive_table;; kt++) { if (kt->length <= 0) goto not_a_directive; if (kt->length == ident_length && !strncmp(kt->name, (const char *)ident, ident_length)) break; } if (!kt->command_reads_line) { /* Nonzero means do not delete comments within the directive. * #define needs this when -traditional. */ int comments = CPP_TRADITIONAL(pfile) && kt->traditional_comments; int save_put_out_comments = CPP_OPTIONS(pfile)->put_out_comments; CPP_OPTIONS(pfile)->put_out_comments = comments; after_ident = CPP_WRITTEN(pfile); copy_rest_of_line(pfile); CPP_OPTIONS(pfile)->put_out_comments = save_put_out_comments; } /* For #pragma and #define, we may want to pass through the directive. * Other directives may create output, but we don't want the directive * itself out, so we pop it now. For example #include may write a #line * command (see comment in do_include), and conditionals may emit * #failed ... #endfailed stuff. But note that popping the buffer * means the parameters to kt->func may point after pfile->limit * so these parameters are invalid as soon as something gets appended * to the token_buffer. */ line_end = CPP_PWRITTEN(pfile); if (!kt->pass_thru && kt->type != T_DEFINE) CPP_SET_WRITTEN(pfile, old_written); (*kt->func) (pfile, kt, pfile->token_buffer + after_ident, line_end); if (kt->pass_thru || (kt->type == T_DEFINE && CPP_OPTIONS(pfile)->dump_macros == dump_definitions)) { /* Just leave the entire #define in the output stack. */ } else if (kt->type == T_DEFINE && CPP_OPTIONS(pfile)->dump_macros == dump_names) { unsigned char *p = pfile->token_buffer + old_written + 7; /* Skip "#define". */ SKIP_WHITE_SPACE(p); while (is_idchar[*p]) p++; pfile->limit = p; CPP_PUTC(pfile, '\n'); } else if (kt->type == T_DEFINE) CPP_SET_WRITTEN(pfile, old_written); done_a_directive: return 1; not_a_directive: return 0; } /* Pass a directive through to the output file. * BUF points to the contents of the directive, as a contiguous string. * LIMIT points to the first character past the end of the directive. * KEYWORD is the keyword-table entry for the directive. */ static void pass_thru_directive(char *buf, char *limit, cpp_reader *pfile, struct directive *keyword) { unsigned keyword_length = keyword->length; CPP_RESERVE(pfile, 1 + keyword_length + (limit - buf)); CPP_PUTC_Q(pfile, '#'); CPP_PUTS_Q(pfile, keyword->name, keyword_length); if (limit != buf && buf[0] != ' ') CPP_PUTC_Q(pfile, ' '); CPP_PUTS_Q(pfile, buf, limit - buf); } /* The arglist structure is built by do_define to tell * collect_definition where the argument names begin. That * is, for a define like "#define f(x,y,z) foo+x-bar*y", the arglist * would contain pointers to the strings x, y, and z. * Collect_definition would then build a DEFINITION node, * with reflist nodes pointing to the places x, y, and z had * appeared. So the arglist is just convenience data passed * between these two routines. It is not kept around after * the current #define has been processed and entered into the * hash table. */ struct arglist { struct arglist *next; char *name; int length; int argno; char rest_args; }; /* Read a replacement list for a macro with parameters. * Build the DEFINITION structure. * Reads characters of text starting at BUF until END. * ARGLIST specifies the formal parameters to look for * in the text of the definition; NARGS is the number of args * in that list, or -1 for a macro name that wants no argument list. * MACRONAME is the macro name itself (so we can avoid recursive expansion) * and NAMELEN is its length in characters. * * Note that comments, backslash-newlines, and leading white space * have already been deleted from the argument. */ static DEFINITION * collect_expansion(cpp_reader *pfile, unsigned char *buf, unsigned char *limit, int nargs, struct arglist *arglist) { DEFINITION *defn; unsigned char *p, *lastp, *exp_p; reflist *endpat = NULL; /* Pointer to first nonspace after last ## seen. */ unsigned char *concat = 0; /* Pointer to first nonspace after last single-# seen. */ unsigned char *stringify = 0; int maxsize; int expected_delimiter = '\0'; /* Scan thru the replacement list, ignoring comments and quoted * strings, picking up on the macro calls. It does a linear search * thru the arg list on every potential symbol. Profiling might say * that something smarter should happen. */ if (limit < buf) abort(); /* Find the beginning of the trailing whitespace. */ p = buf; while (p < limit && is_space[limit[-1]]) limit--; /* Allocate space for the text in the macro definition. * Leading and trailing whitespace chars need 2 bytes each. * Each other input char may or may not need 1 byte, * so this is an upper bound. The extra 5 are for invented * leading and trailing newline-marker and final null. */ maxsize = (sizeof(DEFINITION) + (limit - p) + 5); /* Occurrences of '@' get doubled, so allocate extra space for them. */ while (p < limit) if (*p++ == '@') maxsize++; defn = (DEFINITION *) xcalloc(1, maxsize); defn->nargs = nargs; exp_p = defn->expansion = (unsigned char *)defn + sizeof(DEFINITION); lastp = exp_p; p = buf; /* Add one initial space escape-marker to prevent accidental * token-pasting (often removed by macroexpand). */ *exp_p++ = '@'; *exp_p++ = ' '; if (limit - p >= 2 && p[0] == '#' && p[1] == '#') { cpp_error(pfile, "`##' at start of macro definition"); p += 2; } /* Process the main body of the definition. */ while (p < limit) { int skipped_arg = 0; unsigned char c = *p++; *exp_p++ = c; if (!CPP_TRADITIONAL(pfile)) { switch (c) { case '\'': case '\"': if (expected_delimiter != '\0') { if (c == expected_delimiter) expected_delimiter = '\0'; } else expected_delimiter = c; break; case '\\': if (p < limit && expected_delimiter) { /* In a string, backslash goes through * and makes next char ordinary. */ *exp_p++ = *p++; } break; case '@': /* An '@' in a string or character constant stands for itself, * and does not need to be escaped. */ if (!expected_delimiter) *exp_p++ = c; break; case '#': /* # is ordinary inside a string. */ if (expected_delimiter) break; if (p < limit && *p == '#') { /* ##: concatenate preceding and following tokens. */ /* Take out the first #, discard preceding whitespace. */ exp_p--; while (exp_p > lastp && is_hor_space[exp_p[-1]]) --exp_p; /* Skip the second #. */ p++; /* Discard following whitespace. */ SKIP_WHITE_SPACE(p); concat = p; if (p == limit) cpp_error(pfile, "`##' at end of macro definition"); } else if (nargs >= 0) { /* Single #: stringify following argument ref. * Don't leave the # in the expansion. */ exp_p--; SKIP_WHITE_SPACE(p); if (p == limit || !is_idstart[*p]) cpp_error(pfile, "`#' operator is not followed by a macro argument name"); else stringify = p; } break; } } else { /* In -traditional mode, recognize arguments inside strings and * and character constants, and ignore special properties of #. * Arguments inside strings are considered "stringified", but no * extra quote marks are supplied. */ switch (c) { case '\'': case '\"': if (expected_delimiter != '\0') { if (c == expected_delimiter) expected_delimiter = '\0'; } else expected_delimiter = c; break; case '\\': /* Backslash quotes delimiters and itself, but not macro args. */ if (expected_delimiter != 0 && p < limit && (*p == expected_delimiter || *p == '\\')) { *exp_p++ = *p++; continue; } break; case '/': if (expected_delimiter != '\0') /* No comments inside strings. */ break; if (*p == '*') { /* If we find a comment that wasn't removed by handle_directive, * this must be -traditional. So replace the comment with * nothing at all. */ exp_p--; p += 1; while (p < limit && !(p[-2] == '*' && p[-1] == '/')) p++; } break; } } /* Handle the start of a symbol. */ if (is_idchar[c] && nargs > 0) { unsigned char *id_beg = p - 1; int id_len; --exp_p; while (p != limit && is_idchar[*p]) p++; id_len = p - id_beg; if (is_idstart[c]) { struct arglist *arg; for (arg = arglist; arg; arg = arg->next) { reflist *tpat; if (arg->name[0] == c && arg->length == id_len && strncmp((const char *)arg->name, (const char *)id_beg, id_len) == 0) { if (expected_delimiter && CPP_OPTIONS(pfile)->warn_stringify) { if (CPP_TRADITIONAL(pfile)) { cpp_warning(pfile, "macro argument `%.*s' is stringified.", id_len, arg->name); } else { cpp_warning(pfile, "macro arg `%.*s' would be stringified with -traditional.", id_len, arg->name); } } /* If ANSI, don't actually substitute inside a string. */ if (!CPP_TRADITIONAL(pfile) && expected_delimiter) break; /* make a pat node for this arg and append it to the end of * the pat list */ tpat = (reflist *) xmalloc(sizeof(reflist)); tpat->next = NULL; tpat->raw_before = concat == id_beg; tpat->raw_after = 0; tpat->rest_args = arg->rest_args; tpat->stringify = (CPP_TRADITIONAL(pfile) ? expected_delimiter != '\0' : stringify == id_beg); if (!endpat) defn->pattern = tpat; else endpat->next = tpat; endpat = tpat; tpat->argno = arg->argno; tpat->nchars = exp_p - lastp; { unsigned char *p1 = p; SKIP_WHITE_SPACE(p1); if (p1 + 2 <= limit && p1[0] == '#' && p1[1] == '#') tpat->raw_after = 1; } lastp = exp_p; /* place to start copying from next time */ skipped_arg = 1; break; } } } /* If this was not a macro arg, copy it into the expansion. */ if (!skipped_arg) { unsigned char *lim1 = p; p = id_beg; while (p != lim1) *exp_p++ = *p++; if (stringify == id_beg) cpp_error(pfile, "`#' operator should be followed by a macro argument name"); } } } if (!CPP_TRADITIONAL(pfile) && expected_delimiter == 0) { /* If ANSI, put in a "@ " marker to prevent token pasting. * But not if "inside a string" (which in ANSI mode * happens only for -D option). */ *exp_p++ = '@'; *exp_p++ = ' '; } *exp_p = '\0'; defn->length = exp_p - defn->expansion; /* Crash now if we overrun the allocated size. */ if (defn->length + 1 > maxsize) abort(); return defn; } /* * special extension string that can be added to the last macro argument to * allow it to absorb the "rest" of the arguments when expanded. Ex: * #define wow(a, b...) process (b, a, b) * { wow (1, 2, 3); } -> { process (2, 3, 1, 2, 3); } * { wow (one, two); } -> { process (two, one, two); } * if this "rest_arg" is used with the concat token '##' and if it is not * supplied then the token attached to with ## will not be outputted. Ex: * #define wow (a, b...) process (b ## , a, ## b) * { wow (1, 2); } -> { process (2, 1, 2); } * { wow (one); } -> { process (one); { */ static char rest_extension[] = "..."; #define REST_EXTENSION_LENGTH (sizeof (rest_extension) - 1) /* Create a DEFINITION node from a #define directive. Arguments are * as for do_define. */ static void create_definition(MACRODEF *mdef, unsigned char *buf, unsigned char *limit, cpp_reader *pfile, int predefinition) { unsigned char *bp; /* temp ptr into input buffer */ unsigned char *symname; /* remember where symbol name starts */ int sym_length; /* and how long it is */ int rest_args = 0; long line, col; const char *file = CPP_BUFFER(pfile) ? CPP_BUFFER(pfile)->nominal_fname : ""; DEFINITION *defn; int arglengths = 0; /* Accumulate lengths of arg names * plus number of args. */ cpp_buf_line_and_col(CPP_BUFFER(pfile), &line, &col); bp = buf; while (is_hor_space[*bp]) bp++; symname = bp; /* remember where it starts */ sym_length = check_macro_name(pfile, bp, "macro"); bp += sym_length; /* Lossage will occur if identifiers or control keywords are broken * across lines using backslash. This is not the right place to take * care of that. */ if (*bp == '(') { struct arglist *arg_ptrs = NULL; int argno = 0; bp++; /* skip '(' */ SKIP_WHITE_SPACE(bp); /* Loop over macro argument names. */ while (*bp != ')') { struct arglist *temp; temp = (struct arglist *)alloca(sizeof(struct arglist)); temp->name = (char *)bp; temp->next = arg_ptrs; temp->argno = argno++; temp->rest_args = 0; arg_ptrs = temp; if (rest_args) cpp_pedwarn(pfile, "another parameter follows `%s'", rest_extension); if (!is_idstart[*bp]) cpp_pedwarn(pfile, "invalid character in macro parameter name"); /* Find the end of the arg name. */ while (is_idchar[*bp]) { bp++; /* do we have a "special" rest-args extension here? */ if ((unsigned)(limit - bp) > REST_EXTENSION_LENGTH && strncmp(rest_extension, (const char *)bp, REST_EXTENSION_LENGTH) == 0) { rest_args = 1; temp->rest_args = 1; break; } } temp->length = (char *)bp - temp->name; if (rest_args == 1) bp += REST_EXTENSION_LENGTH; arglengths += temp->length + 2; SKIP_WHITE_SPACE(bp); if (temp->length == 0 || (*bp != ',' && *bp != ')')) { cpp_error(pfile, "badly punctuated parameter list in `#define'"); goto nope; } if (*bp == ',') { bp++; SKIP_WHITE_SPACE(bp); } if (bp >= limit) { cpp_error(pfile, "unterminated parameter list in `#define'"); goto nope; } { struct arglist *otemp; for (otemp = temp->next; otemp; otemp = otemp->next) if (temp->length == otemp->length && strncmp((const char *)temp->name, (const char *)otemp->name, temp->length) == 0) { char *name; name = (char *)alloca(temp->length + 1); strncpy(name, (const char *)temp->name, temp->length); name[temp->length] = '\0'; cpp_error(pfile, "duplicate argument name `%s' in `#define'", name); goto nope; } } } ++bp; /* skip paren */ SKIP_WHITE_SPACE(bp); /* now everything from bp before limit is the definition. */ defn = collect_expansion(pfile, bp, limit, argno, arg_ptrs); defn->rest_args = rest_args; /* Now set defn->args.argnames to the result of concatenating * the argument names in reverse order * with comma-space between them. */ defn->args.argnames = (unsigned char *)xmalloc(arglengths + 1); { struct arglist *temp; int i = 0; for (temp = arg_ptrs; temp; temp = temp->next) { memcpy(&defn->args.argnames[i], temp->name, temp->length); i += temp->length; if (temp->next) { defn->args.argnames[i++] = ','; defn->args.argnames[i++] = ' '; } } defn->args.argnames[i] = 0; } } else { /* Simple expansion or empty definition. */ if (bp < limit) { if (is_hor_space[*bp]) { bp++; SKIP_WHITE_SPACE(bp); } else { switch (*bp) { case '!': case '"': case '#': case '%': case '&': case '\'': case ')': case '*': case '+': case ',': case '-': case '.': case '/': case ':': case ';': case '<': case '=': case '>': case '?': case '[': case '\\': case ']': case '^': case '{': case '|': case '}': case '~': cpp_warning(pfile, "missing white space after `#define %.*s'", sym_length, symname); break; default: cpp_pedwarn(pfile, "missing white space after `#define %.*s'", sym_length, symname); break; } } } /* now everything from bp before limit is the definition. */ defn = collect_expansion(pfile, bp, limit, -1, NULL); defn->args.argnames = (unsigned char *)""; } defn->line = line; defn->file = file; /* OP is null if this is a predefinition */ defn->predefined = predefinition; mdef->defn = defn; mdef->symnam = (char *)symname; mdef->symlen = sym_length; return; nope: mdef->defn = 0; } /* Check a purported macro name SYMNAME, and yield its length. * USAGE is the kind of name this is intended for. */ static int check_macro_name(cpp_reader *pfile, unsigned char *symname, const char *usage) { unsigned char *p; int sym_length; for (p = symname; is_idchar[*p]; p++) ; sym_length = p - symname; if (sym_length == 0) { cpp_error(pfile, "invalid %s name", usage); } else if (!is_idstart[*symname]) { unsigned char *msg; /* what pain... */ msg = (unsigned char *)alloca(sym_length + 1); memcpy(msg, symname, sym_length); msg[sym_length] = 0; cpp_error(pfile, "invalid %s name `%s'", usage, msg); } else { if (!strncmp((const char *)symname, "defined", 7) && sym_length == 7) cpp_error(pfile, "invalid %s name `defined'", usage); } return sym_length; } /* * return zero if two DEFINITIONs are isomorphic */ static int compare_defs(DEFINITION *d1, DEFINITION *d2) { reflist *a1, *a2; unsigned char *p1 = d1->expansion; unsigned char *p2 = d2->expansion; int first = 1; if (d1->nargs != d2->nargs) return 1; if (strcmp((char *)d1->args.argnames, (char *)d2->args.argnames)) return 1; for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2; a1 = a1->next, a2 = a2->next) { if (! ((a1->nchars == a2->nchars && !strncmp((const char *)p1, (const char *)p2, a1->nchars)) || !comp_def_part(first, p1, a1->nchars, p2, a2->nchars, 0)) || a1->argno != a2->argno || a1->stringify != a2->stringify || a1->raw_before != a2->raw_before || a1->raw_after != a2->raw_after) return 1; first = 0; p1 += a1->nchars; p2 += a2->nchars; } if (a1 != a2) return 1; if (comp_def_part(first, p1, d1->length - (p1 - d1->expansion), p2, d2->length - (p2 - d2->expansion), 1)) return 1; return 0; } /* Return 1 if two parts of two macro definitions are effectively different. * One of the parts starts at BEG1 and has LEN1 chars; * the other has LEN2 chars at BEG2. * Any sequence of whitespace matches any other sequence of whitespace. * FIRST means these parts are the first of a macro definition; * so ignore leading whitespace entirely. * LAST means these parts are the last of a macro definition; * so ignore trailing whitespace entirely. */ static int comp_def_part(int first, unsigned char *beg1, int len1, unsigned char *beg2, int len2, int last) { unsigned char *end1 = beg1 + len1; unsigned char *end2 = beg2 + len2; if (first) { while (beg1 != end1 && is_space[*beg1]) beg1++; while (beg2 != end2 && is_space[*beg2]) beg2++; } if (last) { while (beg1 != end1 && is_space[end1[-1]]) end1--; while (beg2 != end2 && is_space[end2[-1]]) end2--; } while (beg1 != end1 && beg2 != end2) { if (is_space[*beg1] && is_space[*beg2]) { while (beg1 != end1 && is_space[*beg1]) beg1++; while (beg2 != end2 && is_space[*beg2]) beg2++; } else if (*beg1 == *beg2) { beg1++; beg2++; } else break; } return (beg1 != end1) || (beg2 != end2); } /* Process a #define command. * BUF points to the contents of the #define command, as a contiguous string. * LIMIT points to the first character past the end of the definition. * KEYWORD is the keyword-table entry for #define, * or NULL for a "predefined" macro. */ static int do_define(cpp_reader *pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit) { int hashcode; MACRODEF mdef; HASHNODE *hp; create_definition(&mdef, buf, limit, pfile, !keyword); if (!mdef.defn) return 1; hashcode = hashf(mdef.symnam, mdef.symlen, HASHSIZE); if ((hp = cpp_lookup(mdef.symnam, mdef.symlen, hashcode))) { int ok = 0; /* Redefining a precompiled key is ok. */ if (hp->type == T_PCSTRING) ok = 1; /* Redefining a macro is ok if the definitions are the same. */ else if (hp->type == T_MACRO) ok = !compare_defs(mdef.defn, hp->value.defn); /* Redefining a constant is ok with -D. */ else if (hp->type == T_CONST) ok = !CPP_OPTIONS(pfile)->done_initializing; /* Print the warning if it's not ok. */ if (!ok) { char *msg; /* what pain... */ /* If we are passing through #define and #undef directives, do * that for this re-definition now. */ if (CPP_OPTIONS(pfile)->debug_output && keyword) pass_thru_directive((char *)buf, (char *)limit, pfile, keyword); msg = (char *)alloca(mdef.symlen + 22); *msg = '`'; memcpy(msg + 1, mdef.symnam, mdef.symlen); strcpy((msg + mdef.symlen + 1), "' redefined"); cpp_pedwarn(pfile, msg); if (hp->type == T_MACRO) cpp_pedwarn_with_file_and_line(pfile, hp->value.defn->file, hp->value.defn->line, "this is the location of the previous definition", NULL, NULL, NULL); } /* Replace the old definition. */ hp->type = T_MACRO; hp->value.defn = mdef.defn; } else { /* If we are passing through #define and #undef directives, do * that for this new definition now. */ if (CPP_OPTIONS(pfile)->debug_output && keyword) pass_thru_directive((char *)buf, (char *)limit, pfile, keyword); install(mdef.symnam, mdef.symlen, T_MACRO, 0, (char *)mdef.defn, hashcode); } return 0; } /* This structure represents one parsed argument in a macro call. * `raw' points to the argument text as written (`raw_length' is its length). * `expanded' points to the argument's macro-expansion * (its length is `expand_length'). * `stringified_length' is the length the argument would have * if stringified. * `use_count' is the number of times this macro arg is substituted * into the macro. If the actual use count exceeds 10, * the value stored is 10. */ /* raw and expanded are relative to ARG_BASE */ #define ARG_BASE ((pfile)->token_buffer) struct argdata { /* Strings relative to pfile->token_buffer */ long raw, expanded, stringified; int raw_length, expand_length; int stringified_length; char newlines; char use_count; }; cpp_buffer * cpp_push_buffer(cpp_reader *pfile, unsigned char *buffer, long length) { #ifdef STATIC_BUFFERS cpp_buffer *buf = CPP_BUFFER(pfile); if (buf == pfile->buffer_stack) cpp_fatal("macro or `#include' recursion too deep"); buf--; memset((char *)buf, 0, sizeof(cpp_buffer)); CPP_BUFFER(pfile) = buf; #else cpp_buffer *buf = (cpp_buffer *) xmalloc(sizeof(cpp_buffer)); memset((char *)buf, 0, sizeof(cpp_buffer)); CPP_PREV_BUFFER(buf) = CPP_BUFFER(pfile); CPP_BUFFER(pfile) = buf; #endif buf->if_stack = pfile->if_stack; buf->cleanup = null_cleanup; buf->underflow = null_underflow; buf->buf = buf->cur = buffer; buf->alimit = buf->rlimit = buffer + length; return buf; } static cpp_buffer * cpp_pop_buffer(cpp_reader *pfile) { cpp_buffer *buf = CPP_BUFFER(pfile); #ifdef STATIC_BUFFERS (*buf->cleanup) (buf, pfile); return ++CPP_BUFFER(pfile); #else cpp_buffer *next_buf = CPP_PREV_BUFFER(buf); (*buf->cleanup) (buf, pfile); CPP_BUFFER(pfile) = next_buf; free(buf); return next_buf; #endif } /* Scan until CPP_BUFFER (PFILE) is exhausted into PFILE->token_buffer. * Pop the buffer when done. */ static void cpp_scan_buffer(cpp_reader *pfile) { cpp_buffer *buffer = CPP_BUFFER(pfile); for (;;) { enum cpp_token token = cpp_get_token(pfile); if (token == CPP_EOF) /* Should not happen ... */ break; if (token == CPP_POP && CPP_BUFFER(pfile) == buffer) { cpp_pop_buffer(pfile); break; } } } /* * Rescan a string (which may have escape marks) into pfile's buffer. * Place the result in pfile->token_buffer. * * The input is copied before it is scanned, so it is safe to pass * it something from the token_buffer that will get overwritten * (because it follows CPP_WRITTEN). This is used by do_include. */ static void cpp_expand_to_buffer(cpp_reader *pfile, unsigned char *buf, int length) { cpp_buffer *ip; unsigned char *limit = buf + length; unsigned char *buf1; if (length < 0) abort(); /* Set up the input on the input stack. */ buf1 = (unsigned char *)alloca(length + 1); { unsigned char *p1 = buf; unsigned char *p2 = buf1; while (p1 != limit) *p2++ = *p1++; } buf1[length] = 0; ip = cpp_push_buffer(pfile, buf1, length); ip->has_escapes = 1; /* Scan the input, create the output. */ cpp_scan_buffer(pfile); CPP_NUL_TERMINATE(pfile); } static void adjust_position(unsigned char *buf, unsigned char *limit, long *linep, long *colp) { while (buf < limit) { unsigned char ch = *buf++; if (ch == '\n') (*linep)++, (*colp) = 1; else (*colp)++; } } /* Move line_base forward, updating lineno and colno. */ static void update_position(cpp_buffer *pbuf) { unsigned char *old_pos = pbuf->buf + pbuf->line_base; unsigned char *new_pos = pbuf->cur; struct parse_marker *mark; for (mark = pbuf->marks; mark; mark = mark->next) { if (pbuf->buf + mark->position < new_pos) new_pos = pbuf->buf + mark->position; } pbuf->line_base += new_pos - old_pos; adjust_position(old_pos, new_pos, &pbuf->lineno, &pbuf->colno); } void cpp_buf_line_and_col(cpp_buffer *pbuf, long *linep, long *colp) { long dummy; if (!colp) colp = &dummy; if (pbuf) { *linep = pbuf->lineno; *colp = pbuf->colno; adjust_position(pbuf->buf + pbuf->line_base, pbuf->cur, linep, colp); } else { *linep = 0; *colp = 0; } } /* Return the cpp_buffer that corresponds to a file (not a macro). */ cpp_buffer * cpp_file_buffer(cpp_reader *pfile) { cpp_buffer *ip = CPP_BUFFER(pfile); for (; ip != CPP_NULL_BUFFER(pfile); ip = CPP_PREV_BUFFER(ip)) if (ip->fname) return ip; return NULL; } static long count_newlines(unsigned char *buf, unsigned char *limit) { long count = 0; while (buf < limit) { unsigned char ch = *buf++; if (ch == '\n') count++; } return count; } /* * write out a #line command, for instance, after an #include file. * If CONDITIONAL is nonzero, we can omit the #line if it would * appear to be a no-op, and we can output a few newlines instead * if we want to increase the line number by a small amount. * FILE_CHANGE says whether we are entering a file, leaving, or neither. */ static void output_line_command(cpp_reader *pfile, int conditional, enum file_change_code file_change) { long line, col; cpp_buffer *ip = CPP_BUFFER(pfile); if (CPP_OPTIONS(pfile)->no_line_commands || !ip->fname || CPP_OPTIONS(pfile)->no_output) { return; } update_position(ip); line = CPP_BUFFER(pfile)->lineno; col = CPP_BUFFER(pfile)->colno; adjust_position(CPP_LINE_BASE(ip), ip->cur, &line, &col); if (conditional) { if (line == pfile->lineno) return; /* If the inherited line number is a little too small, * output some newlines instead of a #line command. */ if (line > pfile->lineno && line < pfile->lineno + 8) { CPP_RESERVE(pfile, 20); while (line > pfile->lineno) { CPP_PUTC_Q(pfile, '\n'); pfile->lineno++; } return; } } CPP_RESERVE(pfile, 4 * strlen(ip->nominal_fname) + 50); { #ifdef OUTPUT_LINE_COMMANDS static char sharp_line[] = "#line "; #else static char sharp_line[] = "# "; #endif CPP_PUTS_Q(pfile, sharp_line, sizeof(sharp_line) - 1); } sprintf((char *)CPP_PWRITTEN(pfile), "%d ", (int)line); CPP_ADJUST_WRITTEN(pfile, strlen((char *)CPP_PWRITTEN(pfile))); quote_string(pfile, ip->nominal_fname); if (file_change != same_file) { CPP_PUTC_Q(pfile, ' '); CPP_PUTC_Q(pfile, file_change == enter_file ? '1' : '2'); } /* Tell cc1 if following text comes from a system header file. */ if (ip->system_header_p) { CPP_PUTC_Q(pfile, ' '); CPP_PUTC_Q(pfile, '3'); } #ifndef NO_IMPLICIT_EXTERN_C /* Tell cc1plus if following text should be treated as C. */ if (ip->system_header_p == 2 && CPP_OPTIONS(pfile)->cplusplus) { CPP_PUTC_Q(pfile, ' '); CPP_PUTC_Q(pfile, '4'); } #endif CPP_PUTC_Q(pfile, '\n'); pfile->lineno = line; } /* * Parse a macro argument and append the info on PFILE's token_buffer. * REST_ARGS means to absorb the rest of the args. * Return nonzero to indicate a syntax error. */ static enum cpp_token macarg(cpp_reader *pfile, int rest_args) { int paren = 0; enum cpp_token token; char save_put_out_comments = CPP_OPTIONS(pfile)->put_out_comments; CPP_OPTIONS(pfile)->put_out_comments = 0; token = CPP_OTHER; /* Try to parse as much of the argument as exists at this * input stack level. */ pfile->no_macro_expand++; for (;;) { token = cpp_get_token(pfile); switch (token) { case CPP_EOF: goto done; case CPP_POP: /* If we've hit end of file, it's an error (reported by caller). * Ditto if it's the end of cpp_expand_to_buffer text. * If we've hit end of macro, just continue. */ if (!CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile))) goto done; break; case CPP_LPAREN: paren++; break; case CPP_RPAREN: if (--paren < 0) goto found; break; case CPP_COMMA: /* if we've returned to lowest level and * we aren't absorbing all args */ if (paren == 0 && rest_args == 0) goto found; break; found: /* Remove ',' or ')' from argument buffer. */ CPP_ADJUST_WRITTEN(pfile, -1); goto done; default:; } } done: CPP_OPTIONS(pfile)->put_out_comments = save_put_out_comments; pfile->no_macro_expand--; return token; } /* Turn newlines to spaces in the string of length LENGTH at START, * except inside of string constants. * The string is copied into itself with its beginning staying fixed. */ static int change_newlines(unsigned char *start, int length) { unsigned char *ibp; unsigned char *obp; unsigned char *limit; int c; ibp = start; limit = start + length; obp = start; while (ibp < limit) { *obp++ = c = *ibp++; switch (c) { case '\'': case '\"': /* Notice and skip strings, so that we don't delete newlines in them. */ { int quotec = c; while (ibp < limit) { *obp++ = c = *ibp++; if (c == quotec) break; if (c == '\n' && quotec == '\'') break; } } break; } } return obp - start; } static struct tm * timestamp(cpp_reader *pfile) { if (!pfile->timebuf) { time_t t = time((time_t *) 0); pfile->timebuf = localtime(&t); } return pfile->timebuf; } static const char *monthnames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; /* * expand things like __FILE__. Place the expansion into the output * buffer *without* rescanning. */ static void special_symbol(HASHNODE *hp, cpp_reader *pfile) { const char *buf; char *bufx; int len; int true_indepth; cpp_buffer *ip = NULL; struct tm *timebuf; int paren = 0; /* For special `defined' keyword */ for (ip = CPP_BUFFER(pfile);; ip = CPP_PREV_BUFFER(ip)) { if (!ip) { cpp_error(pfile, "cccp error: not in any file?!"); return; /* the show must go on */ } if (ip->fname) break; } switch (hp->type) { case T_FILE: case T_BASE_FILE: { const char *string; if (hp->type == T_BASE_FILE) { while (CPP_PREV_BUFFER(ip)) ip = CPP_PREV_BUFFER(ip); } string = ip->nominal_fname; if (!string) string = ""; CPP_RESERVE(pfile, 3 + 4 * strlen(string)); quote_string(pfile, string); return; } case T_INCLUDE_LEVEL: true_indepth = 0; for (ip = CPP_BUFFER(pfile); ip; ip = CPP_PREV_BUFFER(ip)) if (ip->fname) true_indepth++; bufx = (char *)alloca(12); sprintf(bufx, "%d", true_indepth - 1); buf = bufx; break; case T_VERSION: bufx = (char *)alloca(3 + strlen(version_string)); sprintf(bufx, "\"%s\"", version_string); buf = bufx; break; #ifndef NO_BUILTIN_SIZE_TYPE case T_SIZE_TYPE: buf = SIZE_TYPE; break; #endif #ifndef NO_BUILTIN_PTRDIFF_TYPE case T_PTRDIFF_TYPE: buf = PTRDIFF_TYPE; break; #endif case T_WCHAR_TYPE: buf = CPP_WCHAR_TYPE(pfile); break; case T_USER_LABEL_PREFIX_TYPE: buf = USER_LABEL_PREFIX; break; case T_REGISTER_PREFIX_TYPE: buf = REGISTER_PREFIX; break; case T_CONST: bufx = (char *)alloca(4 * sizeof(int)); sprintf(bufx, "%d", hp->value.ival); buf = bufx; break; case T_SPECLINE: { long line = ip->lineno; long col = ip->colno; adjust_position(CPP_LINE_BASE(ip), ip->cur, &line, &col); bufx = (char *)alloca(10); sprintf(bufx, "%d", (int)line); buf = bufx; } break; case T_DATE: case T_TIME: bufx = (char *)alloca(20); timebuf = timestamp(pfile); if (hp->type == T_DATE) sprintf(bufx, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon], timebuf->tm_mday, timebuf->tm_year + 1900); else sprintf(bufx, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min, timebuf->tm_sec); buf = bufx; break; case T_SPEC_DEFINED: buf = " 0 "; /* Assume symbol is not defined */ ip = CPP_BUFFER(pfile); SKIP_WHITE_SPACE(ip->cur); if (*ip->cur == '(') { paren++; ip->cur++; /* Skip over the paren */ SKIP_WHITE_SPACE(ip->cur); } if (!is_idstart[*ip->cur]) goto oops; if ((hp = cpp_lookup((const char *)ip->cur, -1, -1))) { buf = " 1 "; } while (is_idchar[*ip->cur]) ++ip->cur; SKIP_WHITE_SPACE(ip->cur); if (paren) { if (*ip->cur != ')') goto oops; ++ip->cur; } break; oops: cpp_error(pfile, "`defined' without an identifier"); break; default: cpp_error(pfile, "cccp error: invalid special hash type"); /* time for gdb */ abort(); } len = strlen(buf); CPP_RESERVE(pfile, len + 1); CPP_PUTS_Q(pfile, buf, len); CPP_NUL_TERMINATE_Q(pfile); return; } /* Initialize the built-in macros. */ static void initialize_builtins(cpp_reader *pfile) { install("__LINE__", -1, T_SPECLINE, 0, 0, -1); install("__DATE__", -1, T_DATE, 0, 0, -1); install("__FILE__", -1, T_FILE, 0, 0, -1); install("__BASE_FILE__", -1, T_BASE_FILE, 0, 0, -1); install("__INCLUDE_LEVEL__", -1, T_INCLUDE_LEVEL, 0, 0, -1); install("__VERSION__", -1, T_VERSION, 0, 0, -1); #ifndef NO_BUILTIN_SIZE_TYPE install("__SIZE_TYPE__", -1, T_SIZE_TYPE, 0, 0, -1); #endif #ifndef NO_BUILTIN_PTRDIFF_TYPE install("__PTRDIFF_TYPE__ ", -1, T_PTRDIFF_TYPE, 0, 0, -1); #endif install("__WCHAR_TYPE__", -1, T_WCHAR_TYPE, 0, 0, -1); install("__USER_LABEL_PREFIX__", -1, T_USER_LABEL_PREFIX_TYPE, 0, 0, -1); install("__REGISTER_PREFIX__", -1, T_REGISTER_PREFIX_TYPE, 0, 0, -1); install("__TIME__", -1, T_TIME, 0, 0, -1); if (!CPP_TRADITIONAL(pfile)) install("__STDC__", -1, T_CONST, STDC_VALUE, 0, -1); if (CPP_OPTIONS(pfile)->objc) install("__OBJC__", -1, T_CONST, 1, 0, -1); /* This is supplied using a -D by the compiler driver * so that it is present only when truly compiling with GNU C. */ /* install ("__GNUC__", -1, T_CONST, 2, 0, -1); */ if (CPP_OPTIONS(pfile)->debug_output) { char directive[2048]; struct directive *dp = &directive_table[0]; struct tm *timebuf = timestamp(pfile); cpp_buffer *pbuffer = CPP_BUFFER(pfile); while (CPP_PREV_BUFFER(pbuffer)) pbuffer = CPP_PREV_BUFFER(pbuffer); sprintf(directive, " __BASE_FILE__ \"%s\"\n", pbuffer->nominal_fname); output_line_command(pfile, 0, same_file); pass_thru_directive(directive, &directive[strlen(directive)], pfile, dp); sprintf(directive, " __VERSION__ \"%s\"\n", version_string); output_line_command(pfile, 0, same_file); pass_thru_directive(directive, &directive[strlen(directive)], pfile, dp); #ifndef NO_BUILTIN_SIZE_TYPE sprintf(directive, " __SIZE_TYPE__ %s\n", SIZE_TYPE); output_line_command(pfile, 0, same_file); pass_thru_directive(directive, &directive[strlen(directive)], pfile, dp); #endif #ifndef NO_BUILTIN_PTRDIFF_TYPE sprintf(directive, " __PTRDIFF_TYPE__ %s\n", PTRDIFF_TYPE); output_line_command(pfile, 0, same_file); pass_thru_directive(directive, &directive[strlen(directive)], pfile, dp); #endif sprintf(directive, " __WCHAR_TYPE__ %s\n", CPP_WCHAR_TYPE(pfile)); output_line_command(pfile, 0, same_file); pass_thru_directive(directive, &directive[strlen(directive)], pfile, dp); sprintf(directive, " __DATE__ \"%s %2d %4d\"\n", monthnames[timebuf->tm_mon], timebuf->tm_mday, timebuf->tm_year + 1900); output_line_command(pfile, 0, same_file); pass_thru_directive(directive, &directive[strlen(directive)], pfile, dp); sprintf(directive, " __TIME__ \"%02d:%02d:%02d\"\n", timebuf->tm_hour, timebuf->tm_min, timebuf->tm_sec); output_line_command(pfile, 0, same_file); pass_thru_directive(directive, &directive[strlen(directive)], pfile, dp); if (!CPP_TRADITIONAL(pfile)) { sprintf(directive, " __STDC__ 1"); output_line_command(pfile, 0, same_file); pass_thru_directive(directive, &directive[strlen(directive)], pfile, dp); } if (CPP_OPTIONS(pfile)->objc) { sprintf(directive, " __OBJC__ 1"); output_line_command(pfile, 0, same_file); pass_thru_directive(directive, &directive[strlen(directive)], pfile, dp); } } } /* Return 1 iff a token ending in C1 followed directly by a token C2 * could cause mis-tokenization. */ static int unsafe_chars(int c1, int c2) { switch (c1) { case '+': case '-': if (c2 == c1 || c2 == '=') return 1; goto letter; case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'e': case 'E': if (c2 == '-' || c2 == '+') return 1; /* could extend a pre-processing number */ goto letter; case 'L': if (c2 == '\'' || c2 == '\"') return 1; /* Could turn into L"xxx" or L'xxx'. */ goto letter; letter: case '_': case 'a': case 'b': case 'c': case 'd': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': /* We're in the middle of either a name or a pre-processing number. */ return (is_idchar[c2] || c2 == '.'); case '<': case '>': case '!': case '%': case '#': case ':': case '^': case '&': case '|': case '*': case '/': case '=': return (c2 == c1 || c2 == '='); } return 0; } /* Expand a macro call. * HP points to the symbol that is the macro being called. * Put the result of expansion onto the input stack * so that subsequent input by our caller will use it. * * If macro wants arguments, caller has already verified that * an argument list follows; arguments come from the input stack. */ static void macroexpand(cpp_reader *pfile, HASHNODE *hp) { int nargs; DEFINITION *defn = hp->value.defn; unsigned char *xbuf; long start_line, start_column; int xbuf_len; struct argdata *args; long old_written = CPP_WRITTEN(pfile); int rest_args, rest_zero; int i; pfile->output_escapes++; cpp_buf_line_and_col(cpp_file_buffer(pfile), &start_line, &start_column); rest_zero = 0; args = NULL; nargs = defn->nargs; if (nargs >= 0) { enum cpp_token token; token = CPP_OTHER; args = (struct argdata *)alloca((nargs + 1) * sizeof(struct argdata)); for (i = 0; i < nargs; i++) { args[i].raw = args[i].expanded = 0; args[i].raw_length = 0; args[i].expand_length = args[i].stringified_length = -1; args[i].use_count = 0; } /* Parse all the macro args that are supplied. I counts them. * The first NARGS args are stored in ARGS. * The rest are discarded. If rest_args is set then we assume * macarg absorbed the rest of the args. */ i = 0; rest_args = 0; rest_args = 0; FORWARD(1); /* Discard the open-parenthesis before the first arg. */ do { if (rest_args) continue; if (i < nargs || (nargs == 0 && i == 0)) { /* if we are working on last arg which absorbs rest of args... */ if (i == nargs - 1 && defn->rest_args) rest_args = 1; args[i].raw = CPP_WRITTEN(pfile); token = macarg(pfile, rest_args); args[i].raw_length = CPP_WRITTEN(pfile) - args[i].raw; args[i].newlines = 0; /* FIXME */ } else token = macarg(pfile, 0); if (token == CPP_EOF || token == CPP_POP) { cpp_error_with_line(pfile, start_line, start_column, "unterminated macro call"); return; } i++; } while (token == CPP_COMMA); /* If we got one arg but it was just whitespace, call that 0 args. */ if (i == 1) { unsigned char *bp = ARG_BASE + args[0].raw; unsigned char *lim = bp + args[0].raw_length; /* cpp.texi says for foo ( ) we provide one argument. * However, if foo wants just 0 arguments, treat this as 0. */ if (nargs == 0) while (bp != lim && is_space[*bp]) bp++; if (bp == lim) i = 0; } /* Don't output an error message if we have already output one for * a parse error above. */ if (nargs == 0 && i > 0) { cpp_error(pfile, "arguments given to macro `%s'", hp->name); } else if (i < nargs) { /* traditional C allows foo() if foo wants one argument. */ if (nargs == 1 && i == 0 && CPP_TRADITIONAL(pfile)); /* the rest args token is allowed to absorb 0 tokens */ else if (i == nargs - 1 && defn->rest_args) rest_zero = 1; else if (i == 0) cpp_error(pfile, "macro `%s' used without args", hp->name); else if (i == 1) cpp_error(pfile, "macro `%s' used with just one arg", hp->name); else cpp_error(pfile, "macro `%s' used with only %d args", hp->name, i); } else if (i > nargs) { cpp_error(pfile, "macro `%s' used with too many (%d) args", hp->name, i); } } /* If macro wants zero args, we parsed the arglist for checking only. * Read directly from the macro definition. */ if (nargs <= 0) { xbuf = defn->expansion; xbuf_len = defn->length; } else { unsigned char *exp = defn->expansion; int offset; /* offset in expansion, * copied a piece at a time */ int totlen; /* total amount of exp buffer filled so far */ reflist *ap, *last_ap; /* Macro really takes args. Compute the expansion of this call. */ /* Compute length in characters of the macro's expansion. * Also count number of times each arg is used. */ xbuf_len = defn->length; for (ap = defn->pattern; ap; ap = ap->next) { if (ap->stringify) { struct argdata *arg = &args[ap->argno]; /* Stringify it it hasn't already been */ if (arg->stringified_length < 0) { int arglen = arg->raw_length; int escaped = 0; int in_string = 0; int c; /* Initially need_space is -1. Otherwise, 1 means the * previous character was a space, but we suppressed it; * 0 means the previous character was a non-space. */ int need_space = -1; i = 0; arg->stringified = CPP_WRITTEN(pfile); if (!CPP_TRADITIONAL(pfile)) CPP_PUTC(pfile, '\"'); /* insert beginning quote */ for (; i < arglen; i++) { c = (ARG_BASE + arg->raw)[i]; if (!in_string) { /* Internal sequences of whitespace are replaced by * one space except within an string or char token. */ if (is_space[c]) { if (CPP_WRITTEN(pfile) > arg->stringified && (CPP_PWRITTEN(pfile))[-1] == '@') { /* "@ " escape markers are removed */ CPP_ADJUST_WRITTEN(pfile, -1); continue; } if (need_space == 0) need_space = 1; continue; } else if (need_space > 0) CPP_PUTC(pfile, ' '); need_space = 0; } if (escaped) escaped = 0; else { if (c == '\\') escaped = 1; if (in_string) { if (c == in_string) in_string = 0; } else if (c == '\"' || c == '\'') in_string = c; } /* Escape these chars */ if (c == '\"' || (in_string && c == '\\')) CPP_PUTC(pfile, '\\'); if (isprint(c)) CPP_PUTC(pfile, c); else { CPP_RESERVE(pfile, 4); sprintf((char *)CPP_PWRITTEN(pfile), "\\%03o", (unsigned int)c); CPP_ADJUST_WRITTEN(pfile, 4); } } if (!CPP_TRADITIONAL(pfile)) CPP_PUTC(pfile, '\"'); /* insert ending quote */ arg->stringified_length = CPP_WRITTEN(pfile) - arg->stringified; } xbuf_len += args[ap->argno].stringified_length; } else if (ap->raw_before || ap->raw_after || CPP_TRADITIONAL(pfile)) /* Add 4 for two newline-space markers to prevent * token concatenation. */ xbuf_len += args[ap->argno].raw_length + 4; else { /* We have an ordinary (expanded) occurrence of the arg. * So compute its expansion, if we have not already. */ if (args[ap->argno].expand_length < 0) { args[ap->argno].expanded = CPP_WRITTEN(pfile); cpp_expand_to_buffer(pfile, ARG_BASE + args[ap->argno].raw, args[ap->argno].raw_length); args[ap->argno].expand_length = CPP_WRITTEN(pfile) - args[ap->argno].expanded; } /* Add 4 for two newline-space markers to prevent * token concatenation. */ xbuf_len += args[ap->argno].expand_length + 4; } if (args[ap->argno].use_count < 10) args[ap->argno].use_count++; } xbuf = (unsigned char *)xmalloc(xbuf_len + 1); /* Generate in XBUF the complete expansion * with arguments substituted in. * TOTLEN is the total size generated so far. * OFFSET is the index in the definition * of where we are copying from. */ offset = totlen = 0; for (last_ap = NULL, ap = defn->pattern; ap; last_ap = ap, ap = ap->next) { struct argdata *arg = &args[ap->argno]; int count_before = totlen; /* Add chars to XBUF. */ for (i = 0; i < ap->nchars; i++, offset++) xbuf[totlen++] = exp[offset]; /* If followed by an empty rest arg with concatenation, * delete the last run of nonwhite chars. */ if (rest_zero && totlen > count_before && ((ap->rest_args && ap->raw_before) || (last_ap && last_ap->rest_args && last_ap->raw_after))) { /* Delete final whitespace. */ while (totlen > count_before && is_space[xbuf[totlen - 1]]) totlen--; /* Delete the nonwhites before them. */ while (totlen > count_before && !is_space[xbuf[totlen - 1]]) totlen--; } if (ap->stringify != 0) { memcpy(xbuf + totlen, ARG_BASE + arg->stringified, arg->stringified_length); totlen += arg->stringified_length; } else if (ap->raw_before || ap->raw_after || CPP_TRADITIONAL(pfile)) { unsigned char *p1 = ARG_BASE + arg->raw; unsigned char *l1 = p1 + arg->raw_length; if (ap->raw_before) { while (p1 != l1 && is_space[*p1]) p1++; while (p1 != l1 && is_idchar[*p1]) xbuf[totlen++] = *p1++; } if (ap->raw_after) { /* Arg is concatenated after: delete trailing whitespace, * whitespace markers, and no-reexpansion markers. */ while (p1 != l1) { if (is_space[l1[-1]]) l1--; else if (l1[-1] == '-') { unsigned char *p2 = l1 - 1; /* If a `-' is preceded by an odd number of newlines then it * and the last newline are a no-reexpansion marker. */ while (p2 != p1 && p2[-1] == '\n') p2--; if ((l1 - 1 - p2) & 1) { l1 -= 2; } else break; } else break; } } memcpy(xbuf + totlen, p1, l1 - p1); totlen += l1 - p1; } else { unsigned char *expanded = ARG_BASE + arg->expanded; if (!ap->raw_before && totlen > 0 && arg->expand_length && !CPP_TRADITIONAL(pfile) && unsafe_chars(xbuf[totlen - 1], expanded[0])) { xbuf[totlen++] = '@'; xbuf[totlen++] = ' '; } memcpy(xbuf + totlen, expanded, arg->expand_length); totlen += arg->expand_length; if (!ap->raw_after && totlen > 0 && offset < defn->length && !CPP_TRADITIONAL(pfile) && unsafe_chars(xbuf[totlen - 1], exp[offset])) { xbuf[totlen++] = '@'; xbuf[totlen++] = ' '; } /* If a macro argument with newlines is used multiple times, * then only expand the newlines once. This avoids creating * output lines which don't correspond to any input line, * which confuses gdb and gcov. */ if (arg->use_count > 1 && arg->newlines > 0) { /* Don't bother doing change_newlines for subsequent * uses of arg. */ arg->use_count = 1; arg->expand_length = change_newlines(expanded, arg->expand_length); } } if (totlen > xbuf_len) abort(); } /* if there is anything left of the definition * after handling the arg list, copy that in too. */ for (i = offset; i < defn->length; i++) { /* if we've reached the end of the macro */ if (exp[i] == ')') rest_zero = 0; if (!(rest_zero && last_ap && last_ap->rest_args && last_ap->raw_after)) xbuf[totlen++] = exp[i]; } xbuf[totlen] = 0; xbuf_len = totlen; } pfile->output_escapes--; /* Now put the expansion on the input stack * so our caller will commence reading from it. */ push_macro_expansion(pfile, xbuf, xbuf_len, hp); CPP_BUFFER(pfile)->has_escapes = 1; /* Pop the space we've used in the token_buffer for argument expansion. */ CPP_SET_WRITTEN(pfile, old_written); /* Recursive macro use sometimes works traditionally. * #define foo(x,y) bar (x (y,0), y) * foo (foo, baz) */ if (!CPP_TRADITIONAL(pfile)) hp->type = T_DISABLED; } static void push_macro_expansion(cpp_reader *pfile, unsigned char *xbuf, int xbuf_len, HASHNODE *hp) { cpp_buffer *mbuf = cpp_push_buffer(pfile, xbuf, xbuf_len); mbuf->cleanup = macro_cleanup; mbuf->data = hp; /* The first chars of the expansion should be a "@ " added by * collect_expansion. This is to prevent accidental token-pasting * between the text preceding the macro invocation, and the macro * expansion text. * * We would like to avoid adding unneeded spaces (for the sake of * tools that use cpp, such as imake). In some common cases we can * tell that it is safe to omit the space. * * The character before the macro invocation cannot have been an * idchar (or else it would have been pasted with the idchars of * the macro name). Therefore, if the first non-space character * of the expansion is an idchar, we do not need the extra space * to prevent token pasting. * * Also, we don't need the extra space if the first char is '(', * or some other (less common) characters. */ if (xbuf[0] == '@' && xbuf[1] == ' ' && (is_idchar[xbuf[2]] || xbuf[2] == '(' || xbuf[2] == '\'' || xbuf[2] == '\"')) mbuf->cur += 2; } /* Like cpp_get_token, except that it does not read past end-of-line. * Also, horizontal space is skipped, and macros are popped. */ static enum cpp_token get_directive_token(cpp_reader *pfile) { for (;;) { long old_written = CPP_WRITTEN(pfile); enum cpp_token token; cpp_skip_hspace(pfile); if (PEEKC() == '\n') return CPP_VSPACE; token = cpp_get_token(pfile); switch (token) { case CPP_POP: if (!CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile))) return token; /* ... else fall though ... */ /* FALLTHROUGH */ case CPP_HSPACE: case CPP_COMMENT: CPP_SET_WRITTEN(pfile, old_written); break; default: return token; } } } /* Handle #include and #import. * This function expects to see "fname" or on the input. * * The input is normally in part of the output_buffer following * CPP_WRITTEN, and will get overwritten by output_line_command. * I.e. in input file specification has been popped by handle_directive. * This is safe. */ static int do_include(cpp_reader *pfile, struct directive *keyword, unsigned char *unused1 __UNUSED__, unsigned char *unused2 __UNUSED__) { int importing = (keyword->type == T_IMPORT); int skip_dirs = (keyword->type == T_INCLUDE_NEXT); char *fname; /* Dynamically allocated fname buffer */ unsigned char *fbeg, *fend; /* Beginning and end of fname */ enum cpp_token token; /* Chain of dirs to search */ file_name_list *search_start = CPP_OPTIONS(pfile)->include; file_name_list dsp[1]; /* First in chain, if #include "..." */ file_name_list *searchptr = 0; long old_written = CPP_WRITTEN(pfile); int flen; int f; /* file number */ int angle_brackets = 0; /* 0 for "...", 1 for <...> */ f = -1; /* JF we iz paranoid! */ if (importing && CPP_OPTIONS(pfile)->warn_import && !CPP_OPTIONS(pfile)->inhibit_warnings && !CPP_BUFFER(pfile)->system_header_p && !pfile->import_warning) { pfile->import_warning = 1; cpp_warning(pfile, "using `#import' is not recommended"); fprintf(stderr, "The fact that a certain header file need not be processed more than once\n"); fprintf(stderr, "should be indicated in the header file, not where it is used.\n"); fprintf(stderr, "The best way to do this is with a conditional of this form:\n\n"); fprintf(stderr, " #ifndef _FOO_H_INCLUDED\n"); fprintf(stderr, " #define _FOO_H_INCLUDED\n"); fprintf(stderr, " ... ...\n"); fprintf(stderr, " #endif /* Not _FOO_H_INCLUDED */\n\n"); fprintf(stderr, "Then users can use `#include' any number of times.\n"); fprintf(stderr, "GNU C automatically avoids processing the file more than once\n"); fprintf(stderr, "when it is equipped with such a conditional.\n"); } pfile->parsing_include_directive++; token = get_directive_token(pfile); pfile->parsing_include_directive--; if (token == CPP_STRING) { /* FIXME - check no trailing garbage */ fbeg = pfile->token_buffer + old_written + 1; fend = CPP_PWRITTEN(pfile) - 1; if (fbeg[-1] == '<') { angle_brackets = 1; /* If -I-, start with the first -I dir after the -I-. */ if (CPP_OPTIONS(pfile)->first_bracket_include) search_start = CPP_OPTIONS(pfile)->first_bracket_include; } /* If -I- was specified, don't search current dir, only spec'd ones. */ else if (!CPP_OPTIONS(pfile)->ignore_srcdir) { cpp_buffer *fp; /* We have "filename". Figure out directory this source * file is coming from and put it on the front of the list. */ for (fp = CPP_BUFFER(pfile); fp; fp = CPP_PREV_BUFFER(fp)) { int n; const char *ep, *nam; if ((nam = fp->nominal_fname)) { /* Found a named file. Figure out dir of the file, * and put it in front of the search list. */ dsp[0].next = search_start; search_start = dsp; #ifndef VMS ep = strrchr(nam, '/'); #else /* VMS */ ep = strrchr(nam, ']'); if (!ep) ep = strrchr(nam, '>'); if (!ep) ep = strrchr(nam, ':'); if (ep) ep++; #endif /* VMS */ if (ep) { n = ep - nam; dsp[0].fname = (char *)alloca(n + 1); strncpy(dsp[0].fname, nam, n); dsp[0].fname[n] = '\0'; if (n + INCLUDE_LEN_FUDGE > pfile->max_include_len) pfile->max_include_len = n + INCLUDE_LEN_FUDGE; } else { dsp[0].fname = 0; /* Current directory */ } dsp[0].got_name_map = 0; break; } } } } else { cpp_error(pfile, "`#%s' expects \"FILENAME\" or ", keyword->name); CPP_SET_WRITTEN(pfile, old_written); skip_rest_of_line(pfile); return 0; } *fend = 0; token = get_directive_token(pfile); if (token != CPP_VSPACE) { cpp_error(pfile, "junk at end of `#include'"); while (token != CPP_VSPACE && token != CPP_EOF && token != CPP_POP) token = get_directive_token(pfile); } /* For #include_next, skip in the search path * past the dir in which the containing file was found. */ if (skip_dirs) { cpp_buffer *fp; for (fp = CPP_BUFFER(pfile); fp; fp = CPP_PREV_BUFFER(fp)) if (fp->fname) { /* fp->dir is null if the containing file was specified with * an absolute file name. In that case, don't skip anything. */ if (fp->dir == SELF_DIR_DUMMY) search_start = CPP_OPTIONS(pfile)->include; else if (fp->dir) search_start = fp->dir->next; break; } } CPP_SET_WRITTEN(pfile, old_written); flen = fend - fbeg; if (flen == 0) { cpp_error(pfile, "empty file name in `#%s'", keyword->name); return 0; } /* Allocate this permanently, because it gets stored in the definitions * of macros. */ fname = (char *)xmalloc(pfile->max_include_len + flen + 4); /* + 2 above for slash and terminating null. */ /* + 2 added for '.h' on VMS (to support '#include filename') */ /* If specified file name is absolute, just open it. */ if (*fbeg == '/') { strncpy(fname, (const char *)fbeg, flen); fname[flen] = 0; if (redundant_include_p(pfile, fname)) return 0; if (importing) f = lookup_import(pfile, fname, NULL); else f = open_include_file(pfile, fname, NULL); if (f == -2) return 0; /* Already included this file */ } else { /* Search directory path, trying to open the file. * Copy each filename tried into FNAME. */ for (searchptr = search_start; searchptr; searchptr = searchptr->next) { if (searchptr->fname) { /* The empty string in a search path is ignored. * This makes it possible to turn off entirely * a standard piece of the list. */ if (searchptr->fname[0] == 0) continue; strcpy(fname, searchptr->fname); strcat(fname, "/"); fname[strlen(fname) + flen] = 0; } else { fname[0] = 0; } strncat(fname, (const char *)fbeg, flen); #ifdef VMS /* Change this 1/2 Unix 1/2 VMS file specification into a * full VMS file specification */ if (searchptr->fname && (searchptr->fname[0] != 0)) { /* Fix up the filename */ hack_vms_include_specification(fname); } else { /* This is a normal VMS filespec, so use it unchanged. */ strncpy(fname, fbeg, flen); fname[flen] = 0; /* if it's '#include filename', add the missing .h */ if (!strchr(fname, '.')) { strcat(fname, ".h"); } } #endif /* VMS */ /* ??? There are currently 3 separate mechanisms for avoiding processing * of redundant include files: #import, #pragma once, and * redundant_include_p. It would be nice if they were unified. */ if (redundant_include_p(pfile, fname)) return 0; if (importing) f = lookup_import(pfile, fname, searchptr); else f = open_include_file(pfile, fname, searchptr); if (f == -2) return 0; /* Already included this file */ #ifdef EACCES else if (f == -1 && errno == EACCES) cpp_warning(pfile, "Header file %s exists, but is not readable", fname); #endif if (f >= 0) break; } } if (f < 0) { /* A file that was not found. */ strncpy(fname, (const char *)fbeg, flen); fname[flen] = 0; /* If generating dependencies and -MG was specified, we assume missing * files are leaf files, living in the same directory as the source file * or other similar place; these missing files may be generated from * other files and may not exist yet (eg: y.tab.h). */ if (CPP_OPTIONS(pfile)->print_deps_missing_files && CPP_PRINT_DEPS(pfile) > (angle_brackets || (pfile->system_include_depth > 0))) { /* If it was requested as a system header file, * then assume it belongs in the first place to look for such. */ if (angle_brackets) { for (searchptr = search_start; searchptr; searchptr = searchptr->next) { if (searchptr->fname) { char *p; if (searchptr->fname[0] == 0) continue; p = (char *)alloca(strlen(searchptr->fname) + strlen(fname) + 2); strcpy(p, searchptr->fname); strcat(p, "/"); strcat(p, fname); deps_output(pfile, p, ' '); break; } } } else { /* Otherwise, omit the directory, as if the file existed * in the directory with the source. */ deps_output(pfile, fname, ' '); } } /* If -M was specified, and this header file won't be added to the * dependency list, then don't count this as an error, because we can * still produce correct output. Otherwise, we can't produce correct * output, because there may be dependencies we need inside the missing * file, and we don't know what directory this missing file exists in. */ else if (CPP_PRINT_DEPS(pfile) && (CPP_PRINT_DEPS(pfile) <= (angle_brackets || (pfile->system_include_depth > 0)))) cpp_warning(pfile, "No include path in which to find %s", fname); else if (search_start) cpp_error_from_errno(pfile, fname); else cpp_error(pfile, "No include path in which to find %s", fname); } else { /* Check to see if this include file is a once-only include file. * If so, give up. */ file_name_list *ptr; for (ptr = pfile->dont_repeat_files; ptr; ptr = ptr->next) { if (!strcmp(ptr->fname, fname)) { close(f); return 0; /* This file was once'd. */ } } for (ptr = pfile->all_include_files; ptr; ptr = ptr->next) { if (!strcmp(ptr->fname, fname)) break; /* This file was included before. */ } if (!ptr) { /* This is the first time for this file. */ /* Add it to list of files included. */ ptr = (file_name_list *) xmalloc(sizeof(file_name_list)); ptr->control_macro = 0; ptr->c_system_include_path = 0; ptr->next = pfile->all_include_files; pfile->all_include_files = ptr; ptr->fname = savestring(fname); ptr->got_name_map = 0; /* For -M, add this file to the dependencies. */ if (CPP_PRINT_DEPS(pfile) > (angle_brackets || (pfile->system_include_depth > 0))) deps_output(pfile, fname, ' '); } /* Handle -H option. */ if (CPP_OPTIONS(pfile)->print_include_names) { cpp_buffer *buf = CPP_BUFFER(pfile); while ((buf = CPP_PREV_BUFFER(buf))) putc('.', stderr); fprintf(stderr, "%s\n", fname); } if (angle_brackets) pfile->system_include_depth++; /* Actually process the file. */ /* Record file on "seen" list for #import. */ add_import(pfile, f, fname); /* Actually process the file */ cpp_push_buffer(pfile, NULL, 0); if (finclude(pfile, f, fname, is_system_include(pfile, fname), searchptr != dsp ? searchptr : SELF_DIR_DUMMY)) { output_line_command(pfile, 0, enter_file); pfile->only_seen_white = 2; } if (angle_brackets) pfile->system_include_depth--; } return 0; } /* Return nonzero if there is no need to include file NAME * because it has already been included and it contains a conditional * to make a repeated include do nothing. */ static int redundant_include_p(cpp_reader *pfile, char *name) { file_name_list *l = pfile->all_include_files; for (; l; l = l->next) if (!strcmp(name, l->fname) && l->control_macro && cpp_lookup((const char *)l->control_macro, -1, -1)) return 1; return 0; } /* Return nonzero if the given FILENAME is an absolute pathname which * designates a file within one of the known "system" include file * directories. We assume here that if the given FILENAME looks like * it is the name of a file which resides either directly in a "system" * include file directory, or within any subdirectory thereof, then the * given file must be a "system" include file. This function tells us * if we should suppress pedantic errors/warnings for the given FILENAME. * * The value is 2 if the file is a C-language system header file * for which C++ should (on most systems) assume `extern "C"'. */ static int is_system_include(cpp_reader *pfile, char *filename) { file_name_list *searchptr; for (searchptr = CPP_OPTIONS(pfile)->first_system_include; searchptr; searchptr = searchptr->next) if (searchptr->fname) { char *sys_dir = searchptr->fname; unsigned length = strlen(sys_dir); if (!strncmp(sys_dir, filename, length) && filename[length] == '/') { if (searchptr->c_system_include_path) return 2; else return 1; } } return 0; } /* * Install a name in the assertion hash table. * * If LEN is >= 0, it is the length of the name. * Otherwise, compute the length by scanning the entire name. * * If HASH is >= 0, it is the precomputed hash code. * Otherwise, compute the hash code. */ static ASSERTION_HASHNODE * assertion_install(cpp_reader *pfile, const char *name, int len, int hash) { ASSERTION_HASHNODE *hp; int i, bucket; i = sizeof(ASSERTION_HASHNODE) + len + 1; hp = (ASSERTION_HASHNODE *) xmalloc(i); bucket = hash; hp->bucket_hdr = &pfile->assertion_hashtab[bucket]; hp->next = pfile->assertion_hashtab[bucket]; pfile->assertion_hashtab[bucket] = hp; hp->prev = NULL; if (hp->next) hp->next->prev = hp; hp->length = len; hp->value = 0; hp->name = ((char *)hp) + sizeof(ASSERTION_HASHNODE); memcpy(hp->name, name, len); hp->name[len] = 0; return hp; } /* * find the most recent hash node for name name (ending with first * non-identifier char) installed by install * * If LEN is >= 0, it is the length of the name. * Otherwise, compute the length by scanning the entire name. * * If HASH is >= 0, it is the precomputed hash code. * Otherwise, compute the hash code. */ static ASSERTION_HASHNODE * assertion_lookup(cpp_reader *pfile, const char *name, int len, int hash) { ASSERTION_HASHNODE *bucket; bucket = pfile->assertion_hashtab[hash]; while (bucket) { if (bucket->length == len && strncmp(bucket->name, name, len) == 0) return bucket; bucket = bucket->next; } return NULL; } static void delete_assertion(ASSERTION_HASHNODE *hp) { struct tokenlist_list *tail; if (hp->prev) hp->prev->next = hp->next; if (hp->next) hp->next->prev = hp->prev; for (tail = hp->value; tail;) { struct tokenlist_list *next = tail->next; free_token_list(tail->tokens); free(tail); tail = next; } /* make sure that the bucket chain header that * the deleted guy was on points to the right thing afterwards. */ if (hp == *hp->bucket_hdr) *hp->bucket_hdr = hp->next; free(hp); } /* Convert a character string literal into a nul-terminated string. * The input string is [IN ... LIMIT). * The result is placed in RESULT. RESULT can be the same as IN. * The value returned in the end of the string written to RESULT, * or NULL on error. */ static char * convert_string(cpp_reader *pfile, char *result, char *in, char *limit, int handle_escapes) { unsigned char c; c = *in++; if (c != '\"') return NULL; while (in < limit) { c = *in++; switch (c) { case '\0': return NULL; case '\"': limit = in; break; case '\\': if (handle_escapes) { char *bpc = in; int i = (unsigned char)cpp_parse_escape(pfile, &bpc); in = bpc; if (i >= 0) *result++ = (unsigned char)c; break; } /* else fall through */ default: *result++ = c; } } *result = 0; return result; } /* * interpret #line command. Remembers previously seen fnames * in its very own hash table. */ #define FNAME_HASHSIZE 37 static int do_line(cpp_reader *pfile, struct directive *keyword __UNUSED__, unsigned char *unused1 __UNUSED__, unsigned char *unused2 __UNUSED__) { cpp_buffer *ip = CPP_BUFFER(pfile); int new_lineno; long old_written = CPP_WRITTEN(pfile); enum file_change_code file_change = same_file; enum cpp_token token; token = get_directive_token(pfile); if (token != CPP_NUMBER || !isdigit(pfile->token_buffer[old_written])) { cpp_error(pfile, "invalid format `#line' command"); goto bad_line_directive; } /* The Newline at the end of this line remains to be processed. * To put the next line at the specified line number, * we must store a line number now that is one less. */ new_lineno = atoi((char *)(pfile->token_buffer + old_written)) - 1; CPP_SET_WRITTEN(pfile, old_written); /* NEW_LINENO is one less than the actual line number here. */ if (CPP_PEDANTIC(pfile) && new_lineno < 0) cpp_pedwarn(pfile, "line number out of range in `#line' command"); token = get_directive_token(pfile); if (token == CPP_STRING) { char *fname = (char *)pfile->token_buffer + old_written; char *end_name; static HASHNODE *fname_table[FNAME_HASHSIZE]; HASHNODE *hp, **hash_bucket; unsigned char *p; long num_start; int fname_length; /* Turn the file name, which is a character string literal, * into a null-terminated string. Do this in place. */ end_name = convert_string(pfile, fname, fname, (char *)CPP_PWRITTEN(pfile), 1); if (!end_name) { cpp_error(pfile, "invalid format `#line' command"); goto bad_line_directive; } fname_length = end_name - fname; num_start = CPP_WRITTEN(pfile); token = get_directive_token(pfile); if (token != CPP_VSPACE && token != CPP_EOF && token != CPP_POP) { p = pfile->token_buffer + num_start; if (CPP_PEDANTIC(pfile)) cpp_pedwarn(pfile, "garbage at end of `#line' command"); if (token != CPP_NUMBER || *p < '0' || *p > '4' || p[1] != '\0') { cpp_error(pfile, "invalid format `#line' command"); goto bad_line_directive; } if (*p == '1') file_change = enter_file; else if (*p == 2) file_change = leave_file; else if (*p == 3) ip->system_header_p = 1; else /* if (*p == 4) */ ip->system_header_p = 2; CPP_SET_WRITTEN(pfile, num_start); token = get_directive_token(pfile); p = pfile->token_buffer + num_start; if (token == CPP_NUMBER && p[1] == '\0' && (*p == '3' || *p == '4')) { ip->system_header_p = *p == 3 ? 1 : 2; token = get_directive_token(pfile); } if (token != CPP_VSPACE) { cpp_error(pfile, "invalid format `#line' command"); goto bad_line_directive; } } hash_bucket = &fname_table[hashf(fname, fname_length, FNAME_HASHSIZE)]; for (hp = *hash_bucket; hp; hp = hp->next) if (hp->length == fname_length && strncmp(hp->value.cpval, fname, fname_length) == 0) { ip->nominal_fname = hp->value.cpval; break; } if (!hp) { /* Didn't find it; cons up a new one. */ hp = (HASHNODE *) xcalloc(1, sizeof(HASHNODE) + fname_length + 1); hp->next = *hash_bucket; *hash_bucket = hp; hp->length = fname_length; ip->nominal_fname = hp->value.cpval = ((char *)hp) + sizeof(HASHNODE); memcpy(hp->value.cpval, fname, fname_length); } } else if (token != CPP_VSPACE && token != CPP_EOF) { cpp_error(pfile, "invalid format `#line' command"); goto bad_line_directive; } ip->lineno = new_lineno; bad_line_directive: skip_rest_of_line(pfile); CPP_SET_WRITTEN(pfile, old_written); output_line_command(pfile, 0, file_change); return 0; } /* * remove the definition of a symbol from the symbol table. * according to un*x /lib/cpp, it is not an error to undef * something that has no definitions, so it isn't one here either. */ static int do_undef(cpp_reader *pfile, struct directive *keyword, unsigned char *buf, unsigned char *limit) { int sym_length; HASHNODE *hp; unsigned char *orig_buf = buf; SKIP_WHITE_SPACE(buf); sym_length = check_macro_name(pfile, buf, "macro"); while ((hp = cpp_lookup((const char *)buf, sym_length, -1))) { /* If we are generating additional info for debugging (with -g) we * need to pass through all effective #undef commands. */ if (CPP_OPTIONS(pfile)->debug_output && keyword) pass_thru_directive((char *)orig_buf, (char *)limit, pfile, keyword); if (hp->type != T_MACRO) cpp_warning(pfile, "undefining `%s'", hp->name); delete_macro(hp); } if (CPP_PEDANTIC(pfile)) { buf += sym_length; SKIP_WHITE_SPACE(buf); if (buf != limit) cpp_pedwarn(pfile, "garbage after `#undef' directive"); } return 0; } /* * Report an error detected by the program we are processing. * Use the text of the line in the error message. * (We use error because it prints the filename & line#.) */ static int do_error(cpp_reader *pfile, struct directive *keyword __UNUSED__, unsigned char *buf, unsigned char *limit) { int length = limit - buf; unsigned char *copy = (unsigned char *)xmalloc(length + 1); memcpy(copy, buf, length); copy[length] = 0; SKIP_WHITE_SPACE(copy); cpp_error(pfile, "#error %s", copy); return 0; } /* * Report a warning detected by the program we are processing. * Use the text of the line in the warning message, then continue. * (We use error because it prints the filename & line#.) */ static int do_warning(cpp_reader *pfile, struct directive *keyword __UNUSED__, unsigned char *buf, unsigned char *limit) { int length = limit - buf; unsigned char *copy = (unsigned char *)xmalloc(length + 1); memcpy(copy, buf, length); copy[length] = 0; SKIP_WHITE_SPACE(copy); cpp_warning(pfile, "#warning %s", copy); return 0; } /* Remember the name of the current file being read from so that we can * avoid ever including it again. */ static int do_once(cpp_reader *pfile) { cpp_buffer *ip = NULL; file_name_list *new_; for (ip = CPP_BUFFER(pfile);; ip = CPP_PREV_BUFFER(ip)) { if (!ip) return 0; if (ip->fname) break; } new_ = (file_name_list *) xmalloc(sizeof(file_name_list)); new_->next = pfile->dont_repeat_files; pfile->dont_repeat_files = new_; new_->fname = savestring(ip->fname); new_->control_macro = 0; new_->got_name_map = 0; new_->c_system_include_path = 0; return 0; } /* #ident has already been copied to the output file, so just ignore it. */ static int do_ident(cpp_reader *pfile, struct directive *keyword __UNUSED__, unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__) { /* long old_written = CPP_WRITTEN (pfile); */ /* Allow #ident in system headers, since that's not user's fault. */ if (CPP_PEDANTIC(pfile) && !CPP_BUFFER(pfile)->system_header_p) cpp_pedwarn(pfile, "ANSI C does not allow `#ident'"); /* Leave rest of line to be read by later calls to cpp_get_token. */ return 0; } /* #pragma and its argument line have already been copied to the output file. * Just check for some recognized pragmas that need validation here. */ static int do_pragma(cpp_reader *pfile, struct directive *keyword __UNUSED__, unsigned char *buf, unsigned char *limit __UNUSED__) { while (*buf == ' ' || *buf == '\t') buf++; if (!strncmp((const char *)buf, "once", 4)) { /* Allow #pragma once in system headers, since that's not the user's * fault. */ if (!CPP_BUFFER(pfile)->system_header_p) cpp_warning(pfile, "`#pragma once' is obsolete"); do_once(pfile); } if (!strncmp((const char *)buf, "implementation", 14)) { /* Be quiet about `#pragma implementation' for a file only if it hasn't * been included yet. */ file_name_list *ptr; char *p = (char *)buf + 14, *fname, *inc_fname; int fname_len; SKIP_WHITE_SPACE(p); if (*p == '\n' || *p != '\"') return 0; fname = p + 1; p = strchr(fname, '\"'); fname_len = (p) ? (int)(p - fname) : (int)strlen(fname); for (ptr = pfile->all_include_files; ptr; ptr = ptr->next) { inc_fname = strrchr(ptr->fname, '/'); inc_fname = inc_fname ? inc_fname + 1 : (char *)ptr->fname; if (inc_fname && !strncmp(inc_fname, fname, fname_len)) cpp_warning(pfile, "`#pragma implementation' for `%s' appears after file is included", fname); } } return 0; } /* Just ignore #sccs, on systems where we define it at all. */ /* * handle #if command by * 1) inserting special `defined' keyword into the hash table * that gets turned into 0 or 1 by special_symbol (thus, * if the luser has a symbol called `defined' already, it won't * work inside the #if command) * 2) rescan the input into a temporary output buffer * 3) pass the output buffer to the yacc parser and collect a value * 4) clean up the mess left from steps 1 and 2. * 5) call conditional_skip to skip til the next #endif (etc.), * or not, depending on the value from step 3. */ static int do_if(cpp_reader *pfile, struct directive *keyword __UNUSED__, unsigned char *buf, unsigned char *limit) { HOST_WIDE_INT value = eval_if_expression(pfile, buf, limit - buf); conditional_skip(pfile, value == 0, T_IF, NULL); return 0; } /* * handle a #elif directive by not changing if_stack either. * see the comment above do_else. */ static int do_elif(cpp_reader *pfile, struct directive *keyword __UNUSED__, unsigned char *buf, unsigned char *limit) { if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack) { cpp_error(pfile, "`#elif' not within a conditional"); return 0; } else { if (pfile->if_stack->type != T_IF && pfile->if_stack->type != T_ELIF) { cpp_error(pfile, "`#elif' after `#else'"); if (pfile->if_stack->fname && CPP_BUFFER(pfile)->fname && strcmp(pfile->if_stack->fname, CPP_BUFFER(pfile)->nominal_fname) != 0) fprintf(stderr, ", file %s", pfile->if_stack->fname); fprintf(stderr, ")\n"); } pfile->if_stack->type = T_ELIF; } if (pfile->if_stack->if_succeeded) skip_if_group(pfile, 0); else { HOST_WIDE_INT value = eval_if_expression(pfile, buf, limit - buf); if (value == 0) skip_if_group(pfile, 0); else { ++pfile->if_stack->if_succeeded; /* continue processing input */ output_line_command(pfile, 1, same_file); } } return 0; } /* * evaluate a #if expression in BUF, of length LENGTH, * then parse the result as a C expression and return the value as an int. */ static HOST_WIDE_INT eval_if_expression(cpp_reader *pfile, unsigned char *buf __UNUSED__, int length __UNUSED__) { HASHNODE *save_defined; HOST_WIDE_INT value; long old_written = CPP_WRITTEN(pfile); save_defined = install("defined", -1, T_SPEC_DEFINED, 0, 0, -1); pfile->pcp_inside_if = 1; value = cpp_parse_expr(pfile); pfile->pcp_inside_if = 0; delete_macro(save_defined); /* clean up special symbol */ CPP_SET_WRITTEN(pfile, old_written); /* Pop */ return value; } /* * routine to handle ifdef/ifndef. Try to look up the symbol, * then do or don't skip to the #endif/#else/#elif depending * on what directive is actually being processed. */ static int do_xifdef(cpp_reader *pfile, struct directive *keyword, unsigned char *unused1 __UNUSED__, unsigned char *unused2 __UNUSED__) { int skip; cpp_buffer *ip = CPP_BUFFER(pfile); char *ident; int ident_length; enum cpp_token token; int start_of_file = 0; unsigned char *control_macro = 0; int old_written = CPP_WRITTEN(pfile); /* Detect a #ifndef at start of file (not counting comments). */ if (ip->fname != 0 && keyword->type == T_IFNDEF) start_of_file = pfile->only_seen_white == 2; pfile->no_macro_expand++; token = get_directive_token(pfile); pfile->no_macro_expand--; ident = (char *)pfile->token_buffer + old_written; ident_length = CPP_WRITTEN(pfile) - old_written; CPP_SET_WRITTEN(pfile, old_written); /* Pop */ if (token == CPP_VSPACE || token == CPP_POP || token == CPP_EOF) { skip = (keyword->type == T_IFDEF); if (!CPP_TRADITIONAL(pfile)) cpp_pedwarn(pfile, "`#%s' with no argument", keyword->name); } else if (token == CPP_NAME) { HASHNODE *hp = cpp_lookup(ident, ident_length, -1); skip = (!hp) ^ (keyword->type == T_IFNDEF); if (start_of_file && !skip) { control_macro = (unsigned char *)xmalloc(ident_length + 1); memcpy(control_macro, ident, ident_length + 1); } } else { skip = (keyword->type == T_IFDEF); if (!CPP_TRADITIONAL(pfile)) cpp_error(pfile, "`#%s' with invalid argument", keyword->name); } if (!CPP_TRADITIONAL(pfile)) { int c; cpp_skip_hspace(pfile); c = PEEKC(); if (c != EOF && c != '\n') cpp_pedwarn(pfile, "garbage at end of `#%s' argument", keyword->name); } skip_rest_of_line(pfile); conditional_skip(pfile, skip, T_IF, control_macro); return 0; } /* Push TYPE on stack; then, if SKIP is nonzero, skip ahead. * If this is a #ifndef starting at the beginning of a file, * CONTROL_MACRO is the macro name tested by the #ifndef. * Otherwise, CONTROL_MACRO is 0. */ static void conditional_skip(cpp_reader *pfile, int skip, enum node_type type, unsigned char *control_macro) { IF_STACK_FRAME *temp; temp = (IF_STACK_FRAME *) xcalloc(1, sizeof(IF_STACK_FRAME)); temp->fname = CPP_BUFFER(pfile)->nominal_fname; temp->next = pfile->if_stack; temp->control_macro = control_macro; pfile->if_stack = temp; pfile->if_stack->type = type; if (skip != 0) { skip_if_group(pfile, 0); return; } else { ++pfile->if_stack->if_succeeded; output_line_command(pfile, 1, same_file); } } /* * skip to #endif, #else, or #elif. adjust line numbers, etc. * leaves input ptr at the sharp sign found. * If ANY is nonzero, return at next directive of any sort. */ static void skip_if_group(cpp_reader *pfile, int any) { int c; struct directive *kt; IF_STACK_FRAME *save_if_stack = pfile->if_stack; /* don't pop past here */ int ident_length; char *ident; struct parse_marker line_start_mark; parse_set_mark(&line_start_mark, pfile); if (CPP_OPTIONS(pfile)->output_conditionals) { static char failed[] = "#failed\n"; CPP_PUTS(pfile, failed, sizeof(failed) - 1); pfile->lineno++; output_line_command(pfile, 1, same_file); } beg_of_line: if (CPP_OPTIONS(pfile)->output_conditionals) { cpp_buffer *pbuf = CPP_BUFFER(pfile); unsigned char *start_line = pbuf->buf + line_start_mark.position; CPP_PUTS(pfile, start_line, pbuf->cur - start_line); } parse_move_mark(&line_start_mark, pfile); if (!CPP_TRADITIONAL(pfile)) cpp_skip_hspace(pfile); c = GETC(); if (c == '#') { int old_written = CPP_WRITTEN(pfile); cpp_skip_hspace(pfile); parse_name(pfile, GETC()); ident_length = CPP_WRITTEN(pfile) - old_written; ident = (char *)pfile->token_buffer + old_written; pfile->limit = (unsigned char *)ident; for (kt = directive_table; kt->length >= 0; kt++) { IF_STACK_FRAME *temp; if (ident_length == kt->length && strncmp(ident, kt->name, kt->length) == 0) { /* If we are asked to return on next directive, do so now. */ if (any) goto done; switch (kt->type) { case T_IF: case T_IFDEF: case T_IFNDEF: temp = (IF_STACK_FRAME *) xcalloc(1, sizeof(IF_STACK_FRAME)); temp->next = pfile->if_stack; pfile->if_stack = temp; temp->fname = CPP_BUFFER(pfile)->nominal_fname; temp->type = kt->type; break; case T_ELSE: case T_ENDIF: if (CPP_PEDANTIC(pfile) && pfile->if_stack != save_if_stack) validate_else(pfile, kt->type == T_ELSE ? "#else" : "#endif"); /* FALLTHROUGH */ case T_ELIF: if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack) { cpp_error(pfile, "`#%s' not within a conditional", kt->name); break; } else if (pfile->if_stack == save_if_stack) goto done; /* found what we came for */ if (kt->type != T_ENDIF) { if (pfile->if_stack->type == T_ELSE) cpp_error(pfile, "`#else' or `#elif' after `#else'"); pfile->if_stack->type = kt->type; break; } temp = pfile->if_stack; pfile->if_stack = temp->next; free(temp); break; default:; } break; } /* Don't let erroneous code go by. */ if (kt->length < 0 && !CPP_OPTIONS(pfile)->lang_asm && CPP_PEDANTIC(pfile)) cpp_pedwarn(pfile, "invalid preprocessor directive name"); } c = GETC(); } /* We're in the middle of a line. Skip the rest of it. */ for (;;) { switch (c) { long old; case EOF: goto done; case '/': /* possible comment */ c = skip_comment(pfile, NULL); if (c == EOF) goto done; break; case '\"': case '\'': FORWARD(-1); old = CPP_WRITTEN(pfile); cpp_get_token(pfile); CPP_SET_WRITTEN(pfile, old); break; case '\\': /* Char after backslash loses its special meaning. */ if (PEEKC() == '\n') FORWARD(1); break; case '\n': goto beg_of_line; break; } c = GETC(); } done: if (CPP_OPTIONS(pfile)->output_conditionals) { static char end_failed[] = "#endfailed\n"; CPP_PUTS(pfile, end_failed, sizeof(end_failed) - 1); pfile->lineno++; } pfile->only_seen_white = 1; parse_goto_mark(&line_start_mark, pfile); parse_clear_mark(&line_start_mark); } /* * handle a #else directive. Do this by just continuing processing * without changing if_stack ; this is so that the error message * for missing #endif's etc. will point to the original #if. It * is possible that something different would be better. */ static int do_else(cpp_reader *pfile, struct directive *keyword __UNUSED__, unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__) { cpp_buffer *ip = CPP_BUFFER(pfile); if (CPP_PEDANTIC(pfile)) validate_else(pfile, "#else"); skip_rest_of_line(pfile); if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack) { cpp_error(pfile, "`#else' not within a conditional"); return 0; } else { /* #ifndef can't have its special treatment for containing the whole file * if it has a #else clause. */ pfile->if_stack->control_macro = 0; if (pfile->if_stack->type != T_IF && pfile->if_stack->type != T_ELIF) { cpp_error(pfile, "`#else' after `#else'"); fprintf(stderr, " (matches line %d", pfile->if_stack->lineno); if (strcmp(pfile->if_stack->fname, ip->nominal_fname) != 0) fprintf(stderr, ", file %s", pfile->if_stack->fname); fprintf(stderr, ")\n"); } pfile->if_stack->type = T_ELSE; } if (pfile->if_stack->if_succeeded) skip_if_group(pfile, 0); else { ++pfile->if_stack->if_succeeded; /* continue processing input */ output_line_command(pfile, 1, same_file); } return 0; } /* * unstack after #endif command */ static int do_endif(cpp_reader *pfile, struct directive *keyword __UNUSED__, unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__) { if (CPP_PEDANTIC(pfile)) validate_else(pfile, "#endif"); skip_rest_of_line(pfile); if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack) { cpp_error(pfile, "unbalanced `#endif'"); } else { IF_STACK_FRAME *temp = pfile->if_stack; pfile->if_stack = temp->next; if (temp->control_macro) { /* This #endif matched a #ifndef at the start of the file. * See if it is at the end of the file. */ struct parse_marker start_mark; int c; parse_set_mark(&start_mark, pfile); for (;;) { cpp_skip_hspace(pfile); c = GETC(); if (c != '\n') break; } parse_goto_mark(&start_mark, pfile); parse_clear_mark(&start_mark); if (c == EOF) { /* If we get here, this #endif ends a #ifndef * that contains all of the file (aside from whitespace). * Arrange not to include the file again * if the macro that was tested is defined. * * Do not do this for the top-level file in a -include or any * file in a -imacros. */ { file_name_list *ifile = pfile->all_include_files; for (; ifile; ifile = ifile->next) { if (!strcmp(ifile->fname, CPP_BUFFER(pfile)->fname)) { ifile->control_macro = temp->control_macro; break; } } } } } free(temp); output_line_command(pfile, 1, same_file); } return 0; } /* When an #else or #endif is found while skipping failed conditional, * if -pedantic was specified, this is called to warn about text after * the command name. P points to the first char after the command name. */ static void validate_else(cpp_reader *pfile, const char *directive) { int c; cpp_skip_hspace(pfile); c = PEEKC(); if (c != EOF && c != '\n') cpp_pedwarn(pfile, "text following `%s' violates ANSI standard", directive); } /* Get the next token, and add it to the text in pfile->token_buffer. * Return the kind of token we got. */ enum cpp_token cpp_get_token(cpp_reader *pfile) { int c, c2, c3; long old_written = 0; long start_line = 0, start_column = 0; long newlines = 0; enum cpp_token token; struct cpp_options *opts = CPP_OPTIONS(pfile); CPP_BUFFER(pfile)->prev = CPP_BUFFER(pfile)->cur; get_next: c = GETC(); if (c == EOF) { handle_eof: if (CPP_BUFFER(pfile)->seen_eof) { if (cpp_pop_buffer(pfile) != CPP_NULL_BUFFER(pfile)) goto get_next; else return CPP_EOF; } else { cpp_buffer *next_buf = CPP_PREV_BUFFER(CPP_BUFFER(pfile)); CPP_BUFFER(pfile)->seen_eof = 1; if (CPP_BUFFER(pfile)->nominal_fname && next_buf) { /* We're about to return from an #include file. * Emit #line information now (as part of the CPP_POP) result. * But the #line refers to the file we will pop to. */ cpp_buffer *cur_buffer = CPP_BUFFER(pfile); CPP_BUFFER(pfile) = next_buf; pfile->input_stack_listing_current = 0; output_line_command(pfile, 0, leave_file); CPP_BUFFER(pfile) = cur_buffer; } return CPP_POP; } } else { switch (c) { struct parse_marker start_mark; case '/': if (PEEKC() == '=') goto op2; if (opts->put_out_comments) parse_set_mark(&start_mark, pfile); newlines = 0; cpp_buf_line_and_col(cpp_file_buffer(pfile), &start_line, &start_column); c = skip_comment(pfile, &newlines); if (opts->put_out_comments && (c == '/' || c == EOF)) parse_clear_mark(&start_mark); if (c == '/') goto randomchar; if (c == EOF) { cpp_error_with_line(pfile, start_line, start_column, "unterminated comment"); goto handle_eof; } c = '/'; /* Initial letter of comment. */ return_comment: /* Comments are equivalent to spaces. * For -traditional, a comment is equivalent to nothing. */ if (opts->put_out_comments) { cpp_buffer *pbuf = CPP_BUFFER(pfile); unsigned char *start = pbuf->buf + start_mark.position; int len = pbuf->cur - start; CPP_RESERVE(pfile, 1 + len); CPP_PUTC_Q(pfile, c); CPP_PUTS_Q(pfile, start, len); pfile->lineno += newlines; parse_clear_mark(&start_mark); return CPP_COMMENT; } else if (CPP_TRADITIONAL(pfile)) { return CPP_COMMENT; } else { CPP_RESERVE(pfile, 1); CPP_PUTC_Q(pfile, ' '); return CPP_HSPACE; } case '#': if (!pfile->only_seen_white) goto randomchar; if (handle_directive(pfile)) return CPP_DIRECTIVE; pfile->only_seen_white = 0; return CPP_OTHER; case '\"': case '\'': /* A single quoted string is treated like a double -- some * programs (e.g., troff) are perverse this way */ cpp_buf_line_and_col(cpp_file_buffer(pfile), &start_line, &start_column); old_written = CPP_WRITTEN(pfile); string: CPP_PUTC(pfile, c); while (1) { int cc = GETC(); if (cc == EOF) { if (CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile))) { /* try harder: this string crosses a macro expansion * boundary. This can happen naturally if -traditional. * Otherwise, only -D can make a macro with an unmatched * quote. */ cpp_buffer *next_buf = CPP_PREV_BUFFER(CPP_BUFFER(pfile)); (*CPP_BUFFER(pfile)->cleanup) (CPP_BUFFER(pfile), pfile); CPP_BUFFER(pfile) = next_buf; continue; } if (!CPP_TRADITIONAL(pfile)) { cpp_error_with_line(pfile, start_line, start_column, "unterminated string or character constant"); if (pfile->multiline_string_line != start_line && pfile->multiline_string_line != 0) cpp_error_with_line(pfile, pfile->multiline_string_line, -1, "possible real start of unterminated constant"); pfile->multiline_string_line = 0; } break; } CPP_PUTC(pfile, cc); switch (cc) { case '\n': /* Traditionally, end of line ends a string constant with * no error. So exit the loop and record the new line. */ if (CPP_TRADITIONAL(pfile)) goto while2end; if (c == '\'') { cpp_error_with_line(pfile, start_line, start_column, "unterminated character constant"); goto while2end; } if (CPP_PEDANTIC(pfile) && pfile->multiline_string_line == 0) { cpp_pedwarn_with_line(pfile, start_line, start_column, "string constant runs past end of line"); } if (pfile->multiline_string_line == 0) pfile->multiline_string_line = start_line; break; case '\\': cc = GETC(); if (cc == '\n') { /* Backslash newline is replaced by nothing at all. */ CPP_ADJUST_WRITTEN(pfile, -1); pfile->lineno++; } else { /* ANSI stupidly requires that in \\ the second \ * is *not* prevented from combining with a newline. */ NEWLINE_FIX1(cc); if (cc != EOF) CPP_PUTC(pfile, cc); } break; case '\"': case '\'': if (cc == c) goto while2end; break; } } while2end: pfile->lineno += count_newlines(pfile->token_buffer + old_written, CPP_PWRITTEN(pfile)); pfile->only_seen_white = 0; return c == '\'' ? CPP_CHAR : CPP_STRING; case '$': if (!opts->dollars_in_ident) goto randomchar; goto letter; case ':': if (opts->cplusplus && PEEKC() == ':') goto op2; goto randomchar; case '&': case '+': case '|': NEWLINE_FIX; c2 = PEEKC(); if (c2 == c || c2 == '=') goto op2; goto randomchar; case '*': case '!': case '%': case '=': case '^': NEWLINE_FIX; if (PEEKC() == '=') goto op2; goto randomchar; case '-': NEWLINE_FIX; c2 = PEEKC(); if (c2 == '-' && opts->chill) { /* Chill style comment */ if (opts->put_out_comments) parse_set_mark(&start_mark, pfile); FORWARD(1); /* Skip second '-'. */ for (;;) { c = GETC(); if (c == EOF) break; if (c == '\n') { /* Don't consider final '\n' to be part of comment. */ FORWARD(-1); break; } } c = '-'; goto return_comment; } if (c2 == '-' || c2 == '=' || c2 == '>') goto op2; goto randomchar; case '<': if (pfile->parsing_include_directive) { for (;;) { CPP_PUTC(pfile, c); if (c == '>') break; c = GETC(); NEWLINE_FIX1(c); if (c == '\n' || c == EOF) { cpp_error(pfile, "missing '>' in `#include '"); break; } } return CPP_STRING; } /* else fall through */ case '>': NEWLINE_FIX; c2 = PEEKC(); if (c2 == '=') goto op2; if (c2 != c) goto randomchar; FORWARD(1); CPP_RESERVE(pfile, 4); CPP_PUTC(pfile, c); CPP_PUTC(pfile, c2); NEWLINE_FIX; c3 = PEEKC(); if (c3 == '=') CPP_PUTC_Q(pfile, GETC()); CPP_NUL_TERMINATE_Q(pfile); pfile->only_seen_white = 0; return CPP_OTHER; case '@': if (CPP_BUFFER(pfile)->has_escapes) { c = GETC(); if (c == '-') { if (pfile->output_escapes) CPP_PUTS(pfile, "@-", 2); parse_name(pfile, GETC()); return CPP_NAME; } else if (is_space[c]) { CPP_RESERVE(pfile, 2); if (pfile->output_escapes) CPP_PUTC_Q(pfile, '@'); CPP_PUTC_Q(pfile, c); return CPP_HSPACE; } } if (pfile->output_escapes) { CPP_PUTS(pfile, "@@", 2); return CPP_OTHER; } goto randomchar; case '.': NEWLINE_FIX; c2 = PEEKC(); if (isdigit(c2)) { CPP_RESERVE(pfile, 2); CPP_PUTC_Q(pfile, '.'); c = GETC(); goto number; } /* FIXME - misses the case "..\\\n." */ if (c2 == '.' && PEEKN(1) == '.') { CPP_RESERVE(pfile, 4); CPP_PUTC_Q(pfile, '.'); CPP_PUTC_Q(pfile, '.'); CPP_PUTC_Q(pfile, '.'); FORWARD(2); CPP_NUL_TERMINATE_Q(pfile); pfile->only_seen_white = 0; return CPP_3DOTS; } goto randomchar; op2: token = CPP_OTHER; pfile->only_seen_white = 0; op2any: CPP_RESERVE(pfile, 3); CPP_PUTC_Q(pfile, c); CPP_PUTC_Q(pfile, GETC()); CPP_NUL_TERMINATE_Q(pfile); return token; case 'L': NEWLINE_FIX; c2 = PEEKC(); if ((c2 == '\'' || c2 == '\"') && !CPP_TRADITIONAL(pfile)) { CPP_PUTC(pfile, c); c = GETC(); goto string; } goto letter; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': number: c2 = '.'; for (;;) { CPP_RESERVE(pfile, 2); CPP_PUTC_Q(pfile, c); NEWLINE_FIX; c = PEEKC(); if (c == EOF) break; if (!is_idchar[c] && c != '.' && ((c2 != 'e' && c2 != 'E') || (c != '+' && c != '-'))) break; FORWARD(1); c2 = c; } CPP_NUL_TERMINATE_Q(pfile); pfile->only_seen_white = 0; return CPP_NUMBER; case 'b': case 'c': case 'd': case 'h': case 'o': case 'B': case 'C': case 'D': case 'H': case 'O': if (opts->chill && PEEKC() == '\'') { pfile->only_seen_white = 0; CPP_RESERVE(pfile, 2); CPP_PUTC_Q(pfile, c); CPP_PUTC_Q(pfile, '\''); FORWARD(1); for (;;) { c = GETC(); if (c == EOF) goto chill_number_eof; if (!is_idchar[c]) { if (c == '\\' && PEEKC() == '\n') { FORWARD(2); continue; } break; } CPP_PUTC(pfile, c); } if (c == '\'') { CPP_RESERVE(pfile, 2); CPP_PUTC_Q(pfile, c); CPP_NUL_TERMINATE_Q(pfile); return CPP_STRING; } else { FORWARD(-1); chill_number_eof: CPP_NUL_TERMINATE(pfile); return CPP_NUMBER; } } else goto letter; case '_': case 'a': case 'e': case 'f': case 'g': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'E': case 'F': case 'G': case 'I': case 'J': case 'K': case 'M': case 'N': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': letter: { HASHNODE *hp; char *ident; int before_name_written = CPP_WRITTEN(pfile); int ident_len; parse_name(pfile, c); pfile->only_seen_white = 0; if (pfile->no_macro_expand) return CPP_NAME; ident = (char *)pfile->token_buffer + before_name_written; ident_len = CPP_PWRITTEN(pfile) - (unsigned char *)ident; hp = cpp_lookup(ident, ident_len, -1); if (!hp) return CPP_NAME; if (hp->type == T_DISABLED) { if (pfile->output_escapes) { /* Return "@-IDENT", followed by '\0'. */ int i; CPP_RESERVE(pfile, 3); ident = (char *)pfile->token_buffer + before_name_written; CPP_ADJUST_WRITTEN(pfile, 2); for (i = ident_len; i >= 0; i--) ident[i + 2] = ident[i]; ident[0] = '@'; ident[1] = '-'; } return CPP_NAME; } /* If macro wants an arglist, verify that a '(' follows. * first skip all whitespace, copying it to the output * after the macro name. Then, if there is no '(', * decide this is not a macro call and leave things that way. */ if (hp->type == T_MACRO && hp->value.defn->nargs >= 0) { struct parse_marker macro_mark; int is_macro_call; while (CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile))) { cpp_buffer *next_buf; cpp_skip_hspace(pfile); if (PEEKC() != EOF) break; next_buf = CPP_PREV_BUFFER(CPP_BUFFER(pfile)); (*CPP_BUFFER(pfile)->cleanup) (CPP_BUFFER(pfile), pfile); CPP_BUFFER(pfile) = next_buf; } parse_set_mark(¯o_mark, pfile); for (;;) { cpp_skip_hspace(pfile); c = PEEKC(); is_macro_call = c == '('; if (c != '\n') break; FORWARD(1); } if (!is_macro_call) parse_goto_mark(¯o_mark, pfile); parse_clear_mark(¯o_mark); if (!is_macro_call) return CPP_NAME; } /* This is now known to be a macro call. */ /* it might not actually be a macro. */ if (hp->type != T_MACRO) { int xbuf_len; unsigned char *xbuf; CPP_SET_WRITTEN(pfile, before_name_written); special_symbol(hp, pfile); xbuf_len = CPP_WRITTEN(pfile) - before_name_written; xbuf = (unsigned char *)xmalloc(xbuf_len + 1); CPP_SET_WRITTEN(pfile, before_name_written); memcpy(xbuf, CPP_PWRITTEN(pfile), xbuf_len + 1); push_macro_expansion(pfile, xbuf, xbuf_len, hp); } else { /* Expand the macro, reading arguments as needed, * and push the expansion on the input stack. */ macroexpand(pfile, hp); CPP_SET_WRITTEN(pfile, before_name_written); } /* An extra "@ " is added to the end of a macro expansion * to prevent accidental token pasting. We prefer to avoid * unneeded extra spaces (for the sake of cpp-using tools like * imake). Here we remove the space if it is safe to do so. */ if (pfile->buffer->rlimit - pfile->buffer->cur >= 3 && pfile->buffer->rlimit[-2] == '@' && pfile->buffer->rlimit[-1] == ' ') { int c1 = pfile->buffer->rlimit[-3]; c2 = CPP_BUF_PEEK(CPP_PREV_BUFFER(CPP_BUFFER(pfile))); if (c2 == EOF || !unsafe_chars(c1, c2)) pfile->buffer->rlimit -= 2; } } goto get_next; case ' ': case '\t': case '\v': case '\r': for (;;) { CPP_PUTC(pfile, c); c = PEEKC(); if (c == EOF || !is_hor_space[c]) break; FORWARD(1); } return CPP_HSPACE; case '\\': c2 = PEEKC(); if (c2 != '\n') goto randomchar; token = CPP_HSPACE; goto op2any; case '\n': CPP_PUTC(pfile, c); if (pfile->only_seen_white == 0) pfile->only_seen_white = 1; pfile->lineno++; output_line_command(pfile, 1, same_file); return CPP_VSPACE; case '(': token = CPP_LPAREN; goto char1; case ')': token = CPP_RPAREN; goto char1; case '{': token = CPP_LBRACE; goto char1; case '}': token = CPP_RBRACE; goto char1; case ',': token = CPP_COMMA; goto char1; case ';': token = CPP_SEMICOLON; goto char1; randomchar: default: token = CPP_OTHER; char1: pfile->only_seen_white = 0; CPP_PUTC(pfile, c); return token; } } } #if 0 /* Unused */ /* Like cpp_get_token, but skip spaces and comments. */ enum cpp_token cpp_get_non_space_token(cpp_reader *pfile) { int old_written = CPP_WRITTEN(pfile); for (;;) { enum cpp_token token = cpp_get_token(pfile); if (token != CPP_COMMENT && token != CPP_POP && token != CPP_HSPACE && token != CPP_VSPACE) return token; CPP_SET_WRITTEN(pfile, old_written); } } #endif /* Parse an identifier starting with C. */ int parse_name(cpp_reader *pfile, int c) { for (;;) { if (!is_idchar[c]) { if (c == '\\' && PEEKC() == '\n') { FORWARD(2); continue; } FORWARD(-1); break; } CPP_RESERVE(pfile, 2); /* One more for final NUL. */ CPP_PUTC_Q(pfile, c); c = GETC(); if (c == EOF) break; } CPP_NUL_TERMINATE_Q(pfile); return 1; } /* Maintain and search list of included files, for #import. */ /* Hash a file name for import_hash_table. */ static int import_hash(char *f) { int val = 0; while (*f) val += *f++; return (val % IMPORT_HASH_SIZE); } /* Search for file FILENAME in import_hash_table. * Return -2 if found, either a matching name or a matching inode. * Otherwise, open the file and return a file descriptor if successful * or -1 if unsuccessful. */ static int lookup_import(cpp_reader *pfile, char *filename, file_name_list *searchptr) { struct import_file *i; int h; int hashval; struct stat sb; int fd; hashval = import_hash(filename); /* Attempt to find file in list of already included files */ i = pfile->import_hash_table[hashval]; while (i) { if (!strcmp(filename, i->name)) return -2; /* return found */ i = i->next; } /* Open it and try a match on inode/dev */ fd = open_include_file(pfile, filename, searchptr); if (fd < 0) return fd; fstat(fd, &sb); for (h = 0; h < IMPORT_HASH_SIZE; h++) { i = pfile->import_hash_table[h]; while (i) { /* Compare the inode and the device. * Supposedly on some systems the inode is not a scalar. */ if (!memcmp ((char *)&i->inode, (char *)&sb.st_ino, sizeof(sb.st_ino)) && i->dev == sb.st_dev) { close(fd); return -2; /* return found */ } i = i->next; } } return fd; /* Not found, return open file */ } /* Add the file FNAME, open on descriptor FD, to import_hash_table. */ static void add_import(cpp_reader *pfile, int fd, char *fname) { struct import_file *i; int hashval; struct stat sb; hashval = import_hash(fname); fstat(fd, &sb); i = (struct import_file *)xmalloc(sizeof(struct import_file)); i->name = (char *)xmalloc(strlen(fname) + 1); strcpy(i->name, fname); memcpy((char *)&i->inode, (char *)&sb.st_ino, sizeof(sb.st_ino)); i->dev = sb.st_dev; i->next = pfile->import_hash_table[hashval]; pfile->import_hash_table[hashval] = i; } /* The file_name_map structure holds a mapping of file names for a * particular directory. This mapping is read from the file named * FILE_NAME_MAP_FILE in that directory. Such a file can be used to * map filenames on a file system with severe filename restrictions, * such as DOS. The format of the file name map file is just a series * of lines with two tokens on each line. The first token is the name * to map, and the second token is the actual name to use. */ struct file_name_map { struct file_name_map *map_next; char *map_from; char *map_to; }; static int open_include_file(cpp_reader *pfile __UNUSED__, char *filename, file_name_list *searchptr __UNUSED__) { return open(filename, O_RDONLY, 0666); } /* Process the contents of include file FNAME, already open on descriptor F, * with output to OP. * SYSTEM_HEADER_P is 1 if this file resides in any one of the known * "system" include directories (as decided by the `is_system_include' * function above). * DIRPTR is the link in the dir path through which this file was found, * or 0 if the file name was absolute or via the current directory. * Return 1 on success, 0 on failure. * * The caller is responsible for the cpp_push_buffer. */ static int finclude(cpp_reader *pfile, int f, const char *fname, int system_header_p, file_name_list *dirptr) { int st_mode; long st_size; long i; int length; cpp_buffer *fp; /* For input stack frame */ if (file_size_and_mode(f, &st_mode, &st_size) < 0) { cpp_perror_with_name(pfile, fname); close(f); cpp_pop_buffer(pfile); return 0; } fp = CPP_BUFFER(pfile); fp->nominal_fname = fp->fname = fname; fp->dir = dirptr; fp->system_header_p = system_header_p; fp->lineno = 1; fp->colno = 1; fp->cleanup = file_cleanup; if (S_ISREG(st_mode)) { fp->buf = (unsigned char *)xmalloc(st_size + 2); fp->alimit = fp->buf + st_size + 2; fp->cur = fp->buf; /* Read the file contents, knowing that st_size is an upper bound * on the number of bytes we can read. */ length = safe_read(f, (char *)fp->buf, st_size); fp->rlimit = fp->buf + length; if (length < 0) goto nope; } else if (S_ISDIR(st_mode)) { cpp_error(pfile, "directory `%s' specified in #include", fname); close(f); return 0; } else { /* Cannot count its file size before reading. * First read the entire file into heap and * copy them into buffer on stack. */ int bsize = 2000; st_size = 0; fp->buf = (unsigned char *)xmalloc(bsize + 2); for (;;) { i = safe_read(f, (char *)(fp->buf + st_size), bsize - st_size); if (i < 0) goto nope; /* error! */ st_size += i; if (st_size != bsize) break; /* End of file */ bsize *= 2; fp->buf = (unsigned char *)xrealloc(fp->buf, bsize + 2); } length = st_size; } if ((length > 0 && fp->buf[length - 1] != '\n') /* Backslash-newline at end is not good enough. */ || (length > 1 && fp->buf[length - 2] == '\\')) { fp->buf[length++] = '\n'; } fp->buf[length] = '\0'; fp->rlimit = fp->buf + length; /* Close descriptor now, so nesting does not use lots of descriptors. */ close(f); /* Must do this before calling trigraph_pcp, so that the correct file name * will be printed in warning messages. */ pfile->input_stack_listing_current = 0; return 1; nope: cpp_perror_with_name(pfile, fname); close(f); free(fp->buf); return 1; } int push_parse_file(cpp_reader *pfile, const char *fname) { struct cpp_options *opts = CPP_OPTIONS(pfile); struct cpp_pending *pend; char *p; int f; cpp_buffer *fp; /* The code looks at the defaults through this pointer, rather than through * the constant structure above. This pointer gets changed if an environment * variable specifies other defaults. */ struct default_include *include_defaults = include_defaults_array; /* Add dirs from CPATH after dirs from -I. */ /* There seems to be confusion about what CPATH should do, * so for the moment it is not documented. */ /* Some people say that CPATH should replace the standard include dirs, * but that seems pointless: it comes before them, so it overrides them * anyway. */ p = (char *)getenv("CPATH"); if (p && !opts->no_standard_includes) path_include(pfile, p); /* Now that dollars_in_ident is known, initialize is_idchar. */ initialize_char_syntax(opts); /* Do partial setup of input buffer for the sake of generating * early #line directives (when -g is in effect). */ fp = cpp_push_buffer(pfile, NULL, 0); if (!opts->in_fname) opts->in_fname = ""; fp->nominal_fname = fp->fname = opts->in_fname; fp->lineno = 0; /* Install __LINE__, etc. Must follow initialize_char_syntax * and option processing. */ initialize_builtins(pfile); /* Do standard #defines and assertions * that identify system and machine type. */ if (!opts->inhibit_predefs) { p = (char *)alloca(strlen(predefs) + 1); strcpy(p, predefs); while (*p) { char *q; while (*p == ' ' || *p == '\t') p++; /* Handle -D options. */ if (p[0] == '-' && p[1] == 'D') { q = &p[2]; while (*p && *p != ' ' && *p != '\t') p++; if (*p != 0) *p++ = 0; if (opts->debug_output) output_line_command(pfile, 0, same_file); cpp_define(pfile, (unsigned char *)q); while (*p == ' ' || *p == '\t') p++; } else if (p[0] == '-' && p[1] == 'A') { /* Handle -A options (assertions). */ char *assertion; char *past_name; char *value; char *past_value; char *termination; int save_char; assertion = &p[2]; past_name = assertion; /* Locate end of name. */ while (*past_name && *past_name != ' ' && *past_name != '\t' && *past_name != '(') past_name++; /* Locate `(' at start of value. */ value = past_name; while (*value && (*value == ' ' || *value == '\t')) value++; if (*value++ != '(') abort(); while (*value && (*value == ' ' || *value == '\t')) value++; past_value = value; /* Locate end of value. */ while (*past_value && *past_value != ' ' && *past_value != '\t' && *past_value != ')') past_value++; termination = past_value; while (*termination && (*termination == ' ' || *termination == '\t')) termination++; if (*termination++ != ')') abort(); if (*termination && *termination != ' ' && *termination != '\t') abort(); /* Temporarily null-terminate the value. */ save_char = *termination; *termination = '\0'; /* Install the assertion. */ make_assertion(pfile, "-A", assertion); *termination = (char)save_char; p = termination; while (*p == ' ' || *p == '\t') p++; } else { abort(); } } } /* Now handle the command line options. */ /* Do -U's, -D's and -A's in the order they were seen. */ /* First reverse the list. */ opts->pending = nreverse_pending(opts->pending); for (pend = opts->pending; pend; pend = pend->next) { if (pend->cmd && pend->cmd[0] == '-') { switch (pend->cmd[1]) { case 'U': if (opts->debug_output) output_line_command(pfile, 0, same_file); do_undef(pfile, NULL, (unsigned char *)pend->arg, (unsigned char *)pend->arg + strlen(pend->arg)); break; case 'D': if (opts->debug_output) output_line_command(pfile, 0, same_file); cpp_define(pfile, (unsigned char *)pend->arg); break; case 'A': make_assertion(pfile, "-A", pend->arg); break; } } } opts->done_initializing = 1; { /* read the appropriate environment variable and if it exists * replace include_defaults with the listed path. */ char *epath = 0; switch ((opts->objc << 1) + opts->cplusplus) { case 0: epath = getenv("C_INCLUDE_PATH"); break; case 1: epath = getenv("CPLUS_INCLUDE_PATH"); break; case 2: epath = getenv("OBJC_INCLUDE_PATH"); break; case 3: epath = getenv("OBJCPLUS_INCLUDE_PATH"); break; } /* If the environment var for this language is set, * add to the default list of include directories. */ if (epath) { char *nstore = (char *)alloca(strlen(epath) + 2); int num_dirs; char *startp, *endp; for (num_dirs = 1, startp = epath; *startp; startp++) if (*startp == PATH_SEPARATOR) num_dirs++; include_defaults = (struct default_include *)xmalloc((num_dirs * sizeof(struct default_include)) + sizeof (include_defaults_array)); startp = endp = epath; num_dirs = 0; while (1) { /* Handle cases like c:/usr/lib:d:/gcc/lib */ if ((*endp == PATH_SEPARATOR) || *endp == 0) { strncpy(nstore, startp, endp - startp); if (endp == startp) strcpy(nstore, "."); else nstore[endp - startp] = '\0'; include_defaults[num_dirs].fname = savestring(nstore); include_defaults[num_dirs].cplusplus = opts->cplusplus; include_defaults[num_dirs].cxx_aware = 1; num_dirs++; if (*endp == '\0') break; endp = startp = endp + 1; } else endp++; } /* Put the usual defaults back in at the end. */ memcpy((char *)&include_defaults[num_dirs], (char *)include_defaults_array, sizeof(include_defaults_array)); } } append_include_chain(pfile, opts->before_system, opts->last_before_system); opts->first_system_include = opts->before_system; /* Unless -fnostdinc, * tack on the standard include file dirs to the specified list */ if (!opts->no_standard_includes) { struct default_include *di = include_defaults; char *specd_prefix = opts->include_prefix; char *default_prefix = savestring(GCC_INCLUDE_DIR); int default_len = 0; /* Remove the `include' from /usr/local/lib/gcc.../include. */ if (!strcmp(default_prefix + strlen(default_prefix) - 8, "/include")) { default_len = strlen(default_prefix) - 7; default_prefix[default_len] = 0; } /* Search "translated" versions of GNU directories. * These have /usr/local/lib/gcc... replaced by specd_prefix. */ if (specd_prefix && default_len != 0) for (di = include_defaults; di->fname; di++) { /* Some standard dirs are only for C++. */ if (!di->cplusplus || (opts->cplusplus && !opts->no_standard_cplusplus_includes)) { /* Does this dir start with the prefix? */ if (!strncmp(di->fname, default_prefix, default_len)) { /* Yes; change prefix and add to search list. */ file_name_list *new_ = (file_name_list *) xmalloc(sizeof(file_name_list)); int this_len = strlen(specd_prefix) + strlen(di->fname) - default_len; char *str = (char *)xmalloc(this_len + 1); strcpy(str, specd_prefix); strcat(str, di->fname + default_len); new_->fname = str; new_->control_macro = 0; new_->c_system_include_path = !di->cxx_aware; new_->got_name_map = 0; append_include_chain(pfile, new_, new_); if (!opts->first_system_include) opts->first_system_include = new_; } } } /* Search ordinary names for GNU include directories. */ for (di = include_defaults; di->fname; di++) { /* Some standard dirs are only for C++. */ if (!di->cplusplus || (opts->cplusplus && !opts->no_standard_cplusplus_includes)) { file_name_list *new_ = (file_name_list *) xmalloc(sizeof(file_name_list)); new_->control_macro = 0; new_->c_system_include_path = !di->cxx_aware; new_->fname = (char *)di->fname; new_->got_name_map = 0; append_include_chain(pfile, new_, new_); if (!opts->first_system_include) opts->first_system_include = new_; } } } /* Tack the after_include chain at the end of the include chain. */ append_include_chain(pfile, opts->after_include, opts->last_after_include); if (!opts->first_system_include) opts->first_system_include = opts->after_include; /* With -v, print the list of dirs to search. */ if (opts->verbose) { file_name_list *fl; fprintf(stderr, "#include \"...\" search starts here:\n"); for (fl = opts->include; fl; fl = fl->next) { if (fl == opts->first_bracket_include) fprintf(stderr, "#include <...> search starts here:\n"); fprintf(stderr, " %s\n", fl->fname); } fprintf(stderr, "End of search list.\n"); } /* Scan the -imacros files before the main input. * Much like #including them, but with no_output set * so that only their macro definitions matter. */ opts->no_output++; pfile->no_record_file++; for (pend = opts->pending; pend; pend = pend->next) { if (pend->cmd && strcmp(pend->cmd, "-imacros") == 0) { int fd = open(pend->arg, O_RDONLY, 0666); if (fd < 0) { cpp_perror_with_name(pfile, pend->arg); return FATAL_EXIT_CODE; } cpp_push_buffer(pfile, NULL, 0); finclude(pfile, fd, pend->arg, 0, NULL); cpp_scan_buffer(pfile); } } opts->no_output--; pfile->no_record_file--; /* Copy the entire contents of the main input file into * the stacked input buffer previously allocated for it. */ if (!fname || *fname == 0) { fname = ""; f = 0; } else if ((f = open(fname, O_RDONLY, 0666)) < 0) cpp_pfatal_with_name(pfile, fname); /* -MG doesn't select the form of output and must be specified with one of * -M or -MM. -MG doesn't make sense with -MD or -MMD since they don't * inhibit compilation. */ if (opts->print_deps_missing_files && (opts->print_deps == 0 || !opts->no_output)) cpp_fatal("-MG must be specified with one of -M or -MM"); /* Either of two environment variables can specify output of deps. * Its value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET", * where OUTPUT_FILE is the file to write deps info to * and DEPS_TARGET is the target to mention in the deps. */ if (opts->print_deps == 0 && (getenv("SUNPRO_DEPENDENCIES") != 0 || getenv("DEPENDENCIES_OUTPUT") != 0)) { char *spec = getenv("DEPENDENCIES_OUTPUT"); char *s; char *output_file; if (!spec) { spec = getenv("SUNPRO_DEPENDENCIES"); opts->print_deps = 2; } else opts->print_deps = 1; s = spec; /* Find the space before the DEPS_TARGET, if there is one. */ /* This should use index. (mrs) */ while (*s != 0 && *s != ' ') s++; if (*s != 0) { opts->deps_target = s + 1; output_file = (char *)xmalloc(s - spec + 1); memcpy(output_file, spec, s - spec); output_file[s - spec] = 0; } else { opts->deps_target = 0; output_file = spec; } opts->deps_file = output_file; opts->print_deps_append = 1; } /* For -M, print the expected object file name * as the target of this Make-rule. */ if (opts->print_deps) { pfile->deps_allocated_size = 200; pfile->deps_buffer = (char *)xmalloc(pfile->deps_allocated_size); pfile->deps_buffer[0] = 0; pfile->deps_size = 0; pfile->deps_column = 0; if (opts->deps_target) deps_output(pfile, opts->deps_target, ':'); else if (*opts->in_fname == 0) deps_output(pfile, "-", ':'); else { char *q; int len; /* Discard all directory prefixes from filename. */ if ((q = (char *)strrchr(opts->in_fname, '/')) #ifdef DIR_SEPARATOR && (q = strrchr(opts->in_fname, DIR_SEPARATOR)) #endif ) ++q; else q = (char *)opts->in_fname; /* Copy remainder to mungable area. */ p = (char *)alloca(strlen(q) + 8); strcpy(p, q); /* Output P, but remove known suffixes. */ len = strlen(p); q = p + len; if (len >= 2 && p[len - 2] == '.' && strchr("cCsSm", p[len - 1])) q = p + (len - 2); else if (len >= 3 && p[len - 3] == '.' && p[len - 2] == 'c' && p[len - 1] == 'c') q = p + (len - 3); else if (len >= 4 && p[len - 4] == '.' && p[len - 3] == 'c' && p[len - 2] == 'x' && p[len - 1] == 'x') q = p + (len - 4); else if (len >= 4 && p[len - 4] == '.' && p[len - 3] == 'c' && p[len - 2] == 'p' && p[len - 1] == 'p') q = p + (len - 4); /* Supply our own suffix. */ strcpy(q, ".o"); deps_output(pfile, p, ':'); deps_output(pfile, opts->in_fname, ' '); } } /* Scan the -include files before the main input. * We push these in reverse order, so that the first one is handled first. */ pfile->no_record_file++; opts->pending = nreverse_pending(opts->pending); for (pend = opts->pending; pend; pend = pend->next) { if (pend->cmd && strcmp(pend->cmd, "-include") == 0) { int fd = open(pend->arg, O_RDONLY, 0666); if (fd < 0) { cpp_perror_with_name(pfile, pend->arg); return FATAL_EXIT_CODE; } cpp_push_buffer(pfile, NULL, 0); finclude(pfile, fd, pend->arg, 0, NULL); } } pfile->no_record_file--; /* Free the pending list. */ for (pend = opts->pending; pend;) { struct cpp_pending *next = pend->next; free(pend); pend = next; } opts->pending = NULL; if (finclude(pfile, f, fname, 0, NULL)) output_line_command(pfile, 0, same_file); return SUCCESS_EXIT_CODE; } void init_parse_file(cpp_reader *pfile) { memset((char *)pfile, 0, sizeof(cpp_reader)); pfile->get_token = cpp_get_token; pfile->token_buffer_size = 200; pfile->token_buffer = (unsigned char *)xmalloc(pfile->token_buffer_size); CPP_SET_WRITTEN(pfile, 0); pfile->system_include_depth = 0; pfile->dont_repeat_files = 0; pfile->all_include_files = 0; pfile->max_include_len = 0; pfile->timebuf = NULL; pfile->only_seen_white = 1; pfile->buffer = CPP_NULL_BUFFER(pfile); } static struct cpp_pending * nreverse_pending(struct cpp_pending *list) { struct cpp_pending *prev = 0, *next, *pend; for (pend = list; pend; pend = next) { next = pend->next; pend->next = prev; prev = pend; } return prev; } static void push_pending(cpp_reader *pfile, const char *cmd, const char *arg) { struct cpp_pending *pend = (struct cpp_pending *)xmalloc(sizeof(struct cpp_pending)); pend->cmd = cmd; pend->arg = arg; pend->next = CPP_OPTIONS(pfile)->pending; CPP_OPTIONS(pfile)->pending = pend; } /* Handle command-line options in (argc, argv). * Can be called multiple times, to handle multiple sets of options. * Returns if an unrecognized option is seen. * Returns number of handled arguments. */ int cpp_handle_options(cpp_reader *pfile, int argc, char **argv) { int i; struct cpp_options *opts = CPP_OPTIONS(pfile); for (i = 0; i < argc; i++) { if (argv[i][0] != '-') { if (opts->out_fname) cpp_fatal("Usage: %s [switches] input output", argv[0]); else if (opts->in_fname) opts->out_fname = argv[i]; else opts->in_fname = argv[i]; } else { switch (argv[i][1]) { case 'i': if (!strcmp(argv[i], "-include") || !strcmp(argv[i], "-imacros")) { if (i + 1 == argc) cpp_fatal("Filename missing after `%s' option", argv[i]); else push_pending(pfile, argv[i], argv[i + 1]), i++; } if (!strcmp(argv[i], "-iprefix")) { if (i + 1 == argc) cpp_fatal("Filename missing after `-iprefix' option"); else opts->include_prefix = argv[++i]; } if (!strcmp(argv[i], "-ifoutput")) { opts->output_conditionals = 1; } if (!strcmp(argv[i], "-isystem")) { file_name_list *dirtmp; if (i + 1 == argc) cpp_fatal("Filename missing after `-isystem' option"); dirtmp = (file_name_list *) xmalloc(sizeof(file_name_list)); dirtmp->next = 0; dirtmp->control_macro = 0; dirtmp->c_system_include_path = 1; dirtmp->fname = (char *)xmalloc(strlen(argv[i + 1]) + 1); strcpy(dirtmp->fname, argv[++i]); dirtmp->got_name_map = 0; if (!opts->before_system) opts->before_system = dirtmp; else opts->last_before_system->next = dirtmp; opts->last_before_system = dirtmp; /* Tail follows the last one */ } /* Add directory to end of path for includes, * with the default prefix at the front of its name. */ if (!strcmp(argv[i], "-iwithprefix")) { file_name_list *dirtmp; char *prefix; if (opts->include_prefix) prefix = opts->include_prefix; else { prefix = savestring(GCC_INCLUDE_DIR); /* Remove the `include' from /usr/local/lib/gcc.../include. */ if (!strcmp(prefix + strlen(prefix) - 8, "/include")) prefix[strlen(prefix) - 7] = 0; } dirtmp = (file_name_list *) xmalloc(sizeof(file_name_list)); dirtmp->next = 0; /* New one goes on the end */ dirtmp->control_macro = 0; dirtmp->c_system_include_path = 0; if (i + 1 == argc) cpp_fatal ("Directory name missing after `-iwithprefix' option"); dirtmp->fname = (char *)xmalloc(strlen(argv[i + 1]) + strlen(prefix) + 1); strcpy(dirtmp->fname, prefix); strcat(dirtmp->fname, argv[++i]); dirtmp->got_name_map = 0; if (!opts->after_include) opts->after_include = dirtmp; else opts->last_after_include->next = dirtmp; opts->last_after_include = dirtmp; /* Tail follows the last one */ } /* Add directory to main path for includes, * with the default prefix at the front of its name. */ if (!strcmp(argv[i], "-iwithprefixbefore")) { file_name_list *dirtmp; char *prefix; if (opts->include_prefix) prefix = opts->include_prefix; else { prefix = savestring(GCC_INCLUDE_DIR); /* Remove the `include' from /usr/local/lib/gcc.../include. */ if (!strcmp(prefix + strlen(prefix) - 8, "/include")) prefix[strlen(prefix) - 7] = 0; } dirtmp = (file_name_list *) xmalloc(sizeof(file_name_list)); dirtmp->next = 0; /* New one goes on the end */ dirtmp->control_macro = 0; dirtmp->c_system_include_path = 0; if (i + 1 == argc) cpp_fatal ("Directory name missing after `-iwithprefixbefore' option"); dirtmp->fname = (char *)xmalloc(strlen(argv[i + 1]) + strlen(prefix) + 1); strcpy(dirtmp->fname, prefix); strcat(dirtmp->fname, argv[++i]); dirtmp->got_name_map = 0; append_include_chain(pfile, dirtmp, dirtmp); } /* Add directory to end of path for includes. */ if (!strcmp(argv[i], "-idirafter")) { file_name_list *dirtmp; dirtmp = (file_name_list *) xmalloc(sizeof(file_name_list)); dirtmp->next = 0; /* New one goes on the end */ dirtmp->control_macro = 0; dirtmp->c_system_include_path = 0; if (i + 1 == argc) cpp_fatal ("Directory name missing after `-idirafter' option"); else dirtmp->fname = argv[++i]; dirtmp->got_name_map = 0; if (!opts->after_include) opts->after_include = dirtmp; else opts->last_after_include->next = dirtmp; opts->last_after_include = dirtmp; /* Tail follows the last one */ } break; case 'o': if (opts->out_fname) cpp_fatal("Output filename specified twice"); if (i + 1 == argc) cpp_fatal("Filename missing after -o option"); opts->out_fname = argv[++i]; if (!strcmp(opts->out_fname, "-")) opts->out_fname = ""; break; case 'p': if (!strcmp(argv[i], "-pedantic")) CPP_PEDANTIC(pfile) = 1; else if (!strcmp(argv[i], "-pedantic-errors")) { CPP_PEDANTIC(pfile) = 1; opts->pedantic_errors = 1; } break; case 't': if (!strcmp(argv[i], "-traditional")) { opts->traditional = 1; if (opts->dollars_in_ident > 0) opts->dollars_in_ident = 1; } else if (!strcmp(argv[i], "-trigraphs")) { if (!opts->chill) opts->no_trigraphs = 0; } break; case 'l': if (!strcmp(argv[i], "-lang-c")) opts->cplusplus = 0, opts->cplusplus_comments = 0, opts->objc = 0; if (!strcmp(argv[i], "-lang-c++")) opts->cplusplus = 1, opts->cplusplus_comments = 1, opts->objc = 0; if (!strcmp(argv[i], "-lang-c-c++-comments")) opts->cplusplus = 0, opts->cplusplus_comments = 1, opts->objc = 0; if (!strcmp(argv[i], "-lang-objc")) opts->objc = 1, opts->cplusplus = 0, opts->cplusplus_comments = 1; if (!strcmp(argv[i], "-lang-objc++")) opts->objc = 1, opts->cplusplus = 1, opts->cplusplus_comments = 1; if (!strcmp(argv[i], "-lang-asm")) opts->lang_asm = 1; if (!strcmp(argv[i], "-lint")) opts->for_lint = 1; if (!strcmp(argv[i], "-lang-chill")) opts->objc = 0, opts->cplusplus = 0, opts->chill = 1, opts->traditional = 1, opts->no_trigraphs = 1; break; case '+': opts->cplusplus = 1, opts->cplusplus_comments = 1; break; case 'w': opts->inhibit_warnings = 1; break; case 'W': if (!strcmp(argv[i], "-Wtrigraphs")) opts->warn_trigraphs = 1; else if (!strcmp(argv[i], "-Wno-trigraphs")) opts->warn_trigraphs = 0; else if (!strcmp(argv[i], "-Wcomment")) opts->warn_comments = 1; else if (!strcmp(argv[i], "-Wno-comment")) opts->warn_comments = 0; else if (!strcmp(argv[i], "-Wcomments")) opts->warn_comments = 1; else if (!strcmp(argv[i], "-Wno-comments")) opts->warn_comments = 0; else if (!strcmp(argv[i], "-Wtraditional")) opts->warn_stringify = 1; else if (!strcmp(argv[i], "-Wno-traditional")) opts->warn_stringify = 0; else if (!strcmp(argv[i], "-Wimport")) opts->warn_import = 1; else if (!strcmp(argv[i], "-Wno-import")) opts->warn_import = 0; else if (!strcmp(argv[i], "-Werror")) opts->warnings_are_errors = 1; else if (!strcmp(argv[i], "-Wno-error")) opts->warnings_are_errors = 0; else if (!strcmp(argv[i], "-Wall")) { opts->warn_trigraphs = 1; opts->warn_comments = 1; } break; case 'M': /* The style of the choices here is a bit mixed. * The chosen scheme is a hybrid of keeping all options in one string * and specifying each option in a separate argument: * -M|-MM|-MD file|-MMD file [-MG]. An alternative is: * -M|-MM|-MD file|-MMD file|-MG|-MMG; or more concisely: * -M[M][G][D file]. This is awkward to handle in specs, and is not * as extensible. */ /* ??? -MG must be specified in addition to one of -M or -MM. * This can be relaxed in the future without breaking anything. * The converse isn't true. */ /* -MG isn't valid with -MD or -MMD. This is checked for later. */ if (!strcmp(argv[i], "-MG")) { opts->print_deps_missing_files = 1; break; } if (!strcmp(argv[i], "-M")) opts->print_deps = 2; else if (!strcmp(argv[i], "-MM")) opts->print_deps = 1; else if (!strcmp(argv[i], "-MD")) opts->print_deps = 2; else if (!strcmp(argv[i], "-MMD")) opts->print_deps = 1; /* For -MD and -MMD options, write deps on file named by next arg. */ if (!strcmp(argv[i], "-MD") || !strcmp(argv[i], "-MMD")) { if (i + 1 == argc) cpp_fatal("Filename missing after %s option", argv[i]); opts->deps_file = argv[++i]; } else { /* For -M and -MM, write deps on standard output * and suppress the usual output. */ opts->no_output = 1; } break; case 'd': { char *p = argv[i] + 2; char c; while ((c = *p++) != 0) { /* Arg to -d specifies what parts of macros to dump */ switch (c) { case 'M': opts->dump_macros = dump_only; opts->no_output = 1; break; case 'N': opts->dump_macros = dump_names; break; case 'D': opts->dump_macros = dump_definitions; break; } } } break; case 'g': if (argv[i][2] == '3') opts->debug_output = 1; break; case 'v': fprintf(stderr, "GNU CPP version %s", version_string); #ifdef TARGET_VERSION TARGET_VERSION; #endif fprintf(stderr, "\n"); opts->verbose = 1; break; case 'H': opts->print_include_names = 1; break; case 'D': if (argv[i][2] != 0) push_pending(pfile, "-D", argv[i] + 2); else if (i + 1 == argc) cpp_fatal("Macro name missing after -D option"); else i++, push_pending(pfile, "-D", argv[i]); break; case 'A': { char *p = NULL; if (argv[i][2] != 0) p = argv[i] + 2; else if (i + 1 == argc) cpp_fatal("Assertion missing after -A option"); else p = argv[++i]; if (!strcmp(p, "-")) { struct cpp_pending **ptr; /* -A- eliminates all predefined macros and assertions. * Let's include also any that were specified earlier * on the command line. That way we can get rid of any * that were passed automatically in from GCC. */ opts->inhibit_predefs = 1; for (ptr = &opts->pending; *ptr;) { struct cpp_pending *pend = *ptr; if (pend->cmd && pend->cmd[0] == '-' && (pend->cmd[1] == 'D' || pend->cmd[1] == 'A')) { *ptr = pend->next; free(pend); } else ptr = &pend->next; } } else { push_pending(pfile, "-A", p); } } break; case 'U': /* JF #undef something */ if (argv[i][2] != 0) push_pending(pfile, "-U", argv[i] + 2); else if (i + 1 == argc) cpp_fatal("Macro name missing after -U option"); else push_pending(pfile, "-U", argv[i + 1]), i++; break; case 'C': opts->put_out_comments = 1; break; case 'E': /* -E comes from cc -E; ignore it. */ break; case 'P': opts->no_line_commands = 1; break; case '$': /* Don't include $ in identifiers. */ opts->dollars_in_ident = 0; break; case 'I': /* Add directory to path for includes. */ { file_name_list *dirtmp; if (!CPP_OPTIONS(pfile)->ignore_srcdir && !strcmp(argv[i] + 2, "-")) { CPP_OPTIONS(pfile)->ignore_srcdir = 1; /* Don't use any preceding -I directories for #include <...>. */ CPP_OPTIONS(pfile)->first_bracket_include = 0; } else { dirtmp = (file_name_list *) xmalloc(sizeof(file_name_list)); dirtmp->next = 0; /* New one goes on the end */ dirtmp->control_macro = 0; dirtmp->c_system_include_path = 0; if (argv[i][2] != 0) dirtmp->fname = argv[i] + 2; else if (i + 1 == argc) cpp_fatal("Directory name missing after -I option"); else dirtmp->fname = argv[++i]; dirtmp->got_name_map = 0; append_include_chain(pfile, dirtmp, dirtmp); } } break; case 'n': if (!strcmp(argv[i], "-nostdinc")) /* -nostdinc causes no default include directories. * You must specify all include-file directories with -I. */ opts->no_standard_includes = 1; else if (!strcmp(argv[i], "-nostdinc++")) /* -nostdinc++ causes no default C++-specific include directories. */ opts->no_standard_cplusplus_includes = 1; break; case 'u': /* Sun compiler passes undocumented switch "-undef". * Let's assume it means to inhibit the predefined symbols. */ opts->inhibit_predefs = 1; break; case '\0': /* JF handle '-' as file name meaning stdin or stdout */ if (!opts->in_fname) { opts->in_fname = ""; break; } else if (!opts->out_fname) { opts->out_fname = ""; break; } /* else fall through into error */ /* FALLTHROUGH */ default: return i; } } } return i; } void cpp_finish(cpp_reader *pfile) { struct cpp_options *opts = CPP_OPTIONS(pfile); if (opts->print_deps) { /* Stream on which to print the dependency information. */ FILE *deps_stream; /* Don't actually write the deps file if compilation has failed. */ if (pfile->errors == 0) { const char *deps_mode = opts->print_deps_append ? "a" : "w"; if (!opts->deps_file) deps_stream = stdout; else if (!(deps_stream = fopen(opts->deps_file, deps_mode))) cpp_pfatal_with_name(pfile, opts->deps_file); fputs(pfile->deps_buffer, deps_stream); putc('\n', deps_stream); if (opts->deps_file) { if (ferror(deps_stream) || fclose(deps_stream) != 0) cpp_fatal("I/O error on output"); } } } } static int do_assert(cpp_reader *pfile, struct directive *keyword __UNUSED__, unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__) { long symstart; /* remember where symbol name starts */ int c; int sym_length; /* and how long it is */ struct arglist *tokens = NULL; if (CPP_PEDANTIC(pfile) && CPP_OPTIONS(pfile)->done_initializing && !CPP_BUFFER(pfile)->system_header_p) cpp_pedwarn(pfile, "ANSI C does not allow `#assert'"); cpp_skip_hspace(pfile); symstart = CPP_WRITTEN(pfile); /* remember where it starts */ parse_name(pfile, GETC()); sym_length = check_macro_name(pfile, pfile->token_buffer + symstart, "assertion"); cpp_skip_hspace(pfile); if (PEEKC() != '(') { cpp_error(pfile, "missing token-sequence in `#assert'"); goto error; } { int error_flag = 0; tokens = read_token_list(pfile, &error_flag); if (error_flag) goto error; if (!tokens) { cpp_error(pfile, "empty token-sequence in `#assert'"); goto error; } cpp_skip_hspace(pfile); c = PEEKC(); if (c != EOF && c != '\n') cpp_pedwarn(pfile, "junk at end of `#assert'"); skip_rest_of_line(pfile); } /* If this name isn't already an assertion name, make it one. * Error if it was already in use in some other way. */ { ASSERTION_HASHNODE *hp; const char *symname = (char *)pfile->token_buffer + symstart; int hashcode = hashf(symname, sym_length, ASSERTION_HASHSIZE); struct tokenlist_list *value = (struct tokenlist_list *)xmalloc(sizeof(struct tokenlist_list)); hp = assertion_lookup(pfile, symname, sym_length, hashcode); if (!hp) { if (sym_length == 7 && !strncmp(symname, "defined", sym_length)) cpp_error(pfile, "`defined' redefined as assertion"); hp = assertion_install(pfile, symname, sym_length, hashcode); } /* Add the spec'd token-sequence to the list of such. */ value->tokens = tokens; value->next = hp->value; hp->value = value; } CPP_SET_WRITTEN(pfile, symstart); /* Pop */ return 0; error: CPP_SET_WRITTEN(pfile, symstart); /* Pop */ skip_rest_of_line(pfile); return 1; } static int do_unassert(cpp_reader *pfile, struct directive *keyword __UNUSED__, unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__) { long symstart; /* remember where symbol name starts */ int sym_length; /* and how long it is */ int c; struct arglist *tokens = NULL; int tokens_specified = 0; if (CPP_PEDANTIC(pfile) && CPP_OPTIONS(pfile)->done_initializing && !CPP_BUFFER(pfile)->system_header_p) cpp_pedwarn(pfile, "ANSI C does not allow `#unassert'"); cpp_skip_hspace(pfile); symstart = CPP_WRITTEN(pfile); /* remember where it starts */ parse_name(pfile, GETC()); sym_length = check_macro_name(pfile, pfile->token_buffer + symstart, "assertion"); cpp_skip_hspace(pfile); if (PEEKC() == '(') { int error_flag = 0; tokens = read_token_list(pfile, &error_flag); if (error_flag) goto error; if (!tokens) { cpp_error(pfile, "empty token list in `#unassert'"); goto error; } tokens_specified = 1; } cpp_skip_hspace(pfile); c = PEEKC(); if (c != EOF && c != '\n') cpp_error(pfile, "junk at end of `#unassert'"); skip_rest_of_line(pfile); { ASSERTION_HASHNODE *hp; const char *symname = (char *)pfile->token_buffer + symstart; int hashcode = hashf(symname, sym_length, ASSERTION_HASHSIZE); struct tokenlist_list *tail, *prev; hp = assertion_lookup(pfile, symname, sym_length, hashcode); if (!hp) return 1; /* If no token list was specified, then eliminate this assertion * entirely. */ if (!tokens_specified) delete_assertion(hp); else { /* If a list of tokens was given, then delete any matching list. */ tail = hp->value; prev = 0; while (tail) { struct tokenlist_list *next = tail->next; if (compare_token_lists(tail->tokens, tokens)) { if (prev) prev->next = next; else hp->value = tail->next; free_token_list(tail->tokens); free(tail); } else { prev = tail; } tail = next; } } } CPP_SET_WRITTEN(pfile, symstart); /* Pop */ return 0; error: CPP_SET_WRITTEN(pfile, symstart); /* Pop */ skip_rest_of_line(pfile); return 1; } /* Test whether there is an assertion named NAME * and optionally whether it has an asserted token list TOKENS. * NAME is not null terminated; its length is SYM_LENGTH. * If TOKENS_SPECIFIED is 0, then don't check for any token list. */ static int check_assertion(cpp_reader *pfile, const char *name, int sym_length, int tokens_specified, struct arglist *tokens) { ASSERTION_HASHNODE *hp; int hashcode = hashf(name, sym_length, ASSERTION_HASHSIZE); if (CPP_PEDANTIC(pfile) && !CPP_BUFFER(pfile)->system_header_p) cpp_pedwarn(pfile, "ANSI C does not allow testing assertions"); hp = assertion_lookup(pfile, name, sym_length, hashcode); if (!hp) /* It is not an assertion; just return false. */ return 0; /* If no token list was specified, then value is 1. */ if (!tokens_specified) return 1; { struct tokenlist_list *tail; tail = hp->value; /* If a list of tokens was given, * then succeed if the assertion records a matching list. */ while (tail) { if (compare_token_lists(tail->tokens, tokens)) return 1; tail = tail->next; } /* Fail if the assertion has no matching list. */ return 0; } } /* Compare two lists of tokens for equality including order of tokens. */ static int compare_token_lists(struct arglist *l1, struct arglist *l2) { while (l1 && l2) { if (l1->length != l2->length) return 0; if (strncmp(l1->name, l2->name, l1->length)) return 0; l1 = l1->next; l2 = l2->next; } /* Succeed if both lists end at the same time. */ return l1 == l2; } struct arglist * reverse_token_list(struct arglist *tokens) { struct arglist *prev = 0, *cur, *next; for (cur = tokens; cur; cur = next) { next = cur->next; cur->next = prev; prev = cur; } return prev; } /* Read a space-separated list of tokens ending in a close parenthesis. * Return a list of strings, in the order they were written. * (In case of error, return 0 and store -1 in *ERROR_FLAG.) */ static struct arglist * read_token_list(cpp_reader *pfile, int *error_flag) { struct arglist *token_ptrs = 0; int depth = 1; int length; *error_flag = 0; FORWARD(1); /* Skip '(' */ /* Loop over the assertion value tokens. */ while (depth > 0) { struct arglist *temp; long name_written = CPP_WRITTEN(pfile); int c; cpp_skip_hspace(pfile); c = GETC(); /* Find the end of the token. */ if (c == '(') { CPP_PUTC(pfile, c); depth++; } else if (c == ')') { depth--; if (depth == 0) break; CPP_PUTC(pfile, c); } else if (c == '"' || c == '\'') { FORWARD(-1); cpp_get_token(pfile); } else if (c == '\n') break; else { while (c != EOF && !is_space[c] && c != '(' && c != ')' && c != '"' && c != '\'') { CPP_PUTC(pfile, c); c = GETC(); } if (c != EOF) FORWARD(-1); } length = CPP_WRITTEN(pfile) - name_written; temp = (struct arglist *)xmalloc(sizeof(struct arglist) + length + 1); temp->name = (char *)(temp + 1); memcpy(temp->name, (char *)(pfile->token_buffer + name_written), length); temp->name[length] = 0; temp->next = token_ptrs; token_ptrs = temp; temp->length = length; CPP_ADJUST_WRITTEN(pfile, -length); /* pop */ if (c == EOF || c == '\n') { /* FIXME */ cpp_error(pfile, "unterminated token sequence following `#' operator"); return 0; } } /* We accumulated the names in reverse order. * Now reverse them to get the proper order. */ return reverse_token_list(token_ptrs); } static void free_token_list(struct arglist *tokens) { while (tokens) { struct arglist *next = tokens->next; free(tokens->name); free(tokens); tokens = next; } } /* Get the file-mode and data size of the file open on FD * and store them in *MODE_POINTER and *SIZE_POINTER. */ static int file_size_and_mode(int fd, int *mode_pointer, long int *size_pointer) { struct stat sbuf; if (fstat(fd, &sbuf) < 0) return (-1); if (mode_pointer) *mode_pointer = sbuf.st_mode; if (size_pointer) *size_pointer = sbuf.st_size; return 0; } /* Read LEN bytes at PTR from descriptor DESC, for file FILENAME, * retrying if necessary. Return a negative value if an error occurs, * otherwise return the actual number of bytes read, * which must be LEN unless end-of-file was reached. */ static int safe_read(int desc, char *ptr, int len) { int left = len; while (left > 0) { int nchars = read(desc, ptr, left); if (nchars < 0) { #ifdef EINTR if (errno == EINTR) continue; #endif return nchars; } if (nchars == 0) break; ptr += nchars; left -= nchars; } return len - left; } static char * savestring(const char *input) { unsigned size = strlen(input); char *output = (char *)xmalloc(size + 1); strcpy(output, input); return output; } /* Initialize PMARK to remember the current position of PFILE. */ void parse_set_mark(struct parse_marker *pmark, cpp_reader *pfile) { cpp_buffer *pbuf = CPP_BUFFER(pfile); pmark->next = pbuf->marks; pbuf->marks = pmark; pmark->buf = pbuf; pmark->position = pbuf->cur - pbuf->buf; } /* Cleanup PMARK - we no longer need it. */ static void parse_clear_mark(struct parse_marker *pmark) { struct parse_marker **pp = &pmark->buf->marks; for (;; pp = &(*pp)->next) { if (!*pp) cpp_fatal("internal error", "in parse_set_mark"); if (*pp == pmark) break; } *pp = pmark->next; } /* Backup the current position of PFILE to that saved in PMARK. */ static void parse_goto_mark(struct parse_marker *pmark, cpp_reader *pfile) { cpp_buffer *pbuf = CPP_BUFFER(pfile); if (pbuf != pmark->buf) cpp_fatal("internal error %s", "parse_goto_mark"); pbuf->cur = pbuf->buf + pmark->position; } /* Reset PMARK to point to the current position of PFILE. (Same * as parse_clear_mark (PMARK), parse_set_mark (PMARK, PFILE) but faster. */ static void parse_move_mark(struct parse_marker *pmark, cpp_reader *pfile) { cpp_buffer *pbuf = CPP_BUFFER(pfile); if (pbuf != pmark->buf) cpp_fatal("internal error %s", "parse_move_mark"); pmark->position = pbuf->cur - pbuf->buf; } int cpp_read_check_assertion(cpp_reader *pfile) { int name_start = CPP_WRITTEN(pfile); int name_length, name_written; int result; FORWARD(1); /* Skip '#' */ cpp_skip_hspace(pfile); parse_name(pfile, GETC()); name_written = CPP_WRITTEN(pfile); name_length = name_written - name_start; cpp_skip_hspace(pfile); if (CPP_BUF_PEEK(CPP_BUFFER(pfile)) == '(') { int error_flag; struct arglist *token_ptrs = read_token_list(pfile, &error_flag); result = check_assertion(pfile, (char *)pfile->token_buffer + name_start, name_length, 1, token_ptrs); } else result = check_assertion(pfile, (char *)pfile->token_buffer + name_start, name_length, 0, NULL); CPP_ADJUST_WRITTEN(pfile, -name_length); /* pop */ return result; } static void cpp_print_file_and_line(cpp_reader *pfile) { cpp_buffer *ip = cpp_file_buffer(pfile); if (ip) { long line, col; cpp_buf_line_and_col(ip, &line, &col); cpp_file_line_for_message(pfile, ip->nominal_fname, line, pfile->show_column ? col : -1); } } static void cpp_error_v(cpp_reader *pfile, const char *msg, va_list args) { cpp_print_containing_files(pfile); cpp_print_file_and_line(pfile); cpp_message_v(pfile, 1, msg, args); } void cpp_error(cpp_reader *pfile, const char *msg, ...) { va_list args; va_start(args, msg); cpp_error_v(pfile, msg, args); va_end(args); } /* Print error message but don't count it. */ static void cpp_warning_v(cpp_reader *pfile, const char *msg, va_list args) { if (CPP_OPTIONS(pfile)->inhibit_warnings) return; if (CPP_OPTIONS(pfile)->warnings_are_errors) pfile->errors++; cpp_print_containing_files(pfile); cpp_print_file_and_line(pfile); cpp_message_v(pfile, 0, msg, args); } void cpp_warning(cpp_reader *pfile, const char *msg, ...) { va_list args; va_start(args, msg); cpp_warning_v(pfile, msg, args); va_end(args); } /* Print an error message and maybe count it. */ void cpp_pedwarn(cpp_reader *pfile, const char *msg, ...) { va_list args; va_start(args, msg); if (CPP_OPTIONS(pfile)->pedantic_errors) cpp_error_v(pfile, msg, args); else cpp_warning_v(pfile, msg, args); va_end(args); } static void cpp_error_with_line(cpp_reader *pfile, int line, int column, const char *msg) { cpp_buffer *ip = cpp_file_buffer(pfile); cpp_print_containing_files(pfile); if (ip) cpp_file_line_for_message(pfile, ip->nominal_fname, line, column); cpp_message(pfile, 1, msg, NULL, NULL, NULL); } static void cpp_warning_with_line(cpp_reader *pfile, int line, int column, const char *msg) { cpp_buffer *ip; if (CPP_OPTIONS(pfile)->inhibit_warnings) return; if (CPP_OPTIONS(pfile)->warnings_are_errors) pfile->errors++; cpp_print_containing_files(pfile); ip = cpp_file_buffer(pfile); if (ip) cpp_file_line_for_message(pfile, ip->nominal_fname, line, column); cpp_message(pfile, 0, msg, NULL, NULL, NULL); } static void cpp_pedwarn_with_line(cpp_reader *pfile, int line, int column, const char *msg) { if (CPP_OPTIONS(pfile)->pedantic_errors) cpp_error_with_line(pfile, column, line, msg); else cpp_warning_with_line(pfile, line, column, msg); } /* Report a warning (or an error if pedantic_errors) * giving specified file name and line number, not current. */ void cpp_pedwarn_with_file_and_line(cpp_reader *pfile, const char *file, int line, const char *msg, const char *arg1, const char *arg2, const char *arg3) { if (!CPP_OPTIONS(pfile)->pedantic_errors && CPP_OPTIONS(pfile)->inhibit_warnings) return; if (file) cpp_file_line_for_message(pfile, file, line, -1); cpp_message(pfile, CPP_OPTIONS(pfile)->pedantic_errors, msg, arg1, arg2, arg3); } /* This defines "errno" properly for VMS, and gives us EACCES. */ #include #ifndef errno extern int errno; #endif #ifndef HAVE_STRERROR extern int sys_nerr; #if defined(bsd4_4) extern const char *const sys_errlist[]; #else extern char *sys_errlist[]; #endif #endif /* HAVE_STRERROR */ /* * my_strerror - return the descriptive text associated with an `errno' code. */ static const char * my_strerror(int errnum) { const char *result; #ifndef HAVE_STRERROR result = ((errnum < sys_nerr) ? sys_errlist[errnum] : 0); #else result = strerror(errnum); #endif if (!result) result = "undocumented I/O error"; return result; } /* Error including a message from `errno'. */ static void cpp_error_from_errno(cpp_reader *pfile, const char *name) { cpp_buffer *ip = cpp_file_buffer(pfile); cpp_print_containing_files(pfile); if (ip) cpp_file_line_for_message(pfile, ip->nominal_fname, ip->lineno, -1); cpp_message(pfile, 1, "%s: %s", name, my_strerror(errno), NULL); } void cpp_perror_with_name(cpp_reader *pfile, const char *name) { cpp_message(pfile, 1, "%s: %s: %s", progname, name, my_strerror(errno)); } /* TODO: * No pre-compiled header file support. * * Possibly different enum token codes for each C/C++ token. * * Should clean up remaining directives to that do_XXX functions * only take two arguments and all have command_reads_line. * * Find and cleanup remaining uses of static variables, * * Support for trigraphs. * * Support -dM flag (dump_all_macros). * * Support for_lint flag. */