#include "config.h"
#include "system.h"
#include "cpplib.h"
#include "internal.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 *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 *);
static void
check_output_buffer (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 = xrealloc (pfile->out.base, new_size);
pfile->out.limit = pfile->out.base + new_size;
pfile->out.cur = pfile->out.base + size;
}
}
static void
skip_macro_block_comment (cpp_reader *pfile)
{
const uchar *cur = pfile->buffer->cur;
cur++;
if (*cur == '/')
cur++;
while(! (*cur++ == '/' && cur[-2] == '*') )
;
pfile->buffer->cur = cur;
}
static const uchar *
copy_comment (cpp_reader *pfile, const uchar *cur, int in_define)
{
bool unterminated, copy = false;
source_location src_loc = pfile->line_table->highest_line;
cpp_buffer *buffer = pfile->buffer;
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, CPP_DL_ERROR, src_loc, 0,
"unterminated comment");
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;
}
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 );
out = pfile->out.cur;
continue;
}
out--;
break;
}
pfile->out.cur = out;
return cur - 1;
}
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;
}
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;
}
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;
}
bool
_cpp_read_logical_line_trad (cpp_reader *pfile)
{
do
{
if (pfile->buffer->need_line && !_cpp_get_fresh_line (pfile))
return false;
}
while (!_cpp_scan_out_logical_line (pfile, NULL) || pfile->state.skipping);
return pfile->buffer != NULL;
}
static void
maybe_start_funlike (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 (struct fun_macro *macro, size_t offset)
{
macro->argc++;
if (macro->argc <= macro->node->value.macro->paramc)
macro->args[macro->argc] = offset;
}
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;
const uchar *start_of_input_line;
fmacro.buff = NULL;
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_table->highest_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
&& cur >= pfile->buffer->notes[pfile->buffer->cur_note].pos)
{
pfile->buffer->cur = cur;
_cpp_process_line_notes (pfile, false);
}
c = *cur++;
*out++ = c;
switch (c)
{
case ' ':
case '\t':
case '\f':
case '\v':
case '\0':
continue;
case '\n':
if (context->prev)
{
pfile->out.cur = out - 1;
_cpp_pop_context (pfile);
goto new_context;
}
pfile->out.cur = out - 1;
pfile->buffer->cur = cur;
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))
{
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 '\\':
if (*cur == '\\' || *cur == '"' || *cur == '\'')
*out++ = *cur++;
break;
case '/':
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 (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_table->highest_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->flags & NODE_MACRO_ARG) != 0)
{
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)
{
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 (cur - 1 == start_of_input_line
&& !pfile->context->prev
&& !pfile->state.in_directive)
{
pfile->out.cur = out;
cur = skip_whitespace (pfile, cur, true );
out = pfile->out.cur;
if (*cur == '\n')
{
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))
do_it = lex_identifier (pfile, cur)->is_directive;
if (do_it || CPP_OPTION (pfile, lang) != CLK_ASM)
{
pfile->buffer->cur = cur;
_cpp_handle_directive (pfile, false );
result = false;
goto done;
}
}
}
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:
if (fmacro.buff)
_cpp_release_buff (pfile, fmacro.buff);
if (lex_state == ls_fun_close)
cpp_error_with_line (pfile, CPP_DL_ERROR, fmacro.line, 0,
"unterminated argument list invoking macro \"%s\"",
NODE_NAME (fmacro.node));
return result;
}
static void
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;
macro->traditional = 1;
len = macro->count;
}
_cpp_push_text_context (pfile, node, text, len);
}
static bool
recursive_macro (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, CPP_DL_ERROR,
"detected recursion whilst expanding macro \"%s\"",
NODE_NAME (node));
return recursing;
}
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->params[b->arg_index - 1]);
exp += BLOCK_LEN (b->text_len);
}
}
else
len = macro->count;
return len;
}
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->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 (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 = '\n';
_cpp_push_text_context (pfile, fmacro->node, BUFF_FRONT (buff), len);
pfile->context->buff = buff;
}
}
static bool
scan_parameters (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;
}
if (!ok)
cpp_error (pfile, CPP_DL_ERROR, "syntax error in macro parameter list");
CUR (pfile->context) = cur + (*cur == ')');
return ok;
}
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)
{
exp = _cpp_unaligned_alloc (pfile, len + 1);
memcpy (exp, pfile->out.base, len);
exp[len] = '\n';
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 (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) == '(')
{
bool ok = scan_parameters (pfile, macro);
macro->params = (cpp_hashnode **) BUFF_FRONT (pfile->a_buff);
if (!ok)
macro = NULL;
else
{
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++;
_cpp_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 (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 (const cpp_macro *macro1,
const cpp_macro *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;
}