| /* CPP Library - traditional lexical analysis and macro expansion. |
| Copyright (C) 2002-2021 Free Software Foundation, Inc. |
| Contributed by Neil Booth, May 2002 |
| |
| 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 3, 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; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "cpplib.h" |
| #include "internal.h" |
| |
| /* The replacement text of a function-like macro is stored as a |
| contiguous sequence of aligned blocks, each representing the text |
| between subsequent parameters. |
| |
| Each block comprises the text between its surrounding parameters, |
| the length of that text, and the one-based index of the following |
| parameter. The final block in the replacement text is easily |
| recognizable as it has an argument index of zero. */ |
| |
| struct block |
| { |
| unsigned int text_len; |
| unsigned short arg_index; |
| uchar text[1]; |
| }; |
| |
| #define BLOCK_HEADER_LEN offsetof (struct block, text) |
| #define BLOCK_LEN(TEXT_LEN) CPP_ALIGN (BLOCK_HEADER_LEN + (TEXT_LEN)) |
| |
| /* Structure holding information about a function-like macro |
| invocation. */ |
| struct fun_macro |
| { |
| /* Memory buffer holding the trad_arg array. */ |
| _cpp_buff *buff; |
| |
| /* An array of size the number of macro parameters + 1, containing |
| the offsets of the start of each macro argument in the output |
| buffer. The argument continues until the character before the |
| start of the next one. */ |
| size_t *args; |
| |
| /* The hashnode of the macro. */ |
| cpp_hashnode *node; |
| |
| /* The offset of the macro name in the output buffer. */ |
| size_t offset; |
| |
| /* The line the macro name appeared on. */ |
| location_t line; |
| |
| /* Number of parameters. */ |
| unsigned int paramc; |
| |
| /* Zero-based index of argument being currently lexed. */ |
| unsigned int argc; |
| }; |
| |
| /* Lexing state. It is mostly used to prevent macro expansion. */ |
| enum ls {ls_none = 0, /* Normal state. */ |
| ls_fun_open, /* When looking for '('. */ |
| ls_fun_close, /* When looking for ')'. */ |
| ls_defined, /* After defined. */ |
| ls_defined_close, /* Looking for ')' of defined(). */ |
| ls_hash, /* After # in preprocessor conditional. */ |
| ls_predicate, /* After the predicate, maybe paren? */ |
| ls_answer /* In answer to predicate. */ |
| }; |
| |
| /* Lexing TODO: Maybe handle space in escaped newlines. Stop lex.c |
| from recognizing comments and directives during its lexing pass. */ |
| |
| static const uchar *skip_whitespace (cpp_reader *, const uchar *, int); |
| static cpp_hashnode *lex_identifier (cpp_reader *, const uchar *); |
| static const uchar *copy_comment (cpp_reader *, const uchar *, int); |
| static void check_output_buffer (cpp_reader *, size_t); |
| static void push_replacement_text (cpp_reader *, cpp_hashnode *); |
| static bool scan_parameters (cpp_reader *, unsigned *); |
| static bool recursive_macro (cpp_reader *, cpp_hashnode *); |
| static void save_replacement_text (cpp_reader *, cpp_macro *, unsigned int); |
| static void maybe_start_funlike (cpp_reader *, cpp_hashnode *, const uchar *, |
| struct fun_macro *); |
| static void save_argument (struct fun_macro *, size_t); |
| static void replace_args_and_push (cpp_reader *, struct fun_macro *); |
| static size_t canonicalize_text (uchar *, const uchar *, size_t, uchar *); |
| |
| /* Ensures we have N bytes' space in the output buffer, and |
| reallocates it if not. */ |
| static void |
| check_output_buffer (cpp_reader *pfile, size_t n) |
| { |
| /* We might need two bytes to terminate an unterminated comment, and |
| one more to terminate the line with a NUL. */ |
| n += 2 + 1; |
| |
| if (n > (size_t) (pfile->out.limit - pfile->out.cur)) |
| { |
| size_t size = pfile->out.cur - pfile->out.base; |
| size_t new_size = (size + n) * 3 / 2; |
| |
| pfile->out.base = XRESIZEVEC (unsigned char, pfile->out.base, new_size); |
| pfile->out.limit = pfile->out.base + new_size; |
| pfile->out.cur = pfile->out.base + size; |
| } |
| } |
| |
| /* Skip a C-style block comment in a macro as a result of -CC. |
| PFILE->buffer->cur points to the initial asterisk of the comment, |
| change it to point to after the '*' and '/' characters that terminate it. |
| Return true if the macro has not been termined, in that case set |
| PFILE->buffer->cur to the end of the buffer. */ |
| static bool |
| skip_macro_block_comment (cpp_reader *pfile) |
| { |
| const uchar *cur = pfile->buffer->cur; |
| |
| cur++; |
| if (*cur == '/') |
| cur++; |
| |
| /* People like decorating comments with '*', so check for '/' |
| instead for efficiency. */ |
| while (! (*cur++ == '/' && cur[-2] == '*')) |
| if (cur[-1] == '\n') |
| { |
| pfile->buffer->cur = cur - 1; |
| return true; |
| } |
| |
| pfile->buffer->cur = cur; |
| return false; |
| } |
| |
| /* CUR points to the asterisk introducing a comment in the current |
| context. IN_DEFINE is true if we are in the replacement text of a |
| macro. |
| |
| The asterisk and following comment is copied to the buffer pointed |
| to by pfile->out.cur, which must be of sufficient size. |
| Unterminated comments are diagnosed, and correctly terminated in |
| the output. pfile->out.cur is updated depending upon IN_DEFINE, |
| -C, -CC and pfile->state.in_directive. |
| |
| Returns a pointer to the first character after the comment in the |
| input buffer. */ |
| static const uchar * |
| copy_comment (cpp_reader *pfile, const uchar *cur, int in_define) |
| { |
| bool unterminated, copy = false; |
| location_t src_loc = pfile->line_table->highest_line; |
| cpp_buffer *buffer = pfile->buffer; |
| |
| buffer->cur = cur; |
| if (pfile->context->prev) |
| unterminated = skip_macro_block_comment (pfile); |
| else |
| unterminated = _cpp_skip_block_comment (pfile); |
| |
| if (unterminated) |
| cpp_error_with_line (pfile, CPP_DL_ERROR, src_loc, 0, |
| "unterminated comment"); |
| |
| /* Comments in directives become spaces so that tokens are properly |
| separated when the ISO preprocessor re-lexes the line. The |
| exception is #define. */ |
| if (pfile->state.in_directive) |
| { |
| if (in_define) |
| { |
| if (CPP_OPTION (pfile, discard_comments_in_macro_exp)) |
| pfile->out.cur--; |
| else |
| copy = true; |
| } |
| else |
| pfile->out.cur[-1] = ' '; |
| } |
| else if (CPP_OPTION (pfile, discard_comments)) |
| pfile->out.cur--; |
| else |
| copy = true; |
| |
| if (copy) |
| { |
| size_t len = (size_t) (buffer->cur - cur); |
| memcpy (pfile->out.cur, cur, len); |
| pfile->out.cur += len; |
| if (unterminated) |
| { |
| *pfile->out.cur++ = '*'; |
| *pfile->out.cur++ = '/'; |
| } |
| } |
| |
| return buffer->cur; |
| } |
| |
| /* CUR points to any character in the input buffer. Skips over all |
| contiguous horizontal white space and NULs, including comments if |
| SKIP_COMMENTS, until reaching the first non-horizontal-whitespace |
| character or the end of the current context. Escaped newlines are |
| removed. |
| |
| The whitespace is copied verbatim to the output buffer, except that |
| comments are handled as described in copy_comment(). |
| pfile->out.cur is updated. |
| |
| Returns a pointer to the first character after the whitespace in |
| the input buffer. */ |
| static const uchar * |
| skip_whitespace (cpp_reader *pfile, const uchar *cur, int skip_comments) |
| { |
| uchar *out = pfile->out.cur; |
| |
| for (;;) |
| { |
| unsigned int c = *cur++; |
| *out++ = c; |
| |
| if (is_nvspace (c)) |
| continue; |
| |
| if (c == '/' && *cur == '*' && skip_comments) |
| { |
| pfile->out.cur = out; |
| cur = copy_comment (pfile, cur, false /* in_define */); |
| out = pfile->out.cur; |
| continue; |
| } |
| |
| out--; |
| break; |
| } |
| |
| pfile->out.cur = out; |
| return cur - 1; |
| } |
| |
| /* Lexes and outputs an identifier starting at CUR, which is assumed |
| to point to a valid first character of an identifier. Returns |
| the hashnode, and updates out.cur. */ |
| static cpp_hashnode * |
| lex_identifier (cpp_reader *pfile, const uchar *cur) |
| { |
| size_t len; |
| uchar *out = pfile->out.cur; |
| cpp_hashnode *result; |
| |
| do |
| *out++ = *cur++; |
| while (is_numchar (*cur)); |
| |
| CUR (pfile->context) = cur; |
| len = out - pfile->out.cur; |
| result = CPP_HASHNODE (ht_lookup (pfile->hash_table, pfile->out.cur, |
| len, HT_ALLOC)); |
| pfile->out.cur = out; |
| return result; |
| } |
| |
| /* Overlays the true file buffer temporarily with text of length LEN |
| starting at START. The true buffer is restored upon calling |
| restore_buff(). */ |
| void |
| _cpp_overlay_buffer (cpp_reader *pfile, const uchar *start, size_t len) |
| { |
| cpp_buffer *buffer = pfile->buffer; |
| |
| pfile->overlaid_buffer = buffer; |
| pfile->saved_cur = buffer->cur; |
| pfile->saved_rlimit = buffer->rlimit; |
| pfile->saved_line_base = buffer->next_line; |
| buffer->need_line = false; |
| |
| buffer->cur = start; |
| buffer->line_base = start; |
| buffer->rlimit = start + len; |
| } |
| |
| /* Restores a buffer overlaid by _cpp_overlay_buffer(). */ |
| void |
| _cpp_remove_overlay (cpp_reader *pfile) |
| { |
| cpp_buffer *buffer = pfile->overlaid_buffer; |
| |
| buffer->cur = pfile->saved_cur; |
| buffer->rlimit = pfile->saved_rlimit; |
| buffer->line_base = pfile->saved_line_base; |
| buffer->need_line = true; |
| |
| pfile->overlaid_buffer = NULL; |
| } |
| |
| /* Reads a logical line into the output buffer. Returns TRUE if there |
| is more text left in the buffer. */ |
| bool |
| _cpp_read_logical_line_trad (cpp_reader *pfile) |
| { |
| do |
| { |
| if (pfile->buffer->need_line && !_cpp_get_fresh_line (pfile)) |
| { |
| /* Now pop the buffer that _cpp_get_fresh_line did not. */ |
| _cpp_pop_buffer (pfile); |
| return false; |
| } |
| } |
| while (!_cpp_scan_out_logical_line (pfile, NULL, false) |
| || pfile->state.skipping); |
| |
| return pfile->buffer != NULL; |
| } |
| |
| /* Return true if NODE is a fun_like macro. */ |
| static inline bool |
| fun_like_macro (cpp_hashnode *node) |
| { |
| if (cpp_builtin_macro_p (node)) |
| return (node->value.builtin == BT_HAS_ATTRIBUTE |
| || node->value.builtin == BT_HAS_STD_ATTRIBUTE |
| || node->value.builtin == BT_HAS_BUILTIN |
| || node->value.builtin == BT_HAS_INCLUDE |
| || node->value.builtin == BT_HAS_INCLUDE_NEXT); |
| return node->value.macro->fun_like; |
| } |
| |
| /* Set up state for finding the opening '(' of a function-like |
| macro. */ |
| static void |
| maybe_start_funlike (cpp_reader *pfile, cpp_hashnode *node, const uchar *start, |
| struct fun_macro *macro) |
| { |
| unsigned int n; |
| if (cpp_builtin_macro_p (node)) |
| n = 1; |
| else |
| n = node->value.macro->paramc; |
| |
| if (macro->buff) |
| _cpp_release_buff (pfile, macro->buff); |
| macro->buff = _cpp_get_buff (pfile, (n + 1) * sizeof (size_t)); |
| macro->args = (size_t *) BUFF_FRONT (macro->buff); |
| macro->node = node; |
| macro->offset = start - pfile->out.base; |
| macro->paramc = n; |
| macro->argc = 0; |
| } |
| |
| /* Save the OFFSET of the start of the next argument to MACRO. */ |
| static void |
| save_argument (struct fun_macro *macro, size_t offset) |
| { |
| macro->argc++; |
| if (macro->argc <= macro->paramc) |
| macro->args[macro->argc] = offset; |
| } |
| |
| /* Copies the next logical line in the current buffer (starting at |
| buffer->cur) to the output buffer. The output is guaranteed to |
| terminate with a NUL character. buffer->cur is updated. |
| |
| If MACRO is non-NULL, then we are scanning the replacement list of |
| MACRO, and we call save_replacement_text() every time we meet an |
| argument. |
| |
| If BUILTIN_MACRO_ARG is true, this is called to macro expand |
| arguments of builtin function-like macros. */ |
| bool |
| _cpp_scan_out_logical_line (cpp_reader *pfile, cpp_macro *macro, |
| bool builtin_macro_arg) |
| { |
| bool result = true; |
| cpp_context *context; |
| const uchar *cur; |
| uchar *out; |
| struct fun_macro fmacro; |
| unsigned int c, paren_depth = 0, quote; |
| enum ls lex_state = ls_none; |
| bool header_ok; |
| const uchar *start_of_input_line; |
| |
| fmacro.buff = NULL; |
| fmacro.args = NULL; |
| fmacro.node = NULL; |
| fmacro.offset = 0; |
| fmacro.line = 0; |
| fmacro.paramc = 0; |
| fmacro.argc = 0; |
| |
| quote = 0; |
| header_ok = pfile->state.angled_headers; |
| CUR (pfile->context) = pfile->buffer->cur; |
| RLIMIT (pfile->context) = pfile->buffer->rlimit; |
| if (!builtin_macro_arg) |
| { |
| pfile->out.cur = pfile->out.base; |
| pfile->out.first_line = pfile->line_table->highest_line; |
| } |
| /* start_of_input_line is needed to make sure that directives really, |
| really start at the first character of the line. */ |
| start_of_input_line = pfile->buffer->cur; |
| new_context: |
| context = pfile->context; |
| cur = CUR (context); |
| check_output_buffer (pfile, RLIMIT (context) - cur); |
| out = pfile->out.cur; |
| |
| for (;;) |
| { |
| if (!context->prev |
| && !builtin_macro_arg |
| && cur >= pfile->buffer->notes[pfile->buffer->cur_note].pos) |
| { |
| pfile->buffer->cur = cur; |
| _cpp_process_line_notes (pfile, false); |
| } |
| c = *cur++; |
| *out++ = c; |
| |
| /* Whitespace should "continue" out of the switch, |
| non-whitespace should "break" out of it. */ |
| switch (c) |
| { |
| case ' ': |
| case '\t': |
| case '\f': |
| case '\v': |
| case '\0': |
| continue; |
| |
| case '\n': |
| /* If this is a macro's expansion, pop it. */ |
| if (context->prev) |
| { |
| pfile->out.cur = out - 1; |
| _cpp_pop_context (pfile); |
| goto new_context; |
| } |
| |
| /* Omit the newline from the output buffer. */ |
| pfile->out.cur = out - 1; |
| pfile->buffer->cur = cur; |
| if (builtin_macro_arg) |
| goto done; |
| pfile->buffer->need_line = true; |
| CPP_INCREMENT_LINE (pfile, 0); |
| |
| if ((lex_state == ls_fun_open || lex_state == ls_fun_close) |
| && !pfile->state.in_directive |
| && _cpp_get_fresh_line (pfile)) |
| { |
| /* Newlines in arguments become a space, but we don't |
| clear any in-progress quote. */ |
| if (lex_state == ls_fun_close) |
| out[-1] = ' '; |
| cur = pfile->buffer->cur; |
| continue; |
| } |
| goto done; |
| |
| case '<': |
| if (header_ok) |
| quote = '>'; |
| break; |
| case '>': |
| if (c == quote) |
| quote = 0; |
| break; |
| |
| case '"': |
| case '\'': |
| if (c == quote) |
| quote = 0; |
| else if (!quote) |
| quote = c; |
| break; |
| |
| case '\\': |
| /* Skip escaped quotes here, it's easier than above. */ |
| if (*cur == '\\' || *cur == '"' || *cur == '\'') |
| *out++ = *cur++; |
| break; |
| |
| case '/': |
| /* Traditional CPP does not recognize comments within |
| literals. */ |
| if (!quote && *cur == '*') |
| { |
| pfile->out.cur = out; |
| cur = copy_comment (pfile, cur, macro != 0); |
| out = pfile->out.cur; |
| continue; |
| } |
| break; |
| |
| case '_': |
| case 'a': case 'b': case 'c': case 'd': case 'e': 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 'E': 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': |
| if (!pfile->state.skipping && (quote == 0 || macro)) |
| { |
| cpp_hashnode *node; |
| uchar *out_start = out - 1; |
| |
| pfile->out.cur = out_start; |
| node = lex_identifier (pfile, cur - 1); |
| out = pfile->out.cur; |
| cur = CUR (context); |
| |
| if (cpp_macro_p (node) |
| /* Should we expand for ls_answer? */ |
| && (lex_state == ls_none || lex_state == ls_fun_open) |
| && !pfile->state.prevent_expansion) |
| { |
| /* Macros invalidate MI optimization. */ |
| pfile->mi_valid = false; |
| if (fun_like_macro (node)) |
| { |
| maybe_start_funlike (pfile, node, out_start, &fmacro); |
| lex_state = ls_fun_open; |
| fmacro.line = pfile->line_table->highest_line; |
| continue; |
| } |
| else if (!recursive_macro (pfile, node)) |
| { |
| /* Remove the object-like macro's name from the |
| output, and push its replacement text. */ |
| pfile->out.cur = out_start; |
| push_replacement_text (pfile, node); |
| lex_state = ls_none; |
| goto new_context; |
| } |
| } |
| else if (macro && node->type == NT_MACRO_ARG) |
| { |
| /* Found a parameter in the replacement text of a |
| #define. Remove its name from the output. */ |
| pfile->out.cur = out_start; |
| save_replacement_text (pfile, macro, node->value.arg_index); |
| out = pfile->out.base; |
| } |
| else if (lex_state == ls_hash) |
| { |
| lex_state = ls_predicate; |
| continue; |
| } |
| else if (pfile->state.in_expression |
| && node == pfile->spec_nodes.n_defined) |
| { |
| lex_state = ls_defined; |
| continue; |
| } |
| } |
| break; |
| |
| case '(': |
| if (quote == 0) |
| { |
| paren_depth++; |
| if (lex_state == ls_fun_open) |
| { |
| if (recursive_macro (pfile, fmacro.node)) |
| lex_state = ls_none; |
| else |
| { |
| lex_state = ls_fun_close; |
| paren_depth = 1; |
| out = pfile->out.base + fmacro.offset; |
| fmacro.args[0] = fmacro.offset; |
| } |
| } |
| else if (lex_state == ls_predicate) |
| lex_state = ls_answer; |
| else if (lex_state == ls_defined) |
| lex_state = ls_defined_close; |
| } |
| break; |
| |
| case ',': |
| if (quote == 0 && lex_state == ls_fun_close && paren_depth == 1) |
| save_argument (&fmacro, out - pfile->out.base); |
| break; |
| |
| case ')': |
| if (quote == 0) |
| { |
| paren_depth--; |
| if (lex_state == ls_fun_close && paren_depth == 0) |
| { |
| if (cpp_builtin_macro_p (fmacro.node)) |
| { |
| /* Handle builtin function-like macros like |
| __has_attribute. The already parsed arguments |
| are put into a buffer, which is then preprocessed |
| and the result is fed to _cpp_push_text_context |
| with disabled expansion, where the ISO preprocessor |
| parses it. While in traditional preprocessing |
| macro arguments aren't immediately expanded, they in |
| the end are because the macro with replaced arguments |
| is preprocessed again. For the builtin function-like |
| macros we need the argument immediately though, |
| if we don't preprocess them, they would behave |
| very differently from ISO preprocessor handling |
| of those builtin macros. So, this handling is |
| more similar to traditional preprocessing of |
| #if directives, where we also keep preprocessing |
| until everything is expanded, and then feed the |
| result with disabled expansion to ISO preprocessor |
| for handling the directives. */ |
| lex_state = ls_none; |
| save_argument (&fmacro, out - pfile->out.base); |
| cpp_macro m; |
| memset (&m, '\0', sizeof (m)); |
| m.paramc = fmacro.paramc; |
| if (_cpp_arguments_ok (pfile, &m, fmacro.node, |
| fmacro.argc)) |
| { |
| size_t len = fmacro.args[1] - fmacro.args[0]; |
| uchar *buf; |
| |
| /* Remove the macro's invocation from the |
| output, and push its replacement text. */ |
| pfile->out.cur = pfile->out.base + fmacro.offset; |
| CUR (context) = cur; |
| buf = _cpp_unaligned_alloc (pfile, len + 2); |
| buf[0] = '('; |
| memcpy (buf + 1, pfile->out.base + fmacro.args[0], |
| len); |
| buf[len + 1] = '\n'; |
| |
| const unsigned char *ctx_rlimit = RLIMIT (context); |
| const unsigned char *saved_cur = pfile->buffer->cur; |
| const unsigned char *saved_rlimit |
| = pfile->buffer->rlimit; |
| const unsigned char *saved_line_base |
| = pfile->buffer->line_base; |
| bool saved_need_line = pfile->buffer->need_line; |
| cpp_buffer *saved_overlaid_buffer |
| = pfile->overlaid_buffer; |
| pfile->buffer->cur = buf; |
| pfile->buffer->line_base = buf; |
| pfile->buffer->rlimit = buf + len + 1; |
| pfile->buffer->need_line = false; |
| pfile->overlaid_buffer = pfile->buffer; |
| bool saved_in_directive = pfile->state.in_directive; |
| pfile->state.in_directive = true; |
| cpp_context *saved_prev_context = context->prev; |
| context->prev = NULL; |
| |
| _cpp_scan_out_logical_line (pfile, NULL, true); |
| |
| pfile->state.in_directive = saved_in_directive; |
| check_output_buffer (pfile, 1); |
| *pfile->out.cur = '\n'; |
| pfile->buffer->cur = pfile->out.base + fmacro.offset; |
| pfile->buffer->line_base = pfile->buffer->cur; |
| pfile->buffer->rlimit = pfile->out.cur; |
| CUR (context) = pfile->buffer->cur; |
| RLIMIT (context) = pfile->buffer->rlimit; |
| |
| pfile->state.prevent_expansion++; |
| const uchar *text |
| = _cpp_builtin_macro_text (pfile, fmacro.node); |
| pfile->state.prevent_expansion--; |
| |
| context->prev = saved_prev_context; |
| pfile->buffer->cur = saved_cur; |
| pfile->buffer->rlimit = saved_rlimit; |
| pfile->buffer->line_base = saved_line_base; |
| pfile->buffer->need_line = saved_need_line; |
| pfile->overlaid_buffer = saved_overlaid_buffer; |
| pfile->out.cur = pfile->out.base + fmacro.offset; |
| CUR (context) = cur; |
| RLIMIT (context) = ctx_rlimit; |
| len = ustrlen (text); |
| buf = _cpp_unaligned_alloc (pfile, len + 1); |
| memcpy (buf, text, len); |
| buf[len] = '\n'; |
| text = buf; |
| _cpp_push_text_context (pfile, fmacro.node, |
| text, len); |
| goto new_context; |
| } |
| break; |
| } |
| |
| cpp_macro *m = fmacro.node->value.macro; |
| |
| m->used = 1; |
| lex_state = ls_none; |
| save_argument (&fmacro, out - pfile->out.base); |
| |
| /* A single zero-length argument is no argument. */ |
| if (fmacro.argc == 1 |
| && m->paramc == 0 |
| && out == pfile->out.base + fmacro.offset + 1) |
| fmacro.argc = 0; |
| |
| if (_cpp_arguments_ok (pfile, m, fmacro.node, fmacro.argc)) |
| { |
| /* Remove the macro's invocation from the |
| output, and push its replacement text. */ |
| pfile->out.cur = pfile->out.base + fmacro.offset; |
| CUR (context) = cur; |
| replace_args_and_push (pfile, &fmacro); |
| goto new_context; |
| } |
| } |
| else if (lex_state == ls_answer || lex_state == ls_defined_close) |
| lex_state = ls_none; |
| } |
| break; |
| |
| case '#': |
| if (cur - 1 == start_of_input_line |
| /* A '#' from a macro doesn't start a directive. */ |
| && !pfile->context->prev |
| && !pfile->state.in_directive) |
| { |
| /* A directive. With the way _cpp_handle_directive |
| currently works, we only want to call it if either we |
| know the directive is OK, or we want it to fail and |
| be removed from the output. If we want it to be |
| passed through (the assembler case) then we must not |
| call _cpp_handle_directive. */ |
| pfile->out.cur = out; |
| cur = skip_whitespace (pfile, cur, true /* skip_comments */); |
| out = pfile->out.cur; |
| |
| if (*cur == '\n') |
| { |
| /* Null directive. Ignore it and don't invalidate |
| the MI optimization. */ |
| pfile->buffer->need_line = true; |
| CPP_INCREMENT_LINE (pfile, 0); |
| result = false; |
| goto done; |
| } |
| else |
| { |
| bool do_it = false; |
| |
| if (is_numstart (*cur) |
| && CPP_OPTION (pfile, lang) != CLK_ASM) |
| do_it = true; |
| else if (is_idstart (*cur)) |
| /* Check whether we know this directive, but don't |
| advance. */ |
| do_it = lex_identifier (pfile, cur)->is_directive; |
| |
| if (do_it || CPP_OPTION (pfile, lang) != CLK_ASM) |
| { |
| /* This is a kludge. We want to have the ISO |
| preprocessor lex the next token. */ |
| pfile->buffer->cur = cur; |
| _cpp_handle_directive (pfile, false /* indented */); |
| result = false; |
| goto done; |
| } |
| } |
| } |
| |
| if (pfile->state.in_expression) |
| { |
| lex_state = ls_hash; |
| continue; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Non-whitespace disables MI optimization and stops treating |
| '<' as a quote in #include. */ |
| header_ok = false; |
| if (!pfile->state.in_directive) |
| pfile->mi_valid = false; |
| |
| if (lex_state == ls_none) |
| continue; |
| |
| /* Some of these transitions of state are syntax errors. The |
| ISO preprocessor will issue errors later. */ |
| if (lex_state == ls_fun_open) |
| /* Missing '('. */ |
| lex_state = ls_none; |
| else if (lex_state == ls_hash |
| || lex_state == ls_predicate |
| || lex_state == ls_defined) |
| lex_state = ls_none; |
| |
| /* ls_answer and ls_defined_close keep going until ')'. */ |
| } |
| |
| done: |
| if (fmacro.buff) |
| _cpp_release_buff (pfile, fmacro.buff); |
| |
| if (lex_state == ls_fun_close) |
| cpp_error_with_line (pfile, CPP_DL_ERROR, fmacro.line, 0, |
| "unterminated argument list invoking macro \"%s\"", |
| NODE_NAME (fmacro.node)); |
| return result; |
| } |
| |
| /* Push a context holding the replacement text of the macro NODE on |
| the context stack. NODE is either object-like, or a function-like |
| macro with no arguments. */ |
| static void |
| push_replacement_text (cpp_reader *pfile, cpp_hashnode *node) |
| { |
| size_t len; |
| const uchar *text; |
| uchar *buf; |
| |
| if (cpp_builtin_macro_p (node)) |
| { |
| text = _cpp_builtin_macro_text (pfile, node); |
| len = ustrlen (text); |
| buf = _cpp_unaligned_alloc (pfile, len + 1); |
| memcpy (buf, text, len); |
| buf[len] = '\n'; |
| text = buf; |
| } |
| else |
| { |
| cpp_macro *macro = node->value.macro; |
| macro->used = 1; |
| text = macro->exp.text; |
| len = macro->count; |
| } |
| |
| _cpp_push_text_context (pfile, node, text, len); |
| } |
| |
| /* Returns TRUE if traditional macro recursion is detected. */ |
| static bool |
| recursive_macro (cpp_reader *pfile, cpp_hashnode *node) |
| { |
| bool recursing = !!(node->flags & NODE_DISABLED); |
| |
| /* Object-like macros that are already expanding are necessarily |
| recursive. |
| |
| However, it is possible to have traditional function-like macros |
| that are not infinitely recursive but recurse to any given depth. |
| Further, it is easy to construct examples that get ever longer |
| until the point they stop recursing. So there is no easy way to |
| detect true recursion; instead we assume any expansion more than |
| 20 deep since the first invocation of this macro must be |
| recursing. */ |
| if (recursing && fun_like_macro (node)) |
| { |
| size_t depth = 0; |
| cpp_context *context = pfile->context; |
| |
| do |
| { |
| depth++; |
| if (context->c.macro == node && depth > 20) |
| break; |
| context = context->prev; |
| } |
| while (context); |
| recursing = context != NULL; |
| } |
| |
| if (recursing) |
| cpp_error (pfile, CPP_DL_ERROR, |
| "detected recursion whilst expanding macro \"%s\"", |
| NODE_NAME (node)); |
| |
| return recursing; |
| } |
| |
| /* Return the length of the replacement text of a function-like or |
| object-like non-builtin macro. */ |
| size_t |
| _cpp_replacement_text_len (const cpp_macro *macro) |
| { |
| size_t len; |
| |
| if (macro->fun_like && (macro->paramc != 0)) |
| { |
| const uchar *exp; |
| |
| len = 0; |
| for (exp = macro->exp.text;;) |
| { |
| struct block *b = (struct block *) exp; |
| |
| len += b->text_len; |
| if (b->arg_index == 0) |
| break; |
| len += NODE_LEN (macro->parm.params[b->arg_index - 1]); |
| exp += BLOCK_LEN (b->text_len); |
| } |
| } |
| else |
| len = macro->count; |
| |
| return len; |
| } |
| |
| /* Copy the replacement text of MACRO to DEST, which must be of |
| sufficient size. It is not NUL-terminated. The next character is |
| returned. */ |
| uchar * |
| _cpp_copy_replacement_text (const cpp_macro *macro, uchar *dest) |
| { |
| if (macro->fun_like && (macro->paramc != 0)) |
| { |
| const uchar *exp; |
| |
| for (exp = macro->exp.text;;) |
| { |
| struct block *b = (struct block *) exp; |
| cpp_hashnode *param; |
| |
| memcpy (dest, b->text, b->text_len); |
| dest += b->text_len; |
| if (b->arg_index == 0) |
| break; |
| param = macro->parm.params[b->arg_index - 1]; |
| memcpy (dest, NODE_NAME (param), NODE_LEN (param)); |
| dest += NODE_LEN (param); |
| exp += BLOCK_LEN (b->text_len); |
| } |
| } |
| else |
| { |
| memcpy (dest, macro->exp.text, macro->count); |
| dest += macro->count; |
| } |
| |
| return dest; |
| } |
| |
| /* Push a context holding the replacement text of the macro NODE on |
| the context stack. NODE is either object-like, or a function-like |
| macro with no arguments. */ |
| static void |
| replace_args_and_push (cpp_reader *pfile, struct fun_macro *fmacro) |
| { |
| cpp_macro *macro = fmacro->node->value.macro; |
| |
| if (macro->paramc == 0) |
| push_replacement_text (pfile, fmacro->node); |
| else |
| { |
| const uchar *exp; |
| uchar *p; |
| _cpp_buff *buff; |
| size_t len = 0; |
| int cxtquote = 0; |
| |
| /* Get an estimate of the length of the argument-replaced text. |
| This is a worst case estimate, assuming that every replacement |
| text character needs quoting. */ |
| for (exp = macro->exp.text;;) |
| { |
| struct block *b = (struct block *) exp; |
| |
| len += b->text_len; |
| if (b->arg_index == 0) |
| break; |
| len += 2 * (fmacro->args[b->arg_index] |
| - fmacro->args[b->arg_index - 1] - 1); |
| exp += BLOCK_LEN (b->text_len); |
| } |
| |
| /* Allocate room for the expansion plus \n. */ |
| buff = _cpp_get_buff (pfile, len + 1); |
| |
| /* Copy the expansion and replace arguments. */ |
| /* Accumulate actual length, including quoting as necessary */ |
| p = BUFF_FRONT (buff); |
| len = 0; |
| for (exp = macro->exp.text;;) |
| { |
| struct block *b = (struct block *) exp; |
| size_t arglen; |
| int argquote; |
| uchar *base; |
| uchar *in; |
| |
| len += b->text_len; |
| /* Copy the non-argument text literally, keeping |
| track of whether matching quotes have been seen. */ |
| for (arglen = b->text_len, in = b->text; arglen > 0; arglen--) |
| { |
| if (*in == '"') |
| cxtquote = ! cxtquote; |
| *p++ = *in++; |
| } |
| /* Done if no more arguments */ |
| if (b->arg_index == 0) |
| break; |
| arglen = (fmacro->args[b->arg_index] |
| - fmacro->args[b->arg_index - 1] - 1); |
| base = pfile->out.base + fmacro->args[b->arg_index - 1]; |
| in = base; |
| #if 0 |
| /* Skip leading whitespace in the text for the argument to |
| be substituted. To be compatible with gcc 2.95, we would |
| also need to trim trailing whitespace. Gcc 2.95 trims |
| leading and trailing whitespace, which may be a bug. The |
| current gcc testsuite explicitly checks that this leading |
| and trailing whitespace in actual arguments is |
| preserved. */ |
| while (arglen > 0 && is_space (*in)) |
| { |
| in++; |
| arglen--; |
| } |
| #endif |
| for (argquote = 0; arglen > 0; arglen--) |
| { |
| if (cxtquote && *in == '"') |
| { |
| if (in > base && *(in-1) != '\\') |
| argquote = ! argquote; |
| /* Always add backslash before double quote if argument |
| is expanded in a quoted context */ |
| *p++ = '\\'; |
| len++; |
| } |
| else if (cxtquote && argquote && *in == '\\') |
| { |
| /* Always add backslash before a backslash in an argument |
| that is expanded in a quoted context and also in the |
| range of a quoted context in the argument itself. */ |
| *p++ = '\\'; |
| len++; |
| } |
| *p++ = *in++; |
| len++; |
| } |
| exp += BLOCK_LEN (b->text_len); |
| } |
| |
| /* \n-terminate. */ |
| *p = '\n'; |
| _cpp_push_text_context (pfile, fmacro->node, BUFF_FRONT (buff), len); |
| |
| /* So we free buffer allocation when macro is left. */ |
| pfile->context->buff = buff; |
| } |
| } |
| |
| /* Read and record the parameters, if any, of a function-like macro |
| definition. Destroys pfile->out.cur. |
| |
| Returns true on success, false on failure (syntax error or a |
| duplicate parameter). On success, CUR (pfile->context) is just |
| past the closing parenthesis. */ |
| static bool |
| scan_parameters (cpp_reader *pfile, unsigned *n_ptr) |
| { |
| const uchar *cur = CUR (pfile->context) + 1; |
| bool ok; |
| |
| unsigned nparms = 0; |
| for (;;) |
| { |
| cur = skip_whitespace (pfile, cur, true /* skip_comments */); |
| |
| if (is_idstart (*cur)) |
| { |
| struct cpp_hashnode *id = lex_identifier (pfile, cur); |
| ok = false; |
| if (!_cpp_save_parameter (pfile, nparms, id, id)) |
| break; |
| nparms++; |
| cur = skip_whitespace (pfile, CUR (pfile->context), |
| true /* skip_comments */); |
| if (*cur == ',') |
| { |
| cur++; |
| continue; |
| } |
| ok = (*cur == ')'); |
| break; |
| } |
| |
| ok = (*cur == ')' && !nparms); |
| break; |
| } |
| |
| *n_ptr = nparms; |
| |
| if (!ok) |
| cpp_error (pfile, CPP_DL_ERROR, "syntax error in macro parameter list"); |
| |
| CUR (pfile->context) = cur + (*cur == ')'); |
| |
| return ok; |
| } |
| |
| /* Save the text from pfile->out.base to pfile->out.cur as |
| the replacement text for the current macro, followed by argument |
| ARG_INDEX, with zero indicating the end of the replacement |
| text. */ |
| static void |
| save_replacement_text (cpp_reader *pfile, cpp_macro *macro, |
| unsigned int arg_index) |
| { |
| size_t len = pfile->out.cur - pfile->out.base; |
| uchar *exp; |
| |
| if (macro->paramc == 0) |
| { |
| /* Object-like and function-like macros without parameters |
| simply store their \n-terminated replacement text. */ |
| exp = _cpp_unaligned_alloc (pfile, len + 1); |
| memcpy (exp, pfile->out.base, len); |
| exp[len] = '\n'; |
| macro->exp.text = exp; |
| macro->count = len; |
| } |
| else |
| { |
| /* Store the text's length (unsigned int), the argument index |
| (unsigned short, base 1) and then the text. */ |
| size_t blen = BLOCK_LEN (len); |
| struct block *block; |
| |
| if (macro->count + blen > BUFF_ROOM (pfile->a_buff)) |
| _cpp_extend_buff (pfile, &pfile->a_buff, macro->count + blen); |
| |
| exp = BUFF_FRONT (pfile->a_buff); |
| block = (struct block *) (exp + macro->count); |
| macro->exp.text = exp; |
| |
| /* Write out the block information. */ |
| block->text_len = len; |
| block->arg_index = arg_index; |
| memcpy (block->text, pfile->out.base, len); |
| |
| /* Lex the rest into the start of the output buffer. */ |
| pfile->out.cur = pfile->out.base; |
| |
| macro->count += blen; |
| |
| /* If we've finished, commit the memory. */ |
| if (arg_index == 0) |
| BUFF_FRONT (pfile->a_buff) += macro->count; |
| } |
| } |
| |
| /* Analyze and save the replacement text of a macro. Returns true on |
| success. */ |
| cpp_macro * |
| _cpp_create_trad_definition (cpp_reader *pfile) |
| { |
| const uchar *cur; |
| uchar *limit; |
| cpp_context *context = pfile->context; |
| unsigned nparms = 0; |
| int fun_like = 0; |
| cpp_hashnode **params = NULL; |
| |
| /* The context has not been set up for command line defines, and CUR |
| has not been updated for the macro name for in-file defines. */ |
| pfile->out.cur = pfile->out.base; |
| CUR (context) = pfile->buffer->cur; |
| RLIMIT (context) = pfile->buffer->rlimit; |
| check_output_buffer (pfile, RLIMIT (context) - CUR (context)); |
| |
| /* Is this a function-like macro? */ |
| if (* CUR (context) == '(') |
| { |
| fun_like = +1; |
| if (scan_parameters (pfile, &nparms)) |
| params = (cpp_hashnode **)_cpp_commit_buff |
| (pfile, sizeof (cpp_hashnode *) * nparms); |
| else |
| fun_like = -1; |
| } |
| |
| cpp_macro *macro = NULL; |
| |
| if (fun_like >= 0) |
| { |
| macro = _cpp_new_macro (pfile, cmk_traditional, |
| _cpp_aligned_alloc (pfile, sizeof (cpp_macro))); |
| macro->parm.params = params; |
| macro->paramc = nparms; |
| macro->fun_like = fun_like != 0; |
| } |
| |
| /* Skip leading whitespace in the replacement text. */ |
| pfile->buffer->cur |
| = skip_whitespace (pfile, CUR (context), |
| CPP_OPTION (pfile, discard_comments_in_macro_exp)); |
| |
| pfile->state.prevent_expansion++; |
| _cpp_scan_out_logical_line (pfile, macro, false); |
| pfile->state.prevent_expansion--; |
| |
| _cpp_unsave_parameters (pfile, nparms); |
| |
| if (macro) |
| { |
| /* Skip trailing white space. */ |
| cur = pfile->out.base; |
| limit = pfile->out.cur; |
| while (limit > cur && is_space (limit[-1])) |
| limit--; |
| pfile->out.cur = limit; |
| save_replacement_text (pfile, macro, 0); |
| } |
| |
| return macro; |
| } |
| |
| /* Copy SRC of length LEN to DEST, but convert all contiguous |
| whitespace to a single space, provided it is not in quotes. The |
| quote currently in effect is pointed to by PQUOTE, and is updated |
| by the function. Returns the number of bytes copied. */ |
| static size_t |
| canonicalize_text (uchar *dest, const uchar *src, size_t len, uchar *pquote) |
| { |
| uchar *orig_dest = dest; |
| uchar quote = *pquote; |
| |
| while (len) |
| { |
| if (is_space (*src) && !quote) |
| { |
| do |
| src++, len--; |
| while (len && is_space (*src)); |
| *dest++ = ' '; |
| } |
| else |
| { |
| if (*src == '\'' || *src == '"') |
| { |
| if (!quote) |
| quote = *src; |
| else if (quote == *src) |
| quote = 0; |
| } |
| *dest++ = *src++, len--; |
| } |
| } |
| |
| *pquote = quote; |
| return dest - orig_dest; |
| } |
| |
| /* Returns true if MACRO1 and MACRO2 have expansions different other |
| than in the form of their whitespace. */ |
| bool |
| _cpp_expansions_different_trad (const cpp_macro *macro1, |
| const cpp_macro *macro2) |
| { |
| uchar *p1 = XNEWVEC (uchar, macro1->count + macro2->count); |
| uchar *p2 = p1 + macro1->count; |
| uchar quote1 = 0, quote2 = 0; |
| bool mismatch; |
| size_t len1, len2; |
| |
| if (macro1->paramc > 0) |
| { |
| const uchar *exp1 = macro1->exp.text, *exp2 = macro2->exp.text; |
| |
| mismatch = true; |
| for (;;) |
| { |
| struct block *b1 = (struct block *) exp1; |
| struct block *b2 = (struct block *) exp2; |
| |
| if (b1->arg_index != b2->arg_index) |
| break; |
| |
| len1 = canonicalize_text (p1, b1->text, b1->text_len, "e1); |
| len2 = canonicalize_text (p2, b2->text, b2->text_len, "e2); |
| if (len1 != len2 || memcmp (p1, p2, len1)) |
| break; |
| if (b1->arg_index == 0) |
| { |
| mismatch = false; |
| break; |
| } |
| exp1 += BLOCK_LEN (b1->text_len); |
| exp2 += BLOCK_LEN (b2->text_len); |
| } |
| } |
| else |
| { |
| len1 = canonicalize_text (p1, macro1->exp.text, macro1->count, "e1); |
| len2 = canonicalize_text (p2, macro2->exp.text, macro2->count, "e2); |
| mismatch = (len1 != len2 || memcmp (p1, p2, len1)); |
| } |
| |
| free (p1); |
| return mismatch; |
| } |