#include "config.h"
#include "system.h"
#include "cpplib.h"
#include "cpphash.h"
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))
struct fun_macro
{
_cpp_buff *buff;
size_t *args;
cpp_hashnode *node;
size_t offset;
unsigned int line;
unsigned int argc;
};
enum ls {ls_none = 0,
ls_fun_open,
ls_fun_close,
ls_defined,
ls_defined_close,
ls_hash,
ls_predicate,
ls_answer};
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 void
check_output_buffer (pfile, n)
cpp_reader *pfile;
size_t n;
{
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
= (uchar *) xrealloc (pfile->out.base, new_size);
pfile->out.limit = pfile->out.base + new_size;
pfile->out.cur = pfile->out.base + size;
}
}
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;
}
static const uchar *
skip_escaped_newlines (pfile, cur)
cpp_reader *pfile;
const uchar *cur;
{
const uchar *orig_cur = cur;
while (*cur == '\\' && is_vspace (cur[1]))
cur = handle_newline (pfile, cur + 1);
if (cur != orig_cur && cur == RLIMIT (pfile->context) && pfile->buffer->inc)
cpp_error (pfile, DL_PEDWARN, "backslash-newline at end of file");
return cur;
}
static const uchar *
copy_comment (pfile, cur, in_define)
cpp_reader *pfile;
const uchar *cur;
int in_define;
{
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 == '/')
{
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);
if (out[-2] == '\\')
out -= 2;
else
out[-1] = '\n';
}
}
while (cur < limit);
cpp_error_with_line (pfile, DL_ERROR, from_line, 0, "unterminated comment");
*out++ = '*';
*out++ = '/';
done:
if (pfile->state.in_directive)
{
if (in_define)
{
if (CPP_OPTION (pfile, discard_comments_in_macro_exp))
pfile->out.cur--;
else
pfile->out.cur = out;
}
else
pfile->out.cur[-1] = ' ';
}
else if (CPP_OPTION (pfile, discard_comments))
pfile->out.cur--;
else
pfile->out.cur = out;
return cur;
}
static const uchar *
skip_whitespace (pfile, cur, skip_comments)
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) && 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 );
out = pfile->out.cur;
continue;
}
}
out--;
if (c == '\\' && is_vspace (*cur))
{
cur = skip_escaped_newlines (pfile, cur - 1);
continue;
}
break;
}
pfile->out.cur = out;
return cur - 1;
}
static cpp_hashnode *
lex_identifier (pfile, cur)
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);
}
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;
}
void
_cpp_overlay_buffer (pfile, start, len)
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;
buffer->cur = start;
buffer->rlimit = start + len;
pfile->saved_line = pfile->line;
}
void
_cpp_remove_overlay (pfile)
cpp_reader *pfile;
{
cpp_buffer *buffer = pfile->overlaid_buffer;
buffer->cur = buffer->saved_cur;
buffer->rlimit = buffer->saved_rlimit;
pfile->line = pfile->saved_line;
}
bool
_cpp_read_logical_line_trad (pfile)
cpp_reader *pfile;
{
do
{
if (pfile->buffer->cur == pfile->buffer->rlimit)
{
bool stop = true;
if (pfile->buffer->prev)
{
stop = pfile->buffer->return_at_eof;
_cpp_pop_buffer (pfile);
}
if (stop)
return false;
}
scan_out_logical_line (pfile, NULL);
}
while (pfile->state.skipping);
return true;
}
static void
maybe_start_funlike (pfile, node, start, macro)
cpp_reader *pfile;
cpp_hashnode *node;
const uchar *start;
struct fun_macro *macro;
{
unsigned int n = node->value.macro->paramc + 1;
if (macro->buff)
_cpp_release_buff (pfile, macro->buff);
macro->buff = _cpp_get_buff (pfile, n * sizeof (size_t));
macro->args = (size_t *) BUFF_FRONT (macro->buff);
macro->node = node;
macro->offset = start - pfile->out.base;
macro->argc = 0;
}
static void
save_argument (macro, offset)
struct fun_macro *macro;
size_t offset;
{
macro->argc++;
if (macro->argc <= macro->node->value.macro->paramc)
macro->args[macro->argc] = offset;
}
static void
scan_out_logical_line (pfile, macro)
cpp_reader *pfile;
cpp_macro *macro;
{
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;
pfile->out.first_line = pfile->line;
new_context:
context = pfile->context;
cur = CUR (context);
check_output_buffer (pfile, RLIMIT (context) - cur);
out = pfile->out.cur;
for (;;)
{
c = *cur++;
*out++ = c;
switch (c)
{
case ' ':
case '\t':
case '\f':
case '\v':
continue;
case '\0':
if (cur - 1 != RLIMIT (context))
continue;
if (context->prev)
{
pfile->out.cur = out - 1;
_cpp_pop_context (pfile);
goto new_context;
}
cur--;
if (!pfile->buffer->from_stage3)
cpp_error (pfile, DL_PEDWARN, "no newline at end of file");
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)
{
if (lex_state == ls_fun_close)
out[-1] = ' ';
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 '\\':
if (is_vspace (*cur))
{
out--;
cur = skip_escaped_newlines (pfile, cur - 1);
continue;
}
else
{
cur = skip_escaped_newlines (pfile, cur);
if (*cur == '\\' || *cur == '"' || *cur == '\'')
*out++ = *cur++;
}
break;
case '/':
if (!quote)
{
cur = skip_escaped_newlines (pfile, cur);
if (*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 (node->type == NT_MACRO
&& (lex_state == ls_none || lex_state == ls_fun_open)
&& !pfile->state.prevent_expansion)
{
pfile->mi_valid = false;
if (! (node->flags & NODE_BUILTIN)
&& node->value.macro->fun_like)
{
maybe_start_funlike (pfile, node, out_start, &fmacro);
lex_state = ls_fun_open;
fmacro.line = pfile->line;
continue;
}
else if (!recursive_macro (pfile, node))
{
pfile->out.cur = out_start;
push_replacement_text (pfile, node);
lex_state = ls_none;
goto new_context;
}
}
else if (macro && node->arg_index)
{
pfile->out.cur = out_start;
save_replacement_text (pfile, macro, node->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)
{
cpp_macro *m = fmacro.node->value.macro;
m->used = 1;
lex_state = ls_none;
save_argument (&fmacro, out - pfile->out.base);
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))
{
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 (out - 1 == pfile->out.base
&& !pfile->context->prev
&& !pfile->state.in_directive)
{
pfile->out.cur = out;
cur = skip_whitespace (pfile, cur, true );
out = pfile->out.cur;
if (is_vspace (*cur))
{
out = pfile->out.base;
continue;
}
else
{
bool do_it = false;
if (is_numstart (*cur)
&& CPP_OPTION (pfile, lang) != CLK_ASM)
do_it = true;
else if (is_idstart (*cur))
do_it = lex_identifier (pfile, cur)->directive_index != 0;
if (do_it || CPP_OPTION (pfile, lang) != CLK_ASM)
{
pfile->buffer->cur = cur;
_cpp_handle_directive (pfile, false );
goto start_logical_line;
}
}
}
if (pfile->state.in_expression)
{
lex_state = ls_hash;
continue;
}
break;
default:
break;
}
header_ok = false;
if (!pfile->state.in_directive)
pfile->mi_valid = false;
if (lex_state == ls_none)
continue;
if (lex_state == ls_fun_open)
lex_state = ls_none;
else if (lex_state == ls_hash
|| lex_state == ls_predicate
|| lex_state == ls_defined)
lex_state = ls_none;
}
done:
out[-1] = '\0';
pfile->buffer->cur = cur;
pfile->out.cur = out - 1;
if (fmacro.buff)
_cpp_release_buff (pfile, fmacro.buff);
if (lex_state == ls_fun_close)
cpp_error_with_line (pfile, DL_ERROR, fmacro.line, 0,
"unterminated argument list invoking macro \"%s\"",
NODE_NAME (fmacro.node));
}
static void
push_replacement_text (pfile, node)
cpp_reader *pfile;
cpp_hashnode *node;
{
size_t len;
const uchar *text;
if (node->flags & NODE_BUILTIN)
{
text = _cpp_builtin_macro_text (pfile, node);
len = ustrlen (text);
}
else
{
cpp_macro *macro = node->value.macro;
macro->used = 1;
text = macro->exp.text;
macro->traditional = 1;
len = macro->count;
}
_cpp_push_text_context (pfile, node, text, len);
}
static bool
recursive_macro (pfile, node)
cpp_reader *pfile;
cpp_hashnode *node;
{
bool recursing = !!(node->flags & NODE_DISABLED);
if (recursing && node->value.macro->fun_like)
{
size_t depth = 0;
cpp_context *context = pfile->context;
do
{
depth++;
if (context->macro == node && depth > 20)
break;
context = context->prev;
}
while (context);
recursing = context != NULL;
}
if (recursing)
cpp_error (pfile, DL_ERROR,
"detected recursion whilst expanding macro \"%s\"",
NODE_NAME (node));
return recursing;
}
size_t
_cpp_replacement_text_len (macro)
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->params[b->arg_index - 1]);
exp += BLOCK_LEN (b->text_len);
}
}
else
len = macro->count;
return len;
}
uchar *
_cpp_copy_replacement_text (macro, dest)
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->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;
}
static void
replace_args_and_push (pfile, fmacro)
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;
for (exp = macro->exp.text;;)
{
struct block *b = (struct block *) exp;
len += b->text_len;
if (b->arg_index == 0)
break;
len += (fmacro->args[b->arg_index]
- fmacro->args[b->arg_index - 1] - 1);
exp += BLOCK_LEN (b->text_len);
}
buff = _cpp_get_buff (pfile, len + 1);
p = BUFF_FRONT (buff);
for (exp = macro->exp.text;;)
{
struct block *b = (struct block *) exp;
size_t arglen;
memcpy (p, b->text, b->text_len);
p += b->text_len;
if (b->arg_index == 0)
break;
arglen = (fmacro->args[b->arg_index]
- fmacro->args[b->arg_index - 1] - 1);
memcpy (p, pfile->out.base + fmacro->args[b->arg_index - 1],
arglen);
p += arglen;
exp += BLOCK_LEN (b->text_len);
}
*p = '\0';
_cpp_push_text_context (pfile, fmacro->node, BUFF_FRONT (buff), len);
pfile->context->buff = buff;
}
}
static bool
scan_parameters (pfile, macro)
cpp_reader *pfile;
cpp_macro *macro;
{
const uchar *cur = CUR (pfile->context) + 1;
bool ok;
for (;;)
{
cur = skip_whitespace (pfile, cur, true );
if (is_idstart (*cur))
{
ok = false;
if (_cpp_save_parameter (pfile, macro, lex_identifier (pfile, cur)))
break;
cur = skip_whitespace (pfile, CUR (pfile->context),
true );
if (*cur == ',')
{
cur++;
continue;
}
ok = (*cur == ')');
break;
}
ok = (*cur == ')' && macro->paramc == 0);
break;
}
CUR (pfile->context) = cur + (*cur == ')');
return ok;
}
static void
save_replacement_text (pfile, macro, arg_index)
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)
{
exp = _cpp_unaligned_alloc (pfile, len + 1);
memcpy (exp, pfile->out.base, len);
exp[len] = '\0';
macro->exp.text = exp;
macro->traditional = 1;
macro->count = len;
}
else
{
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;
macro->traditional = 1;
block->text_len = len;
block->arg_index = arg_index;
memcpy (block->text, pfile->out.base, len);
pfile->out.cur = pfile->out.base;
macro->count += blen;
if (arg_index == 0)
BUFF_FRONT (pfile->a_buff) += macro->count;
}
}
bool
_cpp_create_trad_definition (pfile, macro)
cpp_reader *pfile;
cpp_macro *macro;
{
const uchar *cur;
uchar *limit;
cpp_context *context = pfile->context;
pfile->out.cur = pfile->out.base;
CUR (context) = pfile->buffer->cur;
RLIMIT (context) = pfile->buffer->rlimit;
check_output_buffer (pfile, RLIMIT (context) - CUR (context));
if (* CUR (context) == '(')
{
if (!scan_parameters (pfile, macro))
macro = NULL;
else
{
macro->params = (cpp_hashnode **) BUFF_FRONT (pfile->a_buff);
BUFF_FRONT (pfile->a_buff) = (uchar *) ¯o->params[macro->paramc];
macro->fun_like = 1;
}
}
pfile->buffer->cur
= skip_whitespace (pfile, CUR (context),
CPP_OPTION (pfile, discard_comments_in_macro_exp));
pfile->state.prevent_expansion++;
scan_out_logical_line (pfile, macro);
pfile->state.prevent_expansion--;
if (!macro)
return false;
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 true;
}
static size_t
canonicalize_text (dest, src, len, pquote)
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;
}
bool
_cpp_expansions_different_trad (macro1, macro2)
const cpp_macro *macro1, *macro2;
{
uchar *p1 = xmalloc (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;
}