blob: 368298b90cbbb8c71f94251711d0b49cd6a01b4d [file] [log] [blame]
/* C Compatible Compiler Preprocessor (CCCP)
Copyright (C) 1986, 87, 89, 92-96, 1997 Free Software Foundation, Inc.
Written by Paul Rubin, June 1986
Adapted to ANSI C, Richard Stallman, Jan 1987
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 <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <stdio.h>
#include <signal.h>
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
#endif
#endif
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
#if HAVE_FCNTL_H
# include <fcntl.h>
#endif
#if HAVE_LIMITS_H
# include <limits.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <errno.h>
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#endif
typedef unsigned char U_CHAR;
#include "gansidecl.h"
#include "pcp.h"
#ifdef NEED_DECLARATION_INDEX
extern char *index ();
#endif
#ifdef NEED_DECLARATION_RINDEX
extern char *rindex ();
#endif
#ifdef NEED_DECLARATION_GETENV
extern char *getenv ();
#endif
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
# define __attribute__(x)
#endif
#ifndef STANDARD_INCLUDE_DIR
# define STANDARD_INCLUDE_DIR "/usr/include"
#endif
/* By default, colon separates directories in a path. */
#ifndef PATH_SEPARATOR
# define PATH_SEPARATOR ':'
#endif
/* By default, the suffix for object files is ".o". */
#ifdef OBJECT_SUFFIX
# define HAVE_OBJECT_SUFFIX
#else
# define OBJECT_SUFFIX ".o"
#endif
#if defined (__STDC__) && defined (HAVE_VPRINTF)
# include <stdarg.h>
# define PRINTF_ALIST(msg) char *msg, ...
# define PRINTF_DCL(msg)
# define PRINTF_PROTO(ARGS, m, n) PROTO (ARGS) __attribute__ ((format (__printf__, m, n)))
#else
# include <varargs.h>
# define PRINTF_ALIST(msg) msg, va_alist
# define PRINTF_DCL(msg) char *msg; va_dcl
# define PRINTF_PROTO(ARGS, m, n) () __attribute__ ((format (__printf__, m, n)))
# define vfprintf(file, msg, args) \
{ \
char *a0 = va_arg(args, char *); \
char *a1 = va_arg(args, char *); \
char *a2 = va_arg(args, char *); \
char *a3 = va_arg(args, char *); \
fprintf (file, msg, a0, a1, a2, a3); \
}
#endif
#define PRINTF_PROTO_1(ARGS) PRINTF_PROTO(ARGS, 1, 2)
#define PRINTF_PROTO_2(ARGS) PRINTF_PROTO(ARGS, 2, 3)
#define PRINTF_PROTO_3(ARGS) PRINTF_PROTO(ARGS, 3, 4)
/* VMS-specific definitions */
#ifdef VMS
#include <descrip.h>
#define open(fname,mode,prot) VMS_open (fname,mode,prot)
#define fopen(fname,mode) VMS_fopen (fname,mode)
#define freopen(fname,mode,ofile) VMS_freopen (fname,mode,ofile)
#define fstat(fd,stbuf) VMS_fstat (fd,stbuf)
static int VMS_fstat (), VMS_stat ();
static int VMS_open ();
static FILE *VMS_fopen ();
static FILE *VMS_freopen ();
static void hack_vms_include_specification ();
#define INO_T_EQ(a, b) (!bcmp((char *) &(a), (char *) &(b), sizeof (a)))
#define INO_T_HASH(a) 0
#define INCLUDE_LEN_FUDGE 12 /* leave room for VMS syntax conversion */
#endif /* VMS */
/* Windows does not natively support inodes, and neither does MSDOS. */
#if (defined (_WIN32) && ! defined (CYGWIN32)) || defined (__MSDOS__)
#define INO_T_EQ(a, b) 0
#endif
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
#undef MIN
#undef MAX
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
/* Find the largest host integer type and set its size and type.
Watch out: on some crazy hosts `long' is shorter than `int'. */
#ifndef HOST_WIDE_INT
# if HAVE_INTTYPES_H
# include <inttypes.h>
# define HOST_WIDE_INT intmax_t
# else
# if (HOST_BITS_PER_LONG <= HOST_BITS_PER_INT && HOST_BITS_PER_LONGLONG <= HOST_BITS_PER_INT)
# define HOST_WIDE_INT int
# else
# if (HOST_BITS_PER_LONGLONG <= HOST_BITS_PER_LONG || ! (defined LONG_LONG_MAX || defined LLONG_MAX))
# define HOST_WIDE_INT long
# else
# define HOST_WIDE_INT long long
# endif
# endif
# endif
#endif
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#ifndef INO_T_EQ
#define INO_T_EQ(a, b) ((a) == (b))
#endif
#ifndef INO_T_HASH
#define INO_T_HASH(a) (a)
#endif
#ifndef INCLUDE_LEN_FUDGE
#define INCLUDE_LEN_FUDGE 0
#endif
/* External declarations. */
extern char *version_string;
extern char *update_path PROTO((char *, char *));
#ifndef VMS
#ifndef HAVE_STRERROR
extern int sys_nerr;
extern char *sys_errlist[];
#else /* HAVE_STRERROR */
char *strerror ();
#endif
#else /* VMS */
char *strerror (int,...);
#endif
HOST_WIDE_INT parse_escape PROTO((char **, HOST_WIDE_INT));
HOST_WIDE_INT parse_c_expression PROTO((char *, int));
#ifndef errno
extern int errno;
#endif
/* Name under which this program was invoked. */
static char *progname;
/* Nonzero means use extra default include directories for C++. */
static int cplusplus;
/* Nonzero means handle cplusplus style comments */
static int cplusplus_comments;
/* Nonzero means handle #import, for objective C. */
static int objc;
/* Nonzero means this is an assembly file, and allow
unknown directives, which could be comments. */
static int lang_asm;
/* Current maximum length of directory names in the search path
for include files. (Altered as we get more of them.) */
static int max_include_len;
/* Nonzero means turn NOTREACHED into #pragma NOTREACHED etc */
static int for_lint = 0;
/* Nonzero means copy comments into the output file. */
static int put_out_comments = 0;
/* Nonzero means don't process the ANSI trigraph sequences. */
static int no_trigraphs = 0;
/* Nonzero means print the names of included files rather than
the preprocessed output. 1 means just the #include "...",
2 means #include <...> as well. */
static int print_deps = 0;
/* Nonzero if missing .h files in -M output are assumed to be generated
files and not errors. */
static int print_deps_missing_files = 0;
/* Nonzero means print names of header files (-H). */
static int print_include_names = 0;
/* Nonzero means don't output line number information. */
static int no_line_directives;
/* Nonzero means output the text in failing conditionals,
inside #failed ... #endfailed. */
static int output_conditionals;
/* dump_only 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.
dump_names means pass #define and the macro name through to output.
dump_definitions means pass the whole definition (plus #define) through
*/
static enum {dump_none, dump_only, dump_names, dump_definitions}
dump_macros = dump_none;
/* Nonzero means pass all #define and #undef directives which we actually
process through to the output stream. This feature is used primarily
to allow cc1 to record the #defines and #undefs for the sake of
debuggers which understand about preprocessor macros, but it may
also be useful with -E to figure out how symbols are defined, and
where they are defined. */
static int debug_output = 0;
/* Nonzero means pass #include lines through to the output,
even if they are ifdefed out. */
static int dump_includes;
/* Nonzero indicates special processing used by the pcp program. The
special effects of this mode are:
Inhibit all macro expansion, except those inside #if directives.
Process #define directives normally, and output their contents
to the output file.
Output preconditions to pcp_outfile indicating all the relevant
preconditions for use of this file in a later cpp run.
*/
static FILE *pcp_outfile;
/* Nonzero means we are inside an IF during a -pcp run. In this mode
macro expansion is done, and preconditions are output for all macro
uses requiring them. */
static int pcp_inside_if;
/* Nonzero means never to include precompiled files.
This is 1 since there's no way now to make precompiled files,
so it's not worth testing for them. */
static int no_precomp = 1;
/* Nonzero means give all the error messages the ANSI standard requires. */
int pedantic;
/* Nonzero means try to make failure to fit ANSI C an error. */
static int pedantic_errors;
/* Nonzero means don't print warning messages. -w. */
static int inhibit_warnings = 0;
/* Nonzero means warn if slash-star appears in a slash-star comment,
or if newline-backslash appears in a slash-slash comment. */
static int warn_comments;
/* Nonzero means warn if a macro argument is (or would be)
stringified with -traditional. */
static int warn_stringify;
/* Nonzero means warn if there are any trigraphs. */
static int warn_trigraphs;
/* Nonzero means warn if undefined identifiers are evaluated in an #if. */
static int warn_undef;
/* Nonzero means warn if #import is used. */
static int warn_import = 1;
/* Nonzero means turn warnings into errors. */
static int warnings_are_errors;
/* Nonzero means try to imitate old fashioned non-ANSI preprocessor. */
int traditional;
/* Nonzero for the 1989 C Standard, including corrigenda and amendments. */
int c89;
/* Nonzero causes output not to be done,
but directives such as #define that have side effects
are still obeyed. */
static int no_output;
/* Nonzero means we should look for header.gcc files that remap file names. */
static int remap;
/* Nonzero means this file was included with a -imacros or -include
command line and should not be recorded as an include file. */
static int no_record_file;
/* Nonzero means that we have finished processing the command line options.
This flag is used to decide whether or not to issue certain errors
and/or warnings. */
static int done_initializing = 0;
/* Line where a newline was first seen in a string constant. */
static int multiline_string_line = 0;
/* 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 400
static struct file_buf {
char *fname;
/* Filename specified with #line directive. */
char *nominal_fname;
/* Include file description. */
struct include_file *inc;
/* Record where in the search path this file was found.
For #include_next. */
struct file_name_list *dir;
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;
/* True if this is a system header file; see is_system_include. */
char system_header_p;
} instack[INPUT_STACK_MAX];
static int last_error_tick; /* Incremented each time we print it. */
static int input_file_stack_tick; /* Incremented when the status changes. */
/* Current nesting level of input sources.
`instack[indepth]' is the level currently being read. */
static 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 <...>. */
static int system_include_depth = 0;
typedef struct file_buf FILE_BUF;
/* 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 */
static FILE_BUF outbuf;
/* Grow output buffer OBUF points at
so it can hold at least NEEDED more chars. */
#define check_expand(OBUF, NEEDED) \
(((OBUF)->length - ((OBUF)->bufp - (OBUF)->buf) <= (NEEDED)) \
? grow_outbuf ((OBUF), (NEEDED)) : 0)
struct file_name_list
{
struct file_name_list *next;
/* If the following is 1, it is a C-language system include
directory. */
int c_system_include_path;
/* Mapping of file names for this directory. */
struct file_name_map *name_map;
/* Non-zero if name_map is valid. */
int got_name_map;
/* The include directory status. */
struct stat st;
/* The include prefix: "" denotes the working directory,
otherwise fname must end in '/'.
The actual size is dynamically allocated. */
char fname[1];
};
/* #include "file" looks in source file dir, then stack. */
/* #include <file> just looks in the stack. */
/* -I directories are added to the end, then the defaults are added. */
/* The */
static struct default_include {
char *fname; /* The name of the directory. */
char *component; /* The component containing the directory */
int cplusplus; /* Only look here if we're compiling C++. */
int cxx_aware; /* Includes in this directory don't need to
be wrapped in extern "C" when compiling
C++. */
} include_defaults_array[]
#ifdef INCLUDE_DEFAULTS
= INCLUDE_DEFAULTS;
#else
= {
/* Pick up GNU C++ specific include files. */
{ GPLUSPLUS_INCLUDE_DIR, "G++", 1, 1 },
{ OLD_GPLUSPLUS_INCLUDE_DIR, 0, 1, 1 },
#ifdef CROSS_COMPILE
/* This is the dir for fixincludes. Put it just before
the files that we fix. */
{ GCC_INCLUDE_DIR, "GCC", 0, 0 },
/* For cross-compilation, this dir name is generated
automatically in Makefile.in. */
{ CROSS_INCLUDE_DIR, "GCC", 0, 0 },
#ifdef TOOL_INCLUDE_DIR
/* This is another place that the target system's headers might be. */
{ TOOL_INCLUDE_DIR, "BINUTILS", 0, 0 },
#endif
#else /* not CROSS_COMPILE */
#ifdef LOCAL_INCLUDE_DIR
/* This should be /usr/local/include and should come before
the fixincludes-fixed header files. */
{ LOCAL_INCLUDE_DIR, 0, 0, 1 },
#endif
#ifdef TOOL_INCLUDE_DIR
/* This is here ahead of GCC_INCLUDE_DIR because assert.h goes here.
Likewise, behind LOCAL_INCLUDE_DIR, where glibc puts its assert.h. */
{ TOOL_INCLUDE_DIR, "BINUTILS", 0, 0 },
#endif
/* This is the dir for fixincludes. Put it just before
the files that we fix. */
{ GCC_INCLUDE_DIR, "GCC", 0, 0 },
/* Some systems have an extra dir of include files. */
#ifdef SYSTEM_INCLUDE_DIR
{ SYSTEM_INCLUDE_DIR, 0, 0, 0 },
#endif
#ifndef STANDARD_INCLUDE_COMPONENT
#define STANDARD_INCLUDE_COMPONENT 0
#endif
{ STANDARD_INCLUDE_DIR, STANDARD_INCLUDE_COMPONENT, 0, 0 },
#endif /* not CROSS_COMPILE */
{ 0, 0, 0, 0 }
};
#endif /* no INCLUDE_DEFAULTS */
/* The code looks at the defaults through this pointer, rather than through
the constant structure above. This pointer gets changed if an environment
variable specifies other defaults. */
static struct default_include *include_defaults = include_defaults_array;
static struct file_name_list *include = 0; /* First dir to search */
/* First dir to search for <file> */
/* This is the first element to use for #include <...>.
If it is 0, use the entire chain for such includes. */
static struct file_name_list *first_bracket_include = 0;
/* This is the first element in the chain that corresponds to
a directory of system header files. */
static struct file_name_list *first_system_include = 0;
static struct file_name_list *last_include = 0; /* Last in chain */
/* Chain of include directories to put at the end of the other chain. */
static struct file_name_list *after_include = 0;
static struct file_name_list *last_after_include = 0; /* Last in chain */
/* Chain to put at the start of the system include files. */
static struct file_name_list *before_system = 0;
static struct file_name_list *last_before_system = 0; /* Last in chain */
/* Directory prefix that should replace `/usr' in the standard
include file directories. */
static char *include_prefix;
/* Maintain and search list of included files. */
struct include_file {
struct include_file *next; /* for include_hashtab */
struct include_file *next_ino; /* for include_ino_hashtab */
char *fname;
/* If the following is the empty string, it means #pragma once
was seen in this include file, or #import was applied to the file.
Otherwise, if it is nonzero, it is a macro name.
Don't include the file again if that macro is defined. */
U_CHAR *control_macro;
/* Nonzero if the dependency on this include file has been output. */
int deps_output;
struct stat st;
};
/* Hash tables of files already included with #include or #import.
include_hashtab is by full name; include_ino_hashtab is by inode number. */
#define INCLUDE_HASHSIZE 61
static struct include_file *include_hashtab[INCLUDE_HASHSIZE];
static struct include_file *include_ino_hashtab[INCLUDE_HASHSIZE];
/* Global list of strings read in from precompiled files. This list
is kept in the order the strings are read in, with new strings being
added at the end through stringlist_tailp. We use this list to output
the strings at the end of the run.
*/
static STRINGDEF *stringlist;
static STRINGDEF **stringlist_tailp = &stringlist;
/* Structure returned by create_definition */
typedef struct macrodef MACRODEF;
struct macrodef
{
struct definition *defn;
U_CHAR *symnam;
int symlen;
};
enum sharp_token_type {
NO_SHARP_TOKEN = 0, /* token not present */
SHARP_TOKEN = '#', /* token spelled with # only */
WHITE_SHARP_TOKEN, /* token spelled with # and white space */
PERCENT_COLON_TOKEN = '%', /* token spelled with %: only */
WHITE_PERCENT_COLON_TOKEN /* token spelled with %: and white space */
};
/* 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 */
int predefined; /* True if the macro was builtin or */
/* came from the command line */
U_CHAR *expansion;
int line; /* Line number of definition */
char *file; /* File of definition */
char rest_args; /* Nonzero if last arg. absorbs the rest */
struct reflist {
struct reflist *next;
enum sharp_token_type stringify; /* set if a # operator before arg */
enum sharp_token_type raw_before; /* set if a ## operator before arg */
enum sharp_token_type raw_after; /* set if a ## operator after arg */
char rest_args; /* Nonzero if this arg. absorbs the rest */
int nchars; /* Number of literal chars to copy before
this arg occurrence. */
int argno; /* Number of arg to substitute (origin-0) */
} *pattern;
union {
/* 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. */
U_CHAR *argnames;
} args;
};
/* different kinds of things that can appear in the value field
of a hash node. Actually, this may be useless now. */
union hashval {
char *cpval;
DEFINITION *defn;
KEYDEF *keydef;
};
/*
* special extension string that can be added to the last macro argument to
* allow it to absorb the "rest" of the arguments when expanded. Ex:
* #define wow(a, b...) process (b, a, b)
* { wow (1, 2, 3); } -> { process (2, 3, 1, 2, 3); }
* { wow (one, two); } -> { process (two, one, two); }
* if this "rest_arg" is used with the concat token '##' and if it is not
* supplied then the token attached to with ## will not be outputted. Ex:
* #define wow (a, b...) process (b ## , a, ## b)
* { wow (1, 2); } -> { process (2, 1, 2); }
* { wow (one); } -> { process (one); {
*/
static char rest_extension[] = "...";
#define REST_EXTENSION_LENGTH (sizeof (rest_extension) - 1)
/* The structure of a node in the hash table. The hash table
has entries for all tokens defined by #define directives (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, /* the `#define' keyword */
T_INCLUDE, /* the `#include' keyword */
T_INCLUDE_NEXT, /* the `#include_next' keyword */
T_IMPORT, /* the `#import' keyword */
T_IFDEF, /* the `#ifdef' keyword */
T_IFNDEF, /* the `#ifndef' keyword */
T_IF, /* the `#if' keyword */
T_ELSE, /* `#else' */
T_PRAGMA, /* `#pragma' */
T_ELIF, /* `#elif' */
T_UNDEF, /* `#undef' */
T_LINE, /* `#line' */
T_ERROR, /* `#error' */
T_WARNING, /* `#warning' */
T_ENDIF, /* `#endif' */
T_SCCS, /* `#sccs', used on system V. */
T_IDENT, /* `#ident', used on system V. */
T_ASSERT, /* `#assert', taken from system V. */
T_UNASSERT, /* `#unassert', taken from system V. */
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_SIZE_TYPE, /* `__SIZE_TYPE__' */
T_PTRDIFF_TYPE, /* `__PTRDIFF_TYPE__' */
T_WCHAR_TYPE, /* `__WCHAR_TYPE__' */
T_USER_LABEL_PREFIX_TYPE, /* `__USER_LABEL_PREFIX__' */
T_REGISTER_PREFIX_TYPE, /* `__REGISTER_PREFIX__' */
T_IMMEDIATE_PREFIX_TYPE, /* `__IMMEDIATE_PREFIX__' */
T_TIME, /* `__TIME__' */
T_CONST, /* Constant value, used by `__STDC__' */
T_MACRO, /* macro defined by `#define' */
T_DISABLED, /* macro temporarily turned off for rescan */
T_SPEC_DEFINED, /* special `defined' macro for use in #if statements */
T_PCSTRING, /* precompiled string (hashval is KEYDEF *) */
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;
/* 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
static HASHNODE *hashtab[HASHSIZE];
#define HASHSTEP(old, c) ((old << 2) + c)
#define MAKE_POS(v) (v & 0x7fffffff) /* make number positive */
/* Symbols to predefine. */
#ifdef CPP_PREDEFINES
static char *predefs = CPP_PREDEFINES;
#else
static char *predefs = "";
#endif
/* We let tm.h override the types used here, to handle trivial differences
such as the choice of unsigned int or long unsigned int for size_t.
When machines start needing nontrivial differences in the size type,
it would be best to do something here to figure out automatically
from other information what type to use. */
/* The string value for __SIZE_TYPE__. */
#ifndef SIZE_TYPE
#define SIZE_TYPE "long unsigned int"
#endif
/* The string value for __PTRDIFF_TYPE__. */
#ifndef PTRDIFF_TYPE
#define PTRDIFF_TYPE "long int"
#endif
/* The string value for __WCHAR_TYPE__. */
#ifndef WCHAR_TYPE
#define WCHAR_TYPE "int"
#endif
char * wchar_type = WCHAR_TYPE;
#undef WCHAR_TYPE
/* The string value for __USER_LABEL_PREFIX__ */
#ifndef USER_LABEL_PREFIX
#define USER_LABEL_PREFIX ""
#endif
/* The string value for __REGISTER_PREFIX__ */
#ifndef REGISTER_PREFIX
#define REGISTER_PREFIX ""
#endif
/* The string value for __IMMEDIATE_PREFIX__ */
#ifndef IMMEDIATE_PREFIX
#define IMMEDIATE_PREFIX ""
#endif
/* In the definition of a #assert name, this structure forms
a list of the individual values asserted.
Each value is itself a list of "tokens".
These are strings that are compared by name. */
struct tokenlist_list {
struct tokenlist_list *next;
struct arglist *tokens;
};
struct assertion_hashnode {
struct assertion_hashnode *next; /* double links for easy deletion */
struct assertion_hashnode *prev;
/* 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. */
struct assertion_hashnode **bucket_hdr;
int length; /* length of token, for quick comparison */
U_CHAR *name; /* the actual name */
/* List of token-sequences. */
struct tokenlist_list *value;
};
typedef struct assertion_hashnode ASSERTION_HASHNODE;
/* 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 ASSERTION_HASHSIZE 37
static ASSERTION_HASHNODE *assertion_hashtab[ASSERTION_HASHSIZE];
/* Nonzero means inhibit macroexpansion of what seem to be
assertion tests, in rescan. For #if. */
static int assertions_flag;
/* `struct directive' defines one #-directive, including how to handle it. */
#define DO_PROTO PROTO((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *))
struct directive {
int length; /* Length of name */
int (*func) DO_PROTO; /* Function to handle directive */
char *name; /* Name of directive */
enum node_type type; /* Code which describes which directive. */
};
#define IS_INCLUDE_DIRECTIVE_TYPE(t) (T_INCLUDE <= (t) && (t) <= T_IMPORT)
/* These functions are declared to return int instead of void since they
are going to be placed in the table and some old compilers have trouble with
pointers to functions returning void. */
static int do_assert DO_PROTO;
static int do_define DO_PROTO;
static int do_elif DO_PROTO;
static int do_else DO_PROTO;
static int do_endif DO_PROTO;
static int do_error DO_PROTO;
static int do_ident DO_PROTO;
static int do_if DO_PROTO;
static int do_include DO_PROTO;
static int do_line DO_PROTO;
static int do_pragma DO_PROTO;
#ifdef SCCS_DIRECTIVE
static int do_sccs DO_PROTO;
#endif
static int do_unassert DO_PROTO;
static int do_undef DO_PROTO;
static int do_warning DO_PROTO;
static int do_xifdef DO_PROTO;
/* Here is the actual list of #-directives, most-often-used first. */
static struct directive directive_table[] = {
{ 6, do_define, "define", T_DEFINE},
{ 2, do_if, "if", T_IF},
{ 5, do_xifdef, "ifdef", T_IFDEF},
{ 6, do_xifdef, "ifndef", T_IFNDEF},
{ 5, do_endif, "endif", T_ENDIF},
{ 4, do_else, "else", T_ELSE},
{ 4, do_elif, "elif", T_ELIF},
{ 4, do_line, "line", T_LINE},
{ 7, do_include, "include", T_INCLUDE},
{ 12, do_include, "include_next", T_INCLUDE_NEXT},
{ 6, do_include, "import", T_IMPORT},
{ 5, do_undef, "undef", T_UNDEF},
{ 5, do_error, "error", T_ERROR},
{ 7, do_warning, "warning", T_WARNING},
#ifdef SCCS_DIRECTIVE
{ 4, do_sccs, "sccs", T_SCCS},
#endif
{ 6, do_pragma, "pragma", T_PRAGMA},
{ 5, do_ident, "ident", T_IDENT},
{ 6, do_assert, "assert", T_ASSERT},
{ 8, do_unassert, "unassert", T_UNASSERT},
{ -1, 0, "", T_UNUSED},
};
/* When a directive handler is called,
this points to the # (or the : of the %:) that started the directive. */
U_CHAR *directive_start;
/* table to tell if char can be part of a C identifier. */
U_CHAR is_idchar[256];
/* table to tell if char can be first char of a c identifier. */
U_CHAR is_idstart[256];
/* table to tell if c is horizontal space. */
static U_CHAR is_hor_space[256];
/* table to tell if c is horizontal or vertical space. */
U_CHAR is_space[256];
/* names of some characters */
static char *char_name[256];
#define SKIP_WHITE_SPACE(p) do { while (is_hor_space[*p]) p++; } while (0)
#define SKIP_ALL_WHITE_SPACE(p) do { while (is_space[*p]) p++; } while (0)
static int errors = 0; /* Error counter for exit code */
/* Name of output file, for error messages. */
static char *out_fname;
/* 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 */
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 */
U_CHAR *control_macro; /* For #ifndef at start of file,
this is the macro name tested. */
enum node_type type; /* type of last directive seen in this group */
};
typedef struct if_stack IF_STACK_FRAME;
static IF_STACK_FRAME *if_stack = NULL;
/* Buffer of -M output. */
static char *deps_buffer;
/* Number of bytes allocated in above. */
static int deps_allocated_size;
/* Number of bytes used. */
static int deps_size;
/* Number of bytes since the last newline. */
static int deps_column;
/* Nonzero means -I- has been seen,
so don't look for #include "foo" the source-file directory. */
static int ignore_srcdir;
static int safe_read PROTO((int, char *, int));
static void safe_write PROTO((int, char *, int));
int main PROTO((int, char **));
static void path_include PROTO((char *));
static U_CHAR *index0 PROTO((U_CHAR *, int, size_t));
static void trigraph_pcp PROTO((FILE_BUF *));
static void newline_fix PROTO((U_CHAR *));
static void name_newline_fix PROTO((U_CHAR *));
static char *get_lintcmd PROTO((U_CHAR *, U_CHAR *, U_CHAR **, int *, int *));
static void rescan PROTO((FILE_BUF *, int));
static FILE_BUF expand_to_temp_buffer PROTO((U_CHAR *, U_CHAR *, int, int));
static int handle_directive PROTO((FILE_BUF *, FILE_BUF *));
static struct tm *timestamp PROTO((void));
static void special_symbol PROTO((HASHNODE *, FILE_BUF *));
static int is_system_include PROTO((char *));
static char *base_name PROTO((char *));
static int absolute_filename PROTO((char *));
static size_t simplify_filename PROTO((char *));
static char *read_filename_string PROTO((int, FILE *));
static struct file_name_map *read_name_map PROTO((char *));
static int open_include_file PROTO((char *, struct file_name_list *, U_CHAR *, struct include_file **));
static char *remap_include_file PROTO((char *, struct file_name_list *));
static int lookup_ino_include PROTO((struct include_file *));
static void finclude PROTO((int, struct include_file *, FILE_BUF *, int, struct file_name_list *));
static void record_control_macro PROTO((struct include_file *, U_CHAR *));
static char *check_precompiled PROTO((int, struct stat *, char *, char **));
static int check_preconditions PROTO((char *));
static void pcfinclude PROTO((U_CHAR *, U_CHAR *, U_CHAR *, FILE_BUF *));
static void pcstring_used PROTO((HASHNODE *));
static void write_output PROTO((void));
static void pass_thru_directive PROTO((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
static MACRODEF create_definition PROTO((U_CHAR *, U_CHAR *, FILE_BUF *));
static int check_macro_name PROTO((U_CHAR *, char *));
static int compare_defs PROTO((DEFINITION *, DEFINITION *));
static int comp_def_part PROTO((int, U_CHAR *, int, U_CHAR *, int, int));
static DEFINITION *collect_expansion PROTO((U_CHAR *, U_CHAR *, int, struct arglist *));
int check_assertion PROTO((U_CHAR *, int, int, struct arglist *));
static int compare_token_lists PROTO((struct arglist *, struct arglist *));
static struct arglist *read_token_list PROTO((U_CHAR **, U_CHAR *, int *));
static void free_token_list PROTO((struct arglist *));
static ASSERTION_HASHNODE *assertion_install PROTO((U_CHAR *, int, int));
static ASSERTION_HASHNODE *assertion_lookup PROTO((U_CHAR *, int, int));
static void delete_assertion PROTO((ASSERTION_HASHNODE *));
static void do_once PROTO((void));
static HOST_WIDE_INT eval_if_expression PROTO((U_CHAR *, int));
static void conditional_skip PROTO((FILE_BUF *, int, enum node_type, U_CHAR *, FILE_BUF *));
static void skip_if_group PROTO((FILE_BUF *, int, FILE_BUF *));
static void validate_else PROTO((U_CHAR *, U_CHAR *));
static U_CHAR *skip_to_end_of_comment PROTO((FILE_BUF *, int *, int));
static U_CHAR *skip_quoted_string PROTO((U_CHAR *, U_CHAR *, int, int *, int *, int *));
static char *quote_string PROTO((char *, char *));
static U_CHAR *skip_paren_group PROTO((FILE_BUF *));
/* Last arg to output_line_directive. */
enum file_change_code {same_file, enter_file, leave_file};
static void output_line_directive PROTO((FILE_BUF *, FILE_BUF *, int, enum file_change_code));
static void macroexpand PROTO((HASHNODE *, FILE_BUF *));
struct argdata;
static char *macarg PROTO((struct argdata *, int));
static U_CHAR *macarg1 PROTO((U_CHAR *, U_CHAR *, int *, int *, int *, int));
static int discard_comments PROTO((U_CHAR *, int, int));
static int change_newlines PROTO((U_CHAR *, int));
char *my_strerror PROTO((int));
void error PRINTF_PROTO_1((char *, ...));
static void verror PROTO((char *, va_list));
static void error_from_errno PROTO((char *));
void warning PRINTF_PROTO_1((char *, ...));
static void vwarning PROTO((char *, va_list));
static void error_with_line PRINTF_PROTO_2((int, char *, ...));
static void verror_with_line PROTO((int, char *, va_list));
static void vwarning_with_line PROTO((int, char *, va_list));
static void warning_with_line PRINTF_PROTO_2((int, char *, ...));
void pedwarn PRINTF_PROTO_1((char *, ...));
void pedwarn_with_line PRINTF_PROTO_2((int, char *, ...));
static void pedwarn_with_file_and_line PRINTF_PROTO_3((char *, int, char *, ...));
static void print_containing_files PROTO((void));
static int line_for_error PROTO((int));
static int grow_outbuf PROTO((FILE_BUF *, int));
static HASHNODE *install PROTO((U_CHAR *, int, enum node_type, char *, int));
HASHNODE *lookup PROTO((U_CHAR *, int, int));
static void delete_macro PROTO((HASHNODE *));
static int hashf PROTO((U_CHAR *, int, int));
static void dump_single_macro PROTO((HASHNODE *, FILE *));
static void dump_all_macros PROTO((void));
static void dump_defn_1 PROTO((U_CHAR *, int, int, FILE *));
static void dump_arg_n PROTO((DEFINITION *, int, FILE *));
static void initialize_char_syntax PROTO((void));
static void initialize_builtins PROTO((FILE_BUF *, FILE_BUF *));
static void make_definition PROTO((char *, FILE_BUF *));
static void make_undef PROTO((char *, FILE_BUF *));
static void make_assertion PROTO((char *, char *));
static struct file_name_list *new_include_prefix PROTO((struct file_name_list *, char *, char *, char *));
static void append_include_chain PROTO((struct file_name_list *, struct file_name_list *));
static int quote_string_for_make PROTO((char *, char *));
static void deps_output PROTO((char *, int));
static void fatal PRINTF_PROTO_1((char *, ...)) __attribute__ ((noreturn));
void fancy_abort PROTO((void)) __attribute__ ((noreturn));
static void perror_with_name PROTO((char *));
static void pfatal_with_name PROTO((char *)) __attribute__ ((noreturn));
static void pipe_closed PROTO((int)) __attribute__ ((noreturn));
static void memory_full PROTO((void)) __attribute__ ((noreturn));
GENERIC_PTR xmalloc PROTO((size_t));
static GENERIC_PTR xrealloc PROTO((GENERIC_PTR, size_t));
static GENERIC_PTR xcalloc PROTO((size_t, size_t));
static char *savestring PROTO((char *));
/* Read LEN bytes at PTR from descriptor DESC, for file FILENAME,
retrying if necessary. If MAX_READ_LEN is defined, read at most
that bytes at a time. Return a negative value if an error occurs,
otherwise return the actual number of bytes read,
which must be LEN unless end-of-file was reached. */
static int
safe_read (desc, ptr, len)
int desc;
char *ptr;
int len;
{
int left, rcount, nchars;
left = len;
while (left > 0) {
rcount = left;
#ifdef MAX_READ_LEN
if (rcount > MAX_READ_LEN)
rcount = MAX_READ_LEN;
#endif
nchars = read (desc, ptr, rcount);
if (nchars < 0)
{
#ifdef EINTR
if (errno == EINTR)
continue;
#endif
return nchars;
}
if (nchars == 0)
break;
ptr += nchars;
left -= nchars;
}
return len - left;
}
/* Write LEN bytes at PTR to descriptor DESC,
retrying if necessary, and treating any real error as fatal.
If MAX_WRITE_LEN is defined, write at most that many bytes at a time. */
static void
safe_write (desc, ptr, len)
int desc;
char *ptr;
int len;
{
int wcount, written;
while (len > 0) {
wcount = len;
#ifdef MAX_WRITE_LEN
if (wcount > MAX_WRITE_LEN)
wcount = MAX_WRITE_LEN;
#endif
written = write (desc, ptr, wcount);
if (written < 0)
{
#ifdef EINTR
if (errno == EINTR)
continue;
#endif
pfatal_with_name (out_fname);
}
ptr += written;
len -= written;
}
}
int
main (argc, argv)
int argc;
char **argv;
{
struct stat st;
char *in_fname;
char *cp;
int f, i;
FILE_BUF *fp;
char **pend_files = (char **) xmalloc (argc * sizeof (char *));
char **pend_defs = (char **) xmalloc (argc * sizeof (char *));
char **pend_undefs = (char **) xmalloc (argc * sizeof (char *));
char **pend_assertions = (char **) xmalloc (argc * sizeof (char *));
char **pend_includes = (char **) xmalloc (argc * sizeof (char *));
/* Record the option used with each element of pend_assertions.
This is preparation for supporting more than one option for making
an assertion. */
char **pend_assertion_options = (char **) xmalloc (argc * sizeof (char *));
int inhibit_predefs = 0;
int no_standard_includes = 0;
int no_standard_cplusplus_includes = 0;
int missing_newline = 0;
/* Non-0 means don't output the preprocessed program. */
int inhibit_output = 0;
/* Non-0 means -v, so print the full set of include dirs. */
int verbose = 0;
/* File name which deps are being written to.
This is 0 if deps are being written to stdout. */
char *deps_file = 0;
/* Fopen file mode to open deps_file with. */
char *deps_mode = "a";
/* Stream on which to print the dependency information. */
FILE *deps_stream = 0;
/* Target-name to write with the dependency information. */
char *deps_target = 0;
#if defined (RLIMIT_STACK) && defined (HAVE_GETRLIMIT) && defined (HAVE_SETRLIMIT)
/* 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
#ifdef SIGPIPE
signal (SIGPIPE, pipe_closed);
#endif
progname = base_name (argv[0]);
#ifdef VMS
{
/* Remove extension from PROGNAME. */
char *p;
char *s = progname = savestring (progname);
if ((p = rindex (s, ';')) != 0) *p = '\0'; /* strip version number */
if ((p = rindex (s, '.')) != 0 /* strip type iff ".exe" */
&& (p[1] == 'e' || p[1] == 'E')
&& (p[2] == 'x' || p[2] == 'X')
&& (p[3] == 'e' || p[3] == 'E')
&& !p[4])
*p = '\0';
}
#endif
in_fname = NULL;
out_fname = NULL;
/* Initialize is_idchar. */
initialize_char_syntax ();
no_line_directives = 0;
no_trigraphs = 1;
dump_macros = dump_none;
no_output = 0;
cplusplus = 0;
cplusplus_comments = 1;
bzero ((char *) pend_files, argc * sizeof (char *));
bzero ((char *) pend_defs, argc * sizeof (char *));
bzero ((char *) pend_undefs, argc * sizeof (char *));
bzero ((char *) pend_assertions, argc * sizeof (char *));
bzero ((char *) pend_includes, argc * sizeof (char *));
/* 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 {
switch (argv[i][1]) {
case 'i':
if (!strcmp (argv[i], "-include")) {
if (i + 1 == argc)
fatal ("Filename missing after `-include' option");
else
simplify_filename (pend_includes[i] = argv[++i]);
}
if (!strcmp (argv[i], "-imacros")) {
if (i + 1 == argc)
fatal ("Filename missing after `-imacros' option");
else
simplify_filename (pend_files[i] = argv[++i]);
}
if (!strcmp (argv[i], "-iprefix")) {
if (i + 1 == argc)
fatal ("Filename missing after `-iprefix' option");
else
include_prefix = argv[++i];
}
if (!strcmp (argv[i], "-ifoutput")) {
output_conditionals = 1;
}
if (!strcmp (argv[i], "-isystem")) {
struct file_name_list *dirtmp;
if (! (dirtmp = new_include_prefix (NULL_PTR, NULL_PTR,
"", argv[++i])))
break;
dirtmp->c_system_include_path = 1;
if (before_system == 0)
before_system = dirtmp;
else
last_before_system->next = dirtmp;
last_before_system = dirtmp; /* Tail follows the last one */
}
/* Add directory to end of path for includes,
with the default prefix at the front of its name. */
if (!strcmp (argv[i], "-iwithprefix")) {
struct file_name_list *dirtmp;
char *prefix;
if (include_prefix != 0)
prefix = include_prefix;
else {
prefix = savestring (GCC_INCLUDE_DIR);
/* Remove the `include' from /usr/local/lib/gcc.../include. */
if (!strcmp (prefix + strlen (prefix) - 8, "/include"))
prefix[strlen (prefix) - 7] = 0;
}
if (! (dirtmp = new_include_prefix (NULL_PTR, NULL_PTR,
prefix, argv[++i])))
break;
if (after_include == 0)
after_include = dirtmp;
else
last_after_include->next = dirtmp;
last_after_include = dirtmp; /* Tail follows the last one */
}
/* Add directory to main path for includes,
with the default prefix at the front of its name. */
if (!strcmp (argv[i], "-iwithprefixbefore")) {
struct file_name_list *dirtmp;
char *prefix;
if (include_prefix != 0)
prefix = include_prefix;
else {
prefix = savestring (GCC_INCLUDE_DIR);
/* Remove the `include' from /usr/local/lib/gcc.../include. */
if (!strcmp (prefix + strlen (prefix) - 8, "/include"))
prefix[strlen (prefix) - 7] = 0;
}
dirtmp = new_include_prefix (NULL_PTR, NULL_PTR, prefix, argv[++i]);
append_include_chain (dirtmp, dirtmp);
}
/* Add directory to end of path for includes. */
if (!strcmp (argv[i], "-idirafter")) {
struct file_name_list *dirtmp;
if (! (dirtmp = new_include_prefix (NULL_PTR, NULL_PTR,
"", argv[++i])))
break;
if (after_include == 0)
after_include = dirtmp;
else
last_after_include->next = dirtmp;
last_after_include = dirtmp; /* Tail follows the last one */
}
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 'p':
if (!strcmp (argv[i], "-pedantic"))
pedantic = 1;
else if (!strcmp (argv[i], "-pedantic-errors")) {
pedantic = 1;
pedantic_errors = 1;
} else if (!strcmp (argv[i], "-pcp")) {
char *pcp_fname;
if (i + 1 == argc)
fatal ("Filename missing after -pcp option");
pcp_fname = argv[++i];
pcp_outfile
= ((pcp_fname[0] != '-' || pcp_fname[1] != '\0')
? fopen (pcp_fname, "w")
: stdout);
if (pcp_outfile == 0)
pfatal_with_name (pcp_fname);
no_precomp = 1;
}
break;
case 't':
if (!strcmp (argv[i], "-traditional")) {
traditional = 1;
cplusplus_comments = 0;
} else if (!strcmp (argv[i], "-trigraphs")) {
no_trigraphs = 0;
}
break;
case 'l':
if (! strcmp (argv[i], "-lang-c"))
cplusplus = 0, cplusplus_comments = 1, c89 = 0, objc = 0;
if (! strcmp (argv[i], "-lang-c89"))
cplusplus = 0, cplusplus_comments = 0, c89 = 1, objc = 0;
if (! strcmp (argv[i], "-lang-c++"))
cplusplus = 1, cplusplus_comments = 1, c89 = 0, objc = 0;
if (! strcmp (argv[i], "-lang-objc"))
cplusplus = 0, cplusplus_comments = 1, c89 = 0, objc = 1;
if (! strcmp (argv[i], "-lang-objc++"))
cplusplus = 1, cplusplus_comments = 1, c89 = 0, objc = 1;
if (! strcmp (argv[i], "-lang-asm"))
lang_asm = 1;
if (! strcmp (argv[i], "-lint"))
for_lint = 1;
break;
case '+':
cplusplus = 1, cplusplus_comments = 1;
break;
case 'w':
inhibit_warnings = 1;
break;
case 'W':
if (!strcmp (argv[i], "-Wtrigraphs"))
warn_trigraphs = 1;
else if (!strcmp (argv[i], "-Wno-trigraphs"))
warn_trigraphs = 0;
else if (!strcmp (argv[i], "-Wcomment"))
warn_comments = 1;
else if (!strcmp (argv[i], "-Wno-comment"))
warn_comments = 0;
else if (!strcmp (argv[i], "-Wcomments"))
warn_comments = 1;
else if (!strcmp (argv[i], "-Wno-comments"))
warn_comments = 0;
else if (!strcmp (argv[i], "-Wtraditional"))
warn_stringify = 1;
else if (!strcmp (argv[i], "-Wno-traditional"))
warn_stringify = 0;
else if (!strcmp (argv[i], "-Wundef"))
warn_undef = 1;
else if (!strcmp (argv[i], "-Wno-undef"))
warn_undef = 0;
else if (!strcmp (argv[i], "-Wimport"))
warn_import = 1;
else if (!strcmp (argv[i], "-Wno-import"))
warn_import = 0;
else if (!strcmp (argv[i], "-Werror"))
warnings_are_errors = 1;
else if (!strcmp (argv[i], "-Wno-error"))
warnings_are_errors = 0;
else if (!strcmp (argv[i], "-Wall"))
{
warn_trigraphs = 1;
warn_comments = 1;
}
break;
case 'M':
/* The style of the choices here is a bit mixed.
The chosen scheme is a hybrid of keeping all options in one string
and specifying each option in a separate argument:
-M|-MM|-MD file|-MMD file [-MG]. An alternative is:
-M|-MM|-MD file|-MMD file|-MG|-MMG; or more concisely:
-M[M][G][D file]. This is awkward to handle in specs, and is not
as extensible. */
/* ??? -MG must be specified in addition to one of -M or -MM.
This can be relaxed in the future without breaking anything.
The converse isn't true. */
/* -MG isn't valid with -MD or -MMD. This is checked for later. */
if (!strcmp (argv[i], "-MG"))
{
print_deps_missing_files = 1;
break;
}
if (!strcmp (argv[i], "-M"))
print_deps = 2;
else if (!strcmp (argv[i], "-MM"))
print_deps = 1;
else if (!strcmp (argv[i], "-MD"))
print_deps = 2;
else if (!strcmp (argv[i], "-MMD"))
print_deps = 1;
/* For -MD and -MMD options, write deps on file named by next arg. */
if (!strcmp (argv[i], "-MD")
|| !strcmp (argv[i], "-MMD")) {
if (i + 1 == argc)
fatal ("Filename missing after %s option", argv[i]);
i++;
deps_file = argv[i];
deps_mode = "w";
} else {
/* For -M and -MM, write deps on standard output
and suppress the usual output. */
deps_stream = stdout;
inhibit_output = 1;
}
break;
case 'd':
{
char *p = argv[i] + 2;
char c;
while ((c = *p++)) {
/* Arg to -d specifies what parts of macros to dump */
switch (c) {
case 'M':
dump_macros = dump_only;
no_output = 1;
break;
case 'N':
dump_macros = dump_names;
break;
case 'D':
dump_macros = dump_definitions;
break;
case 'I':
dump_includes = 1;
break;
}
}
}
break;
case 'g':
if (argv[i][2] == '3')
debug_output = 1;
break;
case 'v':
fprintf (stderr, "GNU CPP version %s", version_string);
#ifdef TARGET_VERSION
TARGET_VERSION;
#endif
fprintf (stderr, "\n");
verbose = 1;
break;
case 'H':
print_include_names = 1;
break;
case 'D':
if (argv[i][2] != 0)
pend_defs[i] = argv[i] + 2;
else if (i + 1 == argc)
fatal ("Macro name missing after -D option");
else
i++, pend_defs[i] = argv[i];
break;
case 'A':
{
char *p;
if (argv[i][2] != 0)
p = argv[i] + 2;
else if (i + 1 == argc)
fatal ("Assertion missing after -A option");
else
p = argv[++i];
if (!strcmp (p, "-")) {
/* -A- eliminates all predefined macros and assertions.
Let's include also any that were specified earlier
on the command line. That way we can get rid of any
that were passed automatically in from GCC. */
int j;
inhibit_predefs = 1;
for (j = 0; j < i; j++)
pend_defs[j] = pend_assertions[j] = 0;
} else {
pend_assertions[i] = p;
pend_assertion_options[i] = "-A";
}
}
break;
case 'U': /* JF #undef something */
if (argv[i][2] != 0)
pend_undefs[i] = argv[i] + 2;
else if (i + 1 == argc)
fatal ("Macro name missing after -U option");
else
pend_undefs[i] = argv[i+1], i++;
break;
case 'C':
put_out_comments = 1;
break;
case 'E': /* -E comes from cc -E; ignore it. */
break;
case 'P':
no_line_directives = 1;
break;
case '$': /* Don't include $ in identifiers. */
is_idchar['$'] = is_idstart['$'] = 0;
break;
case 'I': /* Add directory to path for includes. */
{
struct file_name_list *dirtmp;
if (! ignore_srcdir && !strcmp (argv[i] + 2, "-")) {
ignore_srcdir = 1;
/* Don't use any preceding -I directories for #include <...>. */
first_bracket_include = 0;
}
else {
dirtmp = new_include_prefix (last_include, NULL_PTR, "",
argv[i][2] ? argv[i] + 2 : argv[++i]);
append_include_chain (dirtmp, dirtmp);
}
}
break;
case 'n':
if (!strcmp (argv[i], "-nostdinc"))
/* -nostdinc causes no default include directories.
You must specify all include-file directories with -I. */
no_standard_includes = 1;
else if (!strcmp (argv[i], "-nostdinc++"))
/* -nostdinc++ causes no default C++-specific include directories. */
no_standard_cplusplus_includes = 1;
else if (!strcmp (argv[i], "-noprecomp"))
no_precomp = 1;
break;
case 'r':
if (!strcmp (argv[i], "-remap"))
remap = 1;
break;
case 'u':
/* Sun compiler passes undocumented switch "-undef".
Let's assume it means to inhibit the predefined symbols. */
inhibit_predefs = 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]);
}
}
}
/* Add dirs from CPATH after dirs from -I. */
/* There seems to be confusion about what CPATH should do,
so for the moment it is not documented. */
/* Some people say that CPATH should replace the standard include dirs,
but that seems pointless: it comes before them, so it overrides them
anyway. */
cp = getenv ("CPATH");
if (cp && ! no_standard_includes)
path_include (cp);
/* Initialize output buffer */
outbuf.buf = (U_CHAR *) xmalloc (OUTBUF_SIZE);
outbuf.bufp = outbuf.buf;
outbuf.length = OUTBUF_SIZE;
/* Do partial setup of input buffer for the sake of generating
early #line directives (when -g is in effect). */
fp = &instack[++indepth];
if (in_fname == NULL)
in_fname = "";
fp->nominal_fname = fp->fname = in_fname;
fp->lineno = 0;
/* In C++, wchar_t is a distinct basic type, and we can expect
__wchar_t to be defined by cc1plus. */
if (cplusplus)
wchar_type = "__wchar_t";
/* Install __LINE__, etc. Must follow initialize_char_syntax
and option processing. */
initialize_builtins (fp, &outbuf);
/* Do standard #defines and assertions
that identify system and machine type. */
if (!inhibit_predefs) {
char *p = (char *) alloca (strlen (predefs) + 1);
strcpy (p, predefs);
while (*p) {
char *q;
while (*p == ' ' || *p == '\t')
p++;
/* Handle -D options. */
if (p[0] == '-' && p[1] == 'D') {
q = &p[2];
while (*p && *p != ' ' && *p != '\t')
p++;
if (*p != 0)
*p++= 0;
if (debug_output)
output_line_directive (fp, &outbuf, 0, same_file);
make_definition (q, &outbuf);
while (*p == ' ' || *p == '\t')
p++;
} else if (p[0] == '-' && p[1] == 'A') {
/* Handle -A options (assertions). */
char *assertion;
char *past_name;
char *value;
char *past_value;
char *termination;
int save_char;
assertion = &p[2];
past_name = assertion;
/* Locate end of name. */
while (*past_name && *past_name != ' '
&& *past_name != '\t' && *past_name != '(')
past_name++;
/* Locate `(' at start of value. */
value = past_name;
while (*value && (*value == ' ' || *value == '\t'))
value++;
if (*value++ != '(')
abort ();
while (*value && (*value == ' ' || *value == '\t'))
value++;
past_value = value;
/* Locate end of value. */
while (*past_value && *past_value != ' '
&& *past_value != '\t' && *past_value != ')')
past_value++;
termination = past_value;
while (*termination && (*termination == ' ' || *termination == '\t'))
termination++;
if (*termination++ != ')')
abort ();
if (*termination && *termination != ' ' && *termination != '\t')
abort ();
/* Temporarily null-terminate the value. */
save_char = *termination;
*termination = '\0';
/* Install the assertion. */
make_assertion ("-A", assertion);
*termination = (char) save_char;
p = termination;
while (*p == ' ' || *p == '\t')
p++;
} else {
abort ();
}
}
}
/* Now handle the command line options. */
/* Do -U's, -D's and -A's in the order they were seen. */
for (i = 1; i < argc; i++) {
if (pend_undefs[i]) {
if (debug_output)
output_line_directive (fp, &outbuf, 0, same_file);
make_undef (pend_undefs[i], &outbuf);
}
if (pend_defs[i]) {
if (debug_output)
output_line_directive (fp, &outbuf, 0, same_file);
make_definition (pend_defs[i], &outbuf);
}
if (pend_assertions[i])
make_assertion (pend_assertion_options[i], pend_assertions[i]);
}
done_initializing = 1;
{ /* Read the appropriate environment variable and if it exists
replace include_defaults with the listed path. */
char *epath = 0;
switch ((objc << 1) + cplusplus)
{
case 0:
epath = getenv ("C_INCLUDE_PATH");
break;
case 1:
epath = getenv ("CPLUS_INCLUDE_PATH");
break;
case 2:
epath = getenv ("OBJC_INCLUDE_PATH");
break;
case 3:
epath = getenv ("OBJCPLUS_INCLUDE_PATH");
break;
}
/* If the environment var for this language is set,
add to the default list of include directories. */
if (epath) {
int num_dirs;
char *startp, *endp;
for (num_dirs = 1, startp = epath; *startp; startp++)
if (*startp == PATH_SEPARATOR)
num_dirs++;
include_defaults
= (struct default_include *) xmalloc ((num_dirs
* sizeof (struct default_include))
+ sizeof (include_defaults_array));
startp = endp = epath;
num_dirs = 0;
while (1) {
char c = *endp++;
if (c == PATH_SEPARATOR || !c) {
endp[-1] = 0;
include_defaults[num_dirs].fname
= startp == endp ? "." : savestring (startp);
endp[-1] = c;
include_defaults[num_dirs].component = 0;
include_defaults[num_dirs].cplusplus = cplusplus;
include_defaults[num_dirs].cxx_aware = 1;
num_dirs++;
if (!c)
break;
startp = endp;
}
}
/* Put the usual defaults back in at the end. */
bcopy ((char *) include_defaults_array,
(char *) &include_defaults[num_dirs],
sizeof (include_defaults_array));
}
}
append_include_chain (before_system, last_before_system);
first_system_include = before_system;
/* Unless -fnostdinc,
tack on the standard include file dirs to the specified list */
if (!no_standard_includes) {
struct default_include *p = include_defaults;
char *specd_prefix = include_prefix;
char *default_prefix = savestring (GCC_INCLUDE_DIR);
int default_len = 0;
/* Remove the `include' from /usr/local/lib/gcc.../include. */
if (!strcmp (default_prefix + strlen (default_prefix) - 8, "/include")) {
default_len = strlen (default_prefix) - 7;
default_prefix[default_len] = 0;
}
/* Search "translated" versions of GNU directories.
These have /usr/local/lib/gcc... replaced by specd_prefix. */
if (specd_prefix != 0 && default_len != 0)
for (p = include_defaults; p->fname; p++) {
/* Some standard dirs are only for C++. */
if (!p->cplusplus || (cplusplus && !no_standard_cplusplus_includes)) {
/* Does this dir start with the prefix? */
if (!strncmp (p->fname, default_prefix, default_len)) {
/* Yes; change prefix and add to search list. */
struct file_name_list *new
= new_include_prefix (NULL_PTR, NULL_PTR, specd_prefix,
p->fname + default_len);
if (new) {
new->c_system_include_path = !p->cxx_aware;
append_include_chain (new, new);
if (first_system_include == 0)
first_system_include = new;
}
}
}
}
/* Search ordinary names for GNU include directories. */
for (p = include_defaults; p->fname; p++) {
/* Some standard dirs are only for C++. */
if (!p->cplusplus || (cplusplus && !no_standard_cplusplus_includes)) {
struct file_name_list *new
= new_include_prefix (NULL_PTR, p->component, "", p->fname);
if (new) {
new->c_system_include_path = !p->cxx_aware;
append_include_chain (new, new);
if (first_system_include == 0)
first_system_include = new;
}
}
}
}
/* Tack the after_include chain at the end of the include chain. */
append_include_chain (after_include, last_after_include);
if (first_system_include == 0)
first_system_include = after_include;
/* With -v, print the list of dirs to search. */
if (verbose) {
struct file_name_list *p;
fprintf (stderr, "#include \"...\" search starts here:\n");
for (p = include; p; p = p->next) {
if (p == first_bracket_include)
fprintf (stderr, "#include <...> search starts here:\n");
if (!p->fname[0])
fprintf (stderr, " .\n");
else if (!strcmp (p->fname, "/") || !strcmp (p->fname, "//"))
fprintf (stderr, " %s\n", p->fname);
else
/* Omit trailing '/'. */
fprintf (stderr, " %.*s\n", (int) strlen (p->fname) - 1, p->fname);
}
fprintf (stderr, "End of search list.\n");
}
/* -MG doesn't select the form of output and must be specified with one of
-M or -MM. -MG doesn't make sense with -MD or -MMD since they don't
inhibit compilation. */
if (print_deps_missing_files && (print_deps == 0 || !inhibit_output))
fatal ("-MG must be specified with one of -M or -MM");
/* 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
&& (getenv ("SUNPRO_DEPENDENCIES") != 0
|| getenv ("DEPENDENCIES_OUTPUT") != 0)) {
char *spec = getenv ("DEPENDENCIES_OUTPUT");
char *s;
char *output_file;
if (spec == 0) {
spec = getenv ("SUNPRO_DEPENDENCIES");
print_deps = 2;
}
else
print_deps = 1;
s = spec;
/* Find the space before the DEPS_TARGET, if there is one. */
/* This should use index. (mrs) */
while (*s != 0 && *s != ' ') s++;
if (*s != 0) {
deps_target = s + 1;
output_file = xmalloc (s - spec + 1);
bcopy (spec, output_file, s - spec);
output_file[s - spec] = 0;
}
else {
deps_target = 0;
output_file = spec;
}
deps_file = output_file;
deps_mode = "a";
}
/* For -M, print the expected object file name
as the target of this Make-rule. */
if (print_deps) {
deps_allocated_size = 200;
deps_buffer = xmalloc (deps_allocated_size);
deps_buffer[0] = 0;
deps_size = 0;
deps_column = 0;
if (deps_target) {
deps_output (deps_target, ':');
} else if (*in_fname == 0) {
deps_output ("-", ':');
} else {
char *p, *q;
int len;
q = base_name (in_fname);
/* Copy remainder to mungable area. */
p = (char *) alloca (strlen(q) + 8);
strcpy (p, q);
/* Output P, but remove known suffixes. */
len = strlen (p);
q = p + len;
if (len >= 2
&& p[len - 2] == '.'
&& index("cCsSm", p[len - 1]))
q = p + (len - 2);
else if (len >= 3
&& p[len - 3] == '.'
&& p[len - 2] == 'c'
&& p[len - 1] == 'c')
q = p + (len - 3);
else if (len >= 4
&& p[len - 4] == '.'
&& p[len - 3] == 'c'
&& p[len - 2] == 'x'
&& p[len - 1] == 'x')
q = p + (len - 4);
else if (len >= 4
&& p[len - 4] == '.'
&& p[len - 3] == 'c'
&& p[len - 2] == 'p'
&& p[len - 1] == 'p')
q = p + (len - 4);
/* Supply our own suffix. */
strcpy (q, OBJECT_SUFFIX);
deps_output (p, ':');
deps_output (in_fname, ' ');
}
}
/* Scan the -imacros files before the main input.
Much like #including them, but with no_output set
so that only their macro definitions matter. */
no_output++; no_record_file++;
for (i = 1; i < argc; i++)
if (pend_files[i]) {
struct include_file *inc;
int fd = open_include_file (pend_files[i], NULL_PTR, NULL_PTR, &inc);
if (fd < 0) {
perror_with_name (pend_files[i]);
return FATAL_EXIT_CODE;
}
finclude (fd, inc, &outbuf, 0, NULL_PTR);
}
no_output--; no_record_file--;
/* Copy the entire contents of the main input file into
the stacked input buffer previously allocated for it. */
/* 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 perror;
if (fstat (f, &st) != 0)
pfatal_with_name (in_fname);
fp->nominal_fname = fp->fname = in_fname;
fp->lineno = 1;
fp->system_header_p = 0;
/* JF all this is mine about reading pipes and ttys */
if (! S_ISREG (st.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;
if (S_ISDIR (st.st_mode))
fatal ("Input file `%s' is a directory", in_fname);
bsize = 2000;
size = 0;
fp->buf = (U_CHAR *) xmalloc (bsize + 2);
for (;;) {
cnt = safe_read (f, (char *) fp->buf + size, bsize - size);
if (cnt < 0) goto perror; /* error! */
size += cnt;
if (size != bsize) break; /* End of file */
bsize *= 2;
fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 2);
}
fp->length = size;
} else {
/* Read a file whose size we can determine in advance.
For the sake of VMS, st.st_size is just an upper bound. */
size_t s = (size_t) st.st_size;
if (s != st.st_size || s + 2 < s)
memory_full ();
fp->buf = (U_CHAR *) xmalloc (s + 2);
fp->length = safe_read (f, (char *) fp->buf, s);
if (fp->length < 0) goto perror;
}
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')
/* Backslash-newline at end is not good enough. */
|| (fp->length > 1 && fp->buf[fp->length - 2] == '\\')) {
fp->buf[fp->length++] = '\n';
missing_newline = 1;
}
fp->buf[fp->length] = '\0';
/* Unless inhibited, convert trigraphs in the input. */
if (!no_trigraphs)
trigraph_pcp (fp);
/* 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_directive (fp, &outbuf, 0, same_file);
/* Scan the -include files before the main input. */
no_record_file++;
for (i = 1; i < argc; i++)
if (pend_includes[i]) {
struct include_file *inc;
int fd = open_include_file (pend_includes[i], NULL_PTR, NULL_PTR, &inc);
if (fd < 0) {
perror_with_name (pend_includes[i]);
return FATAL_EXIT_CODE;
}
finclude (fd, inc, &outbuf, 0, NULL_PTR);
}
no_record_file--;
/* Scan the input, processing macros and directives. */
rescan (&outbuf, 0);
if (missing_newline)
fp->lineno--;
if (pedantic && missing_newline)
pedwarn ("file does not end in newline");
/* Now we have processed the entire input
Write whichever kind of output has been requested. */
if (dump_macros == dump_only)
dump_all_macros ();
else if (! inhibit_output) {
write_output ();
}
if (print_deps) {
/* Don't actually write the deps file if compilation has failed. */
if (errors == 0) {
if (deps_file && ! (deps_stream = fopen (deps_file, deps_mode)))
pfatal_with_name (deps_file);
fputs (deps_buffer, deps_stream);
putc ('\n', deps_stream);
if (deps_file) {
if (ferror (deps_stream) || fclose (deps_stream) != 0)
fatal ("I/O error on output");
}
}
}
if (pcp_outfile && pcp_outfile != stdout
&& (ferror (pcp_outfile) || fclose (pcp_outfile) != 0))
fatal ("I/O error on `-pcp' output");
if (ferror (stdout) || fclose (stdout) != 0)
fatal ("I/O error on output");
if (errors)
exit (FATAL_EXIT_CODE);
exit (SUCCESS_EXIT_CODE);
perror:
pfatal_with_name (in_fname);
return 0;
}
/* Given a colon-separated list of file names PATH,
add all the names to the search path for include files. */
static void
path_include (path)
char *path;
{
char *p;
p = path;
if (*p)
while (1) {
char *q = p;
char c;
struct file_name_list *dirtmp;
/* Find the end of this name. */
while ((c = *q++) != PATH_SEPARATOR && c)
continue;
q[-1] = 0;
dirtmp = new_include_prefix (last_include, NULL_PTR,
"", p == q ? "." : p);
q[-1] = c;
append_include_chain (dirtmp, dirtmp);
/* Advance past this name. */
p = q;
if (! c)
break;
}
}
/* Return the address of the first character in S that equals C.
S is an array of length N, possibly containing '\0's, and followed by '\0'.
Return 0 if there is no such character. Assume that C itself is not '\0'.
If we knew we could use memchr, we could just invoke memchr (S, C, N),
but unfortunately memchr isn't autoconfigured yet. */
static U_CHAR *
index0 (s, c, n)
U_CHAR *s;
int c;
size_t n;
{
char *p = (char *) s;
for (;;) {
char *q = index (p, c);
if (q)
return (U_CHAR *) q;
else {
size_t l = strlen (p);
if (l == n)
return 0;
l++;
p += l;
n -= l;
}
}
}
/* Pre-C-Preprocessor to translate ANSI trigraph idiocy in BUF
before main CCCP processing. Name `pcp' is also in honor of the
drugs the trigraph designers must have been on.
Using an extra pass through the buffer takes a little extra time,
but is infinitely less hairy than trying to handle trigraphs inside
strings, etc. everywhere, and also makes sure that trigraphs are
only translated in the top level of processing. */
static void
trigraph_pcp (buf)
FILE_BUF *buf;
{
register U_CHAR c, *fptr, *bptr, *sptr, *lptr;
int len;
fptr = bptr = sptr = buf->buf;
lptr = fptr + buf->length;
while ((sptr = index0 (sptr, '?', (size_t) (lptr - sptr))) != NULL) {
if (*++sptr != '?')
continue;
switch (*++sptr) {
case '=':
c = '#';
break;
case '(':
c = '[';
break;
case '/':
c = '\\';
break;
case ')':
c = ']';
break;
case '\'':
c = '^';
break;
case '<':
c = '{';
break;
case '!':
c = '|';
break;
case '>':
c = '}';
break;
case '-':
c = '~';
break;
case '?':
sptr--;
continue;
default:
continue;
}
len = sptr - fptr - 2;
/* BSD doc says bcopy () works right for overlapping strings. In ANSI
C, this will be memmove (). */
if (bptr != fptr && len > 0)
bcopy ((char *) fptr, (char *) bptr, len);
bptr += len;
*bptr++ = c;
fptr = ++sptr;
}
len = buf->length - (fptr - buf->buf);
if (bptr != fptr && len > 0)
bcopy ((char *) fptr, (char *) bptr, len);
buf->length -= fptr - bptr;
buf->buf[buf->length] = '\0';
if (warn_trigraphs && fptr != bptr)
warning_with_line (0, "%lu trigraph(s) encountered",
(unsigned long) (fptr - bptr) / 2);
}
/* Move all backslash-newline pairs out of embarrassing places.
Exchange all such pairs following BP
with any potentially-embarrassing 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;
{
register U_CHAR *p = bp;
/* First count the backslash-newline pairs here. */
while (p[0] == '\\' && p[1] == '\n')
p += 2;
/* What follows the backslash-newlines is not embarrassing. */
if (*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 (bp < p) {
*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;
{
register U_CHAR *p = bp;
/* First count the backslash-newline pairs here. */
while (p[0] == '\\' && p[1] == '\n')
p += 2;
/* What follows the backslash-newlines is not embarrassing. */
if (!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 (bp < p) {
*bp++ = '\\';
*bp++ = '\n';
}
}
/* Look for lint commands in comments.
When we come in here, ibp points into a comment. Limit is as one expects.
scan within the comment -- it should start, after lwsp, with a lint command.
If so that command is returned as a (constant) string.
Upon return, any arg will be pointed to with argstart and will be
arglen long. Note that we don't parse that arg since it will just
be printed out again. */
static char *
get_lintcmd (ibp, limit, argstart, arglen, cmdlen)
register U_CHAR *ibp;
register U_CHAR *limit;
U_CHAR **argstart; /* point to command arg */
int *arglen, *cmdlen; /* how long they are */
{
HOST_WIDE_INT linsize;
register U_CHAR *numptr; /* temp for arg parsing */
*arglen = 0;
SKIP_WHITE_SPACE (ibp);
if (ibp >= limit) return NULL;
linsize = limit - ibp;
/* Oh, I wish C had lexical functions... hell, I'll just open-code the set */
if ((linsize >= 10) && !bcmp (ibp, "NOTREACHED", 10)) {
*cmdlen = 10;
return "NOTREACHED";
}
if ((linsize >= 8) && !bcmp (ibp, "ARGSUSED", 8)) {
*cmdlen = 8;
return "ARGSUSED";
}
if ((linsize >= 11) && !bcmp (ibp, "LINTLIBRARY", 11)) {
*cmdlen = 11;
return "LINTLIBRARY";
}
if ((linsize >= 7) && !bcmp (ibp, "VARARGS", 7)) {
*cmdlen = 7;
ibp += 7; linsize -= 7;
if ((linsize == 0) || ! isdigit (*ibp)) return "VARARGS";
/* OK, read a number */
for (numptr = *argstart = ibp; (numptr < limit) && isdigit (*numptr);
numptr++);
*arglen = numptr - *argstart;
return "VARARGS";
}
return NULL;
}
/*
* 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. */
register U_CHAR c;
/* Length of pending accumulated identifier. */
register int ident_length = 0;
/* Hash code of pending accumulated identifier. */
register int hash = 0;
/* Current input level (&instack[indepth]). */
FILE_BUF *ip;
/* Pointer for scanning input. */
register U_CHAR *ibp;
/* Pointer to end of input. End of scan is controlled by LIMIT. */
register U_CHAR *limit;
/* Pointer for storing output. */
register 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;
/* 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; \
obp = op->bufp; } while (0)
if (no_output && instack[indepth].fname != 0)
skip_if_group (&instack[indepth], 1, NULL);
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 == '\n' && !ip->macro) {
/* At the top level, always merge lines ending with backslash-newline,
even in middle of identifier. But do not merge lines in a macro,
since backslash might be followed by a newline-space marker. */
++ibp;
++ip->lineno;
--obp; /* remove backslash from obuf */
break;
}
/* If ANSI, backslash is just another character outside a string. */
if (!traditional)
goto randomchar;
/* 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;
if (ibp < limit)
*obp++ = *ibp++;
break;
case '%':
if (ident_length || ip->macro || traditional)
goto randomchar;
while (*ibp == '\\' && ibp[1] == '\n') {
ibp += 2;
++ip->lineno;
}
if (*ibp != ':')
break;
/* Treat this %: digraph as if it were #. */
/* Fall through. */
case '#':
if (assertions_flag) {
if (ident_length)
goto specialchar;
/* Copy #foo (bar lose) without macro expansion. */
obp[-1] = '#'; /* In case it was '%'. */
SKIP_WHITE_SPACE (ibp);
while (is_idchar[*ibp])
*obp++ = *ibp++;
SKIP_WHITE_SPACE (ibp);
if (*ibp == '(') {
ip->bufp = ibp;
skip_paren_group (ip);
bcopy ((char *) ibp, (char *) obp, ip->bufp - ibp);
obp += ip->bufp - ibp;
ibp = ip->bufp;
}
break;
}
/* If this is expanding a macro definition, don't recognize
preprocessing directives. */
if (ip->macro != 0)
goto randomchar;
/* If this is expand_into_temp_buffer,
don't recognize them either. Warn about them
only after an actual newline at this level,
not at the beginning of the input level. */
if (! ip->fname) {
if (ip->buf != beg_of_line)
warning ("preprocessing directive not recognized within macro arg");
goto randomchar;
}
if (ident_length)
goto specialchar;
/* # keyword: a # must be first nonblank char on the line */
if (beg_of_line == 0)
goto randomchar;
{
U_CHAR *bp;
/* 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;
/* If -traditional, require # to be at beginning of line. */
if (!traditional) {
while (1) {
if (is_hor_space[*bp])
bp++;
else if (*bp == '\\' && bp[1] == '\n')
bp += 2;
else if (*bp == '/' && bp[1] == '*') {
bp += 2;
while (!(*bp == '*' && bp[1] == '/'))
bp++;
bp += 2;
}
/* There is no point in trying to deal with C++ // comments here,
because if there is one, then this # must be part of the
comment and we would never reach here. */
else break;
}
if (c == '%') {
if (bp[0] != '%')
break;
while (bp[1] == '\\' && bp[2] == '\n')
bp += 2;
if (bp + 1 != ibp)
break;
/* %: appears at start of line; skip past the ':' too. */
bp++;
ibp++;
}
}
if (bp + 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, 0);
RECACHE;
beg_of_line = ibp;
break;
}
*obp++ = '#'; /* Copy # (even if it was originally %:). */
/* Don't expand an identifier that could be a macro directive.
(Section 3.8.3 of the ANSI C standard) */
SKIP_WHITE_SPACE (ibp);
if (is_idstart[*ibp])
{
*obp++ = *ibp++;
while (is_idchar[*ibp])
*obp++ = *ibp++;
}
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, 0);
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 */
/* Handle any pending identifier;
but the L in L'...' or L"..." is not an identifier. */
if (ident_length
&& ! (ident_length == 1 && hash == HASHSTEP (0, 'L')))
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.
This can happen naturally if -traditional.
Otherwise, only -D can make a macro with an unmatched quote. */
POPMACRO;
RECACHE;
continue;
}
if (!traditional) {
error_with_line (line_for_error (start_line),
"unterminated string or character constant");
error_with_line (multiline_string_line,
"possible real start of unterminated constant");
multiline_string_line = 0;
}
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. */
if (traditional) {
beg_of_line = ibp;
goto while2end;
}
if (c == '\'') {
error_with_line (line_for_error (start_line),
"unterminated character constant");
goto while2end;
}
if (multiline_string_line == 0) {
if (pedantic)
pedwarn_with_line (line_for_error (start_line),
"string constant runs past end of line");
multiline_string_line = ip->lineno - 1;
}
break;
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);
if (*ibp != '*'
&& !(cplusplus_comments && *ibp == '/'))
goto randomchar;
if (ip->macro != 0)
goto randomchar;
if (ident_length)
goto specialchar;
if (*ibp == '/') {
/* C++ style comment... */
start_line = ip->lineno;
/* Comments are equivalent to spaces. */
if (! put_out_comments)
obp[-1] = ' ';
{
U_CHAR *before_bp = ibp;
while (++ibp < limit) {
if (*ibp == '\n') {
if (ibp[-1] != '\\') {
if (put_out_comments) {
bcopy ((char *) before_bp, (char *) obp, ibp - before_bp);
obp += ibp - before_bp;
}
break;
}
if (warn_comments)
warning ("multiline `//' comment");
++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;
}
}
break;
}
}
/* Ordinary C comment. Skip it, optionally copying it to output. */
start_line = ip->lineno;
++ibp; /* Skip the star. */
/* If this cpp is for lint, we peek inside the comments: */
if (for_lint) {
U_CHAR *argbp;
int cmdlen, arglen;
char *lintcmd = get_lintcmd (ibp, limit, &argbp, &arglen, &cmdlen);
if (lintcmd != NULL) {
op->bufp = obp;
check_expand (op, cmdlen + arglen + 14);
obp = op->bufp;
/* I believe it is always safe to emit this newline: */
obp[-1] = '\n';
bcopy ("#pragma lint ", (char *) obp, 13);
obp += 13;
bcopy (lintcmd, (char *) obp, cmdlen);
obp += cmdlen;
if (arglen != 0) {
*(obp++) = ' ';
bcopy (argbp, (char *) obp, arglen);
obp += arglen;
}
/* OK, now bring us back to the state we were in before we entered
this branch. We need #line because the #pragma's newline always
messes up the line count. */
op->bufp = obp;
output_line_directive (ip, op, 0, same_file);
check_expand (op, limit - ibp + 2);
obp = op->bufp;
*(obp++) = '/';
}
}
/* Comments are equivalent to spaces.
Note that we already output the slash; we might not want it.
For -traditional, a comment is equivalent to nothing. */
if (! put_out_comments) {
if (traditional)
obp--;
else
obp[-1] = ' ';
}
else
*obp++ = '*';
{
U_CHAR *before_bp = ibp;
for (;;) {
switch (*ibp++) {
case '*':
if (ibp[-2] == '/' && warn_comments)
warning ("`/*' within comment");
if (*ibp == '\\' && ibp[1] == '\n')
newline_fix (ibp);
if (*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;
break;
case 0:
if (limit < ibp) {
error_with_line (line_for_error (start_line),
"unterminated comment");
goto limit_reached;
}
break;
}
}
comment_end:
ibp++;
if (put_out_comments) {
bcopy ((char *) before_bp, (char *) obp, ibp - before_bp);
obp += ibp - before_bp;
}
}
break;
case '$':
if (! is_idchar['$'])
goto randomchar;
if (pedantic)
pedwarn ("`$' in identifier");
goto letter;
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) {
for (;;) {
while (ibp[0] == '\\' && ibp[1] == '\n') {
++ip->lineno;
ibp += 2;
}
c = *ibp++;
if (!is_idchar[c] && c != '.') {
--ibp;
break;
}
*obp++ = c;
/* A sign can be part of a preprocessing number
if it follows an `e' or `p'. */
if (c == 'e' || c == 'E' || c == 'p' || c == 'P') {
while (ibp[0] == '\\' && ibp[1] == '\n') {
++ip->lineno;
ibp += 2;
}
if (*ibp == '+' || *ibp == '-') {
*obp++ = *ibp++;
/* But traditional C does not let the token go past the sign,
and C89 does not allow `p'. */
if (traditional || (c89 && (c == 'p' || c == 'P')))
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':
letter:
ident_length++;
/* Compute step of hash function, to avoid a proc call on every token */
hash = HASHSTEP (hash, c);
break;
case '\n':
if (ip->fname == 0 && *ibp == '-') {
/* Newline - inhibits expansion of preceding token.
If expanding a macro arg, we keep the newline -.
In final output, it is deleted.
We recognize Newline - in macro bodies and macro args. */
if (! concatenated) {
ident_length = 0;
hash = 0;
}
ibp++;
if (!output_marks) {
obp--;
} else {
/* If expanding a macro arg, keep the newline -. */
*obp++ = '-';