mirror of
git://gcc.gnu.org/git/gcc.git
synced 2024-12-31 00:25:49 +08:00
0fef3fd0ea
* cpphash.h (struct spec_nodes): Remove n__CHAR_UNSIGNED__. * cpphash.c (_cpp_init_hashtable): Similarly. * cppinit.c (cpp_create_reader): Default the signed_char flag. (init_builtins): Define __CHAR_UNSIGNED__ appropriately. (COMMAND_LINE_OPTIONS): Recognise -f{un,}signed-char. (cpp_handle_option): Handle the new options. * cpplex.c (cpp_interpret_charconst): Use new flag. * cpplib.h (struct cpp_options): New member signed_char. * gcc.c (cpp_unique_options): Remove %c spec and documentation. (cpp_options): Handle -fsigned-char and -funsigned-char. (static_specs): Remove signed_char_spec. (do_spec1): Don't handle %c. * system.h: Poison SIGNED_CHAR_SPEC. * tradcif.y (yylex): Use flag_signed_char. * tradcpp.h (flag_signed_char): New. * tradcpp.c (flag_signed_char): New. (main): Handle new command-line options. (initialize_builtins): Define __CHAR_UNSIGNED__ if appropriate. config: * alpha/alpha.h (SIGNED_CHAR_SPEC): Remove. * avr/avr.h: Remove old comments. * i960/i960.h (CPP_SPEC): Pass -fsigned-char if -mic*. (CC1_SPEC): Pass -fsigned-char if -mic*. (SIGNED_CHAR_SPEC): Remove. doc: * tm.texi (SIGNED_CHAR_SPEC): Remove documentation. testsuite: * gcc.dg/cpp/uchar-1.c, uchar-2.c, uchar-3.c: New tests. From-SVN: r49444
5239 lines
135 KiB
C
5239 lines
135 KiB
C
/* C Compatible Compiler Preprocessor (CCCP)
|
||
Copyright (C) 1986, 1987, 1989, 2000, 2001 Free Software Foundation, Inc.
|
||
Written by Paul Rubin, June 1986
|
||
Adapted to ANSI C, Richard Stallman, Jan 1987
|
||
Dusted off, polished, and adapted for use as traditional
|
||
preprocessor only, Zack Weinberg, Jul 2000
|
||
|
||
This program is free software; you can redistribute it and/or modify it
|
||
under the terms of the GNU General Public License as published by the
|
||
Free Software Foundation; either version 2, or (at your option) any
|
||
later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; if not, write to the Free Software
|
||
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||
|
||
#include "config.h"
|
||
#include "system.h"
|
||
#include "version.h"
|
||
#include "cppdefault.h"
|
||
#include "tradcpp.h"
|
||
#include "mkdeps.h"
|
||
#include "intl.h"
|
||
|
||
typedef unsigned char U_CHAR;
|
||
|
||
/* Name under which this program was invoked. */
|
||
|
||
static const char *progname;
|
||
|
||
/* Current maximum length of directory names in the search path
|
||
for include files. (Altered as we get more of them.) */
|
||
|
||
size_t max_include_len;
|
||
|
||
/* Nonzero means copy comments into the output file. */
|
||
|
||
int put_out_comments = 0;
|
||
|
||
/* mkdeps.h opaque structure that encapsulates dependency information. */
|
||
struct deps *deps;
|
||
|
||
/* Nonzero means print the names of included files rather than
|
||
the preprocessed output. 1 means just the #include "...",
|
||
2 means #include <...> as well. */
|
||
|
||
int print_deps = 0;
|
||
|
||
/* Nonzero means print dummy targets for each header file. */
|
||
|
||
int print_deps_phony_targets = 0;
|
||
|
||
/* If true, fopen (deps_file, "a") else fopen (deps_file, "w"). */
|
||
|
||
int deps_append = 0;
|
||
|
||
/* File name which deps are being written to. This is 0 if deps are
|
||
being written to stdout. */
|
||
|
||
const char *deps_file = 0;
|
||
|
||
/* Nonzero if missing .h files in -M output are assumed to be
|
||
generated files and not errors. */
|
||
|
||
int deps_missing_files = 0;
|
||
|
||
/* Nonzero means don't output line number information. */
|
||
|
||
int no_line_commands;
|
||
|
||
/* Nonzero means inhibit output of the preprocessed text
|
||
and instead output the definitions of all user-defined macros
|
||
in a form suitable for use as input to cccp. */
|
||
|
||
int dump_macros;
|
||
|
||
/* Nonzero means don't print warning messages. -w. */
|
||
|
||
int inhibit_warnings = 0;
|
||
|
||
/* Non-0 means don't output the preprocessed program. */
|
||
int inhibit_output = 0;
|
||
|
||
/* Nonzero means chars are signed. */
|
||
#if DEFAULT_SIGNED_CHAR
|
||
int flag_signed_char = 1;
|
||
#else
|
||
int flag_signed_char = 0;
|
||
#endif
|
||
|
||
/* Nonzero means warn if slash-star appears in a comment. */
|
||
|
||
int warn_comments;
|
||
|
||
/* Nonzero causes output not to be done,
|
||
but directives such as #define that have side effects
|
||
are still obeyed. */
|
||
|
||
int no_output;
|
||
|
||
/* Value of __USER_LABEL_PREFIX__. Target-dependent, also controlled
|
||
by -f(no-)leading-underscore. */
|
||
static const char *user_label_prefix;
|
||
|
||
/* I/O buffer structure.
|
||
The `fname' field is nonzero for source files and #include files
|
||
and for the dummy text used for -D and -U.
|
||
It is zero for rescanning results of macro expansion
|
||
and for expanding macro arguments. */
|
||
#define INPUT_STACK_MAX 200
|
||
struct file_name_list;
|
||
struct file_buf {
|
||
const char *fname;
|
||
int lineno;
|
||
int length;
|
||
U_CHAR *buf;
|
||
U_CHAR *bufp;
|
||
/* Macro that this level is the expansion of.
|
||
Included so that we can reenable the macro
|
||
at the end of this level. */
|
||
struct hashnode *macro;
|
||
/* Value of if_stack at start of this file.
|
||
Used to prohibit unmatched #endif (etc) in an include file. */
|
||
struct if_stack *if_stack;
|
||
/* Object to be freed at end of input at this level. */
|
||
U_CHAR *free_ptr;
|
||
/* Position to start scanning for #include_next in this file. */
|
||
struct file_name_list *next_header_dir;
|
||
} instack[INPUT_STACK_MAX];
|
||
|
||
typedef struct file_buf FILE_BUF;
|
||
|
||
/* Current nesting level of input sources.
|
||
`instack[indepth]' is the level currently being read. */
|
||
int indepth = -1;
|
||
#define CHECK_DEPTH(code) \
|
||
if (indepth >= (INPUT_STACK_MAX - 1)) \
|
||
{ \
|
||
error_with_line (line_for_error (instack[indepth].lineno), \
|
||
"macro or #include recursion too deep"); \
|
||
code; \
|
||
}
|
||
|
||
/* Current depth in #include directives that use <...>. */
|
||
int system_include_depth = 0;
|
||
|
||
/* The output buffer. Its LENGTH field is the amount of room allocated
|
||
for the buffer, not the number of chars actually present. To get
|
||
that, subtract outbuf.buf from outbuf.bufp. */
|
||
|
||
#define OUTBUF_SIZE 10 /* initial size of output buffer */
|
||
FILE_BUF outbuf;
|
||
|
||
/* Grow output buffer OBUF points at
|
||
so it can hold at least NEEDED more chars. */
|
||
|
||
#define check_expand(OBUF, NEEDED) do { \
|
||
if ((OBUF)->length - ((OBUF)->bufp - (OBUF)->buf) <= (NEEDED)) \
|
||
grow_outbuf ((OBUF), (NEEDED)); \
|
||
} while (0)
|
||
|
||
struct file_name_list
|
||
{
|
||
struct file_name_list *next;
|
||
const char *fname;
|
||
};
|
||
|
||
struct file_name_list *include = 0; /* First dir to search */
|
||
/* First dir to search for <file> */
|
||
struct file_name_list *first_bracket_include = 0;
|
||
struct file_name_list *last_include = 0; /* Last in chain */
|
||
|
||
/* List of included files that contained #once. */
|
||
struct file_name_list *dont_repeat_files = 0;
|
||
|
||
/* List of other included files. */
|
||
struct file_name_list *all_include_files = 0;
|
||
|
||
/* Structure allocated for every #define. For a simple replacement
|
||
such as
|
||
#define foo bar ,
|
||
nargs = -1, the `pattern' list is null, and the expansion is just
|
||
the replacement text. Nargs = 0 means a functionlike macro with no args,
|
||
e.g.,
|
||
#define getchar() getc (stdin) .
|
||
When there are args, the expansion is the replacement text with the
|
||
args squashed out, and the reflist is a list describing how to
|
||
build the output from the input: e.g., "3 chars, then the 1st arg,
|
||
then 9 chars, then the 3rd arg, then 0 chars, then the 2nd arg".
|
||
The chars here come from the expansion. Whatever is left of the
|
||
expansion after the last arg-occurrence is copied after that arg.
|
||
Note that the reflist can be arbitrarily long---
|
||
its length depends on the number of times the arguments appear in
|
||
the replacement text, not how many args there are. Example:
|
||
#define f(x) x+x+x+x+x+x+x would have replacement text "++++++" and
|
||
pattern list
|
||
{ (0, 1), (1, 1), (1, 1), ..., (1, 1), NULL }
|
||
where (x, y) means (nchars, argno). */
|
||
|
||
typedef struct definition DEFINITION;
|
||
struct definition {
|
||
int nargs;
|
||
int length; /* length of expansion string */
|
||
U_CHAR *expansion;
|
||
struct reflist {
|
||
struct reflist *next;
|
||
char stringify; /* nonzero if this arg was preceded by a
|
||
# operator. */
|
||
char raw_before; /* Nonzero if a ## operator before arg. */
|
||
char raw_after; /* Nonzero if a ## operator after arg. */
|
||
int nchars; /* Number of literal chars to copy before
|
||
this arg occurrence. */
|
||
int argno; /* Number of arg to substitute (origin-0) */
|
||
} *pattern;
|
||
/* Names of macro args, concatenated in reverse order
|
||
with comma-space between them.
|
||
The only use of this is that we warn on redefinition
|
||
if this differs between the old and new definitions. */
|
||
const U_CHAR *argnames;
|
||
};
|
||
|
||
/* Chained list of answers to an assertion. */
|
||
struct answer
|
||
{
|
||
struct answer *next;
|
||
const unsigned char *answer;
|
||
size_t len;
|
||
};
|
||
|
||
/* different kinds of things that can appear in the value field
|
||
of a hash node. Actually, this may be useless now. */
|
||
union hashval {
|
||
const char *cpval;
|
||
DEFINITION *defn;
|
||
struct answer *answers;
|
||
};
|
||
|
||
/* The structure of a node in the hash table. The hash table
|
||
has entries for all tokens defined by #define commands (type T_MACRO),
|
||
plus some special tokens like __LINE__ (these each have their own
|
||
type, and the appropriate code is run when that type of node is seen.
|
||
It does not contain control words like "#define", which are recognized
|
||
by a separate piece of code. */
|
||
|
||
/* different flavors of hash nodes --- also used in keyword table */
|
||
enum node_type {
|
||
T_DEFINE = 1, /* `#define' */
|
||
T_INCLUDE, /* `#include' */
|
||
T_INCLUDE_NEXT,/* `#include_next' */
|
||
T_IFDEF, /* `#ifdef' */
|
||
T_IFNDEF, /* `#ifndef' */
|
||
T_IF, /* `#if' */
|
||
T_ELSE, /* `#else' */
|
||
T_ELIF, /* `#elif' */
|
||
T_UNDEF, /* `#undef' */
|
||
T_LINE, /* `#line' */
|
||
T_ENDIF, /* `#endif' */
|
||
T_ERROR, /* `#error' */
|
||
T_WARNING, /* `#warning' */
|
||
T_ASSERT, /* `#assert' */
|
||
T_UNASSERT, /* `#unassert' */
|
||
T_SPECLINE, /* special symbol `__LINE__' */
|
||
T_DATE, /* `__DATE__' */
|
||
T_FILE, /* `__FILE__' */
|
||
T_BASE_FILE, /* `__BASE_FILE__' */
|
||
T_INCLUDE_LEVEL, /* `__INCLUDE_LEVEL__' */
|
||
T_VERSION, /* `__VERSION__' */
|
||
T_TIME, /* `__TIME__' */
|
||
T_CONST, /* Constant value, used by `__STDC__' */
|
||
T_MACRO, /* macro defined by `#define' */
|
||
T_SPEC_DEFINED, /* special `defined' macro for use in #if statements */
|
||
T_UNUSED /* Used for something not defined. */
|
||
};
|
||
|
||
struct hashnode {
|
||
struct hashnode *next; /* double links for easy deletion */
|
||
struct hashnode *prev;
|
||
struct hashnode **bucket_hdr; /* also, a back pointer to this node's hash
|
||
chain is kept, in case the node is the head
|
||
of the chain and gets deleted. */
|
||
enum node_type type; /* type of special token */
|
||
int length; /* length of token, for quick comparison */
|
||
U_CHAR *name; /* the actual name */
|
||
union hashval value; /* pointer to expansion, or whatever */
|
||
};
|
||
|
||
typedef struct hashnode HASHNODE;
|
||
|
||
static HASHNODE *parse_assertion PARAMS ((const unsigned char *,
|
||
const unsigned char *,
|
||
struct answer **, int));
|
||
static struct answer **find_answer PARAMS ((HASHNODE *,
|
||
const struct answer *));
|
||
static int parse_answer PARAMS ((const unsigned char *, const unsigned char *,
|
||
struct answer **, int));
|
||
static unsigned char *canonicalize_text PARAMS ((const unsigned char *,
|
||
const unsigned char *,
|
||
const unsigned char **));
|
||
|
||
/* Some definitions for the hash table. The hash function MUST be
|
||
computed as shown in hashf () below. That is because the rescan
|
||
loop computes the hash value `on the fly' for most tokens,
|
||
in order to avoid the overhead of a lot of procedure calls to
|
||
the hashf () function. Hashf () only exists for the sake of
|
||
politeness, for use when speed isn't so important. */
|
||
|
||
#define HASHSIZE 1403
|
||
HASHNODE *hashtab[HASHSIZE];
|
||
#define HASHSTEP(old, c) ((old << 2) + c)
|
||
#define MAKE_POS(v) (v & 0x7fffffff) /* make number positive */
|
||
|
||
/* `struct directive' defines one #-directive, including how to handle it. */
|
||
|
||
struct directive {
|
||
const int length; /* Length of name */
|
||
void (*const func) PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
/* Function to handle directive */
|
||
const char *const name; /* Name of directive */
|
||
const enum node_type type; /* Code which describes which directive. */
|
||
};
|
||
|
||
/* Last arg to output_line_command. */
|
||
enum file_change_code {same_file, enter_file, leave_file};
|
||
|
||
/* This structure represents one parsed argument in a macro call.
|
||
`raw' points to the argument text as written (`raw_length' is its length).
|
||
`expanded' points to the argument's macro-expansion
|
||
(its length is `expand_length').
|
||
`stringified_length' is the length the argument would have
|
||
if stringified.
|
||
`free1' and `free2', if nonzero, point to blocks to be freed
|
||
when the macro argument data is no longer needed. */
|
||
|
||
struct argdata {
|
||
U_CHAR *raw, *expanded;
|
||
int raw_length, expand_length;
|
||
int stringified_length;
|
||
U_CHAR *free1, *free2;
|
||
char newlines;
|
||
char comments;
|
||
};
|
||
|
||
/* The arglist structure is built by do_define to tell
|
||
collect_definition where the argument names begin. That
|
||
is, for a define like "#define f(x,y,z) foo+x-bar*y", the arglist
|
||
would contain pointers to the strings x, y, and z.
|
||
Collect_definition would then build a DEFINITION node,
|
||
with reflist nodes pointing to the places x, y, and z had
|
||
appeared. So the arglist is just convenience data passed
|
||
between these two routines. It is not kept around after
|
||
the current #define has been processed and entered into the
|
||
hash table. */
|
||
|
||
struct arglist {
|
||
struct arglist *next;
|
||
U_CHAR *name;
|
||
int length;
|
||
int argno;
|
||
};
|
||
|
||
/* Function prototypes. */
|
||
|
||
static void do_define PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_error PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_warning PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_line PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_include PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_include_next PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_undef PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_if PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_ifdef PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_ifndef PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_else PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_elif PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_endif PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_assert PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_unassert PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
|
||
static void do_xifdef PARAMS ((U_CHAR *, U_CHAR *, enum node_type));
|
||
|
||
static struct hashnode *install PARAMS ((const U_CHAR *, int, enum node_type, int));
|
||
static int hashf PARAMS ((const U_CHAR *, int, int));
|
||
static int compare_defs PARAMS ((DEFINITION *, DEFINITION *));
|
||
static int comp_def_part PARAMS ((int, const U_CHAR *, int,
|
||
const U_CHAR *, int, int));
|
||
static void delete_macro PARAMS ((HASHNODE *));
|
||
|
||
/* First arg to v_message. */
|
||
enum msgtype { MT_WARNING = 0, MT_ERROR, MT_FATAL };
|
||
static void v_message PARAMS ((enum msgtype mtype, int line,
|
||
const char *msgid, va_list ap))
|
||
ATTRIBUTE_PRINTF (3, 0);
|
||
|
||
static int line_for_error PARAMS ((int));
|
||
|
||
/* We know perfectly well which file this is, so we don't need to
|
||
use __FILE__. */
|
||
#undef abort
|
||
#if (GCC_VERSION >= 2007)
|
||
#define abort() fancy_abort(__LINE__, __FUNCTION__)
|
||
#else
|
||
#define abort() fancy_abort(__LINE__, 0);
|
||
#endif
|
||
|
||
static void macroexpand PARAMS ((HASHNODE *, FILE_BUF *));
|
||
static void special_symbol PARAMS ((HASHNODE *, FILE_BUF *));
|
||
static void dump_all_macros PARAMS ((void));
|
||
static void dump_defn_1 PARAMS ((const U_CHAR *, int, int));
|
||
static void dump_arg_n PARAMS ((DEFINITION *, int));
|
||
static void conditional_skip PARAMS ((FILE_BUF *, int, enum node_type));
|
||
static void skip_if_group PARAMS ((FILE_BUF *, int));
|
||
static void output_line_command PARAMS ((FILE_BUF *, FILE_BUF *,
|
||
int, enum file_change_code));
|
||
|
||
static int eval_if_expression PARAMS ((const U_CHAR *, int));
|
||
|
||
static void output_deps PARAMS ((void));
|
||
static void initialize_builtins PARAMS ((void));
|
||
static void run_directive PARAMS ((const char *, size_t,
|
||
enum node_type));
|
||
static void make_definition PARAMS ((const char *));
|
||
static void make_undef PARAMS ((const char *));
|
||
static void make_assertion PARAMS ((const char *));
|
||
|
||
static void grow_outbuf PARAMS ((FILE_BUF *, int));
|
||
static int handle_directive PARAMS ((FILE_BUF *, FILE_BUF *));
|
||
static void process_include PARAMS ((struct file_name_list *,
|
||
const U_CHAR *, int, int, FILE_BUF *));
|
||
static void finclude PARAMS ((int, const char *,
|
||
struct file_name_list *, FILE_BUF *));
|
||
static void init_dependency_output PARAMS ((void));
|
||
static void rescan PARAMS ((FILE_BUF *, int));
|
||
static void newline_fix PARAMS ((U_CHAR *));
|
||
static void name_newline_fix PARAMS ((U_CHAR *));
|
||
static U_CHAR *macarg1 PARAMS ((U_CHAR *, const U_CHAR *, int *,
|
||
int *, int *));
|
||
static const char *macarg PARAMS ((struct argdata *));
|
||
static int discard_comments PARAMS ((U_CHAR *, int, int));
|
||
static int file_size_and_mode PARAMS ((int, int *, long *));
|
||
|
||
static U_CHAR *skip_to_end_of_comment PARAMS ((FILE_BUF *, int *));
|
||
static U_CHAR *skip_quoted_string PARAMS ((const U_CHAR *, const U_CHAR *,
|
||
int, int *, int *, int *));
|
||
|
||
int main PARAMS ((int, char **));
|
||
|
||
/* Convenience. Write U"string" to get an unsigned string constant. */
|
||
#define U (const unsigned char *)
|
||
|
||
/* Here is the actual list of #-directives, most-often-used first. */
|
||
|
||
static const struct directive directive_table[] = {
|
||
{ 6, do_define, "define", T_DEFINE },
|
||
{ 7, do_include, "include", T_INCLUDE },
|
||
{ 5, do_endif, "endif", T_ENDIF },
|
||
{ 5, do_ifdef, "ifdef", T_IFDEF },
|
||
{ 2, do_if, "if", T_IF, },
|
||
{ 4, do_else, "else", T_ELSE },
|
||
{ 6, do_ifndef, "ifndef", T_IFNDEF },
|
||
{ 5, do_undef, "undef", T_UNDEF },
|
||
{ 4, do_line, "line", T_LINE },
|
||
{ 4, do_elif, "elif", T_ELIF },
|
||
{ 5, do_error, "error", T_ERROR },
|
||
{ 7, do_warning, "warning", T_WARNING },
|
||
{ 12, do_include_next, "include_next", T_INCLUDE_NEXT },
|
||
{ 6, do_assert, "assert", T_ASSERT },
|
||
{ 8, do_unassert,"unassert",T_UNASSERT},
|
||
{ -1, 0, "", T_UNUSED},
|
||
};
|
||
|
||
#define SKIP_WHITE_SPACE(p) do { while (is_nvspace(*p)) p++; } while (0)
|
||
#define SKIP_ALL_WHITE_SPACE(p) do { while (is_space(*p)) p++; } while (0)
|
||
|
||
int errors = 0; /* Error counter for exit code */
|
||
|
||
static FILE_BUF expand_to_temp_buffer PARAMS ((const U_CHAR *, const U_CHAR *, int));
|
||
static DEFINITION *collect_expansion PARAMS ((U_CHAR *, U_CHAR *, int,
|
||
struct arglist *));
|
||
|
||
/* Stack of conditionals currently in progress
|
||
(including both successful and failing conditionals). */
|
||
|
||
struct if_stack {
|
||
struct if_stack *next; /* for chaining to the next stack frame */
|
||
const char *fname; /* copied from input when frame is made */
|
||
int lineno; /* similarly */
|
||
int if_succeeded; /* true if a leg of this if-group
|
||
has been passed through rescan */
|
||
enum node_type type; /* type of last directive seen in this group */
|
||
};
|
||
typedef struct if_stack IF_STACK_FRAME;
|
||
IF_STACK_FRAME *if_stack = NULL;
|
||
|
||
/* Nonzero means -I- has been seen,
|
||
so don't look for #include "foo" the source-file directory. */
|
||
int ignore_srcdir;
|
||
|
||
/* Pending directives. */
|
||
enum pending_dir_t {PD_NONE = 0, PD_DEFINE, PD_UNDEF, PD_ASSERTION, PD_FILE};
|
||
|
||
typedef struct pending_dir pending_dir;
|
||
struct pending_dir
|
||
{
|
||
const char *arg;
|
||
enum pending_dir_t type;
|
||
};
|
||
|
||
int
|
||
main (argc, argv)
|
||
int argc;
|
||
char **argv;
|
||
{
|
||
int st_mode;
|
||
long st_size;
|
||
const char *in_fname, *out_fname;
|
||
int f, i;
|
||
FILE_BUF *fp;
|
||
pending_dir *pend = (pending_dir *) xcalloc (argc, sizeof (pending_dir));
|
||
int no_standard_includes = 0;
|
||
|
||
hex_init ();
|
||
|
||
#ifdef RLIMIT_STACK
|
||
/* Get rid of any avoidable limit on stack size. */
|
||
{
|
||
struct rlimit rlim;
|
||
|
||
/* Set the stack limit huge so that alloca (particularly stringtab
|
||
* in dbxread.c) does not fail. */
|
||
getrlimit (RLIMIT_STACK, &rlim);
|
||
rlim.rlim_cur = rlim.rlim_max;
|
||
setrlimit (RLIMIT_STACK, &rlim);
|
||
}
|
||
#endif /* RLIMIT_STACK defined */
|
||
|
||
progname = argv[0];
|
||
|
||
in_fname = NULL;
|
||
out_fname = NULL;
|
||
|
||
no_line_commands = 0;
|
||
dump_macros = 0;
|
||
no_output = 0;
|
||
|
||
max_include_len = cpp_GCC_INCLUDE_DIR_len + 7; /* ??? */
|
||
|
||
gcc_init_libintl ();
|
||
|
||
/* It's simplest to just create this struct whether or not it will
|
||
be needed. */
|
||
deps = deps_init ();
|
||
|
||
/* Process switches and find input file name. */
|
||
|
||
for (i = 1; i < argc; i++) {
|
||
if (argv[i][0] != '-') {
|
||
if (out_fname != NULL)
|
||
fatal ("usage: %s [switches] input output", argv[0]);
|
||
else if (in_fname != NULL)
|
||
out_fname = argv[i];
|
||
else
|
||
in_fname = argv[i];
|
||
} else {
|
||
int c = argv[i][1];
|
||
|
||
switch (c) {
|
||
case 'E':
|
||
case '$':
|
||
break; /* Ignore for compatibility with ISO/extended cpp. */
|
||
|
||
case 'l':
|
||
if (!strcmp (argv[i], "-lang-c++")
|
||
|| !strcmp (argv[i], "-lang-objc++"))
|
||
fatal ("-traditional is not supported in C++");
|
||
else if (!strcmp (argv[i], "-lang-c89"))
|
||
fatal ("-traditional and -ansi are mutually exclusive");
|
||
else if (!strcmp (argv[i], "-lang-objc"))
|
||
pend[i].type = PD_DEFINE, pend[i].arg = "__OBJC__";
|
||
else if (!strcmp (argv[i], "-lang-asm"))
|
||
pend[i].type = PD_DEFINE, pend[i].arg = "__ASSEMBLER__";
|
||
else if (!strcmp (argv[i], "-lang-fortran"))
|
||
pend[i].type = PD_DEFINE, pend[i].arg = "_LANGUAGE_FORTRAN";
|
||
/* All other possibilities ignored. */
|
||
break;
|
||
|
||
case 'i':
|
||
if (!strcmp (argv[i], "-include"))
|
||
{
|
||
if (i + 1 == argc)
|
||
fatal ("filename missing after -i option");
|
||
else
|
||
pend[i].type = PD_FILE, pend[i].arg = argv[i + 1], i++;
|
||
}
|
||
else if (!strcmp (argv[i], "-iprefix"))
|
||
i++; /* Ignore for compatibility */
|
||
else if (!strcmp (argv[i], "-isystem")
|
||
|| !strcmp (argv[i], "-iwithprefix")
|
||
|| !strcmp (argv[i], "-iwithprefixbefore")
|
||
|| !strcmp (argv[i], "-idirafter"))
|
||
goto add_include; /* best we can do */
|
||
|
||
break;
|
||
|
||
case 'o':
|
||
if (out_fname != NULL)
|
||
fatal ("output filename specified twice");
|
||
if (i + 1 == argc)
|
||
fatal ("filename missing after -o option");
|
||
out_fname = argv[++i];
|
||
if (!strcmp (out_fname, "-"))
|
||
out_fname = "";
|
||
break;
|
||
|
||
case 'w':
|
||
inhibit_warnings = 1;
|
||
break;
|
||
|
||
case 'W':
|
||
if (!strcmp (argv[i], "-Wcomments"))
|
||
warn_comments = 1;
|
||
else if (!strcmp (argv[i], "-Wcomment"))
|
||
warn_comments = 1;
|
||
else if (!strcmp (argv[i], "-Wall")) {
|
||
warn_comments = 1;
|
||
}
|
||
break;
|
||
|
||
case 'f':
|
||
if (!strcmp (argv[i], "-fleading-underscore"))
|
||
user_label_prefix = "_";
|
||
else if (!strcmp (argv[i], "-fno-leading-underscore"))
|
||
user_label_prefix = "";
|
||
else if (!strcmp (argv[i], "-fsigned-char"))
|
||
flag_signed_char = 1;
|
||
else if (!strcmp (argv[i], "-funsigned-char"))
|
||
flag_signed_char = 0;
|
||
break;
|
||
|
||
case 'M':
|
||
{
|
||
char *p = NULL;
|
||
|
||
/* -MD and -MMD for tradcpp are deprecated and undocumented
|
||
(use -M or -MM with -MF instead), and probably should be
|
||
removed with the next major GCC version. For the moment
|
||
we allow these for the benefit of Automake 1.4, which
|
||
uses these when dependency tracking is enabled. Automake
|
||
1.5 will fix this. */
|
||
if (!strncmp (argv[i], "-MD", 3)) {
|
||
p = argv[i] + 3;
|
||
print_deps = 2;
|
||
} else if (!strncmp (argv[i], "-MMD", 4)) {
|
||
p = argv[i] + 4;
|
||
print_deps = 1;
|
||
} else if (!strcmp (argv[i], "-M")) {
|
||
print_deps = 2;
|
||
} else if (!strcmp (argv[i], "-MM")) {
|
||
print_deps = 1;
|
||
} else if (!strcmp (argv[i], "-MG")) {
|
||
deps_missing_files = 1;
|
||
} else if (!strcmp (argv[i], "-MF")) {
|
||
p = argv[i] + 3;
|
||
} else if (!strcmp (argv[i], "-MP")) {
|
||
print_deps_phony_targets = 1;
|
||
} else if (!strcmp (argv[i], "-MQ") || !strcmp (argv[i], "-MT")) {
|
||
/* Add a target. -MQ quotes for Make. */
|
||
const char *tgt = argv[i] + 3;
|
||
int quoted = argv[i][2] == 'Q';
|
||
|
||
if (*tgt == '\0' && i + 1 == argc)
|
||
fatal ("target missing after %s option", argv[i]);
|
||
else
|
||
{
|
||
if (*tgt == '\0')
|
||
tgt = argv[++i];
|
||
|
||
deps_add_target (deps, tgt, quoted);
|
||
}
|
||
}
|
||
|
||
if (p) {
|
||
if (*p)
|
||
deps_file = p;
|
||
else if (i + 1 == argc)
|
||
fatal ("filename missing after %s option", argv[i]);
|
||
else
|
||
deps_file = argv[++i];
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'd':
|
||
dump_macros = 1;
|
||
no_output = 1;
|
||
break;
|
||
|
||
case 'v':
|
||
fprintf (stderr, "GNU traditional CPP version %s\n", version_string);
|
||
break;
|
||
|
||
case 'D':
|
||
case 'U':
|
||
case 'A':
|
||
{
|
||
char *p;
|
||
|
||
if (argv[i][2] != 0)
|
||
p = argv[i] + 2;
|
||
else if (i + 1 == argc)
|
||
fatal ("macro name missing after -%c option", c);
|
||
else
|
||
p = argv[++i];
|
||
|
||
if (c == 'D')
|
||
pend[i].type = PD_DEFINE;
|
||
else if (c == 'U')
|
||
pend[i].type = PD_UNDEF;
|
||
else
|
||
pend[i].type = PD_ASSERTION;
|
||
pend[i].arg = p;
|
||
}
|
||
break;
|
||
|
||
case 'C':
|
||
put_out_comments = 1;
|
||
break;
|
||
|
||
case 'p':
|
||
if (!strcmp (argv[i], "-pedantic"))
|
||
fatal ("-pedantic and -traditional are mutually exclusive");
|
||
break;
|
||
|
||
case 't':
|
||
if (!strcmp (argv[i], "-trigraphs"))
|
||
fatal ("-trigraphs and -traditional are mutually exclusive");
|
||
break;
|
||
|
||
case 'P':
|
||
no_line_commands = 1;
|
||
break;
|
||
|
||
case 'I': /* Add directory to path for includes. */
|
||
add_include:
|
||
{
|
||
struct file_name_list *dirtmp;
|
||
|
||
if (! ignore_srcdir && !strcmp (argv[i] + 2, "-"))
|
||
ignore_srcdir = 1;
|
||
else {
|
||
dirtmp = (struct file_name_list *)
|
||
xmalloc (sizeof (struct file_name_list));
|
||
dirtmp->next = 0; /* New one goes on the end */
|
||
if (include == 0)
|
||
include = dirtmp;
|
||
else
|
||
last_include->next = dirtmp;
|
||
last_include = dirtmp; /* Tail follows the last one */
|
||
if (argv[i][1] == 'I' && argv[i][2] != 0)
|
||
dirtmp->fname = argv[i] + 2;
|
||
else if (i + 1 == argc)
|
||
fatal ("directory name missing after -I option");
|
||
else
|
||
dirtmp->fname = argv[++i];
|
||
if (strlen (dirtmp->fname) > max_include_len)
|
||
max_include_len = strlen (dirtmp->fname);
|
||
if (ignore_srcdir && first_bracket_include == 0)
|
||
first_bracket_include = dirtmp;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'n':
|
||
/* -nostdinc causes no default include directories.
|
||
You must specify all include-file directories with -I. */
|
||
no_standard_includes = 1;
|
||
break;
|
||
|
||
case '\0': /* JF handle '-' as file name meaning stdin or stdout */
|
||
if (in_fname == NULL) {
|
||
in_fname = "";
|
||
break;
|
||
} else if (out_fname == NULL) {
|
||
out_fname = "";
|
||
break;
|
||
} /* else fall through into error */
|
||
|
||
default:
|
||
fatal ("invalid option `%s'", argv[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
init_dependency_output ();
|
||
|
||
/* After checking the environment variables, check if -M or -MM has
|
||
not been specified, but other -M options have. */
|
||
if (print_deps == 0
|
||
&& (deps_missing_files || deps_file || print_deps_phony_targets))
|
||
fatal ("you must additionally specify either -M or -MM");
|
||
|
||
if (user_label_prefix == 0)
|
||
user_label_prefix = USER_LABEL_PREFIX;
|
||
|
||
if (print_deps)
|
||
{
|
||
/* Set the default target (if there is none already), and
|
||
the dependency on the main file. */
|
||
deps_add_default_target (deps, in_fname);
|
||
|
||
deps_add_dep (deps, in_fname);
|
||
}
|
||
|
||
/* Install __LINE__, etc. Must follow option processing. */
|
||
initialize_builtins ();
|
||
|
||
/* Do defines specified with -D and undefines specified with -U. */
|
||
for (i = 1; i < argc; i++)
|
||
if (pend[i].type == PD_DEFINE)
|
||
make_definition (pend[i].arg);
|
||
else if (pend[i].type == PD_UNDEF)
|
||
make_undef (pend[i].arg);
|
||
else if (pend[i].type == PD_ASSERTION)
|
||
make_assertion (pend[i].arg);
|
||
|
||
/* Unless -fnostdinc,
|
||
tack on the standard include file dirs to the specified list */
|
||
if (!no_standard_includes) {
|
||
const struct default_include *di;
|
||
struct file_name_list *old_last_include = last_include;
|
||
struct file_name_list *dirtmp;
|
||
for (di = cpp_include_defaults; di->fname; di++) {
|
||
if (di->cplusplus)
|
||
continue;
|
||
dirtmp = (struct file_name_list *)
|
||
xmalloc (sizeof (struct file_name_list));
|
||
dirtmp->next = 0; /* New one goes on the end */
|
||
if (include == 0)
|
||
include = dirtmp;
|
||
else
|
||
last_include->next = dirtmp;
|
||
last_include = dirtmp; /* Tail follows the last one */
|
||
dirtmp->fname = di->fname;
|
||
if (strlen (dirtmp->fname) > max_include_len)
|
||
max_include_len = strlen (dirtmp->fname);
|
||
}
|
||
|
||
if (ignore_srcdir && first_bracket_include == 0)
|
||
first_bracket_include = old_last_include->next;
|
||
}
|
||
|
||
/* Initialize output buffer */
|
||
|
||
outbuf.buf = (U_CHAR *) xmalloc (OUTBUF_SIZE);
|
||
outbuf.bufp = outbuf.buf;
|
||
outbuf.length = OUTBUF_SIZE;
|
||
|
||
/* Scan the -i files before the main input.
|
||
Much like #including them, but with no_output set
|
||
so that only their macro definitions matter. */
|
||
|
||
no_output++;
|
||
indepth++;
|
||
for (i = 1; i < argc; i++)
|
||
if (pend[i].type == PD_FILE)
|
||
{
|
||
int fd = open (pend[i].arg, O_RDONLY, 0666);
|
||
if (fd < 0)
|
||
{
|
||
perror_with_name (pend[i].arg);
|
||
return FATAL_EXIT_CODE;
|
||
}
|
||
|
||
/* For -M, add this file to the dependencies. */
|
||
if (print_deps)
|
||
deps_add_dep (deps, pend[i].arg);
|
||
|
||
finclude (fd, pend[i].arg, 0, &outbuf);
|
||
}
|
||
indepth--;
|
||
no_output--;
|
||
|
||
/* Pending directives no longer needed. */
|
||
free ((PTR) pend);
|
||
|
||
/* Create an input stack level for the main input file
|
||
and copy the entire contents of the file into it. */
|
||
|
||
fp = &instack[++indepth];
|
||
|
||
/* JF check for stdin */
|
||
if (in_fname == NULL || *in_fname == 0) {
|
||
in_fname = "";
|
||
f = 0;
|
||
} else if ((f = open (in_fname, O_RDONLY, 0666)) < 0)
|
||
goto sys_error;
|
||
|
||
if (file_size_and_mode (f, &st_mode, &st_size))
|
||
goto sys_error;
|
||
fp->fname = in_fname;
|
||
fp->lineno = 1;
|
||
/* JF all this is mine about reading pipes and ttys */
|
||
if (!S_ISREG (st_mode)) {
|
||
/* Read input from a file that is not a normal disk file.
|
||
We cannot preallocate a buffer with the correct size,
|
||
so we must read in the file a piece at the time and make it bigger. */
|
||
int size;
|
||
int bsize;
|
||
int cnt;
|
||
U_CHAR *bufp;
|
||
|
||
bsize = 2000;
|
||
size = 0;
|
||
fp->buf = (U_CHAR *) xmalloc (bsize + 2);
|
||
bufp = fp->buf;
|
||
for (;;) {
|
||
cnt = read (f, bufp, bsize - size);
|
||
if (cnt < 0) goto sys_error; /* error! */
|
||
if (cnt == 0) break; /* End of file */
|
||
size += cnt;
|
||
bufp += cnt;
|
||
if (bsize == size) { /* Buffer is full! */
|
||
bsize *= 2;
|
||
fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 2);
|
||
bufp = fp->buf + size; /* May have moved */
|
||
}
|
||
}
|
||
fp->length = size;
|
||
} else {
|
||
/* Read a file whose size we can determine in advance.
|
||
For the sake of VMS, st_size is just an upper bound. */
|
||
long i;
|
||
fp->length = 0;
|
||
fp->buf = (U_CHAR *) xmalloc (st_size + 2);
|
||
|
||
while (st_size > 0) {
|
||
i = read (f, fp->buf + fp->length, st_size);
|
||
if (i <= 0) {
|
||
if (i == 0) break;
|
||
goto sys_error;
|
||
}
|
||
fp->length += i;
|
||
st_size -= i;
|
||
}
|
||
}
|
||
fp->bufp = fp->buf;
|
||
fp->if_stack = if_stack;
|
||
|
||
/* Make sure data ends with a newline. And put a null after it. */
|
||
|
||
if (fp->length > 0 && fp->buf[fp->length-1] != '\n')
|
||
fp->buf[fp->length++] = '\n';
|
||
fp->buf[fp->length] = '\0';
|
||
|
||
/* Now that we know the input file is valid, open the output. */
|
||
|
||
if (!out_fname || !strcmp (out_fname, ""))
|
||
out_fname = "stdout";
|
||
else if (! freopen (out_fname, "w", stdout))
|
||
pfatal_with_name (out_fname);
|
||
|
||
output_line_command (fp, &outbuf, 0, same_file);
|
||
|
||
/* Scan the input, processing macros and directives. */
|
||
|
||
rescan (&outbuf, 0);
|
||
|
||
/* Now we have processed the entire input
|
||
Write whichever kind of output has been requested. */
|
||
|
||
|
||
if (dump_macros)
|
||
dump_all_macros ();
|
||
else if (! inhibit_output)
|
||
if (write (fileno (stdout), outbuf.buf, outbuf.bufp - outbuf.buf) < 0)
|
||
fatal ("I/O error on output");
|
||
|
||
/* Don't write the deps file if preprocessing has failed. */
|
||
if (print_deps && errors == 0)
|
||
output_deps ();
|
||
|
||
/* Destruct the deps object. */
|
||
deps_free (deps);
|
||
|
||
if (ferror (stdout))
|
||
fatal ("I/O error on output");
|
||
|
||
if (errors)
|
||
exit (FATAL_EXIT_CODE);
|
||
exit (SUCCESS_EXIT_CODE);
|
||
|
||
sys_error:
|
||
pfatal_with_name (in_fname);
|
||
}
|
||
|
||
/* Set up dependency-file output. */
|
||
static void
|
||
init_dependency_output ()
|
||
{
|
||
char *spec, *s, *output_file;
|
||
|
||
/* Either of two environment variables can specify output of deps.
|
||
Its value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET",
|
||
where OUTPUT_FILE is the file to write deps info to
|
||
and DEPS_TARGET is the target to mention in the deps. */
|
||
|
||
if (print_deps == 0)
|
||
{
|
||
spec = getenv ("DEPENDENCIES_OUTPUT");
|
||
if (spec)
|
||
print_deps = 1;
|
||
else
|
||
{
|
||
spec = getenv ("SUNPRO_DEPENDENCIES");
|
||
if (spec)
|
||
print_deps = 2;
|
||
else
|
||
return;
|
||
}
|
||
|
||
/* Find the space before the DEPS_TARGET, if there is one. */
|
||
s = strchr (spec, ' ');
|
||
if (s)
|
||
{
|
||
/* Let the caller perform MAKE quoting. */
|
||
deps_add_target (deps, s + 1, 0);
|
||
output_file = (char *) xmalloc (s - spec + 1);
|
||
memcpy (output_file, spec, s - spec);
|
||
output_file[s - spec] = 0;
|
||
}
|
||
else
|
||
output_file = spec;
|
||
|
||
/* Command line overrides environment variables. */
|
||
if (deps_file == 0)
|
||
deps_file = output_file;
|
||
deps_append = 1;
|
||
}
|
||
|
||
/* If dependencies go to standard output, or -MG is used, we should
|
||
suppress output. The user may be requesting other stuff to
|
||
stdout, with -dM, -v etc. We let them shoot themselves in the
|
||
foot. */
|
||
if (deps_file == 0 || deps_missing_files)
|
||
inhibit_output = 1;
|
||
}
|
||
|
||
/* Use mkdeps.c to output dependency information. */
|
||
static void
|
||
output_deps ()
|
||
{
|
||
/* Stream on which to print the dependency information. */
|
||
FILE *deps_stream = 0;
|
||
const char *const deps_mode = deps_append ? "a" : "w";
|
||
|
||
if (deps_file == 0)
|
||
deps_stream = stdout;
|
||
else
|
||
{
|
||
deps_stream = fopen (deps_file, deps_mode);
|
||
if (deps_stream == 0)
|
||
{
|
||
error_from_errno (deps_file);
|
||
return;
|
||
}
|
||
}
|
||
|
||
deps_write (deps, deps_stream, 72);
|
||
|
||
if (print_deps_phony_targets)
|
||
deps_phony_targets (deps, deps_stream);
|
||
|
||
/* Don't close stdout. */
|
||
if (deps_file)
|
||
{
|
||
if (ferror (deps_stream) || fclose (deps_stream) != 0)
|
||
fatal ("I/O error on output");
|
||
}
|
||
}
|
||
|
||
/* Move all backslash-newline pairs out of embarrassing places.
|
||
Exchange all such pairs following BP
|
||
with any potentially-embarrasing characters that follow them.
|
||
Potentially-embarrassing characters are / and *
|
||
(because a backslash-newline inside a comment delimiter
|
||
would cause it not to be recognized). */
|
||
static void
|
||
newline_fix (bp)
|
||
U_CHAR *bp;
|
||
{
|
||
U_CHAR *p = bp;
|
||
int count = 0;
|
||
|
||
/* First count the backslash-newline pairs here. */
|
||
|
||
while (*p++ == '\\' && *p++ == '\n')
|
||
count++;
|
||
|
||
p = bp + count * 2;
|
||
|
||
/* Exit if what follows the backslash-newlines is not embarrassing. */
|
||
|
||
if (count == 0 || (*p != '/' && *p != '*'))
|
||
return;
|
||
|
||
/* Copy all potentially embarrassing characters
|
||
that follow the backslash-newline pairs
|
||
down to where the pairs originally started. */
|
||
|
||
while (*p == '*' || *p == '/')
|
||
*bp++ = *p++;
|
||
|
||
/* Now write the same number of pairs after the embarrassing chars. */
|
||
while (count-- > 0) {
|
||
*bp++ = '\\';
|
||
*bp++ = '\n';
|
||
}
|
||
}
|
||
|
||
/* Like newline_fix but for use within a directive-name.
|
||
Move any backslash-newlines up past any following symbol constituents. */
|
||
static void
|
||
name_newline_fix (bp)
|
||
U_CHAR *bp;
|
||
{
|
||
U_CHAR *p = bp;
|
||
int count = 0;
|
||
|
||
/* First count the backslash-newline pairs here. */
|
||
|
||
while (*p++ == '\\' && *p++ == '\n')
|
||
count++;
|
||
|
||
p = bp + count * 2;
|
||
|
||
/* What follows the backslash-newlines is not embarrassing. */
|
||
|
||
if (count == 0 || !is_idchar (*p))
|
||
return;
|
||
|
||
/* Copy all potentially embarrassing characters
|
||
that follow the backslash-newline pairs
|
||
down to where the pairs originally started. */
|
||
|
||
while (is_idchar (*p))
|
||
*bp++ = *p++;
|
||
|
||
/* Now write the same number of pairs after the embarrassing chars. */
|
||
while (count-- > 0) {
|
||
*bp++ = '\\';
|
||
*bp++ = '\n';
|
||
}
|
||
}
|
||
|
||
/*
|
||
* The main loop of the program.
|
||
*
|
||
* Read characters from the input stack, transferring them to the
|
||
* output buffer OP.
|
||
*
|
||
* Macros are expanded and push levels on the input stack.
|
||
* At the end of such a level it is popped off and we keep reading.
|
||
* At the end of any other kind of level, we return.
|
||
* #-directives are handled, except within macros.
|
||
*
|
||
* If OUTPUT_MARKS is nonzero, keep Newline markers found in the input
|
||
* and insert them when appropriate. This is set while scanning macro
|
||
* arguments before substitution. It is zero when scanning for final output.
|
||
* There are three types of Newline markers:
|
||
* * Newline - follows a macro name that was not expanded
|
||
* because it appeared inside an expansion of the same macro.
|
||
* This marker prevents future expansion of that identifier.
|
||
* When the input is rescanned into the final output, these are deleted.
|
||
* These are also deleted by ## concatenation.
|
||
* * Newline Space (or Newline and any other whitespace character)
|
||
* stands for a place that tokens must be separated or whitespace
|
||
* is otherwise desirable, but where the ANSI standard specifies there
|
||
* is no whitespace. This marker turns into a Space (or whichever other
|
||
* whitespace char appears in the marker) in the final output,
|
||
* but it turns into nothing in an argument that is stringified with #.
|
||
* Such stringified arguments are the only place where the ANSI standard
|
||
* specifies with precision that whitespace may not appear.
|
||
*
|
||
* During this function, IP->bufp is kept cached in IBP for speed of access.
|
||
* Likewise, OP->bufp is kept in OBP. Before calling a subroutine
|
||
* IBP, IP and OBP must be copied back to memory. IP and IBP are
|
||
* copied back with the RECACHE macro. OBP must be copied back from OP->bufp
|
||
* explicitly, and before RECACHE, since RECACHE uses OBP.
|
||
*/
|
||
|
||
static void
|
||
rescan (op, output_marks)
|
||
FILE_BUF *op;
|
||
int output_marks;
|
||
{
|
||
/* Character being scanned in main loop. */
|
||
U_CHAR c;
|
||
|
||
/* Length of pending accumulated identifier. */
|
||
int ident_length = 0;
|
||
|
||
/* Hash code of pending accumulated identifier. */
|
||
int hash = 0;
|
||
|
||
/* Current input level (&instack[indepth]). */
|
||
FILE_BUF *ip;
|
||
|
||
/* Pointer for scanning input. */
|
||
U_CHAR *ibp;
|
||
|
||
/* Pointer to end of input. End of scan is controlled by LIMIT. */
|
||
U_CHAR *limit;
|
||
|
||
/* Pointer for storing output. */
|
||
U_CHAR *obp;
|
||
|
||
/* REDO_CHAR is nonzero if we are processing an identifier
|
||
after backing up over the terminating character.
|
||
Sometimes we process an identifier without backing up over
|
||
the terminating character, if the terminating character
|
||
is not special. Backing up is done so that the terminating character
|
||
will be dispatched on again once the identifier is dealt with. */
|
||
int redo_char = 0;
|
||
|
||
/* 1 if within an identifier inside of which a concatenation
|
||
marker (Newline -) has been seen. */
|
||
int concatenated = 0;
|
||
|
||
/* While scanning a comment or a string constant,
|
||
this records the line it started on, for error messages. */
|
||
int start_line;
|
||
|
||
/* Record position of last `real' newline. */
|
||
U_CHAR *beg_of_line;
|
||
|
||
/* This has to be a global bacause of RECACHE. */
|
||
U_CHAR *obufp_before_macroname = NULL;
|
||
|
||
/* Pop the innermost input stack level, assuming it is a macro expansion. */
|
||
|
||
#define POPMACRO \
|
||
do { ip->macro->type = T_MACRO; \
|
||
if (ip->free_ptr) free (ip->free_ptr); \
|
||
--indepth; } while (0)
|
||
|
||
/* Reload `rescan's local variables that describe the current
|
||
level of the input stack. */
|
||
|
||
#define RECACHE \
|
||
do { ip = &instack[indepth]; \
|
||
ibp = ip->bufp; \
|
||
limit = ip->buf + ip->length; \
|
||
op->bufp = obp; \
|
||
check_expand (op, limit - ibp); \
|
||
beg_of_line = 0; \
|
||
obufp_before_macroname += op->bufp - obp; \
|
||
obp = op->bufp; } while (0)
|
||
|
||
if (no_output && instack[indepth].fname != 0)
|
||
skip_if_group (&instack[indepth], 1);
|
||
|
||
obp = op->bufp;
|
||
RECACHE;
|
||
beg_of_line = ibp;
|
||
|
||
/* Our caller must always put a null after the end of
|
||
the input at each input stack level. */
|
||
if (*limit != 0)
|
||
abort ();
|
||
|
||
while (1) {
|
||
c = *ibp++;
|
||
*obp++ = c;
|
||
|
||
switch (c) {
|
||
case '\\':
|
||
if (ibp >= limit)
|
||
break;
|
||
if (*ibp == '\n') {
|
||
/* Always merge lines ending with backslash-newline,
|
||
even in middle of identifier. */
|
||
++ibp;
|
||
++ip->lineno;
|
||
--obp; /* remove backslash from obuf */
|
||
break;
|
||
}
|
||
/* Otherwise, backslash suppresses specialness of following char,
|
||
so copy it here to prevent the switch from seeing it.
|
||
But first get any pending identifier processed. */
|
||
if (ident_length > 0)
|
||
goto specialchar;
|
||
*obp++ = *ibp++;
|
||
break;
|
||
|
||
case '#':
|
||
/* If this is expanding a macro definition, don't recognize
|
||
preprocessor directives. */
|
||
if (ip->macro != 0)
|
||
goto randomchar;
|
||
if (ident_length)
|
||
goto specialchar;
|
||
|
||
/* # keyword: a # must be the first char on the line */
|
||
if (beg_of_line == 0)
|
||
goto randomchar;
|
||
if (beg_of_line + 1 != ibp)
|
||
goto randomchar;
|
||
|
||
/* This # can start a directive. */
|
||
|
||
--obp; /* Don't copy the '#' */
|
||
|
||
ip->bufp = ibp;
|
||
op->bufp = obp;
|
||
if (! handle_directive (ip, op)) {
|
||
#ifdef USE_C_ALLOCA
|
||
alloca (0);
|
||
#endif
|
||
/* Not a known directive: treat it as ordinary text.
|
||
IP, OP, IBP, etc. have not been changed. */
|
||
if (no_output && instack[indepth].fname) {
|
||
/* If not generating expanded output,
|
||
what we do with ordinary text is skip it.
|
||
Discard everything until next # directive. */
|
||
skip_if_group (&instack[indepth], 1);
|
||
RECACHE;
|
||
beg_of_line = ibp;
|
||
break;
|
||
}
|
||
++obp; /* Copy the '#' after all */
|
||
goto randomchar;
|
||
}
|
||
#ifdef USE_C_ALLOCA
|
||
alloca (0);
|
||
#endif
|
||
/* A # directive has been successfully processed. */
|
||
/* If not generating expanded output, ignore everything until
|
||
next # directive. */
|
||
if (no_output && instack[indepth].fname)
|
||
skip_if_group (&instack[indepth], 1);
|
||
obp = op->bufp;
|
||
RECACHE;
|
||
beg_of_line = ibp;
|
||
break;
|
||
|
||
case '\"': /* skip quoted string */
|
||
case '\'':
|
||
/* A single quoted string is treated like a double -- some
|
||
programs (e.g., troff) are perverse this way */
|
||
|
||
if (ident_length)
|
||
goto specialchar;
|
||
|
||
start_line = ip->lineno;
|
||
|
||
/* Skip ahead to a matching quote. */
|
||
|
||
while (1) {
|
||
if (ibp >= limit) {
|
||
if (ip->macro != 0) {
|
||
/* try harder: this string crosses a macro expansion boundary */
|
||
POPMACRO;
|
||
RECACHE;
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
*obp++ = *ibp;
|
||
switch (*ibp++) {
|
||
case '\n':
|
||
++ip->lineno;
|
||
++op->lineno;
|
||
/* Traditionally, end of line ends a string constant with no error.
|
||
So exit the loop and record the new line. */
|
||
beg_of_line = ibp;
|
||
goto while2end;
|
||
|
||
case '\\':
|
||
if (ibp >= limit)
|
||
break;
|
||
if (*ibp == '\n') {
|
||
/* Backslash newline is replaced by nothing at all,
|
||
but keep the line counts correct. */
|
||
--obp;
|
||
++ibp;
|
||
++ip->lineno;
|
||
} else {
|
||
/* ANSI stupidly requires that in \\ the second \
|
||
is *not* prevented from combining with a newline. */
|
||
while (*ibp == '\\' && ibp[1] == '\n') {
|
||
ibp += 2;
|
||
++ip->lineno;
|
||
}
|
||
*obp++ = *ibp++;
|
||
}
|
||
break;
|
||
|
||
case '\"':
|
||
case '\'':
|
||
if (ibp[-1] == c)
|
||
goto while2end;
|
||
break;
|
||
}
|
||
}
|
||
while2end:
|
||
break;
|
||
|
||
case '/':
|
||
if (*ibp == '\\' && ibp[1] == '\n')
|
||
newline_fix (ibp);
|
||
/* Don't look for comments inside a macro definition. */
|
||
if (ip->macro != 0)
|
||
goto randomchar;
|
||
/* A comment constitutes white space, so it can terminate an identifier.
|
||
Process the identifier, if any. */
|
||
if (ident_length)
|
||
goto specialchar;
|
||
|
||
if (*ibp != '*')
|
||
goto randomchar;
|
||
|
||
/* We have a comment. Skip it, optionally copying it to output. */
|
||
|
||
start_line = ip->lineno;
|
||
|
||
++ibp; /* Skip the star. */
|
||
|
||
/* In K+R C, a comment is equivalent to nothing. Note that we
|
||
already output the slash; we might not want it. */
|
||
if (! put_out_comments)
|
||
obp--;
|
||
else
|
||
*obp++ = '*';
|
||
|
||
{
|
||
U_CHAR *before_bp = ibp;
|
||
|
||
while (ibp < limit) {
|
||
switch (*ibp++) {
|
||
case '/':
|
||
if (warn_comments && ibp < limit && *ibp == '*')
|
||
warning("`/*' within comment");
|
||
break;
|
||
case '*':
|
||
if (*ibp == '\\' && ibp[1] == '\n')
|
||
newline_fix (ibp);
|
||
if (ibp >= limit || *ibp == '/')
|
||
goto comment_end;
|
||
break;
|
||
case '\n':
|
||
++ip->lineno;
|
||
/* Copy the newline into the output buffer, in order to
|
||
avoid the pain of a #line every time a multiline comment
|
||
is seen. */
|
||
if (!put_out_comments)
|
||
*obp++ = '\n';
|
||
++op->lineno;
|
||
}
|
||
}
|
||
comment_end:
|
||
|
||
if (ibp >= limit)
|
||
error_with_line (line_for_error (start_line),
|
||
"unterminated comment");
|
||
else {
|
||
ibp++;
|
||
if (put_out_comments) {
|
||
memcpy (obp, before_bp, ibp - before_bp);
|
||
obp += ibp - before_bp;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case '0': case '1': case '2': case '3': case '4':
|
||
case '5': case '6': case '7': case '8': case '9':
|
||
/* If digit is not part of identifier, it starts a number,
|
||
which means that following letters are not an identifier.
|
||
"0x5" does not refer to an identifier "x5".
|
||
So copy all alphanumerics that follow without accumulating
|
||
as an identifier. Periods also, for sake of "3.e7". */
|
||
|
||
if (ident_length == 0) {
|
||
while (ibp < limit) {
|
||
while (ibp < limit && ibp[0] == '\\' && ibp[1] == '\n') {
|
||
++ip->lineno;
|
||
ibp += 2;
|
||
}
|
||
c = *ibp++;
|
||
if (! ISIDNUM (c) && c != '.') {
|
||
--ibp;
|
||
break;
|
||
}
|
||
*obp++ = c;
|
||
/* A sign can be part of a preprocessing number
|
||
if it follows an e. */
|
||
if (c == 'e' || c == 'E') {
|
||
while (ibp < limit && ibp[0] == '\\' && ibp[1] == '\n') {
|
||
++ip->lineno;
|
||
ibp += 2;
|
||
}
|
||
if (ibp < limit && (*ibp == '+' || *ibp == '-')) {
|
||
*obp++ = *ibp++;
|
||
/* Traditional C does not let the token go past the sign. */
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
/* fall through */
|
||
|
||
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':
|
||
ident_length++;
|
||
/* Compute step of hash function, to avoid a proc call on every token */
|
||
hash = HASHSTEP (hash, c);
|
||
break;
|
||
|
||
case '\n':
|
||
/* If reprocessing a macro expansion, newline is a special marker. */
|
||
if (ip->macro != 0) {
|
||
/* Newline White is a "funny space" to separate tokens that are
|
||
supposed to be separate but without space between.
|
||
Here White means any horizontal whitespace character.
|
||
Newline - marks a recursive macro use that is not
|
||
supposed to be expandable. */
|
||
|
||
if (*ibp == '-') {
|
||
/* Newline - inhibits expansion of preceding token.
|
||
If expanding a macro arg, we keep the newline -.
|
||
In final output, it is deleted. */
|
||
if (! concatenated) {
|
||
ident_length = 0;
|
||
hash = 0;
|
||
}
|
||
ibp++;
|
||
if (!output_marks) {
|
||
obp--;
|
||
} else {
|
||
/* If expanding a macro arg, keep the newline -. */
|
||
*obp++ = '-';
|
||
}
|
||
} else if (is_space (*ibp)) {
|
||
/* Newline Space does not prevent expansion of preceding token
|
||
so expand the preceding token and then come back. */
|
||
if (ident_length > 0)
|
||
goto specialchar;
|
||
|
||
/* If generating final output, newline space makes a space. */
|
||
if (!output_marks) {
|
||
obp[-1] = *ibp++;
|
||
/* And Newline Newline makes a newline, so count it. */
|
||
if (obp[-1] == '\n')
|
||
op->lineno++;
|
||
} else {
|
||
/* If expanding a macro arg, keep the newline space.
|
||
If the arg gets stringified, newline space makes nothing. */
|
||
*obp++ = *ibp++;
|
||
}
|
||
} else abort (); /* Newline followed by something random? */
|
||
break;
|
||
}
|
||
|
||
/* If there is a pending identifier, handle it and come back here. */
|
||
if (ident_length > 0)
|
||
goto specialchar;
|
||
|
||
beg_of_line = ibp;
|
||
|
||
/* Update the line counts and output a #line if necessary. */
|
||
++ip->lineno;
|
||
++op->lineno;
|
||
if (ip->lineno != op->lineno) {
|
||
op->bufp = obp;
|
||
output_line_command (ip, op, 1, same_file);
|
||
check_expand (op, ip->length - (ip->bufp - ip->buf));
|
||
obp = op->bufp;
|
||
}
|
||
break;
|
||
|
||
/* Come here either after (1) a null character that is part of the input
|
||
or (2) at the end of the input, because there is a null there. */
|
||
case 0:
|
||
if (ibp <= limit)
|
||
/* Our input really contains a null character. */
|
||
goto randomchar;
|
||
|
||
/* At end of a macro-expansion level, pop it and read next level. */
|
||
if (ip->macro != 0) {
|
||
obp--;
|
||
ibp--;
|
||
/* If we have an identifier that ends here, process it now, so
|
||
we get the right error for recursion. */
|
||
if (ident_length && ! is_idchar (*instack[indepth - 1].bufp)) {
|
||
redo_char = 1;
|
||
goto randomchar;
|
||
}
|
||
POPMACRO;
|
||
RECACHE;
|
||
break;
|
||
}
|
||
|
||
/* If we don't have a pending identifier,
|
||
return at end of input. */
|
||
if (ident_length == 0) {
|
||
obp--;
|
||
ibp--;
|
||
op->bufp = obp;
|
||
ip->bufp = ibp;
|
||
goto ending;
|
||
}
|
||
|
||
/* If we do have a pending identifier, just consider this null
|
||
a special character and arrange to dispatch on it again.
|
||
The second time, IDENT_LENGTH will be zero so we will return. */
|
||
|
||
/* Fall through */
|
||
|
||
specialchar:
|
||
|
||
/* Handle the case of a character such as /, ', " or null
|
||
seen following an identifier. Back over it so that
|
||
after the identifier is processed the special char
|
||
will be dispatched on again. */
|
||
|
||
ibp--;
|
||
obp--;
|
||
redo_char = 1;
|
||
|
||
default:
|
||
|
||
randomchar:
|
||
|
||
if (ident_length > 0) {
|
||
HASHNODE *hp;
|
||
|
||
/* We have just seen an identifier end. If it's a macro, expand it.
|
||
|
||
IDENT_LENGTH is the length of the identifier
|
||
and HASH is its hash code.
|
||
|
||
The identifier has already been copied to the output,
|
||
so if it is a macro we must remove it.
|
||
|
||
If REDO_CHAR is 0, the char that terminated the identifier
|
||
has been skipped in the output and the input.
|
||
OBP-IDENT_LENGTH-1 points to the identifier.
|
||
If the identifier is a macro, we must back over the terminator.
|
||
|
||
If REDO_CHAR is 1, the terminating char has already been
|
||
backed over. OBP-IDENT_LENGTH points to the identifier. */
|
||
|
||
for (hp = hashtab[MAKE_POS (hash) % HASHSIZE]; hp != NULL;
|
||
hp = hp->next) {
|
||
|
||
if (hp->length == ident_length) {
|
||
/* obufp_before_macroname is used only in this block,
|
||
but it has to be global because of RECACHE. */
|
||
int op_lineno_before_macroname;
|
||
int i = ident_length;
|
||
U_CHAR *p = hp->name;
|
||
U_CHAR *q = obp - i;
|
||
|
||
if (! redo_char)
|
||
q--;
|
||
|
||
do { /* All this to avoid a strncmp () */
|
||
if (*p++ != *q++)
|
||
goto hashcollision;
|
||
} while (--i);
|
||
|
||
/* We found a use of a macro name.
|
||
see if the context shows it is a macro call. */
|
||
|
||
/* Back up over terminating character if not already done. */
|
||
if (! redo_char) {
|
||
ibp--;
|
||
obp--;
|
||
}
|
||
|
||
obufp_before_macroname = obp - ident_length;
|
||
op_lineno_before_macroname = op->lineno;
|
||
|
||
/* If macro wants an arglist, verify that a '(' follows.
|
||
first skip all whitespace, copying it to the output
|
||
after the macro name. Then, if there is no '(',
|
||
decide this is not a macro call and leave things that way. */
|
||
if (hp->type == T_MACRO && hp->value.defn->nargs >= 0)
|
||
{
|
||
while (1) {
|
||
/* Scan forward over whitespace, copying it to the output. */
|
||
if (ibp == limit && ip->macro != 0) {
|
||
POPMACRO;
|
||
RECACHE;
|
||
}
|
||
/* A comment: copy it unchanged or discard it. */
|
||
else if (*ibp == '/' && ibp+1 != limit && ibp[1] == '*') {
|
||
if (put_out_comments) {
|
||
*obp++ = '/';
|
||
*obp++ = '*';
|
||
}
|
||
ibp += 2;
|
||
while (ibp + 1 != limit
|
||
&& !(ibp[0] == '*' && ibp[1] == '/')) {
|
||
/* We need not worry about newline-marks,
|
||
since they are never found in comments. */
|
||
if (*ibp == '\n') {
|
||
/* Newline in a file. Count it. */
|
||
++ip->lineno;
|
||
++op->lineno;
|
||
}
|
||
if (put_out_comments)
|
||
*obp++ = *ibp++;
|
||
else
|
||
ibp++;
|
||
}
|
||
ibp += 2;
|
||
if (put_out_comments) {
|
||
*obp++ = '*';
|
||
*obp++ = '/';
|
||
}
|
||
}
|
||
else if (is_space (*ibp)) {
|
||
*obp++ = *ibp++;
|
||
if (ibp[-1] == '\n') {
|
||
if (ip->macro == 0) {
|
||
/* Newline in a file. Count it. */
|
||
++ip->lineno;
|
||
++op->lineno;
|
||
} else if (!output_marks) {
|
||
/* A newline mark, and we don't want marks
|
||
in the output. If it is newline-hyphen,
|
||
discard it entirely. Otherwise, it is
|
||
newline-whitechar, so keep the whitechar. */
|
||
obp--;
|
||
if (*ibp == '-')
|
||
ibp++;
|
||
else {
|
||
if (*ibp == '\n')
|
||
++op->lineno;
|
||
*obp++ = *ibp++;
|
||
}
|
||
} else {
|
||
/* A newline mark; copy both chars to the output. */
|
||
*obp++ = *ibp++;
|
||
}
|
||
}
|
||
}
|
||
else break;
|
||
}
|
||
if (*ibp != '(')
|
||
break;
|
||
}
|
||
|
||
/* This is now known to be a macro call.
|
||
Discard the macro name from the output,
|
||
along with any following whitespace just copied. */
|
||
obp = obufp_before_macroname;
|
||
op->lineno = op_lineno_before_macroname;
|
||
|
||
/* Expand the macro, reading arguments as needed,
|
||
and push the expansion on the input stack. */
|
||
ip->bufp = ibp;
|
||
op->bufp = obp;
|
||
macroexpand (hp, op);
|
||
|
||
/* Reexamine input stack, since macroexpand has pushed
|
||
a new level on it. */
|
||
obp = op->bufp;
|
||
RECACHE;
|
||
break;
|
||
}
|
||
hashcollision:
|
||
;
|
||
} /* End hash-table-search loop */
|
||
ident_length = hash = 0; /* Stop collecting identifier */
|
||
redo_char = 0;
|
||
concatenated = 0;
|
||
} /* End if (ident_length > 0) */
|
||
} /* End switch */
|
||
} /* End per-char loop */
|
||
|
||
/* Come here to return -- but first give an error message
|
||
if there was an unterminated successful conditional. */
|
||
ending:
|
||
if (if_stack != ip->if_stack) {
|
||
const char *str;
|
||
switch (if_stack->type) {
|
||
case T_IF:
|
||
str = "if";
|
||
break;
|
||
case T_IFDEF:
|
||
str = "ifdef";
|
||
break;
|
||
case T_IFNDEF:
|
||
str = "ifndef";
|
||
break;
|
||
case T_ELSE:
|
||
str = "else";
|
||
break;
|
||
case T_ELIF:
|
||
str = "elif";
|
||
break;
|
||
default:
|
||
abort ();
|
||
}
|
||
error_with_line (line_for_error (if_stack->lineno),
|
||
"unterminated #%s conditional", str);
|
||
}
|
||
if_stack = ip->if_stack;
|
||
}
|
||
|
||
/*
|
||
* Rescan a string into a temporary buffer and return the result
|
||
* as a FILE_BUF. Note this function returns a struct, not a pointer.
|
||
*
|
||
* OUTPUT_MARKS nonzero means keep Newline markers found in the input
|
||
* and insert such markers when appropriate. See `rescan' for details.
|
||
* OUTPUT_MARKS is 1 for macroexpanding a macro argument separately
|
||
* before substitution; it is 0 for other uses.
|
||
*/
|
||
static FILE_BUF
|
||
expand_to_temp_buffer (buf, limit, output_marks)
|
||
const U_CHAR *buf, *limit;
|
||
int output_marks;
|
||
{
|
||
FILE_BUF *ip;
|
||
FILE_BUF obuf;
|
||
int length = limit - buf;
|
||
U_CHAR *buf1;
|
||
int odepth = indepth;
|
||
|
||
if (length < 0)
|
||
abort ();
|
||
|
||
/* Set up the input on the input stack. */
|
||
|
||
buf1 = (U_CHAR *) alloca (length + 1);
|
||
{
|
||
const U_CHAR *p1 = buf;
|
||
U_CHAR *p2 = buf1;
|
||
|
||
while (p1 != limit)
|
||
*p2++ = *p1++;
|
||
}
|
||
buf1[length] = 0;
|
||
|
||
/* Set up to receive the output. */
|
||
|
||
obuf.length = length * 2 + 100; /* Usually enough. Why be stingy? */
|
||
obuf.bufp = obuf.buf = (U_CHAR *) xmalloc (obuf.length);
|
||
obuf.fname = 0;
|
||
obuf.macro = 0;
|
||
obuf.free_ptr = 0;
|
||
|
||
CHECK_DEPTH ({return obuf;});
|
||
|
||
++indepth;
|
||
|
||
ip = &instack[indepth];
|
||
ip->fname = 0;
|
||
ip->macro = 0;
|
||
ip->free_ptr = 0;
|
||
ip->length = length;
|
||
ip->buf = ip->bufp = buf1;
|
||
ip->if_stack = if_stack;
|
||
|
||
ip->lineno = obuf.lineno = 1;
|
||
|
||
/* Scan the input, create the output. */
|
||
|
||
rescan (&obuf, output_marks);
|
||
|
||
/* Pop input stack to original state. */
|
||
--indepth;
|
||
|
||
if (indepth != odepth)
|
||
abort ();
|
||
|
||
/* Record the output. */
|
||
obuf.length = obuf.bufp - obuf.buf;
|
||
|
||
return obuf;
|
||
}
|
||
|
||
/*
|
||
* Process a # directive. Expects IP->bufp to point to the '#', as in
|
||
* `#define foo bar'. Passes to the command handler
|
||
* (do_define, do_include, etc.): the addresses of the 1st and
|
||
* last chars of the command (starting immediately after the #
|
||
* keyword), plus op and the keyword table pointer. If the command
|
||
* contains comments it is copied into a temporary buffer sans comments
|
||
* and the temporary buffer is passed to the command handler instead.
|
||
* Likewise for backslash-newlines.
|
||
*
|
||
* Returns nonzero if this was a known # directive.
|
||
* Otherwise, returns zero, without advancing the input pointer.
|
||
*/
|
||
|
||
static int
|
||
handle_directive (ip, op)
|
||
FILE_BUF *ip, *op;
|
||
{
|
||
U_CHAR *bp, *cp;
|
||
const struct directive *kt;
|
||
int ident_length;
|
||
U_CHAR *resume_p;
|
||
|
||
/* Nonzero means we must copy the entire command
|
||
to get rid of comments or backslash-newlines. */
|
||
int copy_command = 0;
|
||
|
||
U_CHAR *ident, *after_ident;
|
||
|
||
bp = ip->bufp;
|
||
/* Skip whitespace and \-newline. */
|
||
while (1) {
|
||
if (is_nvspace (*bp))
|
||
bp++;
|
||
else if (*bp == '/' && (newline_fix (bp + 1), bp[1]) == '*') {
|
||
ip->bufp = bp;
|
||
skip_to_end_of_comment (ip, &ip->lineno);
|
||
bp = ip->bufp;
|
||
} else if (*bp == '\\' && bp[1] == '\n') {
|
||
bp += 2; ip->lineno++;
|
||
} else break;
|
||
}
|
||
|
||
/* Now find end of directive name.
|
||
If we encounter a backslash-newline, exchange it with any following
|
||
symbol-constituents so that we end up with a contiguous name. */
|
||
|
||
cp = bp;
|
||
while (1) {
|
||
if (is_idchar (*cp))
|
||
cp++;
|
||
else {
|
||
if (*cp == '\\' && cp[1] == '\n')
|
||
name_newline_fix (cp);
|
||
if (is_idchar (*cp))
|
||
cp++;
|
||
else break;
|
||
}
|
||
}
|
||
ident_length = cp - bp;
|
||
ident = bp;
|
||
after_ident = cp;
|
||
|
||
/* A line of just `#' becomes blank. */
|
||
|
||
if (ident_length == 0 && *after_ident == '\n') {
|
||
ip->bufp = after_ident;
|
||
return 1;
|
||
}
|
||
|
||
/*
|
||
* Decode the keyword and call the appropriate expansion
|
||
* routine, after moving the input pointer up to the next line.
|
||
*/
|
||
for (kt = directive_table; kt->length > 0; kt++) {
|
||
if (kt->length == ident_length
|
||
&& !strncmp (kt->name, (const char *)ident, ident_length)) {
|
||
U_CHAR *buf;
|
||
U_CHAR *limit = ip->buf + ip->length;
|
||
int unterminated = 0;
|
||
|
||
/* Nonzero means do not delete comments within the directive.
|
||
#define needs this to detect traditional token paste. */
|
||
int keep_comments = kt->type == T_DEFINE;
|
||
|
||
/* Find the end of this command (first newline not backslashed
|
||
and not in a string or comment).
|
||
Set COPY_COMMAND if the command must be copied
|
||
(it contains a backslash-newline or a comment). */
|
||
|
||
buf = bp = after_ident;
|
||
while (bp < limit) {
|
||
U_CHAR c = *bp++;
|
||
switch (c) {
|
||
case '\\':
|
||
if (bp < limit) {
|
||
if (*bp == '\n') {
|
||
ip->lineno++;
|
||
copy_command = 1;
|
||
}
|
||
bp++;
|
||
}
|
||
break;
|
||
|
||
case '\'':
|
||
case '\"':
|
||
bp = skip_quoted_string (bp - 1, limit, ip->lineno, &ip->lineno, ©_command, &unterminated);
|
||
if (unterminated) {
|
||
/* Traditional preprocessing permits unterminated strings. */
|
||
ip->bufp = bp;
|
||
goto endloop1;
|
||
}
|
||
break;
|
||
|
||
/* <...> is special for #include. */
|
||
case '<':
|
||
if (kt->type != T_INCLUDE)
|
||
break;
|
||
while (*bp && *bp != '>') bp++;
|
||
break;
|
||
|
||
case '/':
|
||
if (*bp == '\\' && bp[1] == '\n')
|
||
newline_fix (bp);
|
||
if (*bp == '*') {
|
||
U_CHAR *obp = bp - 1;
|
||
ip->bufp = bp + 1;
|
||
skip_to_end_of_comment (ip, &ip->lineno);
|
||
bp = ip->bufp;
|
||
/* No need to copy the command because of a comment at the end;
|
||
just don't include the comment in the directive. */
|
||
if (bp == limit || *bp == '\n') {
|
||
bp = obp;
|
||
goto endloop1;
|
||
}
|
||
/* Don't remove the comments if this is #define. */
|
||
if (! keep_comments)
|
||
copy_command++;
|
||
}
|
||
break;
|
||
|
||
case '\n':
|
||
--bp; /* Point to the newline */
|
||
ip->bufp = bp;
|
||
goto endloop1;
|
||
}
|
||
}
|
||
ip->bufp = bp;
|
||
|
||
endloop1:
|
||
resume_p = ip->bufp;
|
||
/* BP is the end of the directive.
|
||
RESUME_P is the next interesting data after the directive.
|
||
A comment may come between. */
|
||
|
||
if (copy_command) {
|
||
U_CHAR *xp = buf;
|
||
/* Need to copy entire command into temp buffer before dispatching */
|
||
|
||
cp = (U_CHAR *) alloca (bp - buf + 5); /* room for cmd plus
|
||
some slop */
|
||
buf = cp;
|
||
|
||
/* Copy to the new buffer, deleting comments
|
||
and backslash-newlines (and whitespace surrounding the latter). */
|
||
|
||
while (xp < bp) {
|
||
U_CHAR c = *xp++;
|
||
*cp++ = c;
|
||
|
||
switch (c) {
|
||
case '\n':
|
||
break;
|
||
|
||
/* <...> is special for #include. */
|
||
case '<':
|
||
if (kt->type != T_INCLUDE)
|
||
break;
|
||
while (xp < bp && c != '>') {
|
||
c = *xp++;
|
||
if (c == '\\' && xp < bp && *xp == '\n')
|
||
xp++, ip->lineno++;
|
||
else
|
||
*cp++ = c;
|
||
}
|
||
break;
|
||
|
||
case '\\':
|
||
if (*xp == '\n') {
|
||
xp++;
|
||
cp--;
|
||
if (cp != buf && is_space (cp[-1])) {
|
||
while (cp != buf && is_space(cp[-1])) cp--;
|
||
cp++;
|
||
SKIP_WHITE_SPACE (xp);
|
||
} else if (is_nvspace (*xp)) {
|
||
*cp++ = *xp++;
|
||
SKIP_WHITE_SPACE (xp);
|
||
}
|
||
} else {
|
||
*cp++ = *xp++;
|
||
}
|
||
break;
|
||
|
||
case '\'':
|
||
case '\"':
|
||
{
|
||
const U_CHAR *bp1
|
||
= skip_quoted_string (xp - 1, limit, ip->lineno, 0, 0, 0);
|
||
while (xp != bp1)
|
||
*cp++ = *xp++;
|
||
}
|
||
break;
|
||
|
||
case '/':
|
||
if (*xp == '*') {
|
||
ip->bufp = xp + 1;
|
||
skip_to_end_of_comment (ip, 0);
|
||
if (keep_comments)
|
||
while (xp != ip->bufp)
|
||
*cp++ = *xp++;
|
||
/* Delete the slash. */
|
||
else
|
||
cp--;
|
||
xp = ip->bufp;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Null-terminate the copy. */
|
||
|
||
*cp = 0;
|
||
}
|
||
else
|
||
cp = bp;
|
||
|
||
ip->bufp = resume_p;
|
||
|
||
/* Call the appropriate command handler. buf now points to
|
||
either the appropriate place in the input buffer, or to
|
||
the temp buffer if it was necessary to make one. cp
|
||
points to the first char after the contents of the (possibly
|
||
copied) command, in either case. */
|
||
(*kt->func) (buf, cp, op);
|
||
check_expand (op, ip->length - (ip->bufp - ip->buf));
|
||
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static const char *const
|
||
monthnames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||
|
||
/*
|
||
* expand things like __FILE__. Place the expansion into the output
|
||
* buffer *without* rescanning.
|
||
*/
|
||
static void
|
||
special_symbol (hp, op)
|
||
HASHNODE *hp;
|
||
FILE_BUF *op;
|
||
{
|
||
const char *buf;
|
||
time_t t;
|
||
int i, len;
|
||
int true_indepth;
|
||
FILE_BUF *ip = NULL;
|
||
static struct tm *timebuf = NULL;
|
||
|
||
int paren = 0; /* For special `defined' keyword */
|
||
|
||
for (i = indepth; i >= 0; i--)
|
||
if (instack[i].fname != NULL) {
|
||
ip = &instack[i];
|
||
break;
|
||
}
|
||
if (ip == NULL)
|
||
fatal ("not in any file?!");
|
||
|
||
switch (hp->type) {
|
||
case T_FILE:
|
||
case T_BASE_FILE:
|
||
{
|
||
const char *string;
|
||
if (hp->type == T_FILE)
|
||
string = ip->fname;
|
||
else
|
||
string = instack[0].fname;
|
||
|
||
if (string)
|
||
{
|
||
char *tmp = (char *) alloca (3 + strlen (string));
|
||
sprintf (tmp, "\"%s\"", string);
|
||
buf = tmp;
|
||
}
|
||
else
|
||
buf = "";
|
||
|
||
break;
|
||
}
|
||
|
||
case T_INCLUDE_LEVEL:
|
||
{
|
||
char *tmp = (char *) alloca (8); /* Eigth bytes ought to be more than enough */
|
||
true_indepth = 0;
|
||
for (i = indepth; i >= 0; i--)
|
||
if (instack[i].fname != NULL)
|
||
true_indepth++;
|
||
|
||
sprintf (tmp, "%d", true_indepth - 1);
|
||
buf = tmp;
|
||
break;
|
||
}
|
||
|
||
case T_VERSION:
|
||
{
|
||
char *tmp = (char *) alloca (3 + strlen (version_string));
|
||
sprintf (tmp, "\"%s\"", version_string);
|
||
buf = tmp;
|
||
break;
|
||
}
|
||
|
||
case T_CONST:
|
||
buf = hp->value.cpval;
|
||
break;
|
||
|
||
case T_SPECLINE:
|
||
{
|
||
char *tmp = (char *) alloca (10);
|
||
sprintf (tmp, "%d", ip->lineno);
|
||
buf = tmp;
|
||
break;
|
||
}
|
||
|
||
case T_DATE:
|
||
case T_TIME:
|
||
{
|
||
char *tmp = (char *) alloca (20);
|
||
|
||
if (timebuf == NULL) {
|
||
t = time (0);
|
||
timebuf = localtime (&t);
|
||
}
|
||
if (hp->type == T_DATE)
|
||
sprintf (tmp, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon],
|
||
timebuf->tm_mday, timebuf->tm_year + 1900);
|
||
else
|
||
sprintf (tmp, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min,
|
||
timebuf->tm_sec);
|
||
buf = tmp;
|
||
break;
|
||
}
|
||
|
||
case T_SPEC_DEFINED:
|
||
buf = " 0 "; /* Assume symbol is not defined */
|
||
ip = &instack[indepth];
|
||
SKIP_WHITE_SPACE (ip->bufp);
|
||
if (*ip->bufp == '(') {
|
||
paren++;
|
||
ip->bufp++; /* Skip over the paren */
|
||
SKIP_WHITE_SPACE (ip->bufp);
|
||
}
|
||
|
||
if (!is_idstart (*ip->bufp))
|
||
goto oops;
|
||
{
|
||
HASHNODE *hp = lookup (ip->bufp, -1, -1);
|
||
|
||
if (hp && hp->type != T_UNUSED && hp->type != T_SPEC_DEFINED)
|
||
buf = " 1 ";
|
||
}
|
||
while (is_idchar (*ip->bufp))
|
||
++ip->bufp;
|
||
SKIP_WHITE_SPACE (ip->bufp);
|
||
if (paren) {
|
||
if (*ip->bufp != ')')
|
||
goto oops;
|
||
++ip->bufp;
|
||
}
|
||
break;
|
||
|
||
oops:
|
||
|
||
error ("`defined' must be followed by ident or (ident)");
|
||
break;
|
||
|
||
default:
|
||
error ("cccp error: invalid special hash type"); /* time for gdb */
|
||
abort ();
|
||
}
|
||
len = strlen (buf);
|
||
check_expand (op, len);
|
||
memcpy (op->bufp, buf, len);
|
||
op->bufp += len;
|
||
}
|
||
|
||
|
||
/* Routines to handle #directives */
|
||
|
||
/*
|
||
* Process include file by reading it in and calling rescan.
|
||
* Expects to see "fname" or <fname> on the input.
|
||
*/
|
||
static void
|
||
do_include (buf, limit, op)
|
||
U_CHAR *buf, *limit;
|
||
FILE_BUF *op;
|
||
{
|
||
U_CHAR *fbeg, *fend; /* Beginning and end of fname */
|
||
|
||
struct file_name_list *stackp = include; /* Chain of dirs to search */
|
||
struct file_name_list dsp[1]; /* First in chain, if #include "..." */
|
||
int flen;
|
||
|
||
int retried = 0; /* Have already tried macro
|
||
expanding the include line*/
|
||
FILE_BUF trybuf; /* It got expanded into here */
|
||
int system_header_p = 0; /* 0 for "...", 1 for <...> */
|
||
|
||
get_filename:
|
||
|
||
fbeg = buf;
|
||
SKIP_WHITE_SPACE (fbeg);
|
||
/* Discard trailing whitespace so we can easily see
|
||
if we have parsed all the significant chars we were given. */
|
||
while (limit != fbeg && is_nvspace (limit[-1])) limit--;
|
||
|
||
switch (*fbeg++) {
|
||
case '\"':
|
||
fend = fbeg;
|
||
while (fend != limit && *fend != '\"')
|
||
fend++;
|
||
if (*fend == '\"' && fend + 1 == limit) {
|
||
FILE_BUF *fp;
|
||
|
||
/* We have "filename". Figure out directory this source
|
||
file is coming from and put it on the front of the list. */
|
||
|
||
/* If -I- was specified, don't search current dir, only spec'd ones. */
|
||
if (ignore_srcdir) break;
|
||
|
||
for (fp = &instack[indepth]; fp >= instack; fp--)
|
||
{
|
||
size_t n;
|
||
const char *ep, *nam;
|
||
|
||
if ((nam = fp->fname) != NULL) {
|
||
/* Found a named file. Figure out dir of the file,
|
||
and put it in front of the search list. */
|
||
dsp[0].next = stackp;
|
||
stackp = dsp;
|
||
ep = strrchr (nam, '/');
|
||
if (ep != NULL) {
|
||
char *f;
|
||
n = ep - nam;
|
||
f = (char *) alloca (n + 1);
|
||
strncpy (f, nam, n);
|
||
f[n] = '\0';
|
||
dsp[0].fname = f;
|
||
if (n > max_include_len) max_include_len = n;
|
||
} else {
|
||
dsp[0].fname = 0; /* Current directory */
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
goto fail;
|
||
|
||
case '<':
|
||
fend = fbeg;
|
||
while (fend != limit && *fend != '>') fend++;
|
||
if (*fend == '>' && fend + 1 == limit) {
|
||
system_header_p = 1;
|
||
/* If -I-, start with the first -I dir after the -I-. */
|
||
if (first_bracket_include)
|
||
stackp = first_bracket_include;
|
||
break;
|
||
}
|
||
goto fail;
|
||
|
||
default:
|
||
fail:
|
||
if (retried) {
|
||
error ("#include expects \"fname\" or <fname>");
|
||
return;
|
||
} else {
|
||
trybuf = expand_to_temp_buffer (buf, limit, 0);
|
||
buf = (U_CHAR *) alloca (trybuf.bufp - trybuf.buf + 1);
|
||
memcpy (buf, trybuf.buf, trybuf.bufp - trybuf.buf);
|
||
limit = buf + (trybuf.bufp - trybuf.buf);
|
||
free (trybuf.buf);
|
||
retried++;
|
||
goto get_filename;
|
||
}
|
||
}
|
||
|
||
flen = fend - fbeg;
|
||
process_include (stackp, fbeg, flen, system_header_p, op);
|
||
}
|
||
|
||
static void
|
||
do_include_next (buf, limit, op)
|
||
U_CHAR *buf, *limit;
|
||
FILE_BUF *op;
|
||
{
|
||
U_CHAR *fbeg, *fend; /* Beginning and end of fname */
|
||
|
||
struct file_name_list *stackp; /* Chain of dirs to search */
|
||
int flen;
|
||
|
||
int retried = 0; /* Have already tried macro
|
||
expanding the include line*/
|
||
FILE_BUF trybuf; /* It got expanded into here */
|
||
int system_header_p = 0; /* 0 for "...", 1 for <...> */
|
||
|
||
/* Treat as plain #include if we don't know where to start
|
||
looking. */
|
||
stackp = instack[indepth].next_header_dir;
|
||
if (stackp == 0)
|
||
{
|
||
do_include (buf, limit, op);
|
||
return;
|
||
}
|
||
|
||
get_filename:
|
||
|
||
fbeg = buf;
|
||
SKIP_WHITE_SPACE (fbeg);
|
||
/* Discard trailing whitespace so we can easily see
|
||
if we have parsed all the significant chars we were given. */
|
||
while (limit != fbeg && is_nvspace (limit[-1])) limit--;
|
||
|
||
switch (*fbeg++) {
|
||
case '\"':
|
||
fend = fbeg;
|
||
while (fend != limit && *fend != '\"')
|
||
fend++;
|
||
if (*fend == '\"' && fend + 1 == limit)
|
||
break;
|
||
goto fail;
|
||
|
||
case '<':
|
||
fend = fbeg;
|
||
while (fend != limit && *fend != '>') fend++;
|
||
if (*fend == '>' && fend + 1 == limit) {
|
||
system_header_p = 1;
|
||
break;
|
||
}
|
||
goto fail;
|
||
|
||
default:
|
||
fail:
|
||
if (retried) {
|
||
error ("#include expects \"fname\" or <fname>");
|
||
return;
|
||
} else {
|
||
trybuf = expand_to_temp_buffer (buf, limit, 0);
|
||
buf = (U_CHAR *) alloca (trybuf.bufp - trybuf.buf + 1);
|
||
memcpy (buf, trybuf.buf, trybuf.bufp - trybuf.buf);
|
||
limit = buf + (trybuf.bufp - trybuf.buf);
|
||
free (trybuf.buf);
|
||
retried++;
|
||
goto get_filename;
|
||
}
|
||
}
|
||
|
||
flen = fend - fbeg;
|
||
process_include (stackp, fbeg, flen, system_header_p, op);
|
||
}
|
||
|
||
static void
|
||
process_include (stackp, fbeg, flen, system_header_p, op)
|
||
struct file_name_list *stackp;
|
||
const U_CHAR *fbeg;
|
||
int flen;
|
||
int system_header_p;
|
||
FILE_BUF *op;
|
||
{
|
||
char *fname;
|
||
int f = -1; /* file number */
|
||
|
||
fname = (char *) alloca (max_include_len + flen + 2);
|
||
/* + 2 above for slash and terminating null. */
|
||
|
||
/* If specified file name is absolute, just open it. */
|
||
|
||
if (IS_ABSOLUTE_PATHNAME (fbeg)) {
|
||
strncpy (fname, (const char *)fbeg, flen);
|
||
fname[flen] = 0;
|
||
f = open (fname, O_RDONLY, 0666);
|
||
} else {
|
||
/* Search directory path, trying to open the file.
|
||
Copy each filename tried into FNAME. */
|
||
|
||
for (; stackp; stackp = stackp->next) {
|
||
if (stackp->fname) {
|
||
strcpy (fname, stackp->fname);
|
||
strcat (fname, "/");
|
||
fname[strlen (fname) + flen] = 0;
|
||
} else {
|
||
fname[0] = 0;
|
||
}
|
||
strncat (fname, (const char *)fbeg, flen);
|
||
if ((f = open (fname, O_RDONLY, 0666)) >= 0)
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (f < 0) {
|
||
strncpy (fname, (const char *)fbeg, flen);
|
||
fname[flen] = 0;
|
||
if (deps_missing_files
|
||
&& print_deps > (system_header_p || (system_include_depth > 0))) {
|
||
|
||
/* If requested as a system header, assume it belongs in
|
||
the first system header directory. */
|
||
if (first_bracket_include)
|
||
stackp = first_bracket_include;
|
||
else
|
||
stackp = include;
|
||
|
||
if (!system_header_p || IS_ABSOLUTE_PATHNAME (fbeg) || !stackp->fname)
|
||
deps_add_dep (deps, fname);
|
||
else {
|
||
char *p;
|
||
int len = strlen(stackp->fname);
|
||
|
||
p = (char *) alloca (len + flen + 2);
|
||
memcpy (p, stackp->fname, len);
|
||
p[len++] = '/';
|
||
memcpy (p + len, fbeg, flen);
|
||
len += flen;
|
||
p[len] = '\0';
|
||
deps_add_dep (deps, p);
|
||
}
|
||
} else if (print_deps
|
||
&& print_deps <= (system_header_p
|
||
|| (system_include_depth > 0)))
|
||
warning ("no include path in which to find %.*s", flen, fbeg);
|
||
else
|
||
error_from_errno (fname);
|
||
|
||
} else {
|
||
|
||
/* Check to see if this include file is a once-only include file.
|
||
If so, give up. */
|
||
|
||
struct file_name_list* ptr;
|
||
|
||
for (ptr = dont_repeat_files; ptr; ptr = ptr->next) {
|
||
if (!strcmp (ptr->fname, fname)) {
|
||
close (f);
|
||
return; /* This file was once'd. */
|
||
}
|
||
}
|
||
|
||
for (ptr = all_include_files; ptr; ptr = ptr->next) {
|
||
if (!strcmp (ptr->fname, fname))
|
||
break; /* This file was included before. */
|
||
}
|
||
|
||
if (ptr == 0) {
|
||
/* This is the first time for this file. */
|
||
/* Add it to list of files included. */
|
||
|
||
ptr = (struct file_name_list *) xmalloc (sizeof (struct file_name_list));
|
||
ptr->next = all_include_files;
|
||
all_include_files = ptr;
|
||
ptr->fname = xstrdup (fname);
|
||
|
||
/* For -M, add this file to the dependencies. */
|
||
if (print_deps > (system_header_p || (system_include_depth > 0)))
|
||
deps_add_dep (deps, fname);
|
||
}
|
||
|
||
if (system_header_p)
|
||
system_include_depth++;
|
||
|
||
/* Actually process the file. */
|
||
finclude (f, fname, stackp->next, op);
|
||
|
||
if (system_header_p)
|
||
system_include_depth--;
|
||
|
||
close (f);
|
||
}
|
||
}
|
||
|
||
/* Process the contents of include file FNAME, already open on descriptor F,
|
||
with output to OP. */
|
||
|
||
static void
|
||
finclude (f, fname, nhd, op)
|
||
int f;
|
||
const char *fname;
|
||
struct file_name_list *nhd;
|
||
FILE_BUF *op;
|
||
{
|
||
int st_mode;
|
||
long st_size;
|
||
long i;
|
||
FILE_BUF *fp; /* For input stack frame */
|
||
|
||
CHECK_DEPTH (return;);
|
||
|
||
if (file_size_and_mode (f, &st_mode, &st_size))
|
||
goto nope;
|
||
|
||
fp = &instack[indepth + 1];
|
||
memset (fp, 0, sizeof (FILE_BUF));
|
||
fp->fname = fname;
|
||
fp->length = 0;
|
||
fp->lineno = 1;
|
||
fp->if_stack = if_stack;
|
||
fp->next_header_dir = nhd;
|
||
|
||
if (S_ISREG (st_mode)) {
|
||
fp->buf = (U_CHAR *) xmalloc (st_size + 2);
|
||
fp->bufp = fp->buf;
|
||
|
||
/* Read the file contents, knowing that st_size is an upper bound
|
||
on the number of bytes we can read. */
|
||
while (st_size > 0) {
|
||
i = read (f, fp->buf + fp->length, st_size);
|
||
if (i <= 0) {
|
||
if (i == 0) break;
|
||
goto nope;
|
||
}
|
||
fp->length += i;
|
||
st_size -= i;
|
||
}
|
||
}
|
||
else {
|
||
/* Cannot count its file size before reading. */
|
||
|
||
U_CHAR *bufp;
|
||
U_CHAR *basep;
|
||
int bsize = 2000;
|
||
|
||
st_size = 0;
|
||
basep = (U_CHAR *) xmalloc (bsize + 2);
|
||
bufp = basep;
|
||
|
||
for (;;) {
|
||
i = read (f, bufp, bsize - st_size);
|
||
if (i < 0)
|
||
goto nope; /* error! */
|
||
if (i == 0)
|
||
break; /* End of file */
|
||
st_size += i;
|
||
bufp += i;
|
||
if (bsize == st_size) { /* Buffer is full! */
|
||
bsize *= 2;
|
||
basep = (U_CHAR *) xrealloc (basep, bsize + 2);
|
||
bufp = basep + st_size; /* May have moved */
|
||
}
|
||
}
|
||
fp->buf = basep;
|
||
fp->bufp = fp->buf;
|
||
fp->length = st_size;
|
||
}
|
||
close (f);
|
||
|
||
/* Make sure data ends with a newline. And put a null after it. */
|
||
|
||
if (fp->length > 0 && fp->buf[fp->length-1] != '\n')
|
||
fp->buf[fp->length++] = '\n';
|
||
fp->buf[fp->length] = '\0';
|
||
|
||
indepth++;
|
||
output_line_command (fp, op, 0, enter_file);
|
||
rescan (op, 0);
|
||
indepth--;
|
||
instack[indepth].lineno++;
|
||
instack[indepth].bufp++; /* Skip the new line. */
|
||
output_line_command (&instack[indepth], op, 0, leave_file);
|
||
free (fp->buf);
|
||
return;
|
||
|
||
nope:
|
||
perror_with_name (fname);
|
||
close (f);
|
||
}
|
||
|
||
|
||
/* Process a #define command.
|
||
BUF points to the contents of the #define command, as a continguous string.
|
||
LIMIT points to the first character past the end of the definition.
|
||
KEYWORD is the keyword-table entry for #define. */
|
||
|
||
static void
|
||
do_define (buf, limit, op)
|
||
U_CHAR *buf, *limit;
|
||
FILE_BUF *op ATTRIBUTE_UNUSED;
|
||
{
|
||
U_CHAR *bp; /* temp ptr into input buffer */
|
||
U_CHAR *symname; /* remember where symbol name starts */
|
||
int sym_length; /* and how long it is */
|
||
|
||
DEFINITION *defn;
|
||
int arglengths = 0; /* Accumulate lengths of arg names
|
||
plus number of args. */
|
||
int hashcode;
|
||
|
||
bp = buf;
|
||
|
||
while (is_nvspace (*bp))
|
||
bp++;
|
||
|
||
symname = bp; /* remember where it starts */
|
||
while (is_idchar (*bp) && bp < limit) {
|
||
bp++;
|
||
}
|
||
sym_length = bp - symname;
|
||
if (sym_length == 0)
|
||
{
|
||
error ("invalid macro name");
|
||
return;
|
||
}
|
||
else if (!is_idstart (*symname)) {
|
||
U_CHAR *msg; /* what pain... */
|
||
msg = (U_CHAR *) alloca (sym_length + 1);
|
||
memcpy (msg, symname, sym_length);
|
||
msg[sym_length] = 0;
|
||
error ("invalid macro name `%s'", msg);
|
||
return;
|
||
} else {
|
||
if (! strncmp ((const char *)symname, "defined", 7) && sym_length == 7)
|
||
{
|
||
error ("\"defined\" cannot be used as a macro name");
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* lossage will occur if identifiers or control keywords are broken
|
||
across lines using backslash. This is not the right place to take
|
||
care of that. */
|
||
|
||
if (*bp == '(') {
|
||
struct arglist *arg_ptrs = NULL;
|
||
int argno = 0;
|
||
|
||
bp++; /* skip '(' */
|
||
SKIP_WHITE_SPACE (bp);
|
||
|
||
/* Loop over macro argument names. */
|
||
while (*bp != ')') {
|
||
struct arglist *temp;
|
||
|
||
temp = (struct arglist *) alloca (sizeof (struct arglist));
|
||
temp->name = bp;
|
||
temp->next = arg_ptrs;
|
||
temp->argno = argno++;
|
||
arg_ptrs = temp;
|
||
|
||
if (!is_idstart (*bp))
|
||
warning ("parameter name starts with a digit in #define");
|
||
|
||
/* Find the end of the arg name. */
|
||
while (is_idchar (*bp)) {
|
||
bp++;
|
||
}
|
||
temp->length = bp - temp->name;
|
||
arglengths += temp->length + 2;
|
||
SKIP_WHITE_SPACE (bp);
|
||
if (temp->length == 0 || (*bp != ',' && *bp != ')')) {
|
||
error ("badly punctuated parameter list in #define");
|
||
return;
|
||
}
|
||
if (*bp == ',') {
|
||
bp++;
|
||
SKIP_WHITE_SPACE (bp);
|
||
}
|
||
if (bp >= limit) {
|
||
error ("unterminated parameter list in #define");
|
||
return;
|
||
}
|
||
}
|
||
|
||
++bp; /* skip paren */
|
||
while (is_nvspace (*bp) && bp < limit) /* and leading whitespace */
|
||
++bp;
|
||
/* now everything from bp before limit is the definition. */
|
||
defn = collect_expansion (bp, limit, argno, arg_ptrs);
|
||
|
||
/* Now set defn->argnames to the result of concatenating
|
||
the argument names in reverse order
|
||
with comma-space between them. */
|
||
{
|
||
struct arglist *temp;
|
||
int i = 0;
|
||
U_CHAR *tmp = (U_CHAR *) xmalloc (arglengths + 1);
|
||
|
||
for (temp = arg_ptrs; temp; temp = temp->next) {
|
||
memcpy (&tmp[i], temp->name, temp->length);
|
||
i += temp->length;
|
||
if (temp->next != 0) {
|
||
tmp[i++] = ',';
|
||
tmp[i++] = ' ';
|
||
}
|
||
}
|
||
tmp[i] = 0;
|
||
defn->argnames = tmp;
|
||
|
||
}
|
||
} else {
|
||
/* simple expansion or empty definition; skip leading whitespace */
|
||
while (is_nvspace (*bp) && bp < limit)
|
||
++bp;
|
||
/* now everything from bp before limit is the definition. */
|
||
defn = collect_expansion (bp, limit, -1, 0);
|
||
defn->argnames = (const U_CHAR *) "";
|
||
}
|
||
|
||
hashcode = hashf (symname, sym_length, HASHSIZE);
|
||
|
||
{
|
||
HASHNODE *hp;
|
||
if ((hp = lookup (symname, sym_length, hashcode)) == NULL)
|
||
hp = install (symname, sym_length, T_MACRO, hashcode);
|
||
else {
|
||
if (hp->type != T_MACRO || compare_defs (defn, hp->value.defn))
|
||
warning ("\"%.*s\" redefined", sym_length, symname);
|
||
|
||
/* Replace the old definition. */
|
||
hp->type = T_MACRO;
|
||
}
|
||
|
||
hp->value.defn = defn;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* return zero if two DEFINITIONs are isomorphic
|
||
*/
|
||
static int
|
||
compare_defs (d1, d2)
|
||
DEFINITION *d1, *d2;
|
||
{
|
||
struct reflist *a1, *a2;
|
||
U_CHAR *p1 = d1->expansion;
|
||
U_CHAR *p2 = d2->expansion;
|
||
int first = 1;
|
||
|
||
if (d1->nargs != d2->nargs)
|
||
return 1;
|
||
if (strcmp ((const char *)d1->argnames, (const char *)d2->argnames))
|
||
return 1;
|
||
for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2;
|
||
a1 = a1->next, a2 = a2->next) {
|
||
if (!((a1->nchars == a2->nchars
|
||
&& ! strncmp ((const char *)p1, (const char *)p2, a1->nchars))
|
||
|| ! comp_def_part (first, p1, a1->nchars, p2, a2->nchars, 0))
|
||
|| a1->argno != a2->argno
|
||
|| a1->stringify != a2->stringify
|
||
|| a1->raw_before != a2->raw_before
|
||
|| a1->raw_after != a2->raw_after)
|
||
return 1;
|
||
first = 0;
|
||
p1 += a1->nchars;
|
||
p2 += a2->nchars;
|
||
}
|
||
if (a1 != a2)
|
||
return 1;
|
||
if (comp_def_part (first, p1, d1->length - (p1 - d1->expansion),
|
||
p2, d2->length - (p2 - d2->expansion), 1))
|
||
return 1;
|
||
return 0;
|
||
}
|
||
|
||
/* Return 1 if two parts of two macro definitions are effectively different.
|
||
One of the parts starts at BEG1 and has LEN1 chars;
|
||
the other has LEN2 chars at BEG2.
|
||
Any sequence of whitespace matches any other sequence of whitespace.
|
||
FIRST means these parts are the first of a macro definition;
|
||
so ignore leading whitespace entirely.
|
||
LAST means these parts are the last of a macro definition;
|
||
so ignore trailing whitespace entirely. */
|
||
static int
|
||
comp_def_part (first, beg1, len1, beg2, len2, last)
|
||
int first;
|
||
const U_CHAR *beg1, *beg2;
|
||
int len1, len2;
|
||
int last;
|
||
{
|
||
const U_CHAR *end1 = beg1 + len1;
|
||
const U_CHAR *end2 = beg2 + len2;
|
||
if (first) {
|
||
while (beg1 != end1 && is_space (*beg1)) beg1++;
|
||
while (beg2 != end2 && is_space (*beg2)) beg2++;
|
||
}
|
||
if (last) {
|
||
while (beg1 != end1 && is_space (end1[-1])) end1--;
|
||
while (beg2 != end2 && is_space (end2[-1])) end2--;
|
||
}
|
||
while (beg1 != end1 && beg2 != end2) {
|
||
if (is_space (*beg1) && is_space (*beg2)) {
|
||
while (beg1 != end1 && is_space (*beg1)) beg1++;
|
||
while (beg2 != end2 && is_space (*beg2)) beg2++;
|
||
} else if (*beg1 == *beg2) {
|
||
beg1++; beg2++;
|
||
} else break;
|
||
}
|
||
return (beg1 != end1) || (beg2 != end2);
|
||
}
|
||
|
||
/* Read a replacement list for a macro with parameters.
|
||
Build the DEFINITION structure.
|
||
Reads characters of text starting at BUF until LIMIT.
|
||
ARGLIST specifies the formal parameters to look for
|
||
in the text of the definition; NARGS is the number of args
|
||
in that list, or -1 for a macro name that wants no argument list.
|
||
MACRONAME is the macro name itself (so we can avoid recursive expansion)
|
||
and NAMELEN is its length in characters.
|
||
|
||
Note that comments and backslash-newlines have already been deleted
|
||
from the argument. */
|
||
|
||
/* Leading and trailing Space, Tab, etc. are converted to markers
|
||
Newline Space, Newline Tab, etc.
|
||
Newline Space makes a space in the final output
|
||
but is discarded if stringified. (Newline Tab is similar but
|
||
makes a Tab instead.)
|
||
|
||
If there is no trailing whitespace, a Newline Space is added at the end
|
||
to prevent concatenation that would be contrary to the standard. */
|
||
|
||
static DEFINITION *
|
||
collect_expansion (buf, end, nargs, arglist)
|
||
U_CHAR *buf, *end;
|
||
int nargs;
|
||
struct arglist *arglist;
|
||
{
|
||
DEFINITION *defn;
|
||
U_CHAR *p, *limit, *lastp, *exp_p;
|
||
struct reflist *endpat = NULL;
|
||
/* Pointer to first nonspace after last ## seen. */
|
||
U_CHAR *concat = 0;
|
||
/* Pointer to first nonspace after last single-# seen. */
|
||
U_CHAR *stringify = 0;
|
||
int maxsize;
|
||
int expected_delimiter = '\0';
|
||
|
||
/* Scan thru the replacement list, ignoring comments and quoted
|
||
strings, picking up on the macro calls. It does a linear search
|
||
thru the arg list on every potential symbol. Profiling might say
|
||
that something smarter should happen. */
|
||
|
||
if (end < buf)
|
||
abort ();
|
||
|
||
/* Find the beginning of the trailing whitespace. */
|
||
/* Find end of leading whitespace. */
|
||
limit = end;
|
||
p = buf;
|
||
while (p < limit && is_space (limit[-1])) limit--;
|
||
while (p < limit && is_space (*p)) p++;
|
||
|
||
/* Allocate space for the text in the macro definition.
|
||
Leading and trailing whitespace chars need 2 bytes each.
|
||
Each other input char may or may not need 1 byte,
|
||
so this is an upper bound.
|
||
The extra 2 are for invented trailing newline-marker and final null. */
|
||
maxsize = (sizeof (DEFINITION)
|
||
+ 2 * (end - limit) + 2 * (p - buf)
|
||
+ (limit - p) + 3);
|
||
defn = (DEFINITION *) xcalloc (1, maxsize);
|
||
|
||
defn->nargs = nargs;
|
||
exp_p = defn->expansion = (U_CHAR *) defn + sizeof (DEFINITION);
|
||
lastp = exp_p;
|
||
|
||
p = buf;
|
||
|
||
/* Convert leading whitespace to Newline-markers. */
|
||
while (p < limit && is_space (*p)) {
|
||
*exp_p++ = '\n';
|
||
*exp_p++ = *p++;
|
||
}
|
||
|
||
/* Process the main body of the definition. */
|
||
while (p < limit) {
|
||
int skipped_arg = 0;
|
||
U_CHAR c = *p++;
|
||
|
||
*exp_p++ = c;
|
||
|
||
/* In -traditional mode, recognize arguments inside strings and
|
||
and character constants, and ignore special properties of #.
|
||
Arguments inside strings are considered "stringified", but no
|
||
extra quote marks are supplied. */
|
||
switch (c) {
|
||
case '\'':
|
||
case '\"':
|
||
if (expected_delimiter != '\0') {
|
||
if (c == expected_delimiter)
|
||
expected_delimiter = '\0';
|
||
} else
|
||
expected_delimiter = c;
|
||
break;
|
||
|
||
case '\\':
|
||
/* Backslash quotes delimiters and itself, but not macro args. */
|
||
if (expected_delimiter != 0 && p < limit
|
||
&& (*p == expected_delimiter || *p == '\\')) {
|
||
*exp_p++ = *p++;
|
||
continue;
|
||
}
|
||
break;
|
||
|
||
case '/':
|
||
if (expected_delimiter != '\0') /* No comments inside strings. */
|
||
break;
|
||
if (*p == '*') {
|
||
/* If we find a comment that wasn't removed by handle_directive,
|
||
this must be -traditional. So replace the comment with
|
||
nothing at all. */
|
||
exp_p--;
|
||
p += 1;
|
||
while (p < limit && !(p[-2] == '*' && p[-1] == '/'))
|
||
p++;
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (is_idchar (c) && nargs > 0) {
|
||
U_CHAR *id_beg = p - 1;
|
||
int id_len;
|
||
|
||
--exp_p;
|
||
while (p != limit && is_idchar (*p)) p++;
|
||
id_len = p - id_beg;
|
||
|
||
if (is_idstart (c)) {
|
||
struct arglist *arg;
|
||
|
||
for (arg = arglist; arg != NULL; arg = arg->next) {
|
||
struct reflist *tpat;
|
||
|
||
if (arg->name[0] == c
|
||
&& arg->length == id_len
|
||
&& strncmp ((const char *)arg->name,
|
||
(const char *)id_beg, id_len) == 0) {
|
||
/* make a pat node for this arg and append it to the end of
|
||
the pat list */
|
||
tpat = (struct reflist *) xmalloc (sizeof (struct reflist));
|
||
tpat->next = NULL;
|
||
tpat->raw_before = concat == id_beg;
|
||
tpat->raw_after = 0;
|
||
tpat->stringify = expected_delimiter != '\0';
|
||
|
||
if (endpat == NULL)
|
||
defn->pattern = tpat;
|
||
else
|
||
endpat->next = tpat;
|
||
endpat = tpat;
|
||
|
||
tpat->argno = arg->argno;
|
||
tpat->nchars = exp_p - lastp;
|
||
{
|
||
U_CHAR *p1 = p;
|
||
SKIP_WHITE_SPACE (p1);
|
||
if (p1 + 2 <= limit && p1[0] == '#' && p1[1] == '#')
|
||
tpat->raw_after = 1;
|
||
}
|
||
lastp = exp_p; /* place to start copying from next time */
|
||
skipped_arg = 1;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* If this was not a macro arg, copy it into the expansion. */
|
||
if (! skipped_arg) {
|
||
U_CHAR *lim1 = p;
|
||
p = id_beg;
|
||
while (p != lim1)
|
||
*exp_p++ = *p++;
|
||
if (stringify == id_beg)
|
||
error ("# operator should be followed by a macro argument name");
|
||
}
|
||
}
|
||
}
|
||
|
||
if (limit < end) {
|
||
/* Convert trailing whitespace to Newline-markers. */
|
||
while (limit < end && is_space (*limit)) {
|
||
*exp_p++ = '\n';
|
||
*exp_p++ = *limit++;
|
||
}
|
||
}
|
||
*exp_p = '\0';
|
||
|
||
defn->length = exp_p - defn->expansion;
|
||
|
||
/* Crash now if we overrun the allocated size. */
|
||
if (defn->length + 1 > maxsize)
|
||
abort ();
|
||
|
||
return defn;
|
||
}
|
||
|
||
/*
|
||
* interpret #line command. Remembers previously seen fnames
|
||
* in its very own hash table.
|
||
*/
|
||
#define FNAME_HASHSIZE 37
|
||
static void
|
||
do_line (buf, limit, op)
|
||
U_CHAR *buf, *limit;
|
||
FILE_BUF *op;
|
||
{
|
||
U_CHAR *bp;
|
||
FILE_BUF *ip = &instack[indepth];
|
||
FILE_BUF tem;
|
||
int new_lineno;
|
||
enum file_change_code file_change = same_file;
|
||
|
||
/* Expand any macros. */
|
||
tem = expand_to_temp_buffer (buf, limit, 0);
|
||
|
||
/* Point to macroexpanded line, which is null-terminated now. */
|
||
bp = tem.buf;
|
||
SKIP_WHITE_SPACE (bp);
|
||
|
||
if (!ISDIGIT (*bp)) {
|
||
error ("invalid format #line command");
|
||
return;
|
||
}
|
||
|
||
/* The Newline at the end of this line remains to be processed.
|
||
To put the next line at the specified line number,
|
||
we must store a line number now that is one less. */
|
||
new_lineno = atoi ((const char *)bp);
|
||
|
||
/* skip over the line number. */
|
||
while (ISDIGIT (*bp))
|
||
bp++;
|
||
|
||
SKIP_WHITE_SPACE (bp);
|
||
|
||
if (*bp == '\"') {
|
||
static HASHNODE *fname_table[FNAME_HASHSIZE];
|
||
HASHNODE *hp, **hash_bucket;
|
||
U_CHAR *fname;
|
||
int fname_length;
|
||
|
||
fname = ++bp;
|
||
|
||
while (*bp && *bp != '\"')
|
||
bp++;
|
||
if (*bp != '\"') {
|
||
error ("invalid format #line command");
|
||
return;
|
||
}
|
||
|
||
fname_length = bp - fname;
|
||
|
||
bp++;
|
||
SKIP_WHITE_SPACE (bp);
|
||
if (*bp) {
|
||
if (*bp == '1')
|
||
file_change = enter_file;
|
||
else if (*bp == '2')
|
||
file_change = leave_file;
|
||
else {
|
||
error ("invalid format #line command");
|
||
return;
|
||
}
|
||
|
||
bp++;
|
||
SKIP_WHITE_SPACE (bp);
|
||
if (*bp) {
|
||
error ("invalid format #line command");
|
||
return;
|
||
}
|
||
}
|
||
|
||
hash_bucket =
|
||
&fname_table[hashf (fname, fname_length, FNAME_HASHSIZE)];
|
||
for (hp = *hash_bucket; hp != NULL; hp = hp->next)
|
||
if (hp->length == fname_length &&
|
||
strncmp (hp->value.cpval, (const char *)fname, fname_length) == 0) {
|
||
ip->fname = hp->value.cpval;
|
||
break;
|
||
}
|
||
if (hp == 0) {
|
||
char *q;
|
||
/* Didn't find it; cons up a new one. */
|
||
hp = (HASHNODE *) xcalloc (1, sizeof (HASHNODE) + fname_length + 1);
|
||
hp->next = *hash_bucket;
|
||
*hash_bucket = hp;
|
||
|
||
hp->length = fname_length;
|
||
ip->fname = hp->value.cpval = q = ((char *) hp) + sizeof (HASHNODE);
|
||
memcpy (q, fname, fname_length);
|
||
}
|
||
} else if (*bp) {
|
||
error ("invalid format #line command");
|
||
return;
|
||
}
|
||
|
||
ip->lineno = new_lineno;
|
||
output_line_command (ip, op, 0, file_change);
|
||
ip->bufp++; /* Skip the new line. */
|
||
check_expand (op, ip->length - (ip->bufp - ip->buf));
|
||
}
|
||
|
||
/*
|
||
* remove all definitions of symbol from symbol table.
|
||
* according to un*x /lib/cpp, it is not an error to undef
|
||
* something that has no definitions, so it isn't one here either.
|
||
*/
|
||
static void
|
||
do_undef (buf, limit, op)
|
||
U_CHAR *buf;
|
||
U_CHAR *limit ATTRIBUTE_UNUSED;
|
||
FILE_BUF *op ATTRIBUTE_UNUSED;
|
||
{
|
||
HASHNODE *hp;
|
||
|
||
SKIP_WHITE_SPACE (buf);
|
||
|
||
if (! strncmp ((const char *)buf, "defined", 7) && ! is_idchar (buf[7]))
|
||
warning ("undefining `defined'");
|
||
|
||
while ((hp = lookup (buf, -1, -1)) != NULL) {
|
||
if (hp->type != T_MACRO)
|
||
warning ("undefining `%s'", hp->name);
|
||
delete_macro (hp);
|
||
}
|
||
}
|
||
|
||
/* Read the tokens of the answer into the macro pool. Only commit the
|
||
memory if we intend it as permanent storage, i.e. the #assert case.
|
||
Returns 0 on success. */
|
||
|
||
static int
|
||
parse_answer (buf, limit, answerp, type)
|
||
const unsigned char *buf, *limit;
|
||
struct answer **answerp;
|
||
int type;
|
||
{
|
||
const unsigned char *start;
|
||
|
||
/* Skip leading whitespace. */
|
||
if (buf < limit && *buf == ' ')
|
||
buf++;
|
||
|
||
/* Parentheses are optional here. */
|
||
if (buf == limit && type == T_UNASSERT)
|
||
return 0;
|
||
|
||
if (buf == limit || *buf++ != '(')
|
||
{
|
||
if (type == T_IF)
|
||
return 0;
|
||
|
||
error ("missing '(' after predicate");
|
||
return 1;
|
||
}
|
||
|
||
/* Drop whitespace at start. */
|
||
while (buf < limit && *buf == ' ')
|
||
buf++;
|
||
|
||
start = buf;
|
||
while (buf < limit && *buf != ')')
|
||
buf++;
|
||
|
||
if (buf == limit)
|
||
{
|
||
error ("missing ')' to complete answer");
|
||
return 1;
|
||
}
|
||
|
||
if (buf == start)
|
||
{
|
||
error ("predicate's answer is empty");
|
||
return 1;
|
||
}
|
||
|
||
if ((type == T_ASSERT || type == T_UNASSERT) && buf + 1 != limit)
|
||
{
|
||
error ("extra text at end of directive");
|
||
return 1;
|
||
}
|
||
|
||
/* Lose trailing whitespace. */
|
||
if (buf[-1] == ' ')
|
||
buf--;
|
||
|
||
*answerp = (struct answer *) xmalloc (sizeof (struct answer));
|
||
(*answerp)->answer = start;
|
||
(*answerp)->len = buf - start;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Parses an assertion, returning a pointer to the hash node of the
|
||
predicate, or 0 on error. If an answer was supplied, it is placed
|
||
in ANSWERP, otherwise it is set to 0. */
|
||
static HASHNODE *
|
||
parse_assertion (buf, limit, answerp, type)
|
||
const unsigned char *buf, *limit;
|
||
struct answer **answerp;
|
||
int type;
|
||
{
|
||
HASHNODE *result = 0;
|
||
const unsigned char *climit;
|
||
unsigned char *bp, *symname = canonicalize_text (buf, limit, &climit);
|
||
unsigned int len;
|
||
|
||
bp = symname;
|
||
if (bp < climit && is_idstart (*bp))
|
||
{
|
||
do
|
||
bp++;
|
||
while (bp < climit && is_idchar (*bp));
|
||
}
|
||
len = bp - symname;
|
||
|
||
*answerp = 0;
|
||
if (len == 0)
|
||
{
|
||
if (symname == climit)
|
||
error ("assertion without predicate");
|
||
else
|
||
error ("predicate must be an identifier");
|
||
}
|
||
/* Unfortunately, because of the way we handle #if, we don't avoid
|
||
macro expansion in answers. This is not easy to fix. */
|
||
else if (parse_answer (bp, climit, answerp, type) == 0)
|
||
{
|
||
unsigned char *sym = alloca (len + 1);
|
||
int hashcode;
|
||
|
||
/* Prefix '#' to get it out of macro namespace. */
|
||
sym[0] = '#';
|
||
memcpy (sym + 1, symname, len);
|
||
|
||
hashcode = hashf (sym, len + 1, HASHSIZE);
|
||
result = lookup (sym, len + 1, hashcode);
|
||
if (result == 0)
|
||
result = install (sym, len + 1, T_UNUSED, hashcode);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/* Test an assertion within a preprocessor conditional. Returns zero
|
||
on error or failure, one on success. */
|
||
int
|
||
test_assertion (pbuf)
|
||
unsigned char **pbuf; /* NUL-terminated. */
|
||
{
|
||
unsigned char *buf = *pbuf;
|
||
unsigned char *limit = buf + strlen ((char *) buf);
|
||
struct answer *answer;
|
||
HASHNODE *node;
|
||
int result = 0;
|
||
|
||
node = parse_assertion (buf, limit, &answer, T_IF);
|
||
if (node)
|
||
{
|
||
result = (node->type == T_ASSERT &&
|
||
(answer == 0 || *find_answer (node, answer) != 0));
|
||
|
||
/* Yuk. We update pbuf to point after the assertion test.
|
||
First, move past the identifier. */
|
||
if (is_space (*buf))
|
||
buf++;
|
||
while (is_idchar (*buf))
|
||
buf++;
|
||
/* If we have an answer, we need to move past the parentheses. */
|
||
if (answer)
|
||
while (*buf++ != ')')
|
||
;
|
||
*pbuf = buf;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/* Handle a #error directive. */
|
||
static void
|
||
do_error (buf, limit, op)
|
||
U_CHAR *buf;
|
||
U_CHAR *limit;
|
||
FILE_BUF *op ATTRIBUTE_UNUSED;
|
||
{
|
||
error ("#error%.*s", (int) (limit - buf), buf);
|
||
}
|
||
|
||
/* Handle a #warning directive. */
|
||
static void
|
||
do_warning (buf, limit, op)
|
||
U_CHAR *buf;
|
||
U_CHAR *limit;
|
||
FILE_BUF *op ATTRIBUTE_UNUSED;
|
||
{
|
||
warning ("#warning%.*s", (int) (limit - buf), buf);
|
||
}
|
||
|
||
/* Handle a #assert directive. */
|
||
static void
|
||
do_assert (buf, limit, op)
|
||
U_CHAR *buf;
|
||
U_CHAR *limit;
|
||
FILE_BUF *op ATTRIBUTE_UNUSED;
|
||
{
|
||
struct answer *new_answer;
|
||
HASHNODE *node;
|
||
|
||
node = parse_assertion (buf, limit, &new_answer, T_ASSERT);
|
||
if (node)
|
||
{
|
||
/* Place the new answer in the answer list. First check there
|
||
is not a duplicate. */
|
||
new_answer->next = 0;
|
||
if (node->type == T_ASSERT)
|
||
{
|
||
if (*find_answer (node, new_answer))
|
||
{
|
||
free (new_answer);
|
||
warning ("\"%s\" re-asserted", node->name + 1);
|
||
return;
|
||
}
|
||
new_answer->next = node->value.answers;
|
||
}
|
||
node->type = T_ASSERT;
|
||
node->value.answers = new_answer;
|
||
}
|
||
}
|
||
|
||
/* Function body to be provided later. */
|
||
static void
|
||
do_unassert (buf, limit, op)
|
||
U_CHAR *buf;
|
||
U_CHAR *limit;
|
||
FILE_BUF *op ATTRIBUTE_UNUSED;
|
||
{
|
||
HASHNODE *node;
|
||
struct answer *answer;
|
||
|
||
node = parse_assertion (buf, limit, &answer, T_UNASSERT);
|
||
/* It isn't an error to #unassert something that isn't asserted. */
|
||
if (node)
|
||
{
|
||
if (node->type == T_ASSERT)
|
||
{
|
||
if (answer)
|
||
{
|
||
struct answer **p = find_answer (node, answer), *temp;
|
||
|
||
/* Remove the answer from the list. */
|
||
temp = *p;
|
||
if (temp)
|
||
*p = temp->next;
|
||
|
||
/* Did we free the last answer? */
|
||
if (node->value.answers == 0)
|
||
delete_macro (node);
|
||
}
|
||
else
|
||
delete_macro (node);
|
||
}
|
||
|
||
free (answer);
|
||
}
|
||
}
|
||
|
||
/* Returns a pointer to the pointer to the answer in the answer chain,
|
||
or a pointer to NULL if the answer is not in the chain. */
|
||
static struct answer **
|
||
find_answer (node, candidate)
|
||
HASHNODE *node;
|
||
const struct answer *candidate;
|
||
{
|
||
struct answer **result;
|
||
|
||
for (result = &node->value.answers; *result; result = &(*result)->next)
|
||
{
|
||
struct answer *answer = *result;
|
||
|
||
if (answer->len == candidate->len
|
||
&& !memcmp (answer->answer, candidate->answer, answer->len))
|
||
break;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/* Return a malloced buffer with leading and trailing whitespace
|
||
removed, and all instances of internal whitespace reduced to a
|
||
single space. */
|
||
static unsigned char *
|
||
canonicalize_text (buf, limit, climit)
|
||
const unsigned char *buf, *limit, **climit;
|
||
{
|
||
unsigned int len = limit - buf;
|
||
unsigned char *result = (unsigned char *) xmalloc (len), *dest;
|
||
|
||
for (dest = result; buf < limit;)
|
||
{
|
||
if (! is_space (*buf))
|
||
*dest++ = *buf++;
|
||
else
|
||
{
|
||
while (++buf < limit && is_space (*buf))
|
||
;
|
||
if (dest != result && buf != limit)
|
||
*dest++ = ' ';
|
||
}
|
||
}
|
||
|
||
*climit = dest;
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
* handle #if command by
|
||
* 1) inserting special `defined' keyword into the hash table
|
||
* that gets turned into 0 or 1 by special_symbol (thus,
|
||
* if the luser has a symbol called `defined' already, it won't
|
||
* work inside the #if command)
|
||
* 2) rescan the input into a temporary output buffer
|
||
* 3) pass the output buffer to the yacc parser and collect a value
|
||
* 4) clean up the mess left from steps 1 and 2.
|
||
* 5) call conditional_skip to skip til the next #endif (etc.),
|
||
* or not, depending on the value from step 3.
|
||
*/
|
||
static void
|
||
do_if (buf, limit, op)
|
||
U_CHAR *buf, *limit;
|
||
FILE_BUF *op ATTRIBUTE_UNUSED;
|
||
{
|
||
int value;
|
||
FILE_BUF *ip = &instack[indepth];
|
||
|
||
value = eval_if_expression (buf, limit - buf);
|
||
conditional_skip (ip, value == 0, T_IF);
|
||
}
|
||
|
||
/*
|
||
* handle a #elif directive by not changing if_stack either.
|
||
* see the comment above do_else.
|
||
*/
|
||
static void
|
||
do_elif (buf, limit, op)
|
||
U_CHAR *buf, *limit;
|
||
FILE_BUF *op;
|
||
{
|
||
int value;
|
||
FILE_BUF *ip = &instack[indepth];
|
||
|
||
if (if_stack == instack[indepth].if_stack) {
|
||
error ("#elif not within a conditional");
|
||
return;
|
||
} else {
|
||
if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
|
||
error ("#elif after #else");
|
||
fprintf (stderr, " (matches line %d", if_stack->lineno);
|
||
if (if_stack->fname != NULL && ip->fname != NULL &&
|
||
strcmp (if_stack->fname, ip->fname) != 0)
|
||
fprintf (stderr, ", file %s", if_stack->fname);
|
||
fprintf (stderr, ")\n");
|
||
}
|
||
if_stack->type = T_ELIF;
|
||
}
|
||
|
||
if (if_stack->if_succeeded)
|
||
skip_if_group (ip, 0);
|
||
else {
|
||
value = eval_if_expression (buf, limit - buf);
|
||
if (value == 0)
|
||
skip_if_group (ip, 0);
|
||
else {
|
||
++if_stack->if_succeeded; /* continue processing input */
|
||
output_line_command (ip, op, 1, same_file);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* evaluate a #if expression in BUF, of length LENGTH,
|
||
* then parse the result as a C expression and return the value as an int.
|
||
*/
|
||
static int
|
||
eval_if_expression (buf, length)
|
||
const U_CHAR *buf;
|
||
int length;
|
||
{
|
||
FILE_BUF temp_obuf;
|
||
HASHNODE *save_defined;
|
||
int value;
|
||
|
||
save_defined = install (U"defined", -1, T_SPEC_DEFINED, -1);
|
||
temp_obuf = expand_to_temp_buffer (buf, buf + length, 0);
|
||
delete_macro (save_defined); /* clean up special symbol */
|
||
|
||
value = parse_c_expression ((const char *)temp_obuf.buf);
|
||
|
||
free (temp_obuf.buf);
|
||
|
||
return value;
|
||
}
|
||
|
||
/*
|
||
* routine to handle ifdef/ifndef. Try to look up the symbol,
|
||
* then do or don't skip to the #endif/#else/#elif depending
|
||
* on what directive is actually being processed.
|
||
*/
|
||
static void
|
||
do_xifdef (buf, limit, type)
|
||
U_CHAR *buf, *limit;
|
||
enum node_type type;
|
||
{
|
||
int skip;
|
||
FILE_BUF *ip = &instack[indepth];
|
||
U_CHAR *end;
|
||
|
||
/* Discard leading and trailing whitespace. */
|
||
SKIP_WHITE_SPACE (buf);
|
||
while (limit != buf && is_nvspace (limit[-1])) limit--;
|
||
|
||
/* Find the end of the identifier at the beginning. */
|
||
for (end = buf; is_idchar (*end); end++);
|
||
|
||
if (end == buf)
|
||
skip = (type == T_IFDEF);
|
||
else
|
||
skip = (lookup (buf, end-buf, -1) == NULL) ^ (type == T_IFNDEF);
|
||
|
||
conditional_skip (ip, skip, T_IF);
|
||
}
|
||
|
||
static void
|
||
do_ifdef (buf, limit, op)
|
||
U_CHAR *buf, *limit;
|
||
FILE_BUF *op ATTRIBUTE_UNUSED;
|
||
{
|
||
do_xifdef (buf, limit, T_IFDEF);
|
||
}
|
||
|
||
static void
|
||
do_ifndef (buf, limit, op)
|
||
U_CHAR *buf, *limit;
|
||
FILE_BUF *op ATTRIBUTE_UNUSED;
|
||
{
|
||
do_xifdef (buf, limit, T_IFNDEF);
|
||
}
|
||
|
||
/*
|
||
* push TYPE on stack; then, if SKIP is nonzero, skip ahead.
|
||
*/
|
||
static void
|
||
conditional_skip (ip, skip, type)
|
||
FILE_BUF *ip;
|
||
int skip;
|
||
enum node_type type;
|
||
{
|
||
IF_STACK_FRAME *temp;
|
||
|
||
temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
|
||
temp->fname = ip->fname;
|
||
temp->lineno = ip->lineno;
|
||
temp->next = if_stack;
|
||
if_stack = temp;
|
||
|
||
if_stack->type = type;
|
||
|
||
if (skip != 0) {
|
||
skip_if_group (ip, 0);
|
||
return;
|
||
} else {
|
||
++if_stack->if_succeeded;
|
||
output_line_command (ip, &outbuf, 1, same_file);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* skip to #endif, #else, or #elif. adjust line numbers, etc.
|
||
* leaves input ptr at the sharp sign found.
|
||
* If ANY is nonzero, return at next directive of any sort.
|
||
*/
|
||
static void
|
||
skip_if_group (ip, any)
|
||
FILE_BUF *ip;
|
||
int any;
|
||
{
|
||
U_CHAR *bp = ip->bufp, *cp;
|
||
U_CHAR *endb = ip->buf + ip->length;
|
||
const struct directive *kt;
|
||
IF_STACK_FRAME *save_if_stack = if_stack; /* don't pop past here */
|
||
U_CHAR *beg_of_line = bp;
|
||
|
||
while (bp < endb) {
|
||
switch (*bp++) {
|
||
case '/': /* possible comment */
|
||
if (*bp == '\\' && bp[1] == '\n')
|
||
newline_fix (bp);
|
||
if (*bp == '*') {
|
||
ip->bufp = ++bp;
|
||
bp = skip_to_end_of_comment (ip, &ip->lineno);
|
||
}
|
||
break;
|
||
case '\"':
|
||
case '\'':
|
||
bp = skip_quoted_string (bp - 1, endb, ip->lineno, &ip->lineno, 0, 0);
|
||
break;
|
||
case '\\':
|
||
/* Char after backslash loses its special meaning. */
|
||
if (bp < endb) {
|
||
if (*bp == '\n')
|
||
++ip->lineno; /* But do update the line-count. */
|
||
bp++;
|
||
}
|
||
break;
|
||
case '\n':
|
||
++ip->lineno;
|
||
beg_of_line = bp;
|
||
break;
|
||
case '#':
|
||
ip->bufp = bp - 1;
|
||
|
||
/* # keyword: a # must be first nonblank char on the line */
|
||
if (beg_of_line == 0)
|
||
break;
|
||
/* Scan from start of line, skipping whitespace, comments
|
||
and backslash-newlines, and see if we reach this #.
|
||
If not, this # is not special. */
|
||
bp = beg_of_line;
|
||
while (1) {
|
||
if (is_nvspace (*bp))
|
||
bp++;
|
||
else if (*bp == '\\' && bp[1] == '\n')
|
||
bp += 2;
|
||
else if (*bp == '/' && bp[1] == '*') {
|
||
bp += 2;
|
||
while (!(*bp == '*' && bp[1] == '/')) {
|
||
if (*bp == '\n')
|
||
ip->lineno++;
|
||
bp++;
|
||
}
|
||
bp += 2;
|
||
}
|
||
else break;
|
||
}
|
||
if (bp != ip->bufp) {
|
||
bp = ip->bufp + 1; /* Reset bp to after the #. */
|
||
break;
|
||
}
|
||
|
||
bp = ip->bufp + 1; /* Point after '#'. */
|
||
|
||
/* Skip whitespace and \-newline. */
|
||
while (1) {
|
||
if (is_nvspace (*bp))
|
||
bp++;
|
||
else if (*bp == '\\' && bp[1] == '\n')
|
||
bp += 2;
|
||
else if (*bp == '/' && bp[1] == '*') {
|
||
bp += 2;
|
||
while (!(*bp == '*' && bp[1] == '/'))
|
||
bp++;
|
||
bp += 2;
|
||
}
|
||
else break;
|
||
}
|
||
|
||
cp = bp;
|
||
|
||
/* Now find end of directive name.
|
||
If we encounter a backslash-newline, exchange it with any following
|
||
symbol-constituents so that we end up with a contiguous name. */
|
||
|
||
while (1) {
|
||
if (is_idchar (*bp))
|
||
bp++;
|
||
else {
|
||
if (*bp == '\\' && bp[1] == '\n')
|
||
name_newline_fix (bp);
|
||
if (is_idchar (*bp))
|
||
bp++;
|
||
else break;
|
||
}
|
||
}
|
||
|
||
for (kt = directive_table; kt->length >= 0; kt++) {
|
||
IF_STACK_FRAME *temp;
|
||
if (strncmp ((const char *)cp, kt->name, kt->length) == 0
|
||
&& !is_idchar (cp[kt->length])) {
|
||
|
||
/* If we are asked to return on next directive,
|
||
do so now. */
|
||
if (any)
|
||
return;
|
||
|
||
switch (kt->type) {
|
||
case T_IF:
|
||
case T_IFDEF:
|
||
case T_IFNDEF:
|
||
temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
|
||
temp->next = if_stack;
|
||
if_stack = temp;
|
||
temp->lineno = ip->lineno;
|
||
temp->fname = ip->fname;
|
||
temp->type = kt->type;
|
||
break;
|
||
case T_ELSE:
|
||
case T_ENDIF:
|
||
case T_ELIF:
|
||
if (if_stack == instack[indepth].if_stack) {
|
||
error ("#%s not within a conditional", kt->name);
|
||
break;
|
||
}
|
||
else if (if_stack == save_if_stack)
|
||
return; /* found what we came for */
|
||
|
||
if (kt->type != T_ENDIF) {
|
||
if (if_stack->type == T_ELSE)
|
||
error ("#else or #elif after #else");
|
||
if_stack->type = kt->type;
|
||
break;
|
||
}
|
||
|
||
temp = if_stack;
|
||
if_stack = if_stack->next;
|
||
free (temp);
|
||
break;
|
||
|
||
default:
|
||
/* Anything else is ignored. */
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
ip->bufp = bp;
|
||
/* after this returns, rescan will exit because ip->bufp
|
||
now points to the end of the buffer.
|
||
rescan is responsible for the error message also. */
|
||
}
|
||
|
||
/*
|
||
* handle a #else directive. Do this by just continuing processing
|
||
* without changing if_stack ; this is so that the error message
|
||
* for missing #endif's etc. will point to the original #if. It
|
||
* is possible that something different would be better.
|
||
*/
|
||
static void
|
||
do_else (buf, limit, op)
|
||
U_CHAR *buf ATTRIBUTE_UNUSED;
|
||
U_CHAR *limit ATTRIBUTE_UNUSED;
|
||
FILE_BUF *op;
|
||
{
|
||
FILE_BUF *ip = &instack[indepth];
|
||
|
||
if (if_stack == instack[indepth].if_stack) {
|
||
error ("#else not within a conditional");
|
||
return;
|
||
} else {
|
||
if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
|
||
error ("#else after #else");
|
||
fprintf (stderr, " (matches line %d", if_stack->lineno);
|
||
if (strcmp (if_stack->fname, ip->fname) != 0)
|
||
fprintf (stderr, ", file %s", if_stack->fname);
|
||
fprintf (stderr, ")\n");
|
||
}
|
||
if_stack->type = T_ELSE;
|
||
}
|
||
|
||
if (if_stack->if_succeeded)
|
||
skip_if_group (ip, 0);
|
||
else {
|
||
++if_stack->if_succeeded; /* continue processing input */
|
||
output_line_command (ip, op, 1, same_file);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* unstack after #endif command
|
||
*/
|
||
static void
|
||
do_endif (buf, limit, op)
|
||
U_CHAR *buf ATTRIBUTE_UNUSED;
|
||
U_CHAR *limit ATTRIBUTE_UNUSED;
|
||
FILE_BUF *op;
|
||
{
|
||
if (if_stack == instack[indepth].if_stack)
|
||
error ("unbalanced #endif");
|
||
else {
|
||
IF_STACK_FRAME *temp = if_stack;
|
||
if_stack = if_stack->next;
|
||
free (temp);
|
||
output_line_command (&instack[indepth], op, 1, same_file);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Skip a comment, assuming the input ptr immediately follows the
|
||
* initial slash-star. Bump line counter as necessary.
|
||
* (The canonical line counter is &ip->lineno).
|
||
* Don't use this routine (or the next one) if bumping the line
|
||
* counter is not sufficient to deal with newlines in the string.
|
||
*/
|
||
static U_CHAR *
|
||
skip_to_end_of_comment (ip, line_counter)
|
||
FILE_BUF *ip;
|
||
int *line_counter; /* place to remember newlines, or NULL */
|
||
{
|
||
U_CHAR *limit = ip->buf + ip->length;
|
||
U_CHAR *bp = ip->bufp;
|
||
FILE_BUF *op = &outbuf; /* JF */
|
||
int output = put_out_comments && !line_counter;
|
||
|
||
/* JF this line_counter stuff is a crock to make sure the
|
||
comment is only put out once, no matter how many times
|
||
the comment is skipped. It almost works */
|
||
if (output) {
|
||
*op->bufp++ = '/';
|
||
*op->bufp++ = '*';
|
||
}
|
||
while (bp < limit) {
|
||
if (output)
|
||
*op->bufp++ = *bp;
|
||
switch (*bp++) {
|
||
case '/':
|
||
if (warn_comments && bp < limit && *bp == '*')
|
||
warning("`/*' within comment");
|
||
break;
|
||
case '\n':
|
||
if (line_counter != NULL)
|
||
++*line_counter;
|
||
if (output)
|
||
++op->lineno;
|
||
break;
|
||
case '*':
|
||
if (*bp == '\\' && bp[1] == '\n')
|
||
newline_fix (bp);
|
||
if (*bp == '/') {
|
||
if (output)
|
||
*op->bufp++ = '/';
|
||
ip->bufp = ++bp;
|
||
return bp;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
ip->bufp = bp;
|
||
return bp;
|
||
}
|
||
|
||
/*
|
||
* Skip over a quoted string. BP points to the opening quote.
|
||
* Returns a pointer after the closing quote. Don't go past LIMIT.
|
||
* START_LINE is the line number of the starting point (but it need
|
||
* not be valid if the starting point is inside a macro expansion).
|
||
*
|
||
* The input stack state is not changed.
|
||
*
|
||
* If COUNT_NEWLINES is nonzero, it points to an int to increment
|
||
* for each newline passed.
|
||
*
|
||
* If BACKSLASH_NEWLINES_P is nonzero, store 1 thru it
|
||
* if we pass a backslash-newline.
|
||
*
|
||
* If EOFP is nonzero, set *EOFP to 1 if the string is unterminated.
|
||
*/
|
||
static U_CHAR *
|
||
skip_quoted_string (bp, limit, start_line, count_newlines, backslash_newlines_p, eofp)
|
||
const U_CHAR *bp;
|
||
const U_CHAR *limit;
|
||
int start_line;
|
||
int *count_newlines;
|
||
int *backslash_newlines_p;
|
||
int *eofp;
|
||
{
|
||
U_CHAR c, match;
|
||
|
||
match = *bp++;
|
||
while (1) {
|
||
if (bp >= limit) {
|
||
error_with_line (line_for_error (start_line),
|
||
"unterminated string or character constant");
|
||
if (eofp)
|
||
*eofp = 1;
|
||
break;
|
||
}
|
||
c = *bp++;
|
||
if (c == '\\') {
|
||
while (*bp == '\\' && bp[1] == '\n') {
|
||
if (backslash_newlines_p)
|
||
*backslash_newlines_p = 1;
|
||
if (count_newlines)
|
||
++*count_newlines;
|
||
bp += 2;
|
||
}
|
||
if (*bp == '\n' && count_newlines) {
|
||
if (backslash_newlines_p)
|
||
*backslash_newlines_p = 1;
|
||
++*count_newlines;
|
||
}
|
||
bp++;
|
||
} else if (c == '\n') {
|
||
/* Unterminated strings and character constants are 'legal'. */
|
||
bp--; /* Don't consume the newline. */
|
||
if (eofp)
|
||
*eofp = 1;
|
||
break;
|
||
} else if (c == match)
|
||
break;
|
||
}
|
||
return (U_CHAR *) bp;
|
||
}
|
||
|
||
/*
|
||
* write out a #line command, for instance, after an #include file.
|
||
* If CONDITIONAL is nonzero, we can omit the #line if it would
|
||
* appear to be a no-op, and we can output a few newlines instead
|
||
* if we want to increase the line number by a small amount.
|
||
* FILE_CHANGE says whether we are entering a file, leaving, or neither.
|
||
*/
|
||
|
||
static void
|
||
output_line_command (ip, op, conditional, file_change)
|
||
FILE_BUF *ip, *op;
|
||
int conditional;
|
||
enum file_change_code file_change;
|
||
{
|
||
int len;
|
||
char line_cmd_buf[500];
|
||
|
||
if (no_line_commands
|
||
|| ip->fname == NULL
|
||
|| no_output) {
|
||
op->lineno = ip->lineno;
|
||
return;
|
||
}
|
||
|
||
if (conditional) {
|
||
if (ip->lineno == op->lineno)
|
||
return;
|
||
|
||
/* If the inherited line number is a little too small,
|
||
output some newlines instead of a #line command. */
|
||
if (ip->lineno > op->lineno && ip->lineno < op->lineno + 8) {
|
||
check_expand (op, 10);
|
||
while (ip->lineno > op->lineno) {
|
||
*op->bufp++ = '\n';
|
||
op->lineno++;
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
sprintf (line_cmd_buf, "# %d \"%s\"", ip->lineno, ip->fname);
|
||
if (file_change != same_file)
|
||
strcat (line_cmd_buf, file_change == enter_file ? " 1" : " 2");
|
||
if (system_include_depth > 0)
|
||
strcat (line_cmd_buf, " 3");
|
||
len = strlen (line_cmd_buf);
|
||
line_cmd_buf[len++] = '\n';
|
||
check_expand (op, len + 1);
|
||
if (op->bufp > op->buf && op->bufp[-1] != '\n')
|
||
*op->bufp++ = '\n';
|
||
memcpy (op->bufp, line_cmd_buf, len);
|
||
op->bufp += len;
|
||
op->lineno = ip->lineno;
|
||
}
|
||
|
||
|
||
/* Expand a macro call.
|
||
HP points to the symbol that is the macro being called.
|
||
Put the result of expansion onto the input stack
|
||
so that subsequent input by our caller will use it.
|
||
|
||
If macro wants arguments, caller has already verified that
|
||
an argument list follows; arguments come from the input stack. */
|
||
|
||
static void
|
||
macroexpand (hp, op)
|
||
HASHNODE *hp;
|
||
FILE_BUF *op;
|
||
{
|
||
int nargs;
|
||
DEFINITION *defn = hp->value.defn;
|
||
U_CHAR *xbuf;
|
||
int xbuf_len;
|
||
int start_line = instack[indepth].lineno;
|
||
|
||
CHECK_DEPTH (return;);
|
||
|
||
/* it might not actually be a macro. */
|
||
if (hp->type != T_MACRO) {
|
||
special_symbol (hp, op);
|
||
return;
|
||
}
|
||
|
||
nargs = defn->nargs;
|
||
|
||
if (nargs >= 0) {
|
||
int i;
|
||
struct argdata *args;
|
||
const char *parse_error = 0;
|
||
|
||
args = (struct argdata *) alloca ((nargs + 1) * sizeof (struct argdata));
|
||
|
||
for (i = 0; i < nargs; i++) {
|
||
args[i].raw = args[i].expanded = (U_CHAR *) "";
|
||
args[i].raw_length = args[i].expand_length
|
||
= args[i].stringified_length = 0;
|
||
args[i].free1 = args[i].free2 = 0;
|
||
}
|
||
|
||
/* Parse all the macro args that are supplied. I counts them.
|
||
The first NARGS args are stored in ARGS.
|
||
The rest are discarded. */
|
||
i = 0;
|
||
do {
|
||
/* Discard the open-parenthesis or comma before the next arg. */
|
||
++instack[indepth].bufp;
|
||
parse_error
|
||
= macarg ((i < nargs || (nargs == 0 && i == 0)) ? &args[i] : 0);
|
||
if (parse_error)
|
||
{
|
||
error_with_line (line_for_error (start_line), "%s", parse_error);
|
||
break;
|
||
}
|
||
i++;
|
||
} while (*instack[indepth].bufp != ')');
|
||
|
||
/* If we got one arg but it was just whitespace, call that 0 args. */
|
||
if (i == 1) {
|
||
const U_CHAR *bp = args[0].raw;
|
||
const U_CHAR *lim = bp + args[0].raw_length;
|
||
while (bp != lim && is_space (*bp)) bp++;
|
||
if (bp == lim)
|
||
i = 0;
|
||
}
|
||
|
||
if (nargs == 0 && i > 0)
|
||
error ("arguments given to macro `%s'", hp->name);
|
||
else if (i < nargs) {
|
||
/* traditional C allows foo() if foo wants one argument. */
|
||
if (nargs == 1 && i == 0)
|
||
;
|
||
else if (i == 0)
|
||
error ("no args to macro `%s'", hp->name);
|
||
else if (i == 1)
|
||
error ("only 1 arg to macro `%s'", hp->name);
|
||
else
|
||
error ("only %d args to macro `%s'", i, hp->name);
|
||
} else if (i > nargs)
|
||
error ("too many (%d) args to macro `%s'", i, hp->name);
|
||
|
||
/* Swallow the closeparen. */
|
||
++instack[indepth].bufp;
|
||
|
||
/* If macro wants zero args, we parsed the arglist for checking only.
|
||
Read directly from the macro definition. */
|
||
if (nargs == 0) {
|
||
xbuf = defn->expansion;
|
||
xbuf_len = defn->length;
|
||
} else {
|
||
U_CHAR *exp = defn->expansion;
|
||
int offset; /* offset in expansion,
|
||
copied a piece at a time */
|
||
int totlen; /* total amount of exp buffer filled so far */
|
||
|
||
struct reflist *ap;
|
||
|
||
/* Macro really takes args. Compute the expansion of this call. */
|
||
|
||
/* Compute length in characters of the macro's expansion. */
|
||
xbuf_len = defn->length;
|
||
for (ap = defn->pattern; ap != NULL; ap = ap->next) {
|
||
if (ap->stringify)
|
||
xbuf_len += args[ap->argno].stringified_length;
|
||
else
|
||
xbuf_len += args[ap->argno].raw_length;
|
||
}
|
||
|
||
xbuf = (U_CHAR *) xmalloc (xbuf_len + 1);
|
||
|
||
/* Generate in XBUF the complete expansion
|
||
with arguments substituted in.
|
||
TOTLEN is the total size generated so far.
|
||
OFFSET is the index in the definition
|
||
of where we are copying from. */
|
||
offset = totlen = 0;
|
||
for (ap = defn->pattern; ap != NULL; ap = ap->next) {
|
||
struct argdata *arg = &args[ap->argno];
|
||
|
||
for (i = 0; i < ap->nchars; i++)
|
||
xbuf[totlen++] = exp[offset++];
|
||
|
||
if (ap->stringify != 0) {
|
||
int arglen = arg->raw_length;
|
||
int escaped = 0;
|
||
int in_string = 0;
|
||
int c;
|
||
i = 0;
|
||
while (i < arglen
|
||
&& (c = arg->raw[i], is_space (c)))
|
||
i++;
|
||
while (i < arglen
|
||
&& (c = arg->raw[arglen - 1], is_space (c)))
|
||
arglen--;
|
||
for (; i < arglen; i++) {
|
||
c = arg->raw[i];
|
||
|
||
/* Special markers Newline Space
|
||
generate nothing for a stringified argument. */
|
||
if (c == '\n' && arg->raw[i+1] != '\n') {
|
||
i++;
|
||
continue;
|
||
}
|
||
|
||
/* Internal sequences of whitespace are replaced by one space
|
||
except within an string or char token. */
|
||
if (! in_string
|
||
&& (c == '\n' ? arg->raw[i+1] == '\n' : is_space (c))) {
|
||
while (1) {
|
||
/* Note that Newline Space does occur within whitespace
|
||
sequences; consider it part of the sequence. */
|
||
if (c == '\n' && is_space (arg->raw[i+1]))
|
||
i += 2;
|
||
else if (c != '\n' && is_space (c))
|
||
i++;
|
||
else break;
|
||
c = arg->raw[i];
|
||
}
|
||
i--;
|
||
c = ' ';
|
||
}
|
||
|
||
if (escaped)
|
||
escaped = 0;
|
||
else {
|
||
if (c == '\\')
|
||
escaped = 1;
|
||
if (in_string) {
|
||
if (c == in_string)
|
||
in_string = 0;
|
||
} else if (c == '\"' || c == '\'')
|
||
in_string = c;
|
||
}
|
||
|
||
/* Escape these chars */
|
||
if (c == '\"' || (in_string && c == '\\'))
|
||
xbuf[totlen++] = '\\';
|
||
if (ISPRINT (c))
|
||
xbuf[totlen++] = c;
|
||
else {
|
||
sprintf ((char *) &xbuf[totlen], "\\%03o", (unsigned int) c);
|
||
totlen += 4;
|
||
}
|
||
}
|
||
} else {
|
||
const U_CHAR *p1 = arg->raw;
|
||
const U_CHAR *l1 = p1 + arg->raw_length;
|
||
|
||
if (ap->raw_before) {
|
||
while (p1 != l1 && is_space (*p1)) p1++;
|
||
while (p1 != l1 && is_idchar (*p1))
|
||
xbuf[totlen++] = *p1++;
|
||
/* Delete any no-reexpansion marker that follows
|
||
an identifier at the beginning of the argument
|
||
if the argument is concatenated with what precedes it. */
|
||
if (p1[0] == '\n' && p1[1] == '-')
|
||
p1 += 2;
|
||
}
|
||
if (ap->raw_after) {
|
||
/* Arg is concatenated after: delete trailing whitespace,
|
||
whitespace markers, and no-reexpansion markers. */
|
||
while (p1 != l1) {
|
||
if (is_space (l1[-1])) l1--;
|
||
else if (l1[-1] == '-') {
|
||
const U_CHAR *p2 = l1 - 1;
|
||
/* If a `-' is preceded by an odd number of newlines then it
|
||
and the last newline are a no-reexpansion marker. */
|
||
while (p2 != p1 && p2[-1] == '\n') p2--;
|
||
if ((l1 - 1 - p2) & 1) {
|
||
l1 -= 2;
|
||
}
|
||
else break;
|
||
}
|
||
else break;
|
||
}
|
||
}
|
||
memmove (xbuf + totlen, p1, l1 - p1);
|
||
totlen += l1 - p1;
|
||
}
|
||
|
||
if (totlen > xbuf_len)
|
||
abort ();
|
||
}
|
||
|
||
/* if there is anything left of the definition
|
||
after handling the arg list, copy that in too. */
|
||
|
||
for (i = offset; i < defn->length; i++)
|
||
xbuf[totlen++] = exp[i];
|
||
|
||
xbuf[totlen] = 0;
|
||
xbuf_len = totlen;
|
||
|
||
for (i = 0; i < nargs; i++) {
|
||
if (args[i].free1 != 0)
|
||
free (args[i].free1);
|
||
if (args[i].free2 != 0)
|
||
free (args[i].free2);
|
||
}
|
||
}
|
||
} else {
|
||
xbuf = defn->expansion;
|
||
xbuf_len = defn->length;
|
||
}
|
||
|
||
/* Now put the expansion on the input stack
|
||
so our caller will commence reading from it. */
|
||
{
|
||
FILE_BUF *ip2;
|
||
|
||
ip2 = &instack[++indepth];
|
||
|
||
ip2->fname = 0;
|
||
ip2->lineno = 0;
|
||
ip2->buf = xbuf;
|
||
ip2->length = xbuf_len;
|
||
ip2->bufp = xbuf;
|
||
ip2->free_ptr = (nargs > 0) ? xbuf : 0;
|
||
ip2->macro = hp;
|
||
ip2->if_stack = if_stack;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Parse a macro argument and store the info on it into *ARGPTR.
|
||
* Return nonzero to indicate a syntax error.
|
||
*/
|
||
|
||
static const char *
|
||
macarg (argptr)
|
||
struct argdata *argptr;
|
||
{
|
||
FILE_BUF *ip = &instack[indepth];
|
||
int paren = 0;
|
||
int newlines = 0;
|
||
int comments = 0;
|
||
|
||
/* Try to parse as much of the argument as exists at this
|
||
input stack level. */
|
||
U_CHAR *bp = macarg1 (ip->bufp, ip->buf + ip->length,
|
||
&paren, &newlines, &comments);
|
||
|
||
/* If we find the end of the argument at this level,
|
||
set up *ARGPTR to point at it in the input stack. */
|
||
if (!(ip->fname != 0 && (newlines != 0 || comments != 0))
|
||
&& bp != ip->buf + ip->length) {
|
||
if (argptr != 0) {
|
||
argptr->raw = ip->bufp;
|
||
argptr->raw_length = bp - ip->bufp;
|
||
}
|
||
ip->bufp = bp;
|
||
} else {
|
||
/* This input stack level ends before the macro argument does.
|
||
We must pop levels and keep parsing.
|
||
Therefore, we must allocate a temporary buffer and copy
|
||
the macro argument into it. */
|
||
int bufsize = bp - ip->bufp;
|
||
int extra = newlines;
|
||
U_CHAR *buffer = (U_CHAR *) xmalloc (bufsize + extra + 1);
|
||
int final_start = 0;
|
||
|
||
memcpy (buffer, ip->bufp, bufsize);
|
||
ip->bufp = bp;
|
||
ip->lineno += newlines;
|
||
|
||
while (bp == ip->buf + ip->length) {
|
||
if (instack[indepth].macro == 0) {
|
||
free (buffer);
|
||
return "unterminated macro call";
|
||
}
|
||
ip->macro->type = T_MACRO;
|
||
if (ip->free_ptr)
|
||
free (ip->free_ptr);
|
||
ip = &instack[--indepth];
|
||
newlines = 0;
|
||
comments = 0;
|
||
bp = macarg1 (ip->bufp, ip->buf + ip->length, &paren,
|
||
&newlines, &comments);
|
||
final_start = bufsize;
|
||
bufsize += bp - ip->bufp;
|
||
extra += newlines;
|
||
buffer = (U_CHAR *) xrealloc (buffer, bufsize + extra + 1);
|
||
memcpy (buffer + bufsize - (bp - ip->bufp), ip->bufp, bp - ip->bufp);
|
||
ip->bufp = bp;
|
||
ip->lineno += newlines;
|
||
}
|
||
|
||
/* Now, if arg is actually wanted, record its raw form,
|
||
discarding comments and duplicating newlines in whatever
|
||
part of it did not come from a macro expansion.
|
||
EXTRA space has been preallocated for duplicating the newlines.
|
||
FINAL_START is the index of the start of that part. */
|
||
if (argptr != 0) {
|
||
argptr->raw = buffer;
|
||
argptr->raw_length = bufsize;
|
||
argptr->free1 = buffer;
|
||
argptr->newlines = newlines;
|
||
argptr->comments = comments;
|
||
if ((newlines || comments) && ip->fname != 0)
|
||
argptr->raw_length
|
||
= final_start +
|
||
discard_comments (argptr->raw + final_start,
|
||
argptr->raw_length - final_start,
|
||
newlines);
|
||
argptr->raw[argptr->raw_length] = 0;
|
||
if (argptr->raw_length > bufsize + extra)
|
||
abort ();
|
||
}
|
||
}
|
||
|
||
/* If we are not discarding this argument,
|
||
macroexpand it and compute its length as stringified.
|
||
All this info goes into *ARGPTR. */
|
||
|
||
if (argptr != 0) {
|
||
FILE_BUF obuf;
|
||
const U_CHAR *buf, *lim;
|
||
int totlen;
|
||
|
||
obuf = expand_to_temp_buffer (argptr->raw,
|
||
argptr->raw + argptr->raw_length,
|
||
1);
|
||
|
||
argptr->expanded = obuf.buf;
|
||
argptr->expand_length = obuf.length;
|
||
argptr->free2 = obuf.buf;
|
||
|
||
buf = argptr->raw;
|
||
lim = buf + argptr->raw_length;
|
||
|
||
totlen = 0;
|
||
while (buf != lim) {
|
||
U_CHAR c = *buf++;
|
||
totlen++;
|
||
/* Internal sequences of whitespace are replaced by one space
|
||
in most cases, but not always. So count all the whitespace
|
||
in case we need to keep it all. */
|
||
if (c == '\"' || c == '\\') /* escape these chars */
|
||
totlen++;
|
||
else if (!ISPRINT (c))
|
||
totlen += 3;
|
||
}
|
||
argptr->stringified_length = totlen;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* Scan text from START (inclusive) up to LIMIT (exclusive),
|
||
counting parens in *DEPTHPTR,
|
||
and return if reach LIMIT
|
||
or before a `)' that would make *DEPTHPTR negative
|
||
or before a comma when *DEPTHPTR is zero.
|
||
Single and double quotes are matched and termination
|
||
is inhibited within them. Comments also inhibit it.
|
||
Value returned is pointer to stopping place.
|
||
|
||
Increment *NEWLINES each time a newline is passed.
|
||
Set *COMMENTS to 1 if a comment is seen. */
|
||
|
||
static U_CHAR *
|
||
macarg1 (start, limit, depthptr, newlines, comments)
|
||
U_CHAR *start;
|
||
const U_CHAR *limit;
|
||
int *depthptr, *newlines, *comments;
|
||
{
|
||
U_CHAR *bp = start;
|
||
|
||
while (bp < limit) {
|
||
switch (*bp) {
|
||
case '(':
|
||
(*depthptr)++;
|
||
break;
|
||
case ')':
|
||
if (--(*depthptr) < 0)
|
||
return bp;
|
||
break;
|
||
case '\\':
|
||
/* Traditionally, backslash makes following char not special. */
|
||
if (bp + 1 < limit)
|
||
{
|
||
bp++;
|
||
/* But count source lines anyway. */
|
||
if (*bp == '\n')
|
||
++*newlines;
|
||
}
|
||
break;
|
||
case '\n':
|
||
++*newlines;
|
||
break;
|
||
case '/':
|
||
if (bp[1] == '\\' && bp[2] == '\n')
|
||
newline_fix (bp + 1);
|
||
if (bp[1] != '*' || bp + 1 >= limit)
|
||
break;
|
||
*comments = 1;
|
||
bp += 2;
|
||
while (bp + 1 < limit) {
|
||
if (bp[0] == '*'
|
||
&& bp[1] == '\\' && bp[2] == '\n')
|
||
newline_fix (bp + 1);
|
||
if (bp[0] == '*' && bp[1] == '/')
|
||
break;
|
||
if (*bp == '\n') ++*newlines;
|
||
bp++;
|
||
}
|
||
bp += 1;
|
||
break;
|
||
case '\'':
|
||
case '\"':
|
||
{
|
||
int quotec;
|
||
for (quotec = *bp++; bp + 1 < limit && *bp != quotec; bp++) {
|
||
if (*bp == '\\') {
|
||
bp++;
|
||
if (*bp == '\n')
|
||
++*newlines;
|
||
while (*bp == '\\' && bp[1] == '\n') {
|
||
bp += 2;
|
||
}
|
||
} else if (*bp == '\n') {
|
||
++*newlines;
|
||
if (quotec == '\'')
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case ',':
|
||
if ((*depthptr) == 0)
|
||
return bp;
|
||
break;
|
||
}
|
||
bp++;
|
||
}
|
||
|
||
return bp;
|
||
}
|
||
|
||
/* Discard comments and duplicate newlines
|
||
in the string of length LENGTH at START,
|
||
except inside of string constants.
|
||
The string is copied into itself with its beginning staying fixed.
|
||
|
||
NEWLINES is the number of newlines that must be duplicated.
|
||
We assume that that much extra space is available past the end
|
||
of the string. */
|
||
|
||
static int
|
||
discard_comments (start, length, newlines)
|
||
U_CHAR *start;
|
||
int length;
|
||
int newlines;
|
||
{
|
||
U_CHAR *ibp;
|
||
U_CHAR *obp;
|
||
const U_CHAR *limit;
|
||
int c;
|
||
|
||
/* If we have newlines to duplicate, copy everything
|
||
that many characters up. Then, in the second part,
|
||
we will have room to insert the newlines
|
||
while copying down.
|
||
NEWLINES may actually be too large, because it counts
|
||
newlines in string constants, and we don't duplicate those.
|
||
But that does no harm. */
|
||
if (newlines > 0) {
|
||
ibp = start + length;
|
||
obp = ibp + newlines;
|
||
limit = start;
|
||
while (limit != ibp)
|
||
*--obp = *--ibp;
|
||
}
|
||
|
||
ibp = start + newlines;
|
||
limit = start + length + newlines;
|
||
obp = start;
|
||
|
||
while (ibp < limit) {
|
||
*obp++ = c = *ibp++;
|
||
switch (c) {
|
||
case '\n':
|
||
/* Duplicate the newline. */
|
||
*obp++ = '\n';
|
||
break;
|
||
|
||
case '\\':
|
||
if (*ibp == '\n') {
|
||
obp--;
|
||
ibp++;
|
||
}
|
||
break;
|
||
|
||
case '/':
|
||
if (*ibp == '\\' && ibp[1] == '\n')
|
||
newline_fix (ibp);
|
||
/* Delete any comment. */
|
||
if (ibp[0] != '*' || ibp + 1 >= limit)
|
||
break;
|
||
obp--;
|
||
ibp++;
|
||
while (ibp + 1 < limit) {
|
||
if (ibp[0] == '*'
|
||
&& ibp[1] == '\\' && ibp[2] == '\n')
|
||
newline_fix (ibp + 1);
|
||
if (ibp[0] == '*' && ibp[1] == '/')
|
||
break;
|
||
ibp++;
|
||
}
|
||
ibp += 2;
|
||
break;
|
||
|
||
case '\'':
|
||
case '\"':
|
||
/* Notice and skip strings, so that we don't
|
||
think that comments start inside them,
|
||
and so we don't duplicate newlines in them. */
|
||
{
|
||
int quotec = c;
|
||
while (ibp < limit) {
|
||
*obp++ = c = *ibp++;
|
||
if (c == quotec)
|
||
break;
|
||
if (c == '\n' && quotec == '\'')
|
||
break;
|
||
if (c == '\\' && ibp < limit) {
|
||
while (*ibp == '\\' && ibp[1] == '\n')
|
||
ibp += 2;
|
||
*obp++ = *ibp++;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
return obp - start;
|
||
}
|
||
|
||
|
||
/* Core error handling routine. */
|
||
static void
|
||
v_message (mtype, line, msgid, ap)
|
||
enum msgtype mtype;
|
||
int line;
|
||
const char *msgid;
|
||
va_list ap;
|
||
{
|
||
const char *fname = 0;
|
||
int i;
|
||
|
||
if (mtype == MT_WARNING && inhibit_warnings)
|
||
return;
|
||
|
||
for (i = indepth; i >= 0; i--)
|
||
if (instack[i].fname != NULL) {
|
||
if (line == 0)
|
||
line = instack[i].lineno;
|
||
fname = instack[i].fname;
|
||
break;
|
||
}
|
||
|
||
if (fname)
|
||
fprintf (stderr, "%s:%d: ", fname, line);
|
||
else
|
||
fprintf (stderr, "%s: ", progname);
|
||
|
||
if (mtype == MT_WARNING)
|
||
fputs (_("warning: "), stderr);
|
||
|
||
vfprintf (stderr, _(msgid), ap);
|
||
putc ('\n', stderr);
|
||
|
||
if (mtype == MT_ERROR)
|
||
errors++;
|
||
}
|
||
|
||
/*
|
||
* error - print error message and increment count of errors.
|
||
*/
|
||
void
|
||
error VPARAMS ((const char *msgid, ...))
|
||
{
|
||
VA_OPEN(ap, msgid);
|
||
VA_FIXEDARG (ap, const char *, msgid);
|
||
|
||
v_message (MT_ERROR, 0, msgid, ap);
|
||
VA_CLOSE (ap);
|
||
}
|
||
|
||
void
|
||
error_with_line VPARAMS ((int line, const char *msgid, ...))
|
||
{
|
||
VA_OPEN(ap, msgid);
|
||
VA_FIXEDARG (ap, int, line);
|
||
VA_FIXEDARG (ap, const char *, msgid);
|
||
|
||
v_message (MT_ERROR, line, msgid, ap);
|
||
VA_CLOSE (ap);
|
||
}
|
||
|
||
/* Error including a message from `errno'. */
|
||
void
|
||
error_from_errno (name)
|
||
const char *name;
|
||
{
|
||
error ("%s: %s", name, strerror (errno));
|
||
}
|
||
|
||
/* Print error message but don't count it. */
|
||
void
|
||
warning VPARAMS ((const char *msgid, ...))
|
||
{
|
||
VA_OPEN(ap, msgid);
|
||
VA_FIXEDARG (ap, const char *, msgid);
|
||
|
||
v_message (MT_WARNING, 0, msgid, ap);
|
||
VA_CLOSE (ap);
|
||
}
|
||
|
||
void
|
||
fatal VPARAMS ((const char *msgid, ...))
|
||
{
|
||
VA_OPEN(ap, msgid);
|
||
VA_FIXEDARG (ap, const char *, msgid);
|
||
|
||
v_message (MT_FATAL, 0, msgid, ap);
|
||
VA_CLOSE (ap);
|
||
exit (FATAL_EXIT_CODE);
|
||
}
|
||
|
||
/* More 'friendly' abort that prints the location at which we died. */
|
||
void
|
||
fancy_abort (line, func)
|
||
int line;
|
||
const char *func;
|
||
{
|
||
fatal ("internal error in %s, at tradcpp.c:%d\n\
|
||
Please submit a full bug report.\n\
|
||
See %s for instructions.", func, line, GCCBUGURL);
|
||
}
|
||
|
||
void
|
||
perror_with_name (name)
|
||
const char *name;
|
||
{
|
||
fprintf (stderr, "%s: %s: %s\n", progname, name, strerror (errno));
|
||
errors++;
|
||
}
|
||
|
||
void
|
||
pfatal_with_name (name)
|
||
const char *name;
|
||
{
|
||
perror_with_name (name);
|
||
exit (FATAL_EXIT_CODE);
|
||
}
|
||
|
||
/* Return the line at which an error occurred.
|
||
The error is not necessarily associated with the current spot
|
||
in the input stack, so LINE says where. LINE will have been
|
||
copied from ip->lineno for the current input level.
|
||
If the current level is for a file, we return LINE.
|
||
But if the current level is not for a file, LINE is meaningless.
|
||
In that case, we return the lineno of the innermost file. */
|
||
static int
|
||
line_for_error (line)
|
||
int line;
|
||
{
|
||
int i;
|
||
int line1 = line;
|
||
|
||
for (i = indepth; i >= 0; ) {
|
||
if (instack[i].fname != 0)
|
||
return line1;
|
||
i--;
|
||
if (i < 0)
|
||
return 0;
|
||
line1 = instack[i].lineno;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* If OBUF doesn't have NEEDED bytes after OPTR, make it bigger.
|
||
*
|
||
* As things stand, nothing is ever placed in the output buffer to be
|
||
* removed again except when it's KNOWN to be part of an identifier,
|
||
* so flushing and moving down everything left, instead of expanding,
|
||
* should work ok.
|
||
*/
|
||
|
||
static void
|
||
grow_outbuf (obuf, needed)
|
||
FILE_BUF *obuf;
|
||
int needed;
|
||
{
|
||
U_CHAR *p;
|
||
int minsize;
|
||
|
||
if (obuf->length - (obuf->bufp - obuf->buf) > needed)
|
||
return;
|
||
|
||
/* Make it at least twice as big as it is now. */
|
||
obuf->length *= 2;
|
||
/* Make it have at least 150% of the free space we will need. */
|
||
minsize = (3 * needed) / 2 + (obuf->bufp - obuf->buf);
|
||
if (minsize > obuf->length)
|
||
obuf->length = minsize;
|
||
|
||
p = (U_CHAR *) xrealloc (obuf->buf, obuf->length);
|
||
obuf->bufp = p + (obuf->bufp - obuf->buf);
|
||
obuf->buf = p;
|
||
}
|
||
|
||
/* Symbol table for macro names and special symbols */
|
||
|
||
/*
|
||
* install a name in the main hash table, even if it is already there.
|
||
* name stops with first non alphanumeric, except leading '#'.
|
||
* caller must check against redefinition if that is desired.
|
||
* delete_macro () removes things installed by install () in fifo order.
|
||
* this is important because of the `defined' special symbol used
|
||
* in #if, and also if pushdef/popdef directives are ever implemented.
|
||
*
|
||
* If LEN is >= 0, it is the length of the name.
|
||
* Otherwise, compute the length by scanning the entire name.
|
||
*
|
||
* If HASH is >= 0, it is the precomputed hash code.
|
||
* Otherwise, compute the hash code.
|
||
*
|
||
* caller must set the value, if any is desired.
|
||
*/
|
||
static HASHNODE *
|
||
install (name, len, type, hash)
|
||
const U_CHAR *name;
|
||
int len;
|
||
enum node_type type;
|
||
int hash;
|
||
/* watch out here if sizeof (U_CHAR *) != sizeof (int) */
|
||
{
|
||
HASHNODE *hp;
|
||
int bucket;
|
||
const U_CHAR *p;
|
||
U_CHAR *q;
|
||
|
||
if (len < 0) {
|
||
p = name;
|
||
while (is_idchar (*p))
|
||
p++;
|
||
len = p - name;
|
||
}
|
||
|
||
if (hash < 0)
|
||
hash = hashf (name, len, HASHSIZE);
|
||
|
||
hp = (HASHNODE *) xmalloc (sizeof (HASHNODE) + len + 1);
|
||
bucket = hash;
|
||
hp->bucket_hdr = &hashtab[bucket];
|
||
hp->next = hashtab[bucket];
|
||
hashtab[bucket] = hp;
|
||
hp->prev = NULL;
|
||
if (hp->next != NULL)
|
||
hp->next->prev = hp;
|
||
hp->type = type;
|
||
hp->length = len;
|
||
hp->name = q = ((U_CHAR *) hp) + sizeof (HASHNODE);
|
||
memcpy (q, name, len);
|
||
q[len] = 0;
|
||
return hp;
|
||
}
|
||
|
||
/*
|
||
* find the most recent hash node for name name (ending with first
|
||
* non-identifier char) installed by install
|
||
*
|
||
* If LEN is >= 0, it is the length of the name.
|
||
* Otherwise, compute the length by scanning the entire name.
|
||
*
|
||
* If HASH is >= 0, it is the precomputed hash code.
|
||
* Otherwise, compute the hash code.
|
||
*/
|
||
HASHNODE *
|
||
lookup (name, len, hash)
|
||
const U_CHAR *name;
|
||
int len;
|
||
int hash;
|
||
{
|
||
const U_CHAR *bp;
|
||
HASHNODE *bucket;
|
||
|
||
if (len < 0) {
|
||
for (bp = name; is_idchar (*bp); bp++) ;
|
||
len = bp - name;
|
||
}
|
||
|
||
if (hash < 0)
|
||
hash = hashf (name, len, HASHSIZE);
|
||
|
||
bucket = hashtab[hash];
|
||
while (bucket) {
|
||
if (bucket->length == len
|
||
&& strncmp ((const char *)bucket->name, (const char *)name, len) == 0)
|
||
return bucket;
|
||
bucket = bucket->next;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* Delete a hash node. Some weirdness to free junk from macros.
|
||
* More such weirdness will have to be added if you define more hash
|
||
* types that need it.
|
||
*/
|
||
|
||
/* Note that the DEFINITION of a macro is removed from the hash table
|
||
but its storage is not freed. This would be a storage leak
|
||
except that it is not reasonable to keep undefining and redefining
|
||
large numbers of macros many times.
|
||
In any case, this is necessary, because a macro can be #undef'd
|
||
in the middle of reading the arguments to a call to it.
|
||
If #undef freed the DEFINITION, that would crash. */
|
||
static void
|
||
delete_macro (hp)
|
||
HASHNODE *hp;
|
||
{
|
||
|
||
if (hp->prev != NULL)
|
||
hp->prev->next = hp->next;
|
||
if (hp->next != NULL)
|
||
hp->next->prev = hp->prev;
|
||
|
||
/* make sure that the bucket chain header that
|
||
the deleted guy was on points to the right thing afterwards. */
|
||
if (hp == *hp->bucket_hdr)
|
||
*hp->bucket_hdr = hp->next;
|
||
|
||
free (hp);
|
||
}
|
||
|
||
/*
|
||
* return hash function on name. must be compatible with the one
|
||
* computed a step at a time, elsewhere
|
||
*/
|
||
static int
|
||
hashf (name, len, hashsize)
|
||
const U_CHAR *name;
|
||
int len;
|
||
int hashsize;
|
||
{
|
||
int r = 0;
|
||
|
||
while (len--)
|
||
r = HASHSTEP (r, *name++);
|
||
|
||
return MAKE_POS (r) % hashsize;
|
||
}
|
||
|
||
/* Dump all macro definitions as #defines to stdout. */
|
||
|
||
static void
|
||
dump_all_macros ()
|
||
{
|
||
int bucket;
|
||
|
||
for (bucket = 0; bucket < HASHSIZE; bucket++) {
|
||
HASHNODE *hp;
|
||
|
||
for (hp = hashtab[bucket]; hp; hp= hp->next) {
|
||
if (hp->type == T_MACRO) {
|
||
DEFINITION *defn = hp->value.defn;
|
||
struct reflist *ap;
|
||
int offset;
|
||
int concat;
|
||
|
||
|
||
/* Print the definition of the macro HP. */
|
||
|
||
printf ("#define %s", hp->name);
|
||
if (defn->nargs >= 0) {
|
||
int i;
|
||
|
||
printf ("(");
|
||
for (i = 0; i < defn->nargs; i++) {
|
||
dump_arg_n (defn, i);
|
||
if (i + 1 < defn->nargs)
|
||
printf (", ");
|
||
}
|
||
printf (")");
|
||
}
|
||
|
||
printf (" ");
|
||
|
||
offset = 0;
|
||
concat = 0;
|
||
for (ap = defn->pattern; ap != NULL; ap = ap->next) {
|
||
dump_defn_1 (defn->expansion, offset, ap->nchars);
|
||
if (ap->nchars != 0)
|
||
concat = 0;
|
||
offset += ap->nchars;
|
||
if (ap->stringify)
|
||
printf (" #");
|
||
if (ap->raw_before && !concat)
|
||
printf (" ## ");
|
||
concat = 0;
|
||
dump_arg_n (defn, ap->argno);
|
||
if (ap->raw_after) {
|
||
printf (" ## ");
|
||
concat = 1;
|
||
}
|
||
}
|
||
dump_defn_1 (defn->expansion, offset, defn->length - offset);
|
||
printf ("\n");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Output to stdout a substring of a macro definition.
|
||
BASE is the beginning of the definition.
|
||
Output characters START thru LENGTH.
|
||
Discard newlines outside of strings, thus
|
||
converting funny-space markers to ordinary spaces. */
|
||
static void
|
||
dump_defn_1 (base, start, length)
|
||
const U_CHAR *base;
|
||
int start;
|
||
int length;
|
||
{
|
||
const U_CHAR *p = base + start;
|
||
const U_CHAR *limit = base + start + length;
|
||
|
||
while (p < limit) {
|
||
if (*p != '\n')
|
||
putchar (*p);
|
||
else if (*p == '\"' || *p =='\'') {
|
||
const U_CHAR *p1 = skip_quoted_string (p, limit, 0, 0, 0, 0);
|
||
fwrite (p, p1 - p, 1, stdout);
|
||
p = p1 - 1;
|
||
}
|
||
p++;
|
||
}
|
||
}
|
||
|
||
/* Print the name of argument number ARGNUM of macro definition DEFN.
|
||
Recall that DEFN->argnames contains all the arg names
|
||
concatenated in reverse order with comma-space in between. */
|
||
static void
|
||
dump_arg_n (defn, argnum)
|
||
DEFINITION *defn;
|
||
int argnum;
|
||
{
|
||
const U_CHAR *p = defn->argnames;
|
||
while (argnum + 1 < defn->nargs) {
|
||
p = (const U_CHAR *) strchr ((const char *)p, ' ') + 1;
|
||
argnum++;
|
||
}
|
||
|
||
while (*p && *p != ',') {
|
||
putchar (*p);
|
||
p++;
|
||
}
|
||
}
|
||
|
||
/* Initialize the built-in macros. */
|
||
#define DSC(x) U x, sizeof x - 1
|
||
#define install_spec(name, type) \
|
||
install(DSC(name), type, -1);
|
||
#define install_value(name, val) \
|
||
hp = install(DSC(name), T_CONST, -1); hp->value.cpval = val;
|
||
static void
|
||
initialize_builtins ()
|
||
{
|
||
HASHNODE *hp;
|
||
|
||
install_spec ("__BASE_FILE__", T_BASE_FILE);
|
||
install_spec ("__DATE__", T_DATE);
|
||
install_spec ("__FILE__", T_FILE);
|
||
install_spec ("__TIME__", T_TIME);
|
||
install_spec ("__VERSION__", T_VERSION);
|
||
install_spec ("__INCLUDE_LEVEL__", T_INCLUDE_LEVEL);
|
||
install_spec ("__LINE__", T_SPECLINE);
|
||
|
||
#ifndef NO_BUILTIN_SIZE_TYPE
|
||
install_value ("__SIZE_TYPE__", SIZE_TYPE);
|
||
#endif
|
||
#ifndef NO_BUILTIN_PTRDIFF_TYPE
|
||
install_value ("__PTRDIFF_TYPE__", PTRDIFF_TYPE);
|
||
#endif
|
||
#ifndef NO_BUILTIN_WCHAR_TYPE
|
||
install_value ("__WCHAR_TYPE__", WCHAR_TYPE);
|
||
#endif
|
||
#ifndef NO_BUILTIN_WINT_TYPE
|
||
install_value ("__WINT_TYPE__", WINT_TYPE);
|
||
#endif
|
||
install_value ("__REGISTER_PREFIX__", REGISTER_PREFIX);
|
||
install_value ("__USER_LABEL_PREFIX__", user_label_prefix);
|
||
|
||
if (flag_signed_char == 0)
|
||
install_value ("__CHAR_UNSIGNED__", "1");
|
||
}
|
||
#undef DSC
|
||
#undef install_spec
|
||
#undef install_value
|
||
|
||
/* Common handler of command line directives -U, -D and -A. */
|
||
static void
|
||
run_directive (str, len, type)
|
||
const char *str;
|
||
size_t len;
|
||
enum node_type type;
|
||
{
|
||
const struct directive *kt;
|
||
FILE_BUF *ip = &instack[++indepth];
|
||
ip->fname = "*command line*";
|
||
|
||
ip->buf = ip->bufp = (U_CHAR *) str;
|
||
ip->length = len;
|
||
ip->lineno = 1;
|
||
ip->macro = 0;
|
||
ip->free_ptr = 0;
|
||
ip->if_stack = if_stack;
|
||
|
||
for (kt = directive_table; kt->type != type; kt++)
|
||
;
|
||
|
||
(*kt->func) ((U_CHAR *) str, (U_CHAR *) str + len, NULL);
|
||
--indepth;
|
||
}
|
||
|
||
/* Handle the -D option. If STR is just an identifier, define it with
|
||
* value 1. If STR has anything after the identifier, then it should
|
||
* be identifier-space-definition. */
|
||
static void
|
||
make_definition (str)
|
||
const char *str;
|
||
{
|
||
char *buf, *p;
|
||
size_t count;
|
||
|
||
/* Copy the entire option so we can modify it.
|
||
Change the first "=" in the string to a space. If there is none,
|
||
tack " 1" on the end. */
|
||
|
||
/* Length including the null. */
|
||
count = strlen (str);
|
||
buf = (char *) alloca (count + 2);
|
||
memcpy (buf, str, count);
|
||
|
||
p = strchr (str, '=');
|
||
if (p)
|
||
buf[p - str] = ' ';
|
||
else
|
||
{
|
||
buf[count++] = ' ';
|
||
buf[count++] = '1';
|
||
}
|
||
|
||
run_directive (buf, count, T_DEFINE);
|
||
}
|
||
|
||
/* Handle the -U option. */
|
||
static void
|
||
make_undef (str)
|
||
const char *str;
|
||
{
|
||
run_directive (str, strlen (str), T_UNDEF);
|
||
}
|
||
|
||
/* Handles the #assert (-A) and #unassert (-A-) command line options. */
|
||
static void
|
||
make_assertion (str)
|
||
const char *str;
|
||
{
|
||
enum node_type type = T_ASSERT;
|
||
size_t count;
|
||
const char *p;
|
||
|
||
if (*str == '-')
|
||
{
|
||
str++;
|
||
type = T_UNASSERT;
|
||
}
|
||
|
||
count = strlen (str);
|
||
p = strchr (str, '=');
|
||
if (p)
|
||
{
|
||
/* Copy the entire option so we can modify it. Change the first
|
||
"=" in the string to a '(', and tack a ')' on the end. */
|
||
char *buf = (char *) alloca (count + 1);
|
||
|
||
memcpy (buf, str, count);
|
||
buf[p - str] = '(';
|
||
buf[count++] = ')';
|
||
str = buf;
|
||
}
|
||
|
||
run_directive (str, count, type);
|
||
}
|
||
|
||
/* Get the file-mode and data size of the file open on FD
|
||
and store them in *MODE_POINTER and *SIZE_POINTER. */
|
||
|
||
static int
|
||
file_size_and_mode (fd, mode_pointer, size_pointer)
|
||
int fd;
|
||
int *mode_pointer;
|
||
long *size_pointer;
|
||
{
|
||
struct stat sbuf;
|
||
|
||
if (fstat (fd, &sbuf) < 0) return -1;
|
||
if (mode_pointer) *mode_pointer = sbuf.st_mode;
|
||
if (size_pointer) *size_pointer = sbuf.st_size;
|
||
return 0;
|
||
}
|