/* 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 itunder the terms of the GNU General Public License as published by theFree Software Foundation; either version 3, or (at your option) anylater version. This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details. You should have received a copy of the GNU General Public Licensealong 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 voidcheck_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 boolskip_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 boolfun_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 voidmaybe_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 voidsave_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 == '*')Lines 1–500 of 1321. Load more
/* 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 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 == '*')