/* Lexing TODO: Maybe handle space in escaped newlines. Stop cpplex.c
from recognizing comments and directives during its lexing pass. */
-static const uchar *handle_newline PARAMS ((cpp_reader *, const uchar *));
-static const uchar *skip_escaped_newlines PARAMS ((cpp_reader *,
- const uchar *));
-static const uchar *skip_whitespace PARAMS ((cpp_reader *, const uchar *,
- int));
-static cpp_hashnode *lex_identifier PARAMS ((cpp_reader *, const uchar *));
-static const uchar *copy_comment PARAMS ((cpp_reader *, const uchar *, int));
-static void scan_out_logical_line PARAMS ((cpp_reader *pfile, cpp_macro *));
-static void check_output_buffer PARAMS ((cpp_reader *, size_t));
-static void push_replacement_text PARAMS ((cpp_reader *, cpp_hashnode *));
-static bool scan_parameters PARAMS ((cpp_reader *, cpp_macro *));
-static bool recursive_macro PARAMS ((cpp_reader *, cpp_hashnode *));
-static void save_replacement_text PARAMS ((cpp_reader *, cpp_macro *,
- unsigned int));
-static void maybe_start_funlike PARAMS ((cpp_reader *, cpp_hashnode *,
- const uchar *, struct fun_macro *));
-static void save_argument PARAMS ((struct fun_macro *, size_t));
-static void replace_args_and_push PARAMS ((cpp_reader *, struct fun_macro *));
-static size_t canonicalize_text PARAMS ((uchar *, const uchar *, size_t,
- uchar *));
+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 *, cpp_macro *);
+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 (pfile, n)
- cpp_reader *pfile;
- size_t n;
+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. */
size_t size = pfile->out.cur - pfile->out.base;
size_t new_size = (size + n) * 3 / 2;
- pfile->out.base
- = (uchar *) xrealloc (pfile->out.base, new_size);
+ pfile->out.base = xrealloc (pfile->out.base, new_size);
pfile->out.limit = pfile->out.base + new_size;
pfile->out.cur = pfile->out.base + size;
}
}
-/* To be called whenever a newline character is encountered in the
- input file, at CUR. Handles DOS, Mac and Unix ends of line, and
- increments pfile->line.
-
- Returns a pointer the character after the newline sequence. */
-static const uchar *
-handle_newline (pfile, cur)
- cpp_reader *pfile;
- const uchar *cur;
-{
- pfile->line++;
- if (cur[0] + cur[1] == '\r' + '\n')
- cur++;
- return cur + 1;
-}
-
-/* CUR points to any character in the current context, not necessarily
- a backslash. Advances CUR until all escaped newlines are skipped,
- and returns the new position without updating the context.
-
- Warns if a file buffer ends in an escaped newline. */
-static const uchar *
-skip_escaped_newlines (pfile, cur)
- cpp_reader *pfile;
- const uchar *cur;
+/* Skip a C-style block comment in a macro as a result of -CC.
+ Buffer->cur points to the initial asterisk of the comment. */
+static void
+skip_macro_block_comment (cpp_reader *pfile)
{
- const uchar *orig_cur = cur;
+ const uchar *cur = pfile->buffer->cur;
- while (*cur == '\\' && is_vspace (cur[1]))
- cur = handle_newline (pfile, cur + 1);
+ cur++;
+ if (*cur == '/')
+ cur++;
- if (cur != orig_cur && cur == RLIMIT (pfile->context) && pfile->buffer->inc)
- cpp_error (pfile, DL_PEDWARN, "backslash-newline at end of file");
+ /* People like decorating comments with '*', so check for '/'
+ instead for efficiency. */
+ while(! (*cur++ == '/' && cur[-2] == '*') )
+ ;
- return cur;
+ pfile->buffer->cur = cur;
}
/* CUR points to the asterisk introducing a comment in the current
Returns a pointer to the first character after the comment in the
input buffer. */
static const uchar *
-copy_comment (pfile, cur, in_define)
- cpp_reader *pfile;
- const uchar *cur;
- int in_define;
+copy_comment (cpp_reader *pfile, const uchar *cur, int in_define)
{
+ bool unterminated, copy = false;
unsigned int from_line = pfile->line;
- const uchar *limit = RLIMIT (pfile->context);
- uchar *out = pfile->out.cur;
-
- do
- {
- unsigned int c = *cur++;
- *out++ = c;
-
- if (c == '/')
- {
- /* An immediate slash does not terminate the comment. */
- if (out[-2] == '*' && out - 2 > pfile->out.cur)
- goto done;
-
- if (*cur == '*' && cur[1] != '/'
- && CPP_OPTION (pfile, warn_comments))
- cpp_error_with_line (pfile, DL_WARNING, pfile->line, 0,
- "\"/*\" within comment");
- }
- else if (is_vspace (c))
- {
- cur = handle_newline (pfile, cur - 1);
- /* Canonicalize newline sequences and skip escaped ones. */
- if (out[-2] == '\\')
- out -= 2;
- else
- out[-1] = '\n';
- }
- }
- while (cur < limit);
+ cpp_buffer *buffer = pfile->buffer;
- cpp_error_with_line (pfile, DL_ERROR, from_line, 0, "unterminated comment");
- *out++ = '*';
- *out++ = '/';
+ buffer->cur = cur;
+ if (pfile->context->prev)
+ unterminated = false, skip_macro_block_comment (pfile);
+ else
+ unterminated = _cpp_skip_block_comment (pfile);
+
+ if (unterminated)
+ cpp_error_with_line (pfile, DL_ERROR, from_line, 0,
+ "unterminated comment");
- done:
/* Comments in directives become spaces so that tokens are properly
separated when the ISO preprocessor re-lexes the line. The
exception is #define. */
if (CPP_OPTION (pfile, discard_comments_in_macro_exp))
pfile->out.cur--;
else
- pfile->out.cur = out;
+ copy = true;
}
else
pfile->out.cur[-1] = ' ';
else if (CPP_OPTION (pfile, discard_comments))
pfile->out.cur--;
else
- pfile->out.cur = out;
+ 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 cur;
+ return buffer->cur;
}
/* CUR points to any character in the input buffer. Skips over all
Returns a pointer to the first character after the whitespace in
the input buffer. */
static const uchar *
-skip_whitespace (pfile, cur, skip_comments)
- cpp_reader *pfile;
- const uchar *cur;
- int skip_comments;
+skip_whitespace (cpp_reader *pfile, const uchar *cur, int skip_comments)
{
uchar *out = pfile->out.cur;
unsigned int c = *cur++;
*out++ = c;
- if (is_nvspace (c) && c)
+ if (is_nvspace (c))
continue;
- if (!c && cur - 1 != RLIMIT (pfile->context))
- continue;
-
- if (c == '/' && skip_comments)
- {
- const uchar *tmp = skip_escaped_newlines (pfile, cur);
- if (*tmp == '*')
- {
- pfile->out.cur = out;
- cur = copy_comment (pfile, tmp, false /* in_define */);
- out = pfile->out.cur;
- continue;
- }
- }
-
- out--;
- if (c == '\\' && is_vspace (*cur))
+ if (c == '/' && *cur == '*' && skip_comments)
{
- cur = skip_escaped_newlines (pfile, cur);
+ pfile->out.cur = out;
+ cur = copy_comment (pfile, cur, false /* in_define */);
+ out = pfile->out.cur;
continue;
}
+ out--;
break;
}
to point to a valid first character of an identifier. Returns
the hashnode, and updates out.cur. */
static cpp_hashnode *
-lex_identifier (pfile, cur)
- cpp_reader *pfile;
- const uchar *cur;
+lex_identifier (cpp_reader *pfile, const uchar *cur)
{
size_t len;
uchar *out = pfile->out.cur;
cpp_hashnode *result;
do
- {
- do
- *out++ = *cur++;
- while (is_numchar (*cur));
- cur = skip_escaped_newlines (pfile, cur);
- }
+ *out++ = *cur++;
while (is_numchar (*cur));
CUR (pfile->context) = cur;
starting at START. The true buffer is restored upon calling
restore_buff(). */
void
-_cpp_overlay_buffer (pfile, start, len)
- cpp_reader *pfile;
- const uchar *start;
- size_t len;
+_cpp_overlay_buffer (cpp_reader *pfile, const uchar *start, size_t len)
{
cpp_buffer *buffer = pfile->buffer;
pfile->overlaid_buffer = buffer;
buffer->saved_cur = buffer->cur;
buffer->saved_rlimit = buffer->rlimit;
+ /* Prevent the ISO lexer from scanning a fresh line. */
+ pfile->saved_line = pfile->line--;
+ buffer->need_line = false;
buffer->cur = start;
buffer->rlimit = start + len;
-
- pfile->saved_line = pfile->line;
}
/* Restores a buffer overlaid by _cpp_overlay_buffer(). */
void
-_cpp_remove_overlay (pfile)
- cpp_reader *pfile;
+_cpp_remove_overlay (cpp_reader *pfile)
{
cpp_buffer *buffer = pfile->overlaid_buffer;
buffer->cur = buffer->saved_cur;
buffer->rlimit = buffer->saved_rlimit;
+ buffer->need_line = true;
+ pfile->overlaid_buffer = NULL;
pfile->line = pfile->saved_line;
}
/* 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 (pfile)
- cpp_reader *pfile;
+_cpp_read_logical_line_trad (cpp_reader *pfile)
{
do
{
- if (pfile->buffer->cur == pfile->buffer->rlimit)
- {
- bool stop = true;
-
- /* Don't pop the last buffer. */
- if (pfile->buffer->prev)
- {
- stop = pfile->buffer->return_at_eof;
- _cpp_pop_buffer (pfile);
- }
-
- if (stop)
- return false;
- }
-
- scan_out_logical_line (pfile, NULL);
+ if ((pfile->buffer == NULL || pfile->buffer->need_line)
+ && !_cpp_get_fresh_line (pfile))
+ return false;
}
- while (pfile->state.skipping);
+ while (!_cpp_scan_out_logical_line (pfile, NULL) || pfile->state.skipping);
return true;
}
/* Set up state for finding the opening '(' of a function-like
macro. */
static void
-maybe_start_funlike (pfile, node, start, macro)
- cpp_reader *pfile;
- cpp_hashnode *node;
- const uchar *start;
- struct fun_macro *macro;
+maybe_start_funlike (cpp_reader *pfile, cpp_hashnode *node, const uchar *start, struct fun_macro *macro)
{
unsigned int n = node->value.macro->paramc + 1;
/* Save the OFFSET of the start of the next argument to MACRO. */
static void
-save_argument (macro, offset)
- struct fun_macro *macro;
- size_t offset;
+save_argument (struct fun_macro *macro, size_t offset)
{
macro->argc++;
if (macro->argc <= macro->node->value.macro->paramc)
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. */
-static void
-scan_out_logical_line (pfile, macro)
- cpp_reader *pfile;
- cpp_macro *macro;
+bool
+_cpp_scan_out_logical_line (cpp_reader *pfile, cpp_macro *macro)
{
+ 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;
fmacro.buff = NULL;
- start_logical_line:
quote = 0;
+ header_ok = pfile->state.angled_headers;
CUR (pfile->context) = pfile->buffer->cur;
RLIMIT (pfile->context) = pfile->buffer->rlimit;
pfile->out.cur = pfile->out.base;
for (;;)
{
+ if (!context->prev
+ && cur >= pfile->buffer->notes[pfile->buffer->cur_note].pos)
+ {
+ pfile->buffer->cur = cur;
+ _cpp_process_line_notes (pfile, false);
+ }
c = *cur++;
*out++ = c;
case '\t':
case '\f':
case '\v':
- continue;
-
case '\0':
- if (cur - 1 != RLIMIT (context))
- continue;
+ continue;
+ case '\n':
/* If this is a macro's expansion, pop it. */
if (context->prev)
{
goto new_context;
}
- /* Premature end of file. Fake a new line. */
- cur--;
- if (!pfile->buffer->from_stage3)
- cpp_error (pfile, DL_PEDWARN, "no newline at end of file");
+ /* Omit the newline from the output buffer. */
+ pfile->out.cur = out - 1;
+ pfile->buffer->cur = cur;
+ pfile->buffer->need_line = true;
pfile->line++;
- goto done;
- case '\r': case '\n':
- cur = handle_newline (pfile, cur - 1);
if ((lex_state == ls_fun_open || lex_state == ls_fun_close)
- && !pfile->state.in_directive)
+ && !pfile->state.in_directive
+ && _cpp_get_fresh_line (pfile))
{
- /* Newlines in arguments become a space. */
+ /* 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 (pfile->state.angled_headers && !quote)
+ if (header_ok)
quote = '>';
break;
case '>':
if (c == quote)
- {
- pfile->state.angled_headers = false;
- quote = 0;
- }
+ quote = 0;
break;
case '"':
break;
case '\\':
- if (is_vspace (*cur))
- {
- out--;
- cur = skip_escaped_newlines (pfile, cur - 1);
- continue;
- }
- else
- {
- /* Skip escaped quotes here, it's easier than above, but
- take care to first skip escaped newlines. */
- cur = skip_escaped_newlines (pfile, cur);
- if (*cur == '\\' || *cur == '"' || *cur == '\'')
- *out++ = *cur++;
- }
+ /* 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)
+ if (!quote && *cur == '*')
{
- cur = skip_escaped_newlines (pfile, cur);
- if (*cur == '*')
- {
- pfile->out.cur = out;
- cur = copy_comment (pfile, cur, macro != 0);
- out = pfile->out.cur;
- continue;
- }
+ pfile->out.cur = out;
+ cur = copy_comment (pfile, cur, macro != 0);
+ out = pfile->out.cur;
+ continue;
}
break;
goto new_context;
}
}
- else if (macro && node->arg_index)
+ else if (macro && (node->flags & NODE_MACRO_ARG) != 0)
{
/* 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->arg_index);
+ save_replacement_text (pfile, macro, node->value.arg_index);
out = pfile->out.base;
}
else if (lex_state == ls_hash)
{
cpp_macro *m = fmacro.node->value.macro;
+ m->used = 1;
lex_state = ls_none;
save_argument (&fmacro, out - pfile->out.base);
break;
case '#':
- if (out - 1 == pfile->out.base && !pfile->state.in_directive)
+ if (out - 1 == pfile->out.base
+ /* 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
cur = skip_whitespace (pfile, cur, true /* skip_comments */);
out = pfile->out.cur;
- if (is_vspace (*cur))
+ if (*cur == '\n')
{
/* Null directive. Ignore it and don't invalidate
the MI optimization. */
- out = pfile->out.base;
- continue;
+ pfile->buffer->need_line = true;
+ pfile->line++;
+ result = false;
+ goto done;
}
else
{
bool do_it = false;
- if (is_numstart (*cur))
+ 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)->directive_index != 0;
+ do_it = lex_identifier (pfile, cur)->is_directive;
if (do_it || CPP_OPTION (pfile, lang) != CLK_ASM)
{
preprocessor lex the next token. */
pfile->buffer->cur = cur;
_cpp_handle_directive (pfile, false /* indented */);
- /* #include changes pfile->buffer so we need to
- update the limits of the current context. */
- goto start_logical_line;
+ result = false;
+ goto done;
}
}
}
break;
}
- /* Non-whitespace disables MI optimization. */
+ /* 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;
}
done:
- out[-1] = '\0';
- pfile->buffer->cur = cur;
- pfile->out.cur = out - 1;
if (fmacro.buff)
_cpp_release_buff (pfile, fmacro.buff);
cpp_error_with_line (pfile, 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 (pfile, node)
- cpp_reader *pfile;
- cpp_hashnode *node;
+push_replacement_text (cpp_reader *pfile, cpp_hashnode *node)
{
size_t len;
const uchar *text;
+ uchar *buf;
if (node->flags & NODE_BUILTIN)
{
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;
}
/* Returns TRUE if traditional macro recursion is detected. */
static bool
-recursive_macro (pfile, node)
- cpp_reader *pfile;
- cpp_hashnode *node;
+recursive_macro (cpp_reader *pfile, cpp_hashnode *node)
{
- bool recursing = node->flags & NODE_DISABLED;
+ bool recursing = !!(node->flags & NODE_DISABLED);
/* Object-like macros that are already expanding are necessarily
recursive.
/* Return the length of the replacement text of a function-like or
object-like non-builtin macro. */
size_t
-_cpp_replacement_text_len (macro)
- const cpp_macro *macro;
+_cpp_replacement_text_len (const cpp_macro *macro)
{
size_t len;
- if (macro->fun_like)
+ if (macro->fun_like && (macro->paramc != 0))
{
const uchar *exp;
sufficient size. It is not NUL-terminated. The next character is
returned. */
uchar *
-_cpp_copy_replacement_text (macro, dest)
- const cpp_macro *macro;
- uchar *dest;
+_cpp_copy_replacement_text (const cpp_macro *macro, uchar *dest)
{
- if (macro->fun_like)
+ if (macro->fun_like && (macro->paramc != 0))
{
const uchar *exp;
the context stack. NODE is either object-like, or a function-like
macro with no arguments. */
static void
-replace_args_and_push (pfile, fmacro)
- cpp_reader *pfile;
- struct fun_macro *fmacro;
+replace_args_and_push (cpp_reader *pfile, struct fun_macro *fmacro)
{
cpp_macro *macro = fmacro->node->value.macro;
exp += BLOCK_LEN (b->text_len);
}
- /* Allocate room for the expansion plus NUL. */
+ /* Allocate room for the expansion plus \n. */
buff = _cpp_get_buff (pfile, len + 1);
/* Copy the expansion and replace arguments. */
exp += BLOCK_LEN (b->text_len);
}
- /* NUL-terminate. */
- *p = '\0';
+ /* \n-terminate. */
+ *p = '\n';
_cpp_push_text_context (pfile, fmacro->node, BUFF_FRONT (buff), len);
/* So we free buffer allocation when macro is left. */
duplicate parameter). On success, CUR (pfile->context) is just
past the closing parenthesis. */
static bool
-scan_parameters (pfile, macro)
- cpp_reader *pfile;
- cpp_macro *macro;
+scan_parameters (cpp_reader *pfile, cpp_macro *macro)
{
const uchar *cur = CUR (pfile->context) + 1;
bool ok;
ARG_INDEX, with zero indicating the end of the replacement
text. */
static void
-save_replacement_text (pfile, macro, arg_index)
- cpp_reader *pfile;
- cpp_macro *macro;
- unsigned int arg_index;
+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 NUL-terminated replacement text. */
+ simply store their \n-terminated replacement text. */
exp = _cpp_unaligned_alloc (pfile, len + 1);
memcpy (exp, pfile->out.base, len);
- exp[len] = '\0';
+ exp[len] = '\n';
macro->exp.text = exp;
macro->count = len;
}
/* Analyze and save the replacement text of a macro. Returns true on
success. */
bool
-_cpp_create_trad_definition (pfile, macro)
- cpp_reader *pfile;
- cpp_macro *macro;
+_cpp_create_trad_definition (cpp_reader *pfile, cpp_macro *macro)
{
const uchar *cur;
uchar *limit;
if (* CUR (context) == '(')
{
/* Setting macro to NULL indicates an error occurred, and
- prevents unnecessary work in scan_out_logical_line. */
+ prevents unnecessary work in _cpp_scan_out_logical_line. */
if (!scan_parameters (pfile, macro))
macro = NULL;
else
CPP_OPTION (pfile, discard_comments_in_macro_exp));
pfile->state.prevent_expansion++;
- scan_out_logical_line (pfile, macro);
+ _cpp_scan_out_logical_line (pfile, macro);
pfile->state.prevent_expansion--;
if (!macro)
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 (dest, src, len, pquote)
- uchar *dest;
- const uchar *src;
- size_t len;
- uchar *pquote;
+canonicalize_text (uchar *dest, const uchar *src, size_t len, uchar *pquote)
{
uchar *orig_dest = dest;
uchar quote = *pquote;
/* Returns true if MACRO1 and MACRO2 have expansions different other
than in the form of their whitespace. */
bool
-_cpp_expansions_different_trad (macro1, macro2)
- const cpp_macro *macro1, *macro2;
+_cpp_expansions_different_trad (const cpp_macro *macro1,
+ const cpp_macro *macro2)
{
uchar *p1 = xmalloc (macro1->count + macro2->count);
uchar *p2 = p1 + macro1->count;