| /* CPP Library - traditional lexical analysis and macro expansion. | |
| Copyright (C) 2002-2026 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.cc | |
| 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 terminated, 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 %qs", | |
| 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 %qs", | |
| 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; | |
| } |