| /* CPP Library. |
| Copyright (C) 1986, 87, 89, 92-98, 1999 Free Software Foundation, Inc. |
| Contributed by Per Bothner, 1994-95. |
| Based on CCCP program by Paul Rubin, June 1986 |
| Adapted to ANSI C, Richard Stallman, Jan 1987 |
| |
| 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
| |
| #include "config.h" |
| #include "system.h" |
| |
| #include "cpplib.h" |
| #include "cpphash.h" |
| #include "intl.h" |
| |
| #define SKIP_WHITE_SPACE(p) do { while (is_hor_space[*p]) p++; } while (0) |
| #define SKIP_ALL_WHITE_SPACE(p) do { while (is_space[*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 macro *arguments*.) */ |
| #define CPP_IS_MACRO_BUFFER(PBUF) ((PBUF)->data != NULL) |
| |
| /* Forward declarations. */ |
| |
| static char *my_strerror PROTO ((int)); |
| static void validate_else PROTO ((cpp_reader *, char *)); |
| static HOST_WIDEST_INT eval_if_expression PROTO ((cpp_reader *)); |
| |
| static void conditional_skip PROTO ((cpp_reader *, int, |
| enum node_type, U_CHAR *)); |
| static void skip_if_group PROTO ((cpp_reader *)); |
| |
| static void parse_name PARAMS ((cpp_reader *, int)); |
| static void parse_string PARAMS ((cpp_reader *, int)); |
| static int parse_assertion PARAMS ((cpp_reader *)); |
| |
| /* External declarations. */ |
| |
| extern HOST_WIDEST_INT cpp_parse_expr PARAMS ((cpp_reader *)); |
| |
| /* `struct directive' defines one #-directive, including how to handle it. */ |
| |
| struct directive { |
| int length; /* Length of name */ |
| int (*func) /* Function to handle directive */ |
| PARAMS ((cpp_reader *, struct directive *)); |
| char *name; /* Name of directive */ |
| enum node_type type; /* Code which describes which directive. */ |
| }; |
| |
| /* 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 PARAMS ((cpp_reader *, struct directive *)); |
| static int do_line PARAMS ((cpp_reader *, struct directive *)); |
| static int do_include PARAMS ((cpp_reader *, struct directive *)); |
| static int do_undef PARAMS ((cpp_reader *, struct directive *)); |
| static int do_error PARAMS ((cpp_reader *, struct directive *)); |
| static int do_pragma PARAMS ((cpp_reader *, struct directive *)); |
| static int do_ident PARAMS ((cpp_reader *, struct directive *)); |
| static int do_if PARAMS ((cpp_reader *, struct directive *)); |
| static int do_xifdef PARAMS ((cpp_reader *, struct directive *)); |
| static int do_else PARAMS ((cpp_reader *, struct directive *)); |
| static int do_elif PARAMS ((cpp_reader *, struct directive *)); |
| static int do_endif PARAMS ((cpp_reader *, struct directive *)); |
| #ifdef SCCS_DIRECTIVE |
| static int do_sccs PARAMS ((cpp_reader *, struct directive *)); |
| #endif |
| static int do_assert PARAMS ((cpp_reader *, struct directive *)); |
| static int do_unassert PARAMS ((cpp_reader *, struct directive *)); |
| static int do_warning PARAMS ((cpp_reader *, struct directive *)); |
| |
| #define IS_INCLUDE_DIRECTIVE_TYPE(t) \ |
| ((int) T_INCLUDE <= (int) (t) && (int) (t) <= (int) T_IMPORT) |
| |
| /* 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 }, |
| { 5, do_xifdef, "ifdef", T_IFDEF }, |
| { 6, do_xifdef, "ifndef", T_IFNDEF }, |
| { 7, do_include, "include", T_INCLUDE }, |
| { 12, do_include, "include_next", T_INCLUDE_NEXT }, |
| { 6, do_include, "import", T_IMPORT }, |
| { 5, do_endif, "endif", T_ENDIF }, |
| { 4, do_else, "else", T_ELSE }, |
| { 2, do_if, "if", T_IF }, |
| { 4, do_elif, "elif", T_ELIF }, |
| { 5, do_undef, "undef", T_UNDEF }, |
| { 5, do_error, "error", T_ERROR }, |
| { 7, do_warning, "warning", T_WARNING }, |
| { 6, do_pragma, "pragma", T_PRAGMA }, |
| { 4, do_line, "line", T_LINE }, |
| { 5, do_ident, "ident", T_IDENT }, |
| #ifdef SCCS_DIRECTIVE |
| { 4, do_sccs, "sccs", T_SCCS }, |
| #endif |
| { 6, do_assert, "assert", T_ASSERT }, |
| { 8, do_unassert, "unassert", T_UNASSERT }, |
| { -1, 0, "", T_UNUSED } |
| }; |
| |
| /* Place into PFILE a quoted string representing the string SRC. |
| Caller must reserve enough space in pfile->token_buffer. */ |
| |
| void |
| quote_string (pfile, src) |
| cpp_reader *pfile; |
| const char *src; |
| { |
| U_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; |
| } |
| } |
| |
| /* Re-allocates PFILE->token_buffer so it will hold at least N more chars. */ |
| |
| void |
| cpp_grow_buffer (pfile, n) |
| cpp_reader *pfile; |
| long n; |
| { |
| long old_written = CPP_WRITTEN (pfile); |
| pfile->token_buffer_size = n + 2 * pfile->token_buffer_size; |
| pfile->token_buffer = (U_CHAR *) |
| xrealloc(pfile->token_buffer, pfile->token_buffer_size); |
| CPP_SET_WRITTEN (pfile, old_written); |
| } |
| |
| /* Process the string STR as if it appeared as the body of a #define |
| 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 (pfile, str) |
| cpp_reader *pfile; |
| U_CHAR *str; |
| { |
| U_CHAR *buf, *p; |
| size_t count; |
| |
| /* Copy the entire option so we can modify it. */ |
| count = strlen (str) + 3; |
| buf = (U_CHAR *) alloca (count); |
| memcpy (buf, str, count - 2); |
| /* Change the first "=" in the string to a space. If there is none, |
| tack " 1" on the end. */ |
| p = (U_CHAR *) strchr (buf, '='); |
| if (p) |
| { |
| *p = ' '; |
| count -= 2; |
| } |
| else |
| strcpy (&buf[count-3], " 1"); |
| |
| if (cpp_push_buffer (pfile, buf, count - 1) != NULL) |
| { |
| do_define (pfile, NULL); |
| cpp_pop_buffer (pfile); |
| } |
| } |
| |
| /* Process the string STR as if it appeared as the body of a #assert. */ |
| void |
| cpp_assert (pfile, str) |
| cpp_reader *pfile; |
| U_CHAR *str; |
| { |
| if (cpp_push_buffer (pfile, str, strlen (str)) != NULL) |
| { |
| do_assert (pfile, NULL); |
| cpp_pop_buffer (pfile); |
| } |
| } |
| |
| |
| static enum cpp_token |
| null_underflow (pfile) |
| cpp_reader *pfile ATTRIBUTE_UNUSED; |
| { |
| return CPP_EOF; |
| } |
| |
| static int |
| null_cleanup (pbuf, pfile) |
| cpp_buffer *pbuf ATTRIBUTE_UNUSED; |
| cpp_reader *pfile ATTRIBUTE_UNUSED; |
| { |
| return 0; |
| } |
| |
| /* Skip a comment - C, C++, or Chill style. M is the first character |
| of the comment marker. If this really is a comment, skip to its |
| end and return ' '. If we hit end-of-file before end-of-comment, |
| return EOF. If this is not a comment, return M (which will be |
| '/' or '-'). */ |
| |
| static int |
| skip_comment (pfile, m) |
| cpp_reader *pfile; |
| int m; |
| { |
| if (m == '/' && PEEKC() == '*') |
| { |
| int c, prev_c = -1; |
| long line, col; |
| |
| FORWARD(1); |
| cpp_buf_line_and_col (CPP_BUFFER (pfile), &line, &col); |
| for (;;) |
| { |
| c = GETC (); |
| if (c == EOF) |
| { |
| cpp_error_with_line (pfile, line, col, "unterminated comment"); |
| return EOF; |
| } |
| else if (c == '\n' || c == '\r') |
| CPP_BUMP_LINE (pfile); |
| else if (c == '/' && prev_c == '*') |
| return ' '; |
| else if (c == '*' && prev_c == '/' |
| && CPP_OPTIONS (pfile)->warn_comments) |
| cpp_warning (pfile, "`/*' within comment"); |
| |
| prev_c = c; |
| } |
| } |
| else if ((m == '/' && PEEKC() == '/' |
| && CPP_OPTIONS (pfile)->cplusplus_comments) |
| || (m == '-' && PEEKC() == '-' |
| && CPP_OPTIONS (pfile)->chill)) |
| { |
| FORWARD(1); |
| for (;;) |
| { |
| int c = GETC (); |
| if (c == EOF) |
| return ' '; /* Allow // to be terminated by EOF. */ |
| if (c == '\n') |
| { |
| /* Don't consider final '\n' to be part of comment. */ |
| FORWARD(-1); |
| return ' '; |
| } |
| else if (c == '\r') |
| CPP_BUMP_LINE (pfile); |
| } |
| } |
| else |
| return m; |
| } |
| |
| /* Identical to skip_comment except that it copies the comment into the |
| token_buffer. This is used if put_out_comments. */ |
| static int |
| copy_comment (pfile, m) |
| cpp_reader *pfile; |
| int m; |
| { |
| if (m == '/' && PEEKC() == '*') |
| { |
| int c, prev_c = -1; |
| long line, col; |
| |
| CPP_PUTC (pfile, '/'); |
| CPP_PUTC (pfile, '*'); |
| FORWARD(1); |
| cpp_buf_line_and_col (CPP_BUFFER (pfile), &line, &col); |
| for (;;) |
| { |
| c = GETC (); |
| if (c == EOF) |
| { |
| cpp_error_with_line (pfile, line, col, "unterminated comment"); |
| /* We must pretend this was a legitimate comment, so that the |
| output in token_buffer is not passed back tagged CPP_POP. */ |
| return ' '; |
| } |
| else if (c == '\r') |
| { |
| CPP_BUMP_LINE (pfile); |
| continue; |
| } |
| |
| CPP_PUTC (pfile, c); |
| if (c == '\n') |
| { |
| pfile->lineno++; |
| CPP_BUMP_LINE (pfile); |
| } |
| else if (c == '/' && prev_c == '*') |
| return ' '; |
| else if (c == '*' && prev_c == '/' |
| && CPP_OPTIONS (pfile)->warn_comments) |
| cpp_warning (pfile, "`/*' within comment"); |
| |
| prev_c = c; |
| } |
| } |
| else if ((m == '/' && PEEKC() == '/' |
| && CPP_OPTIONS (pfile)->cplusplus_comments) |
| || (m == '-' && PEEKC() == '-' |
| && CPP_OPTIONS (pfile)->chill)) |
| { |
| CPP_PUTC (pfile, m); |
| CPP_PUTC (pfile, m); |
| FORWARD(1); |
| for (;;) |
| { |
| int c = GETC (); |
| if (c == EOF) |
| return ' '; /* Allow line comments to be terminated by EOF. */ |
| else if (c == '\n') |
| { |
| /* Don't consider final '\n' to be part of comment. */ |
| FORWARD(-1); |
| return ' '; |
| } |
| else if (c == '\r') |
| CPP_BUMP_LINE (pfile); |
| |
| CPP_PUTC (pfile, c); |
| } |
| } |
| else |
| return m; |
| } |
| |
| |
| /* Skip whitespace \-newline and comments. Does not macro-expand. */ |
| |
| void |
| cpp_skip_hspace (pfile) |
| cpp_reader *pfile; |
| { |
| int c; |
| while (1) |
| { |
| c = GETC(); |
| if (c == EOF) |
| return; |
| else 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"); |
| } |
| else if (c == '\r') |
| { |
| CPP_BUFFER (pfile)->lineno++; |
| } |
| else if (c == '/' || c == '-') |
| { |
| c = skip_comment (pfile, c); |
| if (c == EOF) |
| return; |
| else if (c != ' ') |
| { |
| FORWARD(-1); |
| return; |
| } |
| } |
| else if (c == '@' && CPP_BUFFER (pfile)->has_escapes |
| && PEEKC() == ' ') |
| { |
| FORWARD(1); |
| } |
| else |
| { |
| FORWARD(-1); |
| return; |
| } |
| } |
| } |
| |
| /* Read the rest of the current line. |
| The line is appended to PFILE's output buffer. */ |
| |
| static void |
| copy_rest_of_line (pfile) |
| cpp_reader *pfile; |
| { |
| for (;;) |
| { |
| int c = GETC(); |
| switch (c) |
| { |
| case '\n': |
| FORWARD(-1); |
| case EOF: |
| CPP_NUL_TERMINATE (pfile); |
| return; |
| |
| case '\r': |
| CPP_BUFFER (pfile)->lineno++; |
| continue; |
| case '\'': |
| case '\"': |
| parse_string (pfile, c); |
| continue; |
| case '/': |
| if (PEEKC() == '*' && CPP_TRADITIONAL (pfile)) |
| { |
| CPP_PUTS (pfile, "/**/", 4); |
| skip_comment (pfile, c); |
| continue; |
| } |
| /* else fall through */ |
| case '-': |
| c = skip_comment (pfile, c); |
| break; |
| |
| case '\f': |
| case '\v': |
| if (CPP_PEDANTIC (pfile)) |
| cpp_pedwarn (pfile, "%s in preprocessing directive", |
| c == '\f' ? "formfeed" : "vertical tab"); |
| break; |
| |
| } |
| CPP_PUTC (pfile, c); |
| } |
| } |
| |
| /* FIXME: It is almost definitely a performance win to make this do |
| the scan itself. >75% of calls to copy_r_o_l are from here or |
| skip_if_group, which means the common case is to copy stuff into the |
| token_buffer only to discard it. */ |
| void |
| skip_rest_of_line (pfile) |
| 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 (pfile) |
| cpp_reader *pfile; |
| { |
| int c; |
| register struct directive *kt; |
| int ident_length; |
| U_CHAR *ident; |
| 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); |
| 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; |
| } |
| |
| #if 0 |
| if (ident_length == 0 || !is_idstart[*ident]) { |
| U_CHAR *p = ident; |
| while (is_idchar[*p]) { |
| if (*p < '0' || *p > '9') |
| break; |
| p++; |
| } |
| /* Avoid error for `###' and similar cases unless -pedantic. */ |
| if (p == ident) { |
| while (*p == '#' || is_hor_space[*p]) p++; |
| if (*p == '\n') { |
| if (pedantic && !lang_asm) |
| cpp_warning (pfile, "invalid preprocessor directive"); |
| return 0; |
| } |
| } |
| |
| if (!lang_asm) |
| cpp_error (pfile, "invalid preprocessor directive name"); |
| |
| return 0; |
| } |
| #endif |
| /* |
| * 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, ident, ident_length)) |
| break; |
| } |
| |
| /* We may want to pass through #define, #undef, #pragma, and #include. |
| Other directives may create output, but we don't want the directive |
| itself out, so we pop it now. For example conditionals may emit |
| #failed ... #endfailed stuff. */ |
| |
| if (! (kt->type == T_DEFINE |
| || kt->type == T_PRAGMA |
| || (IS_INCLUDE_DIRECTIVE_TYPE (kt->type) |
| && CPP_OPTIONS (pfile)->dump_includes))) |
| CPP_SET_WRITTEN (pfile, old_written); |
| |
| (*kt->func) (pfile, kt); |
| |
| if (kt->type == T_DEFINE) |
| { |
| if (CPP_OPTIONS (pfile)->dump_macros == dump_names) |
| { |
| /* Skip "#define". */ |
| U_CHAR *p = pfile->token_buffer + old_written + 7; |
| |
| SKIP_WHITE_SPACE (p); |
| while (is_idchar[*p]) p++; |
| pfile->limit = p; |
| CPP_PUTC (pfile, '\n'); |
| } |
| else if (CPP_OPTIONS (pfile)->dump_macros != dump_definitions) |
| 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. |
| m 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 (buf, limit, pfile, keyword) |
| U_CHAR *buf, *limit; |
| cpp_reader *pfile; |
| struct directive *keyword; |
| { |
| register 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); |
| #if 0 |
| CPP_PUTS_Q (pfile, '\n'); |
| /* Count the line we have just made in the output, |
| to get in sync properly. */ |
| pfile->lineno++; |
| #endif |
| } |
| |
| /* Check a purported macro name SYMNAME, and yield its length. |
| ASSERTION is nonzero if this is really for an assertion name. */ |
| |
| int |
| check_macro_name (pfile, symname, assertion) |
| cpp_reader *pfile; |
| U_CHAR *symname; |
| int assertion; |
| { |
| U_CHAR *p; |
| int sym_length; |
| |
| for (p = symname; is_idchar[*p]; p++) |
| ; |
| sym_length = p - symname; |
| if (sym_length == 0 |
| || (sym_length == 1 && *symname == 'L' && (*p == '\'' || *p == '"'))) |
| cpp_error (pfile, |
| assertion ? "invalid assertion name" : "invalid macro name"); |
| else if (!is_idstart[*symname] |
| || (! strncmp (symname, "defined", 7) && sym_length == 7)) { |
| U_CHAR *msg; /* what pain... */ |
| msg = (U_CHAR *) alloca (sym_length + 1); |
| bcopy (symname, msg, sym_length); |
| msg[sym_length] = 0; |
| cpp_error (pfile, |
| (assertion |
| ? "invalid assertion name `%s'" |
| : "invalid macro name `%s'"), |
| msg); |
| } |
| return sym_length; |
| } |
| |
| |
| /* Process a #define command. |
| KEYWORD is the keyword-table entry for #define, |
| or NULL for a "predefined" macro. */ |
| |
| static int |
| do_define (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword; |
| { |
| int hashcode; |
| MACRODEF mdef; |
| HASHNODE *hp; |
| long here; |
| U_CHAR *macro, *buf, *end; |
| |
| here = CPP_WRITTEN (pfile); |
| copy_rest_of_line (pfile); |
| |
| /* Copy out the line so we can pop the token buffer. */ |
| buf = pfile->token_buffer + here; |
| end = CPP_PWRITTEN (pfile); |
| macro = alloca (end - buf + 1); |
| bcopy (buf, macro, end - buf + 1); |
| end = macro + (end - buf); |
| |
| CPP_SET_WRITTEN (pfile, here); |
| |
| #if 0 |
| /* If this is a precompiler run (with -pcp) pass thru #define commands. */ |
| if (pcp_outfile && keyword) |
| pass_thru_directive (macro, end, pfile, keyword); |
| #endif |
| |
| mdef = create_definition (macro, end, pfile, keyword == NULL); |
| if (mdef.defn == 0) |
| goto nope; |
| |
| hashcode = hashf (mdef.symnam, mdef.symlen, HASHSIZE); |
| |
| if ((hp = cpp_lookup (pfile, mdef.symnam, mdef.symlen, hashcode)) != NULL) |
| { |
| 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 (pfile, mdef.defn, hp->value.defn); |
| /* Redefining a constant is ok with -D. */ |
| else if (hp->type == T_CONST || hp->type == T_STDC) |
| ok = ! CPP_OPTIONS (pfile)->done_initializing; |
| /* Print the warning if it's not ok. */ |
| if (!ok) |
| { |
| /* 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 (macro, end, pfile, keyword); |
| |
| cpp_pedwarn (pfile, "`%.*s' redefined", mdef.symlen, mdef.symnam); |
| 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"); |
| } |
| /* 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 (macro, end, pfile, keyword); |
| cpp_install (pfile, mdef.symnam, mdef.symlen, T_MACRO, |
| (char *) mdef.defn, hashcode); |
| } |
| |
| return 0; |
| |
| nope: |
| |
| return 1; |
| } |
| |
| |
| /* Allocate a new cpp_buffer for PFILE, and push it on the input buffer stack. |
| If BUFFER != NULL, then use the LENGTH characters in BUFFER |
| as the new input buffer. |
| Return the new buffer, or NULL on failure. */ |
| |
| cpp_buffer * |
| cpp_push_buffer (pfile, buffer, length) |
| cpp_reader *pfile; |
| U_CHAR *buffer; |
| long length; |
| { |
| cpp_buffer *buf = CPP_BUFFER (pfile); |
| cpp_buffer *new; |
| if (++pfile->buffer_stack_depth == CPP_STACK_MAX) |
| { |
| cpp_fatal (pfile, "macro or `#include' recursion too deep"); |
| return NULL; |
| } |
| |
| new = (cpp_buffer *) xcalloc (sizeof (cpp_buffer), 1); |
| |
| new->if_stack = pfile->if_stack; |
| new->cleanup = null_cleanup; |
| new->underflow = null_underflow; |
| new->buf = new->cur = buffer; |
| new->alimit = new->rlimit = buffer + length; |
| new->prev = buf; |
| new->mark = -1; |
| |
| CPP_BUFFER (pfile) = new; |
| return new; |
| } |
| |
| cpp_buffer * |
| cpp_pop_buffer (pfile) |
| cpp_reader *pfile; |
| { |
| cpp_buffer *buf = CPP_BUFFER (pfile); |
| (*buf->cleanup) (buf, pfile); |
| CPP_BUFFER (pfile) = CPP_PREV_BUFFER (buf); |
| free (buf); |
| pfile->buffer_stack_depth--; |
| return CPP_BUFFER (pfile); |
| } |
| |
| /* Scan until CPP_BUFFER (PFILE) is exhausted into PFILE->token_buffer. |
| Pop the buffer when done. */ |
| |
| void |
| cpp_scan_buffer (pfile) |
| 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. |
| */ |
| |
| void |
| cpp_expand_to_buffer (pfile, buf, length) |
| cpp_reader *pfile; |
| U_CHAR *buf; |
| int length; |
| { |
| register cpp_buffer *ip; |
| #if 0 |
| cpp_buffer obuf; |
| #endif |
| U_CHAR *buf1; |
| #if 0 |
| int odepth = indepth; |
| #endif |
| |
| if (length < 0) |
| abort (); |
| |
| /* Set up the input on the input stack. */ |
| |
| buf1 = (U_CHAR *) alloca (length + 1); |
| memcpy (buf1, buf, length); |
| buf1[length] = 0; |
| |
| ip = cpp_push_buffer (pfile, buf1, length); |
| if (ip == NULL) |
| return; |
| ip->has_escapes = 1; |
| #if 0 |
| ip->lineno = obuf.lineno = 1; |
| #endif |
| |
| /* Scan the input, create the output. */ |
| cpp_scan_buffer (pfile); |
| |
| CPP_NUL_TERMINATE (pfile); |
| } |
| |
| void |
| cpp_buf_line_and_col (pbuf, linep, colp) |
| register cpp_buffer *pbuf; |
| long *linep, *colp; |
| { |
| if (pbuf) |
| { |
| *linep = pbuf->lineno; |
| if (colp) |
| *colp = pbuf->cur - pbuf->line_base; |
| } |
| else |
| { |
| *linep = 0; |
| if (colp) |
| *colp = 0; |
| } |
| } |
| |
| /* Return the cpp_buffer that corresponds to a file (not a macro). */ |
| |
| cpp_buffer * |
| cpp_file_buffer (pfile) |
| cpp_reader *pfile; |
| { |
| cpp_buffer *ip = CPP_BUFFER (pfile); |
| |
| for ( ; ip != CPP_NULL_BUFFER (pfile); ip = CPP_PREV_BUFFER (ip)) |
| if (ip->fname != NULL) |
| return ip; |
| return NULL; |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| void |
| output_line_command (pfile, conditional, file_change) |
| cpp_reader *pfile; |
| int conditional; |
| enum file_change_code file_change; |
| { |
| long line, col; |
| cpp_buffer *ip = CPP_BUFFER (pfile); |
| |
| if (ip->fname == NULL) |
| return; |
| |
| if (CPP_OPTIONS (pfile)->no_line_commands |
| || CPP_OPTIONS (pfile)->no_output) |
| return; |
| |
| cpp_buf_line_and_col (CPP_BUFFER (pfile), &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); |
| CPP_PUTS_Q (pfile, "# ", 2); |
| |
| sprintf ((char *) CPP_PWRITTEN (pfile), "%ld ", line); |
| CPP_ADJUST_WRITTEN (pfile, strlen (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; |
| } |
| |
| |
| /* 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 (pfile) |
| 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 ... */ |
| 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 <fname> 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 (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword; |
| { |
| int importing = (keyword->type == T_IMPORT); |
| int skip_dirs = (keyword->type == T_INCLUDE_NEXT); |
| int angle_brackets = 0; /* 0 for "...", 1 for <...> */ |
| int before; /* included before? */ |
| long flen; |
| unsigned char *fbeg, *fend; |
| cpp_buffer *fp; |
| |
| enum cpp_token token; |
| |
| /* Chain of dirs to search */ |
| struct include_hash *ihash; |
| struct file_name_list *search_start; |
| |
| long old_written = CPP_WRITTEN (pfile); |
| |
| int fd; |
| |
| if (CPP_PEDANTIC (pfile) && !CPP_BUFFER (pfile)->system_header_p) |
| { |
| if (importing) |
| cpp_pedwarn (pfile, "ANSI C does not allow `#import'"); |
| if (skip_dirs) |
| cpp_pedwarn (pfile, "ANSI C does not allow `#include_next'"); |
| } |
| |
| 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, "`#import' is obsolete, use an #ifndef wrapper in the header file"); |
| } |
| |
| pfile->parsing_include_directive++; |
| token = get_directive_token (pfile); |
| pfile->parsing_include_directive--; |
| |
| if (token == CPP_STRING) |
| { |
| fbeg = pfile->token_buffer + old_written + 1; |
| fend = CPP_PWRITTEN (pfile) - 1; |
| *fend = '\0'; |
| if (fbeg[-1] == '<') |
| angle_brackets = 1; |
| } |
| #ifdef VMS |
| else if (token == CPP_NAME) |
| { |
| /* Support '#include xyz' like VAX-C to allow for easy use of |
| * all the decwindow include files. It defaults to '#include |
| * <xyz.h>' and generates a warning. */ |
| cpp_warning (pfile, |
| "VAX-C-style include specification found, use '#include <filename.h>' !"); |
| angle_brackets = 1; |
| |
| /* Append the missing `.h' to the name. */ |
| CPP_PUTS (pfile, ".h", 3) |
| CPP_NUL_TERMINATE_Q (pfile); |
| |
| fbeg = pfile->token_buffer + old_written; |
| fend = CPP_PWRITTEN (pfile); |
| } |
| #endif |
| else |
| { |
| cpp_error (pfile, |
| "`#%s' expects \"FILENAME\" or <FILENAME>", keyword->name); |
| CPP_SET_WRITTEN (pfile, old_written); |
| skip_rest_of_line (pfile); |
| return 0; |
| } |
| |
| token = get_directive_token (pfile); |
| if (token != CPP_VSPACE) |
| { |
| cpp_error (pfile, "junk at end of `#include'"); |
| skip_rest_of_line (pfile); |
| } |
| |
| CPP_SET_WRITTEN (pfile, old_written); |
| |
| flen = fend - fbeg; |
| |
| if (flen == 0) |
| { |
| cpp_error (pfile, "empty file name in `#%s'", keyword->name); |
| return 0; |
| } |
| |
| search_start = 0; |
| |
| for (fp = CPP_BUFFER (pfile); |
| fp != CPP_NULL_BUFFER (pfile); |
| fp = CPP_PREV_BUFFER (fp)) |
| if (fp->fname != NULL) |
| break; |
| |
| if (fp == CPP_NULL_BUFFER (pfile)) |
| { |
| cpp_fatal (pfile, "cpp internal error: fp == NULL_BUFFER in do_include"); |
| return 1; |
| } |
| |
| /* For #include_next, skip in the search path past the dir in which the |
| containing file was found. Treat files specified using an absolute path |
| as if there are no more directories to search. Treat the primary source |
| file like any other included source, but generate a warning. */ |
| if (skip_dirs && CPP_PREV_BUFFER(fp) != CPP_NULL_BUFFER (pfile)) |
| { |
| if (fp->ihash->foundhere != ABSOLUTE_PATH) |
| search_start = fp->ihash->foundhere->next; |
| } |
| else |
| { |
| if (skip_dirs) |
| cpp_warning (pfile, "#include_next in primary source file"); |
| |
| if (angle_brackets) |
| search_start = CPP_OPTIONS (pfile)->bracket_include; |
| else |
| { |
| if (!CPP_OPTIONS (pfile)->ignore_srcdir) |
| { |
| if (fp) |
| search_start = fp->actual_dir; |
| } |
| else |
| search_start = CPP_OPTIONS (pfile)->quote_include; |
| } |
| } |
| |
| if (!search_start) |
| { |
| cpp_error (pfile, "No include path in which to find %s", fbeg); |
| return 0; |
| } |
| |
| fd = find_include_file (pfile, fbeg, search_start, &ihash, &before); |
| |
| if (fd == -2) |
| return 0; |
| |
| if (fd == -1) |
| { |
| if (CPP_OPTIONS (pfile)->print_deps_missing_files |
| && CPP_PRINT_DEPS (pfile) > (angle_brackets || |
| (pfile->system_include_depth > 0))) |
| { |
| if (!angle_brackets) |
| deps_output (pfile, fbeg, ' '); |
| else |
| { |
| char *p; |
| struct file_name_list *ptr; |
| /* If requested as a system header, assume it belongs in |
| the first system header directory. */ |
| if (CPP_OPTIONS (pfile)->bracket_include) |
| ptr = CPP_OPTIONS (pfile)->bracket_include; |
| else |
| ptr = CPP_OPTIONS (pfile)->quote_include; |
| |
| p = (char *) alloca (strlen (ptr->name) |
| + strlen (fbeg) + 2); |
| if (*ptr->name != '\0') |
| { |
| strcpy (p, ptr->name); |
| strcat (p, "/"); |
| } |
| strcat (p, fbeg); |
| deps_output (pfile, p, ' '); |
| } |
| } |
| /* 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", fbeg); |
| else |
| cpp_error_from_errno (pfile, fbeg); |
| |
| return 0; |
| } |
| |
| /* For -M, add the file to the dependencies on its first inclusion. */ |
| if (!before && (CPP_PRINT_DEPS (pfile) |
| > (angle_brackets || (pfile->system_include_depth > 0)))) |
| deps_output (pfile, ihash->name, ' '); |
| |
| /* Handle -H option. */ |
| if (CPP_OPTIONS(pfile)->print_include_names) |
| { |
| fp = CPP_BUFFER (pfile); |
| while ((fp = CPP_PREV_BUFFER (fp)) != CPP_NULL_BUFFER (pfile)) |
| putc ('.', stderr); |
| fprintf (stderr, " %s\n", ihash->name); |
| } |
| |
| /* Actually process the file */ |
| |
| if (importing) |
| ihash->control_macro = ""; |
| |
| if (cpp_push_buffer (pfile, NULL, 0) == NULL) |
| { |
| close (fd); |
| return 0; |
| } |
| |
| if (angle_brackets) |
| pfile->system_include_depth++; /* Decremented in file_cleanup. */ |
| |
| if (finclude (pfile, fd, ihash)) |
| { |
| output_line_command (pfile, 0, enter_file); |
| pfile->only_seen_white = 2; |
| } |
| |
| return 0; |
| } |
| |
| /* Interpret #line command. |
| Note that the filename string (if any) is treated as if it were an |
| include filename. That means no escape handling. */ |
| |
| static int |
| do_line (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_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; |
| char *x; |
| |
| token = get_directive_token (pfile); |
| |
| if (token != CPP_NUMBER) |
| { |
| cpp_error (pfile, "token after `#line' is not an integer"); |
| goto bad_line_directive; |
| } |
| |
| new_lineno = strtol (pfile->token_buffer + old_written, &x, 10); |
| if (x[0] != '\0') |
| { |
| cpp_error (pfile, "token after `#line' is not an integer"); |
| goto bad_line_directive; |
| } |
| CPP_SET_WRITTEN (pfile, old_written); |
| |
| 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) |
| { |
| U_CHAR *fname = pfile->token_buffer + old_written + 1; |
| U_CHAR *end_name = CPP_PWRITTEN (pfile) - 1; |
| long num_start = CPP_WRITTEN (pfile); |
| |
| token = get_directive_token (pfile); |
| if (token != CPP_VSPACE && token != CPP_EOF && token != CPP_POP) |
| { |
| U_CHAR *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; |
| } |
| } |
| |
| *end_name = '\0'; |
| |
| if (strcmp (fname, ip->nominal_fname)) |
| { |
| char *newname, *oldname; |
| if (!strcmp (fname, ip->fname)) |
| newname = ip->fname; |
| else if (ip->last_nominal_fname |
| && !strcmp (fname, ip->last_nominal_fname)) |
| newname = ip->last_nominal_fname; |
| else |
| newname = xstrdup (fname); |
| |
| oldname = ip->nominal_fname; |
| ip->nominal_fname = newname; |
| |
| if (ip->last_nominal_fname |
| && ip->last_nominal_fname != oldname |
| && ip->last_nominal_fname != newname |
| && ip->last_nominal_fname != ip->fname) |
| free (ip->last_nominal_fname); |
| |
| if (newname == ip->fname) |
| ip->last_nominal_fname = NULL; |
| else |
| ip->last_nominal_fname = oldname; |
| } |
| } |
| else if (token != CPP_VSPACE && token != CPP_EOF) |
| { |
| cpp_error (pfile, "token after `#line %d' is not a string", new_lineno); |
| 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. */ |
| ip->lineno = new_lineno - 1; |
| CPP_SET_WRITTEN (pfile, old_written); |
| output_line_command (pfile, 0, file_change); |
| return 0; |
| |
| bad_line_directive: |
| skip_rest_of_line (pfile); |
| CPP_SET_WRITTEN (pfile, old_written); |
| return 0; |
| } |
| |
| /* Remove the definition of a symbol from the symbol table. |
| According to the C standard, it is not an error to undef |
| something that has no definitions. */ |
| static int |
| do_undef (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword; |
| { |
| int sym_length; |
| HASHNODE *hp; |
| U_CHAR *buf, *name, *limit; |
| int c; |
| long here = CPP_WRITTEN (pfile); |
| enum cpp_token token; |
| |
| cpp_skip_hspace (pfile); |
| c = GETC(); |
| if (! is_idstart[c]) |
| { |
| cpp_error (pfile, "token after #undef is not an identifier"); |
| skip_rest_of_line (pfile); |
| return 1; |
| } |
| |
| parse_name (pfile, c); |
| buf = pfile->token_buffer + here; |
| limit = CPP_PWRITTEN(pfile); |
| |
| /* Copy out the token so we can pop the token buffer. */ |
| name = alloca (limit - buf + 1); |
| bcopy(buf, name, limit - buf); |
| name[limit - buf] = '\0'; |
| |
| token = get_directive_token (pfile); |
| if (token != CPP_VSPACE && token != CPP_POP) |
| { |
| cpp_pedwarn (pfile, "junk on line after #undef"); |
| skip_rest_of_line (pfile); |
| } |
| |
| CPP_SET_WRITTEN (pfile, here); |
| |
| #if 0 |
| /* If this is a precompiler run (with -pcp) pass thru #undef commands. */ |
| if (pcp_outfile && keyword) |
| pass_thru_directive (buf, limit, pfile, keyword); |
| #endif |
| |
| sym_length = check_macro_name (pfile, buf, 0); |
| |
| while ((hp = cpp_lookup (pfile, name, sym_length, -1)) != NULL) |
| { |
| /* 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 (name, name+sym_length, pfile, keyword); |
| if (hp->type != T_MACRO) |
| cpp_warning (pfile, "undefining `%s'", hp->name); |
| delete_macro (hp); |
| } |
| |
| return 0; |
| } |
| |
| /* Wrap do_undef for -U processing. */ |
| void |
| cpp_undef (pfile, macro) |
| cpp_reader *pfile; |
| U_CHAR *macro; |
| { |
| if (cpp_push_buffer (pfile, macro, strlen (macro))) |
| { |
| do_undef (pfile, NULL); |
| cpp_pop_buffer (pfile); |
| } |
| } |
| |
| |
| /* |
| * 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 (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_UNUSED; |
| { |
| long here = CPP_WRITTEN (pfile); |
| U_CHAR *text; |
| copy_rest_of_line (pfile); |
| text = pfile->token_buffer + here; |
| SKIP_WHITE_SPACE(text); |
| |
| cpp_error (pfile, "#error %s", text); |
| CPP_SET_WRITTEN (pfile, here); |
| return 0; |
| } |
| |
| /* |
| * Report a warning detected by the program we are processing. |
| * Use the text of the line in the warning message, then continue. |
| */ |
| |
| static int |
| do_warning (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_UNUSED; |
| { |
| U_CHAR *text; |
| long here = CPP_WRITTEN(pfile); |
| copy_rest_of_line (pfile); |
| text = pfile->token_buffer + here; |
| SKIP_WHITE_SPACE(text); |
| |
| if (CPP_PEDANTIC (pfile) && !CPP_BUFFER (pfile)->system_header_p) |
| cpp_pedwarn (pfile, "ANSI C does not allow `#warning'"); |
| |
| /* Use `pedwarn' not `warning', because #warning isn't in the C Standard; |
| if -pedantic-errors is given, #warning should cause an error. */ |
| cpp_pedwarn (pfile, "#warning %s", text); |
| CPP_SET_WRITTEN (pfile, here); |
| return 0; |
| } |
| |
| /* Report program identification. */ |
| |
| static int |
| do_ident (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_UNUSED; |
| { |
| /* 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'"); |
| |
| skip_rest_of_line (pfile); /* Correct? Appears to match cccp. */ |
| |
| return 0; |
| } |
| |
| /* Just check for some recognized pragmas that need validation here, |
| and leave the text in the token buffer to be output. */ |
| |
| static int |
| do_pragma (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_UNUSED; |
| { |
| long here = CPP_WRITTEN (pfile); |
| U_CHAR *buf; |
| |
| copy_rest_of_line (pfile); |
| buf = pfile->token_buffer + here; |
| SKIP_WHITE_SPACE (buf); |
| |
| if (!strncmp (buf, "once", 4)) |
| { |
| cpp_buffer *ip = NULL; |
| |
| /* 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"); |
| |
| for (ip = CPP_BUFFER (pfile); ; ip = CPP_PREV_BUFFER (ip)) |
| { |
| if (ip == CPP_NULL_BUFFER (pfile)) |
| return 0; |
| if (ip->fname != NULL) |
| break; |
| } |
| |
| if (CPP_PREV_BUFFER (ip) == CPP_NULL_BUFFER (pfile)) |
| cpp_warning (pfile, "`#pragma once' outside include file"); |
| else |
| ip->ihash->control_macro = ""; /* never repeat */ |
| } |
| |
| if (!strncmp (buf, "implementation", 14)) |
| { |
| /* Be quiet about `#pragma implementation' for a file only if it hasn't |
| been included yet. */ |
| struct include_hash *ptr; |
| U_CHAR *p = buf + 14, *fname, *fcopy; |
| SKIP_WHITE_SPACE (p); |
| if (*p == '\n' || *p != '\"') |
| return 0; |
| |
| fname = p + 1; |
| p = (U_CHAR *) index (fname, '\"'); |
| |
| fcopy = alloca (p - fname + 1); |
| bcopy (fname, fcopy, p - fname); |
| fcopy[p-fname] = '\0'; |
| |
| ptr = include_hash (pfile, fcopy, 0); |
| if (ptr) |
| cpp_warning (pfile, |
| "`#pragma implementation' for `%s' appears after file is included", |
| fcopy); |
| } |
| |
| return 0; |
| } |
| |
| #ifdef SCCS_DIRECTIVE |
| /* Just ignore #sccs, on systems where we define it at all. */ |
| |
| static int |
| do_sccs (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_UNUSED; |
| { |
| if (CPP_PEDANTIC (pfile)) |
| cpp_pedwarn (pfile, "ANSI C does not allow `#sccs'"); |
| skip_rest_of_line (pfile); |
| return 0; |
| } |
| #endif |
| |
| /* |
| * 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 (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_UNUSED; |
| { |
| HOST_WIDEST_INT value = eval_if_expression (pfile); |
| conditional_skip (pfile, value == 0, T_IF, NULL_PTR); |
| return 0; |
| } |
| |
| /* |
| * handle a #elif directive by not changing if_stack either. |
| * see the comment above do_else. |
| */ |
| |
| static int |
| do_elif (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_UNUSED; |
| { |
| 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 0 |
| fprintf (stderr, " (matches line %d", pfile->if_stack->lineno); |
| #endif |
| if (pfile->if_stack->fname != NULL && CPP_BUFFER (pfile)->fname != NULL |
| && 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); |
| else { |
| HOST_WIDEST_INT value = eval_if_expression (pfile); |
| if (value == 0) |
| skip_if_group (pfile); |
| 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_WIDEST_INT |
| eval_if_expression (pfile) |
| cpp_reader *pfile; |
| { |
| HOST_WIDEST_INT value; |
| long old_written = CPP_WRITTEN (pfile); |
| |
| pfile->pcp_inside_if = 1; |
| value = cpp_parse_expr (pfile); |
| pfile->pcp_inside_if = 0; |
| |
| 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 (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword; |
| { |
| int skip; |
| cpp_buffer *ip = CPP_BUFFER (pfile); |
| U_CHAR *ident; |
| int ident_length; |
| enum cpp_token token; |
| int start_of_file = 0; |
| U_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 = 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 (pfile, ident, ident_length, -1); |
| skip = (hp == NULL) ^ (keyword->type == T_IFNDEF); |
| if (start_of_file && !skip) |
| { |
| control_macro = (U_CHAR *) xmalloc (ident_length + 1); |
| bcopy (ident, control_macro, 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); |
| |
| #if 0 |
| if (pcp_outfile) { |
| /* Output a precondition for this macro. */ |
| if (hp && hp->value.defn->predefined) |
| fprintf (pcp_outfile, "#define %s\n", hp->name); |
| else { |
| U_CHAR *cp = buf; |
| fprintf (pcp_outfile, "#undef "); |
| while (is_idchar[*cp]) /* Ick! */ |
| fputc (*cp++, pcp_outfile); |
| putc ('\n', pcp_outfile); |
| } |
| #endif |
| |
| 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 (pfile, skip, type, control_macro) |
| cpp_reader *pfile; |
| int skip; |
| enum node_type type; |
| U_CHAR *control_macro; |
| { |
| IF_STACK_FRAME *temp; |
| |
| temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME)); |
| temp->fname = CPP_BUFFER (pfile)->nominal_fname; |
| #if 0 |
| temp->lineno = CPP_BUFFER (pfile)->lineno; |
| #endif |
| 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); |
| return; |
| } else { |
| ++pfile->if_stack->if_succeeded; |
| output_line_command (pfile, 1, same_file); |
| } |
| } |
| |
| /* Subroutine of skip_if_group. Examine one preprocessing directive and |
| return 0 if skipping should continue, 1 if it should halt. Also |
| adjusts the if_stack as appropriate. |
| The `#' has been read, but not the identifier. */ |
| |
| static int |
| consider_directive_while_skipping (pfile, stack) |
| cpp_reader *pfile; |
| IF_STACK_FRAME *stack; |
| { |
| long ident_len, ident; |
| struct directive *kt; |
| IF_STACK_FRAME *temp; |
| |
| cpp_skip_hspace (pfile); |
| |
| ident = CPP_WRITTEN (pfile); |
| parse_name (pfile, GETC()); |
| ident_len = CPP_WRITTEN (pfile) - ident; |
| |
| CPP_SET_WRITTEN (pfile, ident); |
| |
| for (kt = directive_table; kt->length >= 0; kt++) |
| if (kt->length == ident_len |
| && strncmp (pfile->token_buffer + ident, kt->name, kt->length) == 0) |
| switch (kt->type) |
| { |
| case T_IF: |
| case T_IFDEF: |
| case T_IFNDEF: |
| temp = (IF_STACK_FRAME *) xmalloc (sizeof (IF_STACK_FRAME)); |
| temp->next = pfile->if_stack; |
| pfile->if_stack = temp; |
| temp->fname = CPP_BUFFER(pfile)->nominal_fname; |
| temp->type = kt->type; |
| return 0; |
| |
| case T_ELSE: |
| if (CPP_PEDANTIC (pfile) && pfile->if_stack != stack) |
| validate_else (pfile, "#else"); |
| /* fall through */ |
| case T_ELIF: |
| if (pfile->if_stack->type == T_ELSE) |
| cpp_error (pfile, "`%s' after `#else'", kt->name); |
| |
| if (pfile->if_stack == stack) |
| return 1; |
| else |
| { |
| pfile->if_stack->type = kt->type; |
| return 0; |
| } |
| |
| case T_ENDIF: |
| if (CPP_PEDANTIC (pfile) && pfile->if_stack != stack) |
| validate_else (pfile, "#endif"); |
| |
| if (pfile->if_stack == stack) |
| return 1; |
| |
| temp = pfile->if_stack; |
| pfile->if_stack = temp->next; |
| free (temp); |
| return 0; |
| |
| default: |
| return 0; |
| } |
| |
| /* Don't let erroneous code go by. */ |
| if (!CPP_OPTIONS (pfile)->lang_asm && CPP_PEDANTIC (pfile)) |
| cpp_pedwarn (pfile, "invalid preprocessor directive name"); |
| return 0; |
| } |
| |
| /* skip to #endif, #else, or #elif. adjust line numbers, etc. |
| * leaves input ptr at the sharp sign found. |
| */ |
| static void |
| skip_if_group (pfile) |
| cpp_reader *pfile; |
| { |
| int c; |
| IF_STACK_FRAME *save_if_stack = pfile->if_stack; /* don't pop past here */ |
| U_CHAR *beg_of_line; |
| long old_written; |
| |
| if (CPP_OPTIONS (pfile)->output_conditionals) |
| { |
| CPP_PUTS (pfile, "#failed\n", 8); |
| pfile->lineno++; |
| output_line_command (pfile, 1, same_file); |
| } |
| |
| old_written = CPP_WRITTEN (pfile); |
| |
| for (;;) |
| { |
| beg_of_line = CPP_BUFFER (pfile)->cur; |
| |
| if (! CPP_TRADITIONAL (pfile)) |
| cpp_skip_hspace (pfile); |
| c = GETC(); |
| if (c == '\n') |
| { |
| if (CPP_OPTIONS (pfile)->output_conditionals) |
| CPP_PUTC (pfile, c); |
| CPP_BUMP_LINE (pfile); |
| continue; |
| } |
| else if (c == '#') |
| { |
| if (consider_directive_while_skipping (pfile, save_if_stack)) |
| break; |
| } |
| else if (c == EOF) |
| return; /* Caller will issue error. */ |
| |
| FORWARD(-1); |
| if (CPP_OPTIONS (pfile)->output_conditionals) |
| { |
| CPP_PUTS (pfile, beg_of_line, CPP_BUFFER (pfile)->cur - beg_of_line); |
| copy_rest_of_line (pfile); |
| } |
| else |
| { |
| copy_rest_of_line (pfile); |
| CPP_SET_WRITTEN (pfile, old_written); /* discard it */ |
| } |
| |
| c = GETC(); |
| if (c == EOF) |
| return; /* Caller will issue error. */ |
| else |
| { |
| /* \n */ |
| if (CPP_OPTIONS (pfile)->output_conditionals) |
| { |
| CPP_PUTC (pfile, c); |
| pfile->lineno++; |
| } |
| CPP_BUMP_LINE (pfile); |
| } |
| } |
| |
| /* Back up to the beginning of this line. Caller will process the |
| directive. */ |
| CPP_BUFFER (pfile)->cur = beg_of_line; |
| pfile->only_seen_white = 1; |
| if (CPP_OPTIONS (pfile)->output_conditionals) |
| { |
| CPP_PUTS (pfile, "#endfailed\n", 11); |
| pfile->lineno++; |
| } |
| } |
| |
| /* |
| * 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 (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_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); |
| 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 (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_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 != 0) |
| { |
| /* This #endif matched a #ifndef at the start of the file. |
| See if it is at the end of the file. */ |
| int c; |
| |
| parse_set_mark (pfile); |
| |
| for (;;) |
| { |
| cpp_skip_hspace (pfile); |
| c = GETC (); |
| if (c != '\n') |
| break; |
| } |
| parse_goto_mark (pfile); |
| |
| if (c == EOF) |
| { |
| /* 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. */ |
| struct cpp_buffer *ip; |
| for (ip = CPP_BUFFER (pfile); ; ip = CPP_PREV_BUFFER (ip)) |
| if (ip->fname != NULL) |
| break; |
| ip->ihash->control_macro = (char *) temp->control_macro; |
| } |
| } |
| 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 (pfile, directive) |
| cpp_reader *pfile; |
| 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 (pfile) |
| cpp_reader *pfile; |
| { |
| register int c, c2, c3; |
| enum cpp_token token; |
| struct cpp_options *opts = CPP_OPTIONS (pfile); |
| |
| get_next: |
| c = GETC(); |
| if (c == EOF) |
| { |
| handle_eof: |
| if (CPP_BUFFER (pfile)->seen_eof) |
| { |
| if (CPP_PREV_BUFFER (CPP_BUFFER (pfile)) == CPP_NULL_BUFFER (pfile)) |
| return CPP_EOF; |
| |
| cpp_pop_buffer (pfile); |
| goto get_next; |
| } |
| 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 != CPP_NULL_BUFFER (pfile)) |
| { |
| /* 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) |
| { |
| case '/': |
| if (PEEKC () == '=') |
| goto op2; |
| |
| comment: |
| if (opts->put_out_comments) |
| c = copy_comment (pfile, c); |
| else |
| c = skip_comment (pfile, c); |
| if (c == EOF) |
| goto handle_eof; |
| else if (c != ' ') |
| goto randomchar; |
| |
| /* Comments are equivalent to spaces. |
| For -traditional, a comment is equivalent to nothing. */ |
| if (opts->traditional || opts->put_out_comments) |
| return CPP_COMMENT; |
| else |
| { |
| CPP_PUTC (pfile, c); |
| return CPP_HSPACE; |
| } |
| #if 0 |
| if (opts->for_lint) { |
| U_CHAR *argbp; |
| int cmdlen, arglen; |
| char *lintcmd = get_lintcmd (ibp, limit, &argbp, &arglen, &cmdlen); |
| |
| if (lintcmd != NULL) { |
| /* I believe it is always safe to emit this newline: */ |
| obp[-1] = '\n'; |
| bcopy ("#pragma lint ", (char *) obp, 13); |
| obp += 13; |
| bcopy (lintcmd, (char *) obp, cmdlen); |
| obp += cmdlen; |
| |
| if (arglen != 0) { |
| *(obp++) = ' '; |
| bcopy (argbp, (char *) obp, arglen); |
| obp += arglen; |
| } |
| |
| /* OK, now bring us back to the state we were in before we entered |
| this branch. We need #line because the newline for the pragma |
| could mess things up. */ |
| output_line_command (pfile, 0, same_file); |
| *(obp++) = ' '; /* just in case, if comments are copied thru */ |
| *(obp++) = '/'; |
| } |
| } |
| #endif |
| |
| case '#': |
| #if 0 |
| /* If this is expanding a macro definition, don't recognize |
| preprocessor directives. */ |
| if (ip->macro != 0) |
| goto randomchar; |
| /* If this is expand_into_temp_buffer, recognize them |
| only after an actual newline at this level, |
| not at the beginning of the input level. */ |
| if (ip->fname == 0 && beg_of_line == ip->buf) |
| goto randomchar; |
| if (ident_length) |
| goto specialchar; |
| #endif |
| |
| if (!pfile->only_seen_white) |
| goto randomchar; |
| if (handle_directive (pfile)) |
| return CPP_DIRECTIVE; |
| pfile->only_seen_white = 0; |
| return CPP_OTHER; |
| |
| case '\"': |
| case '\'': |
| string: |
| parse_string (pfile, c); |
| 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 '|': |
| c2 = PEEKC (); |
| if (c2 == c || c2 == '=') |
| goto op2; |
| goto randomchar; |
| |
| case '*': |
| case '!': |
| case '%': |
| case '=': |
| case '^': |
| if (PEEKC () == '=') |
| goto op2; |
| goto randomchar; |
| |
| case '-': |
| c2 = PEEKC (); |
| if (c2 == '-' && opts->chill) |
| goto comment; /* Chill style 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 (); |
| if (c == '\n' || c == EOF) |
| { |
| cpp_error (pfile, |
| "missing '>' in `#include <FILENAME>'"); |
| break; |
| } |
| else if (c == '\r') |
| { |
| /* Backslash newline is replaced by nothing. */ |
| CPP_ADJUST_WRITTEN (pfile, -1); |
| CPP_BUMP_LINE (pfile); |
| } |
| } |
| return CPP_STRING; |
| } |
| /* else fall through */ |
| case '>': |
| 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); |
| 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 (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 '.': |
| c2 = PEEKC (); |
| if (ISDIGIT(c2)) |
| { |
| CPP_RESERVE(pfile, 2); |
| CPP_PUTC_Q (pfile, '.'); |
| c = GETC (); |
| goto number; |
| } |
| 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; |
| CPP_RESERVE(pfile, 3); |
| CPP_PUTC_Q (pfile, c); |
| CPP_PUTC_Q (pfile, GETC ()); |
| CPP_NUL_TERMINATE_Q (pfile); |
| return token; |
| |
| case 'L': |
| 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); |
| c = PEEKC (); |
| if (c == EOF) |
| break; |
| if (!is_idchar[c] && c != '.' |
| && ((c2 != 'e' && c2 != 'E' |
| && ((c2 != 'p' && c2 != 'P') || CPP_C89 (pfile))) |
| || (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]) |
| 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; |
| unsigned 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 = pfile->token_buffer + before_name_written; |
| ident_len = CPP_PWRITTEN (pfile) - ident; |
| hp = cpp_lookup (pfile, 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 = 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) |
| { |
| int is_macro_call, macbuf_whitespace = 0; |
| |
| parse_set_mark (pfile); |
| for (;;) |
| { |
| cpp_skip_hspace (pfile); |
| c = PEEKC (); |
| is_macro_call = c == '('; |
| if (c != EOF) |
| { |
| if (c != '\n') |
| break; |
| FORWARD (1); |
| } |
| else |
| { |
| if (CPP_IS_MACRO_BUFFER (CPP_BUFFER (pfile))) |
| { |
| if (CPP_BUFFER (pfile)->mark != |
| (CPP_BUFFER (pfile)->cur |
| - CPP_BUFFER (pfile)->buf)) |
| macbuf_whitespace = 1; |
| |
| /* The mark goes away automatically when |
| the buffer is popped. */ |
| cpp_pop_buffer (pfile); |
| parse_set_mark (pfile); |
| } |
| else |
| break; |
| } |
| } |
| if (!is_macro_call) |
| { |
| parse_goto_mark (pfile); |
| if (macbuf_whitespace) |
| CPP_PUTC (pfile, ' '); |
| } |
| else |
| parse_clear_mark (pfile); |
| if (!is_macro_call) |
| return CPP_NAME; |
| } |
| /* This is now known to be a macro call. |
| 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); |
| } |
| goto get_next; |
| |
| case ' ': case '\t': case '\v': |
| for (;;) |
| { |
| CPP_PUTC (pfile, c); |
| c = PEEKC (); |
| if (c == EOF || !is_hor_space[c]) |
| break; |
| FORWARD(1); |
| } |
| return CPP_HSPACE; |
| |
| case '\\': |
| goto randomchar; |
| |
| case '\r': |
| /* Backslash newline is ignored. */ |
| CPP_BUMP_LINE (pfile); |
| goto get_next; |
| |
| case '\n': |
| CPP_PUTC (pfile, c); |
| if (pfile->only_seen_white == 0) |
| pfile->only_seen_white = 1; |
| CPP_BUMP_LINE (pfile); |
| pfile->lineno++; |
| if (CPP_BUFFER (pfile)->lineno != 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; |
| } |
| } |
| } |
| |
| /* Like cpp_get_token, but skip spaces and comments. */ |
| |
| enum cpp_token |
| cpp_get_non_space_token (pfile) |
| 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); |
| } |
| } |
| |
| /* Parse an identifier starting with C. */ |
| |
| static void |
| parse_name (pfile, c) |
| cpp_reader *pfile; |
| int c; |
| { |
| for (;;) |
| { |
| if (! is_idchar[c]) |
| { |
| FORWARD (-1); |
| break; |
| } |
| |
| if (c == '$' && CPP_PEDANTIC (pfile)) |
| cpp_pedwarn (pfile, "`$' in identifier"); |
| |
| 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; |
| } |
| |
| /* Parse a string starting with C. A single quoted string is treated |
| like a double -- some programs (e.g., troff) are perverse this way. |
| (However, a single quoted string is not allowed to extend over |
| multiple lines. */ |
| static void |
| parse_string (pfile, c) |
| cpp_reader *pfile; |
| int c; |
| { |
| long start_line, start_column; |
| |
| cpp_buf_line_and_col (cpp_file_buffer (pfile), &start_line, &start_column); |
| |
| 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_pop_buffer (pfile); |
| 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': |
| CPP_BUMP_LINE (pfile); |
| pfile->lineno++; |
| /* Traditionally, end of line ends a string constant with |
| no error. */ |
| if (CPP_TRADITIONAL (pfile)) |
| return; |
| /* Character constants may not extend over multiple lines. */ |
| if (c == '\'') |
| { |
| cpp_error_with_line (pfile, start_line, start_column, |
| "unterminated character constant"); |
| return; |
| } |
| 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 '\r': |
| /* Backslash newline is replaced by nothing at all. */ |
| CPP_ADJUST_WRITTEN (pfile, -1); |
| CPP_BUMP_LINE (pfile); |
| break; |
| |
| case '\\': |
| cc = GETC(); |
| if (cc != EOF) |
| CPP_PUTC (pfile, cc); |
| break; |
| |
| case '\"': |
| case '\'': |
| if (cc == c) |
| return; |
| break; |
| } |
| } |
| } |
| |
| /* Read an assertion into the token buffer, converting to |
| canonical form: `#predicate(a n swe r)' The next non-whitespace |
| character to read should be the first letter of the predicate. |
| Returns 0 for syntax error, 1 for bare predicate, 2 for predicate |
| with answer (see callers for why). In case of 0, an error has been |
| printed. */ |
| static int |
| parse_assertion (pfile) |
| cpp_reader *pfile; |
| { |
| int c, dropwhite; |
| cpp_skip_hspace (pfile); |
| c = PEEKC(); |
| if (! is_idstart[c]) |
| { |
| cpp_error (pfile, "assertion predicate is not an identifier"); |
| return 0; |
| } |
| CPP_PUTC(pfile, '#'); |
| FORWARD(1); |
| parse_name(pfile, c); |
| |
| c = PEEKC(); |
| if (c != '(') |
| { |
| if (is_hor_space[c] || c == '\r') |
| cpp_skip_hspace (pfile); |
| c = PEEKC(); |
| } |
| if (c != '(') |
| return 1; |
| |
| CPP_PUTC(pfile, '('); |
| FORWARD(1); |
| dropwhite = 1; |
| while ((c = GETC()) != ')') |
| { |
| if (is_hor_space[c]) |
| { |
| if (! dropwhite) |
| { |
| CPP_PUTC(pfile, ' '); |
| dropwhite = 1; |
| } |
| } |
| else if (c == '\n' || c == EOF) |
| { |
| if (c == '\n') FORWARD(-1); |
| cpp_error (pfile, "un-terminated assertion answer"); |
| return 0; |
| } |
| else if (c == '\r') |
| CPP_BUMP_LINE (pfile); |
| else |
| { |
| CPP_PUTC (pfile, c); |
| dropwhite = 0; |
| } |
| } |
| |
| if (pfile->limit[-1] == ' ') |
| pfile->limit[-1] = ')'; |
| else if (pfile->limit[-1] == '(') |
| { |
| cpp_error (pfile, "empty token sequence in assertion"); |
| return 0; |
| } |
| else |
| CPP_PUTC (pfile, ')'); |
| |
| CPP_NUL_TERMINATE (pfile); |
| return 2; |
| } |
| |
| static int |
| do_assert (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_UNUSED; |
| { |
| char *sym; |
| int ret, c; |
| HASHNODE *base, *this; |
| int baselen, thislen; |
| |
| 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); |
| sym = (char *) CPP_PWRITTEN (pfile); /* remember where it starts */ |
| ret = parse_assertion (pfile); |
| if (ret == 0) |
| goto error; |
| else if (ret == 1) |
| { |
| cpp_error (pfile, "missing token-sequence in `#assert'"); |
| goto error; |
| } |
| |
| cpp_skip_hspace (pfile); |
| c = PEEKC(); |
| if (c != EOF && c != '\n') |
| { |
| cpp_error (pfile, "junk at end of `#assert'"); |
| goto error; |
| } |
| |
| thislen = strlen (sym); |
| baselen = index (sym, '(') - sym; |
| this = cpp_lookup (pfile, sym, thislen, -1); |
| if (this) |
| { |
| cpp_warning (pfile, "`%s' re-asserted", sym); |
| goto error; |
| } |
| |
| base = cpp_lookup (pfile, sym, baselen, -1); |
| if (! base) |
| base = cpp_install (pfile, sym, baselen, T_ASSERT, 0, -1); |
| else if (base->type != T_ASSERT) |
| { |
| /* Token clash - but with what?! */ |
| cpp_fatal (pfile, |
| "cpp internal error: base->type != T_ASSERT in do_assert"); |
| goto error; |
| } |
| |
| this = cpp_install (pfile, sym, thislen, T_ASSERT, |
| (char *)base->value.aschain, -1); |
| base->value.aschain = this; |
| |
| pfile->limit = (unsigned char *) sym; /* Pop */ |
| return 0; |
| |
| error: |
| pfile->limit = (unsigned char *) sym; /* Pop */ |
| skip_rest_of_line (pfile); |
| return 1; |
| } |
| |
| static int |
| do_unassert (pfile, keyword) |
| cpp_reader *pfile; |
| struct directive *keyword ATTRIBUTE_UNUSED; |
| { |
| int c, ret; |
| char *sym; |
| long baselen, thislen; |
| HASHNODE *base, *this, *next; |
| |
| 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); |
| |
| sym = (char *) CPP_PWRITTEN (pfile); /* remember where it starts */ |
| ret = parse_assertion (pfile); |
| if (ret == 0) |
| goto error; |
| |
| cpp_skip_hspace (pfile); |
| c = PEEKC (); |
| if (c != EOF && c != '\n') |
| cpp_error (pfile, "junk at end of `#unassert'"); |
| |
| thislen = strlen (sym); |
| if (ret == 1) |
| { |
| base = cpp_lookup (pfile, sym, thislen, -1); |
| if (! base) |
| goto error; /* It isn't an error to #undef what isn't #defined, |
| so it isn't an error to #unassert what isn't |
| #asserted either. */ |
| |
| for (this = base->value.aschain; this; this = next) |
| { |
| next = this->value.aschain; |
| delete_macro (this); |
| } |
| delete_macro (base); |
| } |
| else |
| { |
| baselen = index (sym, '(') - sym; |
| base = cpp_lookup (pfile, sym, baselen, -1); |
| if (! base) goto error; |
| this = cpp_lookup (pfile, sym, thislen, -1); |
| if (! this) goto error; |
| |
| next = base; |
| while (next->value.aschain != this) |
| next = next->value.aschain; |
| |
| next->value.aschain = this->value.aschain; |
| delete_macro (this); |
| |
| if (base->value.aschain == NULL) |
| delete_macro (base); /* Last answer for this predicate deleted. */ |
| } |
| |
| pfile->limit = (unsigned char *) sym; /* Pop */ |
| return 0; |
| error: |
| pfile->limit = (unsigned char *) sym; /* Pop */ |
| skip_rest_of_line (pfile); |
| return 1; |
| } |
| |
| /* Process STR as if it appeared as the body of an #unassert. */ |
| void |
| cpp_unassert (pfile, str) |
| cpp_reader *pfile; |
| unsigned char *str; |
| { |
| if (cpp_push_buffer (pfile, str, strlen (str)) != NULL) |
| { |
| do_assert (pfile, NULL); |
| cpp_pop_buffer (pfile); |
| } |
| } |
| |
| int |
| cpp_read_check_assertion (pfile) |
| cpp_reader *pfile; |
| { |
| U_CHAR *name = CPP_PWRITTEN (pfile); |
| int result; |
| HASHNODE *hp; |
| |
| FORWARD (1); /* Skip '#' */ |
| cpp_skip_hspace (pfile); |
| if (! parse_assertion (pfile)) |
| result = 0; |
| else |
| { |
| hp = cpp_lookup (pfile, name, CPP_PWRITTEN (pfile) - name, -1); |
| result = (hp != 0); |
| } |
| |
| pfile->limit = name; |
| return result; |
| } |
| |
| /* Remember the current position of PFILE. */ |
| |
| void |
| parse_set_mark (pfile) |
| cpp_reader *pfile; |
| { |
| cpp_buffer *ip = CPP_BUFFER (pfile); |
| if (ip->mark != -1) |
| cpp_fatal (pfile, |
| "cpp internal error: ip->mark != -1 in parse_set_mark"); |
| |
| ip->mark = ip->cur - ip->buf; |
| } |
| |
| /* Clear the current mark - we no longer need it. */ |
| |
| void |
| parse_clear_mark (pfile) |
| cpp_reader *pfile; |
| { |
| cpp_buffer *ip = CPP_BUFFER (pfile); |
| if (ip->mark == -1) |
| cpp_fatal (pfile, |
| "cpp internal error: ip->mark == -1 in parse_clear_mark"); |
| |
| ip->mark = -1; |
| } |
| |
| /* Backup the current position of PFILE to that saved in its mark, |
| and clear the mark. */ |
| |
| void |
| parse_goto_mark (pfile) |
| cpp_reader *pfile; |
| { |
| cpp_buffer *ip = CPP_BUFFER (pfile); |
| if (ip->mark == -1) |
| cpp_fatal (pfile, |
| "cpp internal error: ip->mark == -1 in parse_goto_mark"); |
| |
| ip->cur = ip->buf + ip->mark; |
| ip->mark = -1; |
| } |
| |
| void |
| cpp_print_file_and_line (pfile) |
| cpp_reader *pfile; |
| { |
| cpp_buffer *ip = cpp_file_buffer (pfile); |
| |
| if (ip != NULL) |
| { |
| 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 |
| v_cpp_error (pfile, msgid, ap) |
| cpp_reader *pfile; |
| const char *msgid; |
| va_list ap; |
| { |
| cpp_print_containing_files (pfile); |
| cpp_print_file_and_line (pfile); |
| v_cpp_message (pfile, 1, msgid, ap); |
| } |
| |
| void |
| cpp_error VPROTO ((cpp_reader * pfile, const char *msgid, ...)) |
| { |
| #ifndef ANSI_PROTOTYPES |
| cpp_reader *pfile; |
| const char *msgid; |
| #endif |
| va_list ap; |
| |
| VA_START(ap, msgid); |
| |
| #ifndef ANSI_PROTOTYPES |
| pfile = va_arg (ap, cpp_reader *); |
| msgid = va_arg (ap, const char *); |
| #endif |
| |
| v_cpp_error (pfile, msgid, ap); |
| va_end(ap); |
| } |
| |
| /* Print error message but don't count it. */ |
| |
| static void |
| v_cpp_warning (pfile, msgid, ap) |
| cpp_reader *pfile; |
| const char *msgid; |
| va_list ap; |
| { |
| 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); |
| v_cpp_message (pfile, 0, msgid, ap); |
| } |
| |
| void |
| cpp_warning VPROTO ((cpp_reader * pfile, const char *msgid, ...)) |
| { |
| #ifndef ANSI_PROTOTYPES |
| cpp_reader *pfile; |
| const char *msgid; |
| #endif |
| va_list ap; |
| |
| VA_START (ap, msgid); |
| |
| #ifndef ANSI_PROTOTYPES |
| pfile = va_arg (ap, cpp_reader *); |
| msgid = va_arg (ap, const char *); |
| #endif |
| |
| v_cpp_warning (pfile, msgid, ap); |
| va_end(ap); |
| } |
| |
| /* Print an error message and maybe count it. */ |
| |
| void |
| cpp_pedwarn VPROTO ((cpp_reader * pfile, const char *msgid, ...)) |
| { |
| #ifndef ANSI_PROTOTYPES |
| cpp_reader *pfile; |
| const char *msgid; |
| #endif |
| va_list ap; |
| |
| VA_START (ap, msgid); |
| |
| #ifndef ANSI_PROTOTYPES |
| pfile = va_arg (ap, cpp_reader *); |
| msgid = va_arg (ap, const char *); |
| #endif |
| |
| if (CPP_OPTIONS (pfile)->pedantic_errors) |
| v_cpp_error (pfile, msgid, ap); |
| else |
| v_cpp_warning (pfile, msgid, ap); |
| va_end(ap); |
| } |
| |
| static void |
| v_cpp_error_with_line (pfile, line, column, msgid, ap) |
| cpp_reader * pfile; |
| int line; |
| int column; |
| const char * msgid; |
| va_list ap; |
| { |
| cpp_buffer *ip = cpp_file_buffer (pfile); |
| |
| cpp_print_containing_files (pfile); |
| |
| if (ip != NULL) |
| cpp_file_line_for_message (pfile, ip->nominal_fname, line, column); |
| |
| v_cpp_message (pfile, 1, msgid, ap); |
| } |
| |
| void |
| cpp_error_with_line VPROTO ((cpp_reader * pfile, int line, int column, |
| const char *msgid, ...)) |
| { |
| #ifndef ANSI_PROTOTYPES |
| cpp_reader *pfile; |
| int line; |
| int column; |
| const char *msgid; |
| #endif |
| va_list ap; |
| |
| VA_START (ap, msgid); |
| |
| #ifndef ANSI_PROTOTYPES |
| pfile = va_arg (ap, cpp_reader *); |
| line = va_arg (ap, int); |
| column = va_arg (ap, int); |
| msgid = va_arg (ap, const char *); |
| #endif |
| |
| v_cpp_error_with_line(pfile, line, column, msgid, ap); |
| va_end(ap); |
| } |
| |
| static void |
| v_cpp_warning_with_line (pfile, line, column, msgid, ap) |
| cpp_reader * pfile; |
| int line; |
| int column; |
| const char *msgid; |
| va_list ap; |
| { |
| 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 != NULL) |
| cpp_file_line_for_message (pfile, ip->nominal_fname, line, column); |
| |
| v_cpp_message (pfile, 0, msgid, ap); |
| } |
| |
| void |
| cpp_warning_with_line VPROTO ((cpp_reader * pfile, int line, int column, |
| const char *msgid, ...)) |
| { |
| #ifndef ANSI_PROTOTYPES |
| cpp_reader *pfile; |
| int line; |
| int column; |
| const char *msgid; |
| #endif |
| va_list ap; |
| |
| VA_START (ap, msgid); |
| |
| #ifndef ANSI_PROTOTYPES |
| pfile = va_arg (ap, cpp_reader *); |
| line = va_arg (ap, int); |
| column = va_arg (ap, int); |
| msgid = va_arg (ap, const char *); |
| #endif |
| |
| v_cpp_warning_with_line (pfile, line, column, msgid, ap); |
| va_end(ap); |
| } |
| |
| void |
| cpp_pedwarn_with_line VPROTO ((cpp_reader * pfile, int line, int column, |
| const char *msgid, ...)) |
| { |
| #ifndef ANSI_PROTOTYPES |
| cpp_reader *pfile; |
| int line; |
| int column; |
| const char *msgid; |
| #endif |
| va_list ap; |
| |
| VA_START (ap, msgid); |
| |
| #ifndef ANSI_PROTOTYPES |
| pfile = va_arg (ap, cpp_reader *); |
| line = va_arg (ap, int); |
| column = va_arg (ap, int); |
| msgid = va_arg (ap, const char *); |
| #endif |
| |
| if (CPP_OPTIONS (pfile)->pedantic_errors) |
| v_cpp_error_with_line (pfile, column, line, msgid, ap); |
| else |
| v_cpp_warning_with_line (pfile, line, column, msgid, ap); |
| va_end(ap); |
| } |
| |
| /* 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 VPROTO ((cpp_reader *pfile, char *file, int line, |
| const char *msgid, ...)) |
| { |
| #ifndef ANSI_PROTOTYPES |
| cpp_reader *pfile; |
| char *file; |
| int line; |
| const char *msgid; |
| #endif |
| va_list ap; |
| |
| VA_START (ap, msgid); |
| |
| #ifndef ANSI_PROTOTYPES |
| pfile = va_arg (ap, cpp_reader *); |
| file = va_arg (ap, char *); |
| line = va_arg (ap, int); |
| msgid = va_arg (ap, const char *); |
| #endif |
| |
| if (!CPP_OPTIONS (pfile)->pedantic_errors |
| && CPP_OPTIONS (pfile)->inhibit_warnings) |
| return; |
| if (file != NULL) |
| cpp_file_line_for_message (pfile, file, line, -1); |
| v_cpp_message (pfile, CPP_OPTIONS (pfile)->pedantic_errors, msgid, ap); |
| va_end(ap); |
| } |
| |
| /* my_strerror - return the descriptive text associated with an |
| `errno' code. */ |
| |
| static char * |
| my_strerror (errnum) |
| int errnum; |
| { |
| char *result; |
| |
| #ifndef VMS |
| #ifndef HAVE_STRERROR |
| result = (char *) ((errnum < sys_nerr) ? sys_errlist[errnum] : 0); |
| #else |
| result = strerror (errnum); |
| #endif |
| #else /* VMS */ |
| /* VAXCRTL's strerror() takes an optional second argument, which only |
| matters when the first argument is EVMSERR. However, it's simplest |
| just to pass it unconditionally. `vaxc$errno' is declared in |
| <errno.h>, and maintained by the library in parallel with `errno'. |
| We assume that caller's `errnum' either matches the last setting of |
| `errno' by the library or else does not have the value `EVMSERR'. */ |
| |
| result = strerror (errnum, vaxc$errno); |
| #endif |
| |
| if (!result) |
| result = "errno = ?"; |
| |
| return result; |
| } |
| |
| /* Error including a message from `errno'. */ |
| |
| void |
| cpp_error_from_errno (pfile, name) |
| cpp_reader *pfile; |
| const char *name; |
| { |
| cpp_message_from_errno (pfile, 1, name); |
| } |
| |
| void |
| cpp_message_from_errno (pfile, is_error, name) |
| cpp_reader *pfile; |
| int is_error; |
| const char *name; |
| { |
| int e = errno; |
| cpp_buffer *ip = cpp_file_buffer (pfile); |
| |
| cpp_print_containing_files (pfile); |
| |
| if (ip != NULL) |
| cpp_file_line_for_message (pfile, ip->nominal_fname, ip->lineno, -1); |
| |
| cpp_message (pfile, is_error, "%s: %s", name, my_strerror (e)); |
| } |
| |
| void |
| cpp_perror_with_name (pfile, 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. |
| * |
| * Find and cleanup remaining uses of static variables, |
| * |
| * Support -dM flag (dump_all_macros). |
| * |
| * Support for_lint flag. |
| */ |