#include "config.h"
#include "system.h"
#include "cpplib.h"
#include "internal.h"
#include "mkdeps.h"
#include "obstack.h"
struct if_stack
{
struct if_stack *next;
unsigned int line;
const cpp_hashnode *mi_cmacro;
bool skip_elses;
bool was_skipping;
int type;
};
typedef void (*pragma_cb) (cpp_reader *);
struct pragma_entry
{
struct pragma_entry *next;
const cpp_hashnode *pragma;
bool is_nspace;
bool allow_expansion;
bool is_internal;
union {
pragma_cb handler;
struct pragma_entry *space;
} u;
};
#define KANDR 0
#define STDC89 1
#define EXTENSION 2
#define COND (1 << 0)
#define IF_COND (1 << 1)
#define INCL (1 << 2)
#define IN_I (1 << 3)
#define EXPAND (1 << 4)
#define IN_I_PCH (1 << 5)
typedef void (*directive_handler) (cpp_reader *);
typedef struct directive directive;
struct directive
{
directive_handler handler;
const uchar *name;
unsigned short length;
unsigned char origin;
unsigned char flags;
};
static void skip_rest_of_line (cpp_reader *);
static void check_eol (cpp_reader *);
static void start_directive (cpp_reader *);
static void prepare_directive_trad (cpp_reader *);
static void end_directive (cpp_reader *, int);
static void directive_diagnostics (cpp_reader *, const directive *, int);
static void run_directive (cpp_reader *, int, const char *, size_t);
static char *glue_header_name (cpp_reader *);
static const char *parse_include (cpp_reader *, int *);
static void push_conditional (cpp_reader *, int, int, const cpp_hashnode *);
static unsigned int read_flag (cpp_reader *, unsigned int);
static int strtoul_for_line (const uchar *, unsigned int, unsigned long *);
static void do_diagnostic (cpp_reader *, int, int);
static cpp_hashnode *lex_macro_node (cpp_reader *);
static int undefine_macros (cpp_reader *, cpp_hashnode *, void *);
static void do_include_common (cpp_reader *, enum include_type);
static struct pragma_entry *lookup_pragma_entry (struct pragma_entry *,
const cpp_hashnode *);
static struct pragma_entry *insert_pragma_entry (cpp_reader *,
struct pragma_entry **,
const cpp_hashnode *,
pragma_cb,
bool, bool);
static void register_pragma (cpp_reader *, const char *, const char *,
pragma_cb, bool, bool);
static int count_registered_pragmas (struct pragma_entry *);
static char ** save_registered_pragmas (struct pragma_entry *, char **);
static char ** restore_registered_pragmas (cpp_reader *, struct pragma_entry *,
char **);
static void do_pragma_once (cpp_reader *);
static void do_pragma_poison (cpp_reader *);
static void do_pragma_system_header (cpp_reader *);
static void do_pragma_dependency (cpp_reader *);
static void do_linemarker (cpp_reader *);
static const cpp_token *get_token_no_padding (cpp_reader *);
static const cpp_token *get__Pragma_string (cpp_reader *);
static void destringize_and_run (cpp_reader *, const cpp_string *);
static int parse_answer (cpp_reader *, struct answer **, int);
static cpp_hashnode *parse_assertion (cpp_reader *, struct answer **, int);
static struct answer ** find_answer (cpp_hashnode *, const struct answer *);
static void handle_assertion (cpp_reader *, const char *, int);
#define DIRECTIVE_TABLE \
D(define, T_DEFINE = 0, KANDR, IN_I) \
D(include, T_INCLUDE, KANDR, INCL | EXPAND) \
\
D(include_pch, T_INCLUDE_PCH, KANDR, INCL | IN_I_PCH) \
D(endif, T_ENDIF, KANDR, COND) \
D(ifdef, T_IFDEF, KANDR, COND | IF_COND) \
D(if, T_IF, KANDR, COND | IF_COND | EXPAND) \
D(else, T_ELSE, KANDR, COND) \
D(ifndef, T_IFNDEF, KANDR, COND | IF_COND) \
D(undef, T_UNDEF, KANDR, IN_I) \
D(line, T_LINE, KANDR, EXPAND) \
D(elif, T_ELIF, STDC89, COND | EXPAND) \
D(error, T_ERROR, STDC89, 0) \
D(pragma, T_PRAGMA, STDC89, IN_I) \
D(warning, T_WARNING, EXTENSION, 0) \
D(include_next, T_INCLUDE_NEXT, EXTENSION, INCL | EXPAND) \
D(ident, T_IDENT, EXTENSION, IN_I) \
D(import, T_IMPORT, EXTENSION, INCL | EXPAND) \
D(assert, T_ASSERT, EXTENSION, 0) \
D(unassert, T_UNASSERT, EXTENSION, 0) \
D(sccs, T_SCCS, EXTENSION, 0)
#define D(name, t, o, f) static void do_##name (cpp_reader *);
DIRECTIVE_TABLE
#undef D
#define D(n, tag, o, f) tag,
enum
{
DIRECTIVE_TABLE
N_DIRECTIVES
};
#undef D
#define D(name, t, origin, flags) \
{ do_##name, (const uchar *) #name, \
sizeof #name - 1, origin, flags },
static const directive dtable[] =
{
DIRECTIVE_TABLE
};
#undef D
#undef DIRECTIVE_TABLE
static const directive linemarker_dir =
{
do_linemarker, U"#", 1, KANDR, IN_I
};
#define SEEN_EOL() (pfile->cur_token[-1].type == CPP_EOF)
static void
skip_rest_of_line (cpp_reader *pfile)
{
while (pfile->context->prev)
_cpp_pop_context (pfile);
if (! SEEN_EOL ())
while (_cpp_lex_token (pfile)->type != CPP_EOF)
;
}
static void
check_eol (cpp_reader *pfile)
{
if (! SEEN_EOL () && _cpp_lex_token (pfile)->type != CPP_EOF
&& CPP_OPTION (pfile, warn_extra_tokens))
cpp_error (pfile, CPP_DL_PEDWARN, "extra tokens at end of #%s directive",
pfile->directive->name);
}
static void
start_directive (cpp_reader *pfile)
{
pfile->state.in_directive = 1;
pfile->state.save_comments = 0;
pfile->directive_result.type = CPP_PADDING;
pfile->directive_line = pfile->line_table->highest_line;
}
static void
end_directive (cpp_reader *pfile, int skip_line)
{
if (CPP_OPTION (pfile, traditional))
{
pfile->state.prevent_expansion--;
if (pfile->directive != &dtable[T_DEFINE])
_cpp_remove_overlay (pfile);
}
else if (skip_line)
{
skip_rest_of_line (pfile);
if (!pfile->keep_tokens)
{
pfile->cur_run = &pfile->base_run;
pfile->cur_token = pfile->base_run.base;
}
}
pfile->state.save_comments = ! CPP_OPTION (pfile, discard_comments);
pfile->state.in_directive = 0;
pfile->state.in_expression = 0;
pfile->state.angled_headers = 0;
pfile->directive = 0;
}
static void
prepare_directive_trad (cpp_reader *pfile)
{
if (pfile->directive != &dtable[T_DEFINE])
{
bool no_expand = (pfile->directive
&& ! (pfile->directive->flags & EXPAND));
bool was_skipping = pfile->state.skipping;
pfile->state.in_expression = (pfile->directive == &dtable[T_IF]
|| pfile->directive == &dtable[T_ELIF]);
if (pfile->state.in_expression)
pfile->state.skipping = false;
if (no_expand)
pfile->state.prevent_expansion++;
_cpp_scan_out_logical_line (pfile, NULL);
if (no_expand)
pfile->state.prevent_expansion--;
pfile->state.skipping = was_skipping;
_cpp_overlay_buffer (pfile, pfile->out.base,
pfile->out.cur - pfile->out.base);
}
pfile->state.prevent_expansion++;
}
static void
directive_diagnostics (cpp_reader *pfile, const directive *dir, int indented)
{
if (CPP_PEDANTIC (pfile)
&& ! pfile->state.skipping
&& dir->origin == EXTENSION)
cpp_error (pfile, CPP_DL_PEDWARN, "#%s is a GCC extension", dir->name);
if (CPP_WTRADITIONAL (pfile))
{
if (dir == &dtable[T_ELIF])
cpp_error (pfile, CPP_DL_WARNING,
"suggest not using #elif in traditional C");
else if (indented && dir->origin == KANDR)
cpp_error (pfile, CPP_DL_WARNING,
"traditional C ignores #%s with the # indented",
dir->name);
else if (!indented && dir->origin != KANDR)
cpp_error (pfile, CPP_DL_WARNING,
"suggest hiding #%s from traditional C with an indented #",
dir->name);
}
}
int
_cpp_handle_directive (cpp_reader *pfile, int indented)
{
const directive *dir = 0;
const cpp_token *dname;
bool was_parsing_args = pfile->state.parsing_args;
bool was_discarding_output = pfile->state.discarding_output;
int skip = 1;
if (was_discarding_output)
pfile->state.prevent_expansion = 0;
if (was_parsing_args)
{
if (CPP_OPTION (pfile, pedantic))
cpp_error (pfile, CPP_DL_PEDWARN,
"embedding a directive within macro arguments is not portable");
pfile->state.parsing_args = 0;
pfile->state.prevent_expansion = 0;
}
start_directive (pfile);
dname = _cpp_lex_token (pfile);
if (dname->type == CPP_NAME)
{
if (dname->val.node->is_directive)
dir = &dtable[dname->val.node->directive_index];
}
else if (dname->type == CPP_NUMBER && CPP_OPTION (pfile, lang) != CLK_ASM)
{
dir = &linemarker_dir;
if (CPP_PEDANTIC (pfile) && ! CPP_OPTION (pfile, preprocessed)
&& ! pfile->state.skipping)
cpp_error (pfile, CPP_DL_PEDWARN,
"style of line directive is a GCC extension");
}
if (dir)
{
if (! (dir->flags & IF_COND))
pfile->mi_valid = false;
if (CPP_OPTION (pfile, preprocessed)
&& (indented || !(dir->flags & IN_I))
&& ! (CPP_OPTION (pfile, pch_preprocess)
&& (dir->flags & IN_I_PCH)))
{
skip = 0;
dir = 0;
}
else
{
pfile->state.angled_headers = dir->flags & INCL;
pfile->state.directive_wants_padding = dir->flags & INCL;
if (! CPP_OPTION (pfile, preprocessed))
directive_diagnostics (pfile, dir, indented);
if (pfile->state.skipping && !(dir->flags & COND))
dir = 0;
}
}
else if (dname->type == CPP_EOF)
;
else
{
if (CPP_OPTION (pfile, lang) == CLK_ASM)
skip = 0;
else if (!pfile->state.skipping)
cpp_error (pfile, CPP_DL_ERROR, "invalid preprocessing directive #%s",
cpp_token_as_text (pfile, dname));
}
pfile->directive = dir;
if (CPP_OPTION (pfile, traditional))
prepare_directive_trad (pfile);
if (dir)
pfile->directive->handler (pfile);
else if (skip == 0)
_cpp_backup_tokens (pfile, 1);
end_directive (pfile, skip);
if (was_parsing_args)
{
pfile->state.parsing_args = 2;
pfile->state.prevent_expansion = 1;
}
if (was_discarding_output)
pfile->state.prevent_expansion = 1;
return skip;
}
static void
run_directive (cpp_reader *pfile, int dir_no, const char *buf, size_t count)
{
cpp_push_buffer (pfile, (const uchar *) buf, count,
true);
if (dir_no == T_PRAGMA && pfile->buffer->prev)
pfile->buffer->file = pfile->buffer->prev->file;
start_directive (pfile);
_cpp_clean_line (pfile);
pfile->directive = &dtable[dir_no];
if (CPP_OPTION (pfile, traditional))
prepare_directive_trad (pfile);
pfile->directive->handler (pfile);
end_directive (pfile, 1);
if (dir_no == T_PRAGMA)
pfile->buffer->file = NULL;
_cpp_pop_buffer (pfile);
}
static cpp_hashnode *
lex_macro_node (cpp_reader *pfile)
{
const cpp_token *token = _cpp_lex_token (pfile);
if (token->type == CPP_NAME)
{
cpp_hashnode *node = token->val.node;
if (node == pfile->spec_nodes.n_defined)
cpp_error (pfile, CPP_DL_ERROR,
"\"defined\" cannot be used as a macro name");
else if (! (node->flags & NODE_POISONED))
return node;
}
else if (token->flags & NAMED_OP)
cpp_error (pfile, CPP_DL_ERROR,
"\"%s\" cannot be used as a macro name as it is an operator in C++",
NODE_NAME (token->val.node));
else if (token->type == CPP_EOF)
cpp_error (pfile, CPP_DL_ERROR, "no macro name given in #%s directive",
pfile->directive->name);
else
cpp_error (pfile, CPP_DL_ERROR, "macro names must be identifiers");
return NULL;
}
static void
do_define (cpp_reader *pfile)
{
cpp_hashnode *node = lex_macro_node (pfile);
if (node)
{
pfile->state.save_comments =
! CPP_OPTION (pfile, discard_comments_in_macro_exp);
if (_cpp_create_definition (pfile, node))
if (pfile->cb.define)
pfile->cb.define (pfile, pfile->directive_line, node);
}
}
static void
do_undef (cpp_reader *pfile)
{
cpp_hashnode *node = lex_macro_node (pfile);
if (node)
{
if (pfile->cb.undef)
pfile->cb.undef (pfile, pfile->directive_line, node);
if (node->type == NT_MACRO)
{
if (node->flags & NODE_WARN)
cpp_error (pfile, CPP_DL_WARNING,
"undefining \"%s\"", NODE_NAME (node));
if (CPP_OPTION (pfile, warn_unused_macros))
_cpp_warn_if_unused_macro (pfile, node, NULL);
_cpp_free_definition (node);
}
}
check_eol (pfile);
}
static int
undefine_macros (cpp_reader *pfile ATTRIBUTE_UNUSED, cpp_hashnode *h,
void *data_p ATTRIBUTE_UNUSED)
{
h->type = NT_VOID;
h->flags &= ~(NODE_POISONED|NODE_BUILTIN|NODE_DISABLED);
return 1;
}
void
cpp_undef_all (cpp_reader *pfile)
{
cpp_forall_identifiers (pfile, undefine_macros, NULL);
}
static char *
glue_header_name (cpp_reader *pfile)
{
const cpp_token *token;
char *buffer;
size_t len, total_len = 0, capacity = 1024;
buffer = xmalloc (capacity);
for (;;)
{
token = get_token_no_padding (pfile);
if (token->type == CPP_GREATER)
break;
if (token->type == CPP_EOF)
{
cpp_error (pfile, CPP_DL_ERROR, "missing terminating > character");
break;
}
len = cpp_token_len (token) + 2;
if (total_len + len > capacity)
{
capacity = (capacity + len) * 2;
buffer = xrealloc (buffer, capacity);
}
if (token->flags & PREV_WHITE)
buffer[total_len++] = ' ';
total_len = (cpp_spell_token (pfile, token, (uchar *) &buffer[total_len],
true)
- (uchar *) buffer);
}
buffer[total_len] = '\0';
return buffer;
}
static const char *
parse_include (cpp_reader *pfile, int *pangle_brackets)
{
char *fname;
const cpp_token *header;
header = get_token_no_padding (pfile);
if (header->type == CPP_STRING || header->type == CPP_HEADER_NAME)
{
fname = xmalloc (header->val.str.len - 1);
memcpy (fname, header->val.str.text + 1, header->val.str.len - 2);
fname[header->val.str.len - 2] = '\0';
*pangle_brackets = header->type == CPP_HEADER_NAME;
}
else if (header->type == CPP_LESS)
{
fname = glue_header_name (pfile);
*pangle_brackets = 1;
}
else
{
const unsigned char *dir;
if (pfile->directive == &dtable[T_PRAGMA])
dir = U"pragma dependency";
else
dir = pfile->directive->name;
cpp_error (pfile, CPP_DL_ERROR, "#%s expects \"FILENAME\" or <FILENAME>",
dir);
return NULL;
}
check_eol (pfile);
return fname;
}
static void
do_include_common (cpp_reader *pfile, enum include_type type)
{
const char *fname;
int angle_brackets;
fname = parse_include (pfile, &angle_brackets);
if (!fname)
return;
if (!*fname)
{
cpp_error (pfile, CPP_DL_ERROR, "empty filename in #%s",
pfile->directive->name);
free ((void *) fname);
return;
}
if (pfile->line_table->depth >= CPP_STACK_MAX)
cpp_error (pfile, CPP_DL_ERROR, "#include nested too deeply");
else
{
skip_rest_of_line (pfile);
if (pfile->cb.include)
pfile->cb.include (pfile, pfile->directive_line,
pfile->directive->name, fname, angle_brackets);
_cpp_stack_include (pfile, fname, angle_brackets, type);
}
free ((void *) fname);
}
static void
do_include (cpp_reader *pfile)
{
do_include_common (pfile, IT_INCLUDE);
}
static void
do_include_pch (cpp_reader *pfile)
{
do_include_common (pfile, IT_INCLUDE_PCH);
}
static void
do_import (cpp_reader *pfile)
{
do_include_common (pfile, IT_IMPORT);
}
static void
do_include_next (cpp_reader *pfile)
{
enum include_type type = IT_INCLUDE_NEXT;
if (! pfile->buffer->prev)
{
cpp_error (pfile, CPP_DL_WARNING,
"#include_next in primary source file");
type = IT_INCLUDE;
}
do_include_common (pfile, type);
}
static unsigned int
read_flag (cpp_reader *pfile, unsigned int last)
{
const cpp_token *token = _cpp_lex_token (pfile);
if (token->type == CPP_NUMBER && token->val.str.len == 1)
{
unsigned int flag = token->val.str.text[0] - '0';
if (flag > last && flag <= 4
&& (flag != 4 || last == 3)
&& (flag != 2 || last == 0))
return flag;
}
if (token->type != CPP_EOF)
cpp_error (pfile, CPP_DL_ERROR, "invalid flag \"%s\" in line directive",
cpp_token_as_text (pfile, token));
return 0;
}
static int
strtoul_for_line (const uchar *str, unsigned int len, long unsigned int *nump)
{
unsigned long reg = 0;
uchar c;
while (len--)
{
c = *str++;
if (!ISDIGIT (c))
return 1;
reg *= 10;
reg += c - '0';
}
*nump = reg;
return 0;
}
static void
do_line (cpp_reader *pfile)
{
const struct line_maps *line_table = pfile->line_table;
const struct line_map *map = &line_table->maps[line_table->used - 1];
unsigned char map_sysp = map->sysp;
const cpp_token *token;
const char *new_file = map->to_file;
unsigned long new_lineno;
unsigned int cap = CPP_OPTION (pfile, c99) ? 2147483647 : 32767;
token = cpp_get_token (pfile);
if (token->type != CPP_NUMBER
|| strtoul_for_line (token->val.str.text, token->val.str.len,
&new_lineno))
{
cpp_error (pfile, CPP_DL_ERROR,
"\"%s\" after #line is not a positive integer",
cpp_token_as_text (pfile, token));
return;
}
if (CPP_PEDANTIC (pfile) && (new_lineno == 0 || new_lineno > cap))
cpp_error (pfile, CPP_DL_PEDWARN, "line number out of range");
token = cpp_get_token (pfile);
if (token->type == CPP_STRING)
{
cpp_string s = { 0, 0 };
if (cpp_interpret_string_notranslate (pfile, &token->val.str, 1,
&s, false, false))
new_file = (const char *)s.text;
check_eol (pfile);
}
else if (token->type != CPP_EOF)
{
cpp_error (pfile, CPP_DL_ERROR, "\"%s\" is not a valid filename",
cpp_token_as_text (pfile, token));
return;
}
skip_rest_of_line (pfile);
_cpp_do_file_change (pfile, LC_RENAME, new_file, new_lineno,
map_sysp);
}
static void
do_linemarker (cpp_reader *pfile)
{
const struct line_maps *line_table = pfile->line_table;
const struct line_map *map = &line_table->maps[line_table->used - 1];
const cpp_token *token;
const char *new_file = map->to_file;
unsigned long new_lineno;
unsigned int new_sysp = map->sysp;
enum lc_reason reason = LC_RENAME;
int flag;
_cpp_backup_tokens (pfile, 1);
token = cpp_get_token (pfile);
if (token->type != CPP_NUMBER
|| strtoul_for_line (token->val.str.text, token->val.str.len,
&new_lineno))
{
cpp_error (pfile, CPP_DL_ERROR,
"\"%s\" after # is not a positive integer",
cpp_token_as_text (pfile, token));
return;
}
token = cpp_get_token (pfile);
if (token->type == CPP_STRING)
{
cpp_string s = { 0, 0 };
if (cpp_interpret_string_notranslate (pfile, &token->val.str,
1, &s, false, false))
new_file = (const char *)s.text;
new_sysp = 0;
flag = read_flag (pfile, 0);
if (flag == 1)
{
reason = LC_ENTER;
_cpp_fake_include (pfile, new_file);
flag = read_flag (pfile, flag);
}
else if (flag == 2)
{
reason = LC_LEAVE;
flag = read_flag (pfile, flag);
}
if (flag == 3)
{
new_sysp = 1;
flag = read_flag (pfile, flag);
if (flag == 4)
new_sysp = 2;
pfile->buffer->sysp = new_sysp;
}
check_eol (pfile);
}
else if (token->type != CPP_EOF)
{
cpp_error (pfile, CPP_DL_ERROR, "\"%s\" is not a valid filename",
cpp_token_as_text (pfile, token));
return;
}
skip_rest_of_line (pfile);
_cpp_do_file_change (pfile, reason, new_file, new_lineno, new_sysp);
}
void
_cpp_do_file_change (cpp_reader *pfile, enum lc_reason reason,
const char *to_file, unsigned int file_line,
unsigned int sysp)
{
const struct line_map *map = linemap_add (pfile->line_table, reason, sysp,
to_file, file_line);
if (map != NULL)
linemap_line_start (pfile->line_table, map->to_line, 127);
if (pfile->cb.file_change)
{
struct line_map *old_maps = pfile->line_table->maps;
source_location old_loc = 0;
if (map)
old_loc = map->start_location;
pfile->cb.file_change (pfile, map);
if (map && old_maps != pfile->line_table->maps)
map = linemap_lookup (pfile->line_table, old_loc);
}
if (map && CPP_OPTION (pfile, defer_file_change_debug_hooks))
{
cpp_token *tok;
if ((reason == LC_ENTER && !MAIN_FILE_P (map)))
{
uchar *s;
cpp_string body;
tok = &pfile->directive_result;
tok->type = CPP_BINCL;
body.len = strlen (map->to_file);
s = _cpp_unaligned_alloc (pfile, body.len + 1);
memcpy (s, map->to_file, body.len + 1);
body.text = s;
tok->val.str = body;
#ifdef USE_MAPPED_LOCATION
tok->src_loc = LAST_SOURCE_LINE_LOCATION (map - 1);
#else
tok->src_loc = LAST_SOURCE_LINE (map - 1);
#endif
}
else if (reason == LC_LEAVE)
{
if (pfile->end_eincl == pfile->cur_eincl->limit)
{
pfile->cur_eincl = _cpp_next_tokenrun (pfile->cur_eincl);
pfile->end_eincl = pfile->cur_eincl->base;
}
tok = pfile->end_eincl++;
tok->type = CPP_EINCL;
tok->src_loc = map->to_line;
tok->flags = 0;
pfile->have_eincl = true;
}
}
}
static void
do_diagnostic (cpp_reader *pfile, int code, int print_dir)
{
if (_cpp_begin_message (pfile, code, pfile->cur_token[-1].src_loc, 0))
{
if (print_dir)
fprintf (stderr, "#%s ", pfile->directive->name);
pfile->state.prevent_expansion++;
cpp_output_line (pfile, stderr);
pfile->state.prevent_expansion--;
}
}
static void
do_error (cpp_reader *pfile)
{
do_diagnostic (pfile, CPP_DL_ERROR, 1);
}
static void
do_warning (cpp_reader *pfile)
{
if (!CPP_OPTION (pfile, no_pound_warnings)
&& (!CPP_IN_SYSTEM_HEADER (pfile)
|| CPP_OPTION (pfile, warn_system_headers)))
do_diagnostic (pfile, CPP_DL_WARNING_SYSHDR, 1);
}
static void
do_ident (cpp_reader *pfile)
{
const cpp_token *str = cpp_get_token (pfile);
if (str->type != CPP_STRING)
cpp_error (pfile, CPP_DL_ERROR, "invalid #ident directive");
else if (pfile->cb.ident)
pfile->cb.ident (pfile, pfile->directive_line, &str->val.str);
check_eol (pfile);
}
static struct pragma_entry *
lookup_pragma_entry (struct pragma_entry *chain, const cpp_hashnode *pragma)
{
while (chain && chain->pragma != pragma)
chain = chain->next;
return chain;
}
static struct pragma_entry *
insert_pragma_entry (cpp_reader *pfile, struct pragma_entry **chain,
const cpp_hashnode *pragma, pragma_cb handler,
bool allow_expansion, bool internal)
{
struct pragma_entry *new;
new = (struct pragma_entry *)
_cpp_aligned_alloc (pfile, sizeof (struct pragma_entry));
new->pragma = pragma;
if (handler)
{
new->is_nspace = 0;
new->u.handler = handler;
}
else
{
new->is_nspace = 1;
new->u.space = NULL;
}
new->allow_expansion = allow_expansion;
new->is_internal = internal;
new->next = *chain;
*chain = new;
return new;
}
static void
register_pragma (cpp_reader *pfile, const char *space, const char *name,
pragma_cb handler, bool allow_expansion, bool internal)
{
struct pragma_entry **chain = &pfile->pragmas;
struct pragma_entry *entry;
const cpp_hashnode *node;
if (!handler)
abort ();
if (space)
{
node = cpp_lookup (pfile, U space, strlen (space));
entry = lookup_pragma_entry (*chain, node);
if (!entry)
entry = insert_pragma_entry (pfile, chain, node, NULL,
allow_expansion, internal);
else if (!entry->is_nspace)
goto clash;
chain = &entry->u.space;
}
node = cpp_lookup (pfile, U name, strlen (name));
entry = lookup_pragma_entry (*chain, node);
if (entry)
{
if (entry->is_nspace)
clash:
cpp_error (pfile, CPP_DL_ICE,
"registering \"%s\" as both a pragma and a pragma namespace",
NODE_NAME (node));
else if (space)
cpp_error (pfile, CPP_DL_ICE, "#pragma %s %s is already registered",
space, name);
else
cpp_error (pfile, CPP_DL_ICE, "#pragma %s is already registered", name);
}
else
insert_pragma_entry (pfile, chain, node, handler, allow_expansion,
internal);
}
void
cpp_register_pragma (cpp_reader *pfile, const char *space, const char *name,
pragma_cb handler, bool allow_expansion)
{
register_pragma (pfile, space, name, handler, allow_expansion, false);
}
void
_cpp_init_internal_pragmas (cpp_reader *pfile)
{
register_pragma (pfile, 0, "once", do_pragma_once, false, true);
register_pragma (pfile, "GCC", "poison", do_pragma_poison, false, true);
register_pragma (pfile, "GCC", "system_header", do_pragma_system_header,
false, true);
register_pragma (pfile, "GCC", "dependency", do_pragma_dependency,
false, true);
}
static int
count_registered_pragmas (struct pragma_entry *pe)
{
int ct = 0;
for (; pe != NULL; pe = pe->next)
{
if (pe->is_nspace)
ct += count_registered_pragmas (pe->u.space);
ct++;
}
return ct;
}
static char **
save_registered_pragmas (struct pragma_entry *pe, char **sd)
{
for (; pe != NULL; pe = pe->next)
{
if (pe->is_nspace)
sd = save_registered_pragmas (pe->u.space, sd);
*sd++ = xmemdup (HT_STR (&pe->pragma->ident),
HT_LEN (&pe->pragma->ident),
HT_LEN (&pe->pragma->ident) + 1);
}
return sd;
}
char **
_cpp_save_pragma_names (cpp_reader *pfile)
{
int ct = count_registered_pragmas (pfile->pragmas);
char **result = XNEWVEC (char *, ct);
(void) save_registered_pragmas (pfile->pragmas, result);
return result;
}
static char **
restore_registered_pragmas (cpp_reader *pfile, struct pragma_entry *pe,
char **sd)
{
for (; pe != NULL; pe = pe->next)
{
if (pe->is_nspace)
sd = restore_registered_pragmas (pfile, pe->u.space, sd);
pe->pragma = cpp_lookup (pfile, U *sd, strlen (*sd));
free (*sd);
sd++;
}
return sd;
}
void
_cpp_restore_pragma_names (cpp_reader *pfile, char **saved)
{
(void) restore_registered_pragmas (pfile, pfile->pragmas, saved);
free (saved);
}
static void
do_pragma (cpp_reader *pfile)
{
const struct pragma_entry *p = NULL;
const cpp_token *token, *pragma_token = pfile->cur_token;
unsigned int count = 1;
const uchar *line_start = CPP_BUFFER (pfile)->cur;
pfile->state.prevent_expansion++;
token = cpp_get_token (pfile);
if (token->type == CPP_NAME)
{
p = lookup_pragma_entry (pfile->pragmas, token->val.node);
if (p && p->is_nspace)
{
count = 2;
token = cpp_get_token (pfile);
if (token->type == CPP_NAME)
p = lookup_pragma_entry (p->u.space, token->val.node);
else
p = NULL;
}
}
if (p)
{
if (p->is_internal || !CPP_OPTION (pfile, defer_pragmas))
{
if (pfile->cb.line_change)
(*pfile->cb.line_change) (pfile, pragma_token, false);
if (p->allow_expansion && !pfile->state.in_deferred_pragma)
pfile->state.prevent_expansion--;
(*p->u.handler) (pfile);
if (p->allow_expansion && !pfile->state.in_deferred_pragma)
pfile->state.prevent_expansion++;
}
else
{
const uchar *line_end;
uchar *s;
cpp_string body;
cpp_token *ptok;
line_end = ustrchr (line_start, '\n');
body.len = (line_end - line_start) + 1;
s = _cpp_unaligned_alloc (pfile, body.len + 1);
memcpy (s, line_start, body.len);
s[body.len] = '\0';
body.text = s;
ptok = &pfile->directive_result;
ptok->src_loc = pragma_token->src_loc;
ptok->type = CPP_PRAGMA;
ptok->flags = pragma_token->flags | NO_EXPAND;
ptok->val.str = body;
}
}
else if (pfile->cb.def_pragma)
{
_cpp_backup_tokens (pfile, count);
pfile->cb.def_pragma (pfile, pfile->directive_line);
}
pfile->state.prevent_expansion--;
}
static void
do_pragma_once (cpp_reader *pfile)
{
if (pfile->buffer->prev == NULL)
cpp_error (pfile, CPP_DL_WARNING, "#pragma once in main file");
check_eol (pfile);
_cpp_mark_file_once_only (pfile, pfile->buffer->file);
}
static void
do_pragma_poison (cpp_reader *pfile)
{
const cpp_token *tok;
cpp_hashnode *hp;
pfile->state.poisoned_ok = 1;
for (;;)
{
tok = _cpp_lex_token (pfile);
if (tok->type == CPP_EOF)
break;
if (tok->type != CPP_NAME)
{
cpp_error (pfile, CPP_DL_ERROR,
"invalid #pragma GCC poison directive");
break;
}
hp = tok->val.node;
if (hp->flags & NODE_POISONED)
continue;
if (hp->type == NT_MACRO)
cpp_error (pfile, CPP_DL_WARNING, "poisoning existing macro \"%s\"",
NODE_NAME (hp));
_cpp_free_definition (hp);
hp->flags |= NODE_POISONED | NODE_DIAGNOSTIC;
}
pfile->state.poisoned_ok = 0;
}
static void
do_pragma_system_header (cpp_reader *pfile)
{
cpp_buffer *buffer = pfile->buffer;
if (buffer->prev == 0)
cpp_error (pfile, CPP_DL_WARNING,
"#pragma system_header ignored outside include file");
else
{
check_eol (pfile);
skip_rest_of_line (pfile);
cpp_make_system_header (pfile, 1, 0);
}
}
static void
do_pragma_dependency (cpp_reader *pfile)
{
const char *fname;
int angle_brackets, ordering;
fname = parse_include (pfile, &angle_brackets);
if (!fname)
return;
ordering = _cpp_compare_file_date (pfile, fname, angle_brackets);
if (ordering < 0)
cpp_error (pfile, CPP_DL_WARNING, "cannot find source file %s", fname);
else if (ordering > 0)
{
cpp_error (pfile, CPP_DL_WARNING,
"current file is older than %s", fname);
if (cpp_get_token (pfile)->type != CPP_EOF)
{
_cpp_backup_tokens (pfile, 1);
do_diagnostic (pfile, CPP_DL_WARNING, 0);
}
}
free ((void *) fname);
}
static const cpp_token *
get_token_no_padding (cpp_reader *pfile)
{
for (;;)
{
const cpp_token *result = cpp_get_token (pfile);
if (result->type != CPP_PADDING)
return result;
}
}
static const cpp_token *
get__Pragma_string (cpp_reader *pfile)
{
const cpp_token *string;
if (get_token_no_padding (pfile)->type != CPP_OPEN_PAREN)
return NULL;
string = get_token_no_padding (pfile);
if (string->type != CPP_STRING && string->type != CPP_WSTRING)
return NULL;
if (get_token_no_padding (pfile)->type != CPP_CLOSE_PAREN)
return NULL;
return string;
}
static void
destringize_and_run (cpp_reader *pfile, const cpp_string *in)
{
const unsigned char *src, *limit;
char *dest, *result;
dest = result = alloca (in->len - 1);
src = in->text + 1 + (in->text[0] == 'L');
limit = in->text + in->len - 1;
while (src < limit)
{
if (*src == '\\' && (src[1] == '\\' || src[1] == '"'))
src++;
*dest++ = *src++;
}
*dest = '\n';
{
cpp_context *saved_context = pfile->context;
cpp_token *saved_cur_token = pfile->cur_token;
tokenrun *saved_cur_run = pfile->cur_run;
pfile->context = XNEW (cpp_context);
pfile->context->macro = 0;
pfile->context->prev = 0;
run_directive (pfile, T_PRAGMA, result, dest - result);
XDELETE (pfile->context);
pfile->context = saved_context;
pfile->cur_token = saved_cur_token;
pfile->cur_run = saved_cur_run;
}
if (pfile->cb.line_change)
pfile->cb.line_change (pfile, pfile->cur_token, false);
}
void
_cpp_do__Pragma (cpp_reader *pfile)
{
const cpp_token *string = get__Pragma_string (pfile);
pfile->directive_result.type = CPP_PADDING;
if (string)
destringize_and_run (pfile, &string->val.str);
else
cpp_error (pfile, CPP_DL_ERROR,
"_Pragma takes a parenthesized string literal");
}
void
cpp_handle_deferred_pragma (cpp_reader *pfile, const cpp_string *s)
{
cpp_context *saved_context = pfile->context;
cpp_token *saved_cur_token = pfile->cur_token;
tokenrun *saved_cur_run = pfile->cur_run;
bool saved_defer_pragmas = CPP_OPTION (pfile, defer_pragmas);
void (*saved_line_change) (cpp_reader *, const cpp_token *, int)
= pfile->cb.line_change;
pfile->context = XNEW (cpp_context);
pfile->context->macro = 0;
pfile->context->prev = 0;
pfile->cb.line_change = NULL;
pfile->state.in_deferred_pragma = true;
CPP_OPTION (pfile, defer_pragmas) = false;
run_directive (pfile, T_PRAGMA, (const char *)s->text, s->len);
XDELETE (pfile->context);
pfile->context = saved_context;
pfile->cur_token = saved_cur_token;
pfile->cur_run = saved_cur_run;
pfile->cb.line_change = saved_line_change;
pfile->state.in_deferred_pragma = false;
CPP_OPTION (pfile, defer_pragmas) = saved_defer_pragmas;
}
static void
do_sccs (cpp_reader *pfile ATTRIBUTE_UNUSED)
{
}
static void
do_ifdef (cpp_reader *pfile)
{
int skip = 1;
if (! pfile->state.skipping)
{
const cpp_hashnode *node = lex_macro_node (pfile);
if (node)
{
skip = node->type != NT_MACRO;
_cpp_mark_macro_used (node);
check_eol (pfile);
}
}
push_conditional (pfile, skip, T_IFDEF, 0);
}
static void
do_ifndef (cpp_reader *pfile)
{
int skip = 1;
const cpp_hashnode *node = 0;
if (! pfile->state.skipping)
{
node = lex_macro_node (pfile);
if (node)
{
skip = node->type == NT_MACRO;
_cpp_mark_macro_used (node);
check_eol (pfile);
}
}
push_conditional (pfile, skip, T_IFNDEF, node);
}
static void
do_if (cpp_reader *pfile)
{
int skip = 1;
if (! pfile->state.skipping)
skip = _cpp_parse_expr (pfile) == false;
push_conditional (pfile, skip, T_IF, pfile->mi_ind_cmacro);
}
static void
do_else (cpp_reader *pfile)
{
cpp_buffer *buffer = pfile->buffer;
struct if_stack *ifs = buffer->if_stack;
if (ifs == NULL)
cpp_error (pfile, CPP_DL_ERROR, "#else without #if");
else
{
if (ifs->type == T_ELSE)
{
cpp_error (pfile, CPP_DL_ERROR, "#else after #else");
cpp_error_with_line (pfile, CPP_DL_ERROR, ifs->line, 0,
"the conditional began here");
}
ifs->type = T_ELSE;
pfile->state.skipping = ifs->skip_elses;
ifs->skip_elses = true;
ifs->mi_cmacro = 0;
if (!ifs->was_skipping
&& (CPP_OPTION (pfile, warn_endif_labels) || CPP_OPTION (pfile, warn_extra_tokens)))
check_eol (pfile);
}
}
static void
do_elif (cpp_reader *pfile)
{
cpp_buffer *buffer = pfile->buffer;
struct if_stack *ifs = buffer->if_stack;
if (ifs == NULL)
cpp_error (pfile, CPP_DL_ERROR, "#elif without #if");
else
{
if (ifs->type == T_ELSE)
{
cpp_error (pfile, CPP_DL_ERROR, "#elif after #else");
cpp_error_with_line (pfile, CPP_DL_ERROR, ifs->line, 0,
"the conditional began here");
}
ifs->type = T_ELIF;
if (ifs->skip_elses)
pfile->state.skipping = 1;
else
{
pfile->state.skipping = 0;
pfile->state.skipping = ! _cpp_parse_expr (pfile);
ifs->skip_elses = ! pfile->state.skipping;
}
ifs->mi_cmacro = 0;
}
}
static void
do_endif (cpp_reader *pfile)
{
cpp_buffer *buffer = pfile->buffer;
struct if_stack *ifs = buffer->if_stack;
if (ifs == NULL)
cpp_error (pfile, CPP_DL_ERROR, "#endif without #if");
else
{
if (!ifs->was_skipping
&& (CPP_OPTION (pfile, warn_endif_labels) || CPP_OPTION (pfile, warn_extra_tokens)))
check_eol (pfile);
if (ifs->next == 0 && ifs->mi_cmacro)
{
pfile->mi_valid = true;
pfile->mi_cmacro = ifs->mi_cmacro;
}
buffer->if_stack = ifs->next;
pfile->state.skipping = ifs->was_skipping;
obstack_free (&pfile->buffer_ob, ifs);
}
}
static void
push_conditional (cpp_reader *pfile, int skip, int type,
const cpp_hashnode *cmacro)
{
struct if_stack *ifs;
cpp_buffer *buffer = pfile->buffer;
ifs = XOBNEW (&pfile->buffer_ob, struct if_stack);
ifs->line = pfile->directive_line;
ifs->next = buffer->if_stack;
ifs->skip_elses = pfile->state.skipping || !skip;
ifs->was_skipping = pfile->state.skipping;
ifs->type = type;
if (pfile->mi_valid && pfile->mi_cmacro == 0)
ifs->mi_cmacro = cmacro;
else
ifs->mi_cmacro = 0;
pfile->state.skipping = skip;
buffer->if_stack = ifs;
}
static int
parse_answer (cpp_reader *pfile, struct answer **answerp, int type)
{
const cpp_token *paren;
struct answer *answer;
unsigned int acount;
paren = cpp_get_token (pfile);
if (paren->type != CPP_OPEN_PAREN)
{
if (type == T_IF)
{
_cpp_backup_tokens (pfile, 1);
return 0;
}
if (type == T_UNASSERT && paren->type == CPP_EOF)
return 0;
cpp_error (pfile, CPP_DL_ERROR, "missing '(' after predicate");
return 1;
}
for (acount = 0;; acount++)
{
size_t room_needed;
const cpp_token *token = cpp_get_token (pfile);
cpp_token *dest;
if (token->type == CPP_CLOSE_PAREN)
break;
if (token->type == CPP_EOF)
{
cpp_error (pfile, CPP_DL_ERROR, "missing ')' to complete answer");
return 1;
}
room_needed = (sizeof (struct answer) + acount * sizeof (cpp_token));
if (BUFF_ROOM (pfile->a_buff) < room_needed)
_cpp_extend_buff (pfile, &pfile->a_buff, sizeof (struct answer));
dest = &((struct answer *) BUFF_FRONT (pfile->a_buff))->first[acount];
*dest = *token;
if (acount == 0)
dest->flags &= ~PREV_WHITE;
}
if (acount == 0)
{
cpp_error (pfile, CPP_DL_ERROR, "predicate's answer is empty");
return 1;
}
answer = (struct answer *) BUFF_FRONT (pfile->a_buff);
answer->count = acount;
answer->next = NULL;
*answerp = answer;
return 0;
}
static cpp_hashnode *
parse_assertion (cpp_reader *pfile, struct answer **answerp, int type)
{
cpp_hashnode *result = 0;
const cpp_token *predicate;
pfile->state.prevent_expansion++;
*answerp = 0;
predicate = cpp_get_token (pfile);
if (predicate->type == CPP_EOF)
cpp_error (pfile, CPP_DL_ERROR, "assertion without predicate");
else if (predicate->type != CPP_NAME)
cpp_error (pfile, CPP_DL_ERROR, "predicate must be an identifier");
else if (parse_answer (pfile, answerp, type) == 0)
{
unsigned int len = NODE_LEN (predicate->val.node);
unsigned char *sym = alloca (len + 1);
sym[0] = '#';
memcpy (sym + 1, NODE_NAME (predicate->val.node), len);
result = cpp_lookup (pfile, sym, len + 1);
}
pfile->state.prevent_expansion--;
return result;
}
static struct answer **
find_answer (cpp_hashnode *node, const struct answer *candidate)
{
unsigned int i;
struct answer **result;
for (result = &node->value.answers; *result; result = &(*result)->next)
{
struct answer *answer = *result;
if (answer->count == candidate->count)
{
for (i = 0; i < answer->count; i++)
if (! _cpp_equiv_tokens (&answer->first[i], &candidate->first[i]))
break;
if (i == answer->count)
break;
}
}
return result;
}
int
_cpp_test_assertion (cpp_reader *pfile, unsigned int *value)
{
struct answer *answer;
cpp_hashnode *node;
node = parse_assertion (pfile, &answer, T_IF);
*value = 0;
if (node)
*value = (node->type == NT_ASSERTION &&
(answer == 0 || *find_answer (node, answer) != 0));
else if (pfile->cur_token[-1].type == CPP_EOF)
_cpp_backup_tokens (pfile, 1);
return node == 0;
}
static void
do_assert (cpp_reader *pfile)
{
struct answer *new_answer;
cpp_hashnode *node;
node = parse_assertion (pfile, &new_answer, T_ASSERT);
if (node)
{
size_t answer_size;
new_answer->next = 0;
if (node->type == NT_ASSERTION)
{
if (*find_answer (node, new_answer))
{
cpp_error (pfile, CPP_DL_WARNING, "\"%s\" re-asserted",
NODE_NAME (node) + 1);
return;
}
new_answer->next = node->value.answers;
}
answer_size = sizeof (struct answer) + ((new_answer->count - 1)
* sizeof (cpp_token));
if (pfile->hash_table->alloc_subobject)
{
struct answer *temp_answer = new_answer;
new_answer = pfile->hash_table->alloc_subobject (answer_size);
memcpy (new_answer, temp_answer, answer_size);
}
else
BUFF_FRONT (pfile->a_buff) += answer_size;
node->type = NT_ASSERTION;
node->value.answers = new_answer;
check_eol (pfile);
}
}
static void
do_unassert (cpp_reader *pfile)
{
cpp_hashnode *node;
struct answer *answer;
node = parse_assertion (pfile, &answer, T_UNASSERT);
if (node && node->type == NT_ASSERTION)
{
if (answer)
{
struct answer **p = find_answer (node, answer), *temp;
temp = *p;
if (temp)
*p = temp->next;
if (node->value.answers == 0)
node->type = NT_VOID;
check_eol (pfile);
}
else
_cpp_free_definition (node);
}
}
void
cpp_define (cpp_reader *pfile, const char *str)
{
char *buf, *p;
size_t count;
count = strlen (str);
buf = alloca (count + 3);
memcpy (buf, str, count);
p = strchr (str, '=');
if (p)
buf[p - str] = ' ';
else
{
buf[count++] = ' ';
buf[count++] = '1';
}
buf[count] = '\n';
run_directive (pfile, T_DEFINE, buf, count);
}
void
_cpp_define_builtin (cpp_reader *pfile, const char *str)
{
size_t len = strlen (str);
char *buf = alloca (len + 1);
memcpy (buf, str, len);
buf[len] = '\n';
run_directive (pfile, T_DEFINE, buf, len);
}
void
cpp_undef (cpp_reader *pfile, const char *macro)
{
size_t len = strlen (macro);
char *buf = alloca (len + 1);
memcpy (buf, macro, len);
buf[len] = '\n';
run_directive (pfile, T_UNDEF, buf, len);
}
void
cpp_assert (cpp_reader *pfile, const char *str)
{
handle_assertion (pfile, str, T_ASSERT);
}
void
cpp_unassert (cpp_reader *pfile, const char *str)
{
handle_assertion (pfile, str, T_UNASSERT);
}
static void
handle_assertion (cpp_reader *pfile, const char *str, int type)
{
size_t count = strlen (str);
const char *p = strchr (str, '=');
char *buf = alloca (count + 2);
memcpy (buf, str, count);
if (p)
{
buf[p - str] = '(';
buf[count++] = ')';
}
buf[count] = '\n';
str = buf;
run_directive (pfile, type, str, count);
}
unsigned int
cpp_errors (cpp_reader *pfile)
{
return pfile->errors;
}
cpp_options *
cpp_get_options (cpp_reader *pfile)
{
return &pfile->opts;
}
void
set_stdin_option (cpp_reader *pfile, int predict_comp_size)
{
if (! CPP_OPTION (pfile, preprocessed))
{
CPP_OPTION (pfile, predictive_compilation) = true;
CPP_OPTION (pfile, predictive_compilation_size) = predict_comp_size;
}
}
cpp_callbacks *
cpp_get_callbacks (cpp_reader *pfile)
{
return &pfile->cb;
}
void
cpp_set_callbacks (cpp_reader *pfile, cpp_callbacks *cb)
{
pfile->cb = *cb;
}
struct deps *
cpp_get_deps (cpp_reader *pfile)
{
if (!pfile->deps)
pfile->deps = deps_init ();
return pfile->deps;
}
cpp_buffer *
cpp_push_buffer (cpp_reader *pfile, const uchar *buffer, size_t len,
int from_stage3)
{
cpp_buffer *new = XOBNEW (&pfile->buffer_ob, cpp_buffer);
memset (new, 0, sizeof (cpp_buffer));
new->next_line = new->buf = buffer;
new->rlimit = buffer + len;
new->from_stage3 = from_stage3;
new->prev = pfile->buffer;
new->need_line = true;
pfile->buffer = new;
return new;
}
void
_cpp_pop_buffer (cpp_reader *pfile)
{
cpp_buffer *buffer = pfile->buffer;
struct _cpp_file *inc = buffer->file;
struct if_stack *ifs;
for (ifs = buffer->if_stack; ifs; ifs = ifs->next)
cpp_error_with_line (pfile, CPP_DL_ERROR, ifs->line, 0,
"unterminated #%s", dtable[ifs->type].name);
pfile->state.skipping = 0;
pfile->buffer = buffer->prev;
free (buffer->notes);
obstack_free (&pfile->buffer_ob, buffer);
if (inc)
{
_cpp_pop_file_buffer (pfile, inc);
_cpp_do_file_change (pfile, LC_LEAVE, 0, 0, 0);
}
}
void
_cpp_init_directives (cpp_reader *pfile)
{
unsigned int i;
cpp_hashnode *node;
for (i = 0; i < (unsigned int) N_DIRECTIVES; i++)
{
node = cpp_lookup (pfile, dtable[i].name, dtable[i].length);
node->is_directive = 1;
node->directive_index = i;
}
}