diff options
Diffstat (limited to 'gnu/usr.bin/cc/cpp/cccp.c')
| -rw-r--r-- | gnu/usr.bin/cc/cpp/cccp.c | 9144 | 
1 files changed, 9144 insertions, 0 deletions
| diff --git a/gnu/usr.bin/cc/cpp/cccp.c b/gnu/usr.bin/cc/cpp/cccp.c new file mode 100644 index 000000000000..86ba2cfaa70e --- /dev/null +++ b/gnu/usr.bin/cc/cpp/cccp.c @@ -0,0 +1,9144 @@ +/* C Compatible Compiler Preprocessor (CCCP) +Copyright (C) 1986, 1987, 1989, 1992 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, 675 Mass Ave, Cambridge, MA 02139, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them.   Help stamp out software-hoarding!  */ + +typedef unsigned char U_CHAR; + +#ifdef EMACS +#define NO_SHORTNAMES +#include "../src/config.h" +#ifdef open +#undef open +#undef read +#undef write +#endif /* open */ +#endif /* EMACS */ + +/* The macro EMACS is defined when cpp is distributed as part of Emacs, +   for the sake of machines with limited C compilers.  */ +#ifndef EMACS +#include "config.h" +#endif /* not EMACS */ + +#ifndef STANDARD_INCLUDE_DIR +#define STANDARD_INCLUDE_DIR "/usr/include" +#endif + +#ifndef LOCAL_INCLUDE_DIR +#define LOCAL_INCLUDE_DIR "/usr/local/include" +#endif + +#if 0 /* We can't get ptrdiff_t, so I arranged not to need PTR_INT_TYPE.  */ +#ifdef __STDC__ +#define PTR_INT_TYPE ptrdiff_t +#else +#define PTR_INT_TYPE long +#endif +#endif /* 0 */ + +#include "pcp.h" + +#ifndef STDC_VALUE +#define STDC_VALUE 1 +#endif + +/* By default, colon separates directories in a path.  */ +#ifndef PATH_SEPARATOR +#define PATH_SEPARATOR ':' +#endif + +/* In case config.h defines these.  */ +#undef bcopy +#undef bzero +#undef bcmp + +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <stdio.h> +#include <signal.h> + +#ifndef VMS +#ifndef USG +#include <sys/time.h>		/* for __DATE__ and __TIME__ */ +#include <sys/resource.h> +#else +#include <time.h> +#include <fcntl.h> +#endif /* USG */ +#endif /* not VMS */ + +/* This defines "errno" properly for VMS, and gives us EACCES. */ +#include <errno.h> + +/* VMS-specific definitions */ +#ifdef VMS +#include <time.h> +#include <perror.h>		/* This defines sys_errlist/sys_nerr properly */ +#include <descrip.h> +#define O_RDONLY	0	/* Open arg for Read/Only  */ +#define O_WRONLY	1	/* Open arg for Write/Only */ +#define read(fd,buf,size)	VMS_read (fd,buf,size) +#define write(fd,buf,size)	VMS_write (fd,buf,size) +#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 strncat(dst,src,cnt) VMS_strncat (dst,src,cnt) +static char * VMS_strncat (); +static int VMS_read (); +static int VMS_write (); +static int VMS_open (); +static FILE * VMS_fopen (); +static FILE * VMS_freopen (); +static void hack_vms_include_specification (); +typedef struct { unsigned :16, :16, :16; } vms_ino_t; +#define ino_t vms_ino_t +#define INCLUDE_LEN_FUDGE 10	/* leave room for VMS syntax conversion */ +#ifdef __GNUC__ +#define BSTRING			/* VMS/GCC supplies the bstring routines */ +#endif /* __GNUC__ */ +#endif /* VMS */ +   +extern char *index (); +extern char *rindex (); + +#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.  */ + +#ifndef HOST_BITS_PER_WIDE_INT + +#if HOST_BITS_PER_LONG > HOST_BITS_PER_INT +#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_LONG +#define HOST_WIDE_INT long +#else +#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_INT +#define HOST_WIDE_INT int +#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 + +/* Define a generic NULL if one hasn't already been defined.  */ + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef GENERIC_PTR +#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__) +#define GENERIC_PTR void * +#else +#define GENERIC_PTR char * +#endif +#endif + +#ifndef NULL_PTR +#define NULL_PTR ((GENERIC_PTR)0) +#endif + +#ifndef INCLUDE_LEN_FUDGE +#define INCLUDE_LEN_FUDGE 0 +#endif + +/* Forward declarations.  */ + +char *xmalloc (); +void error (); +void warning (); + +/* External declarations.  */ + +extern char *getenv (); +extern FILE *fdopen (); +extern char *version_string; +extern struct tm *localtime (); +extern int sys_nerr; +extern char *sys_errlist[]; + +#ifndef errno +extern int errno; +#endif + +/* Forward declarations.  */ + +struct directive; +struct file_buf; +struct arglist; +struct argdata; + +#if defined(USG) || defined(VMS) +#ifndef BSTRING +void bcopy (); +void bzero (); +int bcmp (); +#endif +#endif + +/* These functions are declared to return int instead of void since they +   are going to be placed in a table and some old compilers have trouble with +   pointers to functions returning void.  */ + +static int do_define (); +static int do_line (); +static int do_include (); +static int do_undef (); +static int do_error (); +static int do_pragma (); +static int do_ident (); +static int do_if (); +static int do_xifdef (); +static int do_else (); +static int do_elif (); +static int do_endif (); +static int do_sccs (); +static int do_once (); +static int do_assert (); +static int do_unassert (); +static int do_warning (); + +static void add_import (); +static void append_include_chain (); +static void deps_output (); +static void make_undef (); +static void make_definition (); +static void make_assertion (); +static void path_include (); +static void initialize_builtins (); +static void initialize_char_syntax (); +static void dump_arg_n (); +static void dump_defn_1 (); +static void delete_macro (); +static void trigraph_pcp (); +static void rescan (); +static void finclude (); +static void validate_else (); +static int comp_def_part (); +static void error_from_errno (); +static void error_with_line (); +void pedwarn (); +void pedwarn_with_line (); +static void pedwarn_with_file_and_line (); +static void fatal (); +void fancy_abort (); +static void pfatal_with_name (); +static void perror_with_name (); +static void pipe_closed (); +static void print_containing_files (); +static int lookup_import (); +static int redundant_include_p (); +static is_system_include (); +static int check_preconditions (); +static void pcfinclude (); +static void pcstring_used (); +static void write_output (); +static int check_macro_name (); +static int compare_defs (); +static int compare_token_lists (); +static int eval_if_expression (); +static int discard_comments (); +static int change_newlines (); +static int line_for_error (); +static int hashf (); +static int file_size_and_mode (); + +static struct arglist *read_token_list (); +static void free_token_list (); + +static struct hashnode *install (); +struct hashnode *lookup (); + +static struct assertion_hashnode *assertion_install (); +static struct assertion_hashnode *assertion_lookup (); + +static char *xrealloc (); +static char *xcalloc (); +static char *savestring (); + +static void delete_assertion (); +static void macroexpand (); +static void dump_all_macros (); +static void conditional_skip (); +static void skip_if_group (); +static void output_line_command (); + +/* Last arg to output_line_command.  */ +enum file_change_code {same_file, enter_file, leave_file}; + +static int grow_outbuf (); +static int handle_directive (); +static void memory_full (); + +static U_CHAR *macarg1 (); +static char *macarg (); + +static U_CHAR *skip_to_end_of_comment (); +static U_CHAR *skip_quoted_string (); +static U_CHAR *skip_paren_group (); + +static char *check_precompiled (); +/* static struct macrodef create_definition ();	[moved below] */ +static void dump_single_macro (); + +#ifndef FAILURE_EXIT_CODE +#define FAILURE_EXIT_CODE 33	/* gnu cc command understands this */ +#endif + +#ifndef SUCCESS_EXIT_CODE +#define SUCCESS_EXIT_CODE 0	/* 0 means success on Unix.  */ +#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 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 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_commands; + +/* 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 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 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 #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 causes output not to be done, +   but directives such as #define that have side effects +   are still obeyed.  */ + +static int no_output; + +/* 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 200 +static struct file_buf { +  char *fname; +  /* Filename specified with #line command.  */ +  char *nominal_fname; +  /* 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 header file included using <FILENAME>.  */ +  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; +    char *fname; +    /* If the following is nonzero, it is a macro name. +       Don't include the file again if that macro is defined.  */ +    U_CHAR *control_macro; +  }; + +/* #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. */ +static struct default_include { char *fname; int cplusplus; } include_defaults_array[] +#ifdef INCLUDE_DEFAULTS +  = INCLUDE_DEFAULTS; +#else +  = { +    /* Pick up GNU C++ specific include files.  */ +    { GPLUSPLUS_INCLUDE_DIR, 1}, +#ifdef CROSS_COMPILE +    /* This is the dir for fixincludes.  Put it just before +       the files that we fix.  */ +    { GCC_INCLUDE_DIR, 0}, +    /* For cross-compilation, this dir name is generated +       automatically in Makefile.in.  */ +    { CROSS_INCLUDE_DIR, 0 }, +    /* This is another place that the target system's headers might be.  */ +    { TOOL_INCLUDE_DIR, 0}, +#else /* not CROSS_COMPILE */ +    /* Some systems have an extra dir of include files.  */ +#ifdef SYSTEM_INCLUDE_DIR +    { SYSTEM_INCLUDE_DIR, 0}, +#endif +    { STANDARD_INCLUDE_DIR, 0}, +#endif /* not CROSS_COMPILE */ +    { 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 */ + +/* List of included files that contained #pragma once.  */ +static struct file_name_list *dont_repeat_files = 0; + +/* List of other included files. +   If ->control_macro if nonzero, the file had a #ifndef +   around the entire contents, and ->control_macro gives the macro name.  */ +static struct file_name_list *all_include_files = 0; + +/* Directory prefix that should replace `/usr' in the standard +   include file directories.  */ +static char *include_prefix; + +/* 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; +}; + +static struct macrodef create_definition (); + + +/* 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; +    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. */ +    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 { +  int ival; +  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 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,	/* 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_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 + +/* 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 + +/* 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.  */ + +struct directive { +  int length;			/* Length of name */ +  int (*func)();		/* Function to handle directive */ +  char *name;			/* Name of directive */ +  enum node_type type;		/* Code which describes which directive. */ +  char angle_brackets;		/* Nonzero => <...> is special.  */ +  char traditional_comments;	/* Nonzero: keep comments if -traditional.  */ +  char pass_thru;		/* Copy preprocessed directive to output file.  */ +}; + +/* Here is the actual list of #-directives, most-often-used first.  */ + +static struct directive directive_table[] = { +  {  6, do_define, "define", T_DEFINE, 0, 1}, +  {  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, 1}, +  { 12, do_include, "include_next", T_INCLUDE_NEXT, 1}, +  {  6, do_include, "import", T_IMPORT, 1}, +  {  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, 0, 0, 1}, +  {  5, do_ident, "ident", T_IDENT, 0, 0, 1}, +  {  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 # 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.  */ +U_CHAR is_hor_space[256]; +/* table to tell if c is horizontal or vertical space.  */ +static U_CHAR is_space[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; + +/* Zero means dollar signs are punctuation. +   -$ stores 0; -traditional may store 1.  Default is 1 for VMS, 0 otherwise. +   This must be 0 for correct processing of this ANSI C program: +	#define foo(a) #a +	#define lose(b) foo (b) +	#define test$ +	lose (test)	*/ +static int dollars_in_ident; +#ifndef DOLLARS_IN_IDENTIFIERS +#define DOLLARS_IN_IDENTIFIERS 1 +#endif + +static FILE_BUF expand_to_temp_buffer (); + +static DEFINITION *collect_expansion (); + +/* 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; + +int +main (argc, argv) +     int argc; +     char **argv; +{ +  int st_mode; +  long st_size; +  char *in_fname; +  char *p; +  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; + +  /* 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; + +#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 */ + +  signal (SIGPIPE, pipe_closed); + +  p = argv[0] + strlen (argv[0]); +  while (p != argv[0] && p[-1] != '/') --p; +  progname = p; + +#ifdef VMS +  { +    /* Remove directories from PROGNAME.  */ +    char *s; + +    progname = savestring (argv[0]); + +    if (!(s = rindex (progname, ']'))) +      s = rindex (progname, ':'); +    if (s) +      strcpy (progname, s+1); +    if (s = rindex (progname, '.')) +      *s = '\0'; +  } +#endif + +  in_fname = NULL; +  out_fname = NULL; + +  /* Initialize is_idchar to allow $.  */ +  dollars_in_ident = 1; +  initialize_char_syntax (); +  dollars_in_ident = DOLLARS_IN_IDENTIFIERS > 0; + +  no_line_commands = 0; +  no_trigraphs = 1; +  dump_macros = dump_none; +  no_output = 0; +  cplusplus = 0; +  cplusplus_comments = 0; + +  bzero (pend_files, argc * sizeof (char *)); +  bzero (pend_defs, argc * sizeof (char *)); +  bzero (pend_undefs, argc * sizeof (char *)); +  bzero (pend_assertions, argc * sizeof (char *)); +  bzero (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 +	    pend_includes[i] = argv[i+1], i++; +	} +	if (!strcmp (argv[i], "-imacros")) { +	  if (i + 1 == argc) +	    fatal ("Filename missing after `-imacros' option"); +	  else +	    pend_files[i] = argv[i+1], i++; +	} +	if (!strcmp (argv[i], "-iprefix")) { +	  if (i + 1 == argc) +	    fatal ("Filename missing after `-iprefix' option"); +	  else +	    include_prefix = argv[++i]; +	} +	/* 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; + +	  dirtmp = (struct file_name_list *) +	    xmalloc (sizeof (struct file_name_list)); +	  dirtmp->next = 0;	/* New one goes on the end */ +	  dirtmp->control_macro = 0; +	  if (i + 1 == argc) +	    fatal ("Directory name missing after `-iwithprefix' option"); + +	  dirtmp->fname = (char *) xmalloc (strlen (argv[i+1]) +					    + strlen (include_prefix) + 1); +	  strcpy (dirtmp->fname, include_prefix); +	  strcat (dirtmp->fname, argv[++i]); + +	  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 end of path for includes.  */ +	if (!strcmp (argv[i], "-idirafter")) { +	  struct file_name_list *dirtmp; + +	  dirtmp = (struct file_name_list *) +	    xmalloc (sizeof (struct file_name_list)); +	  dirtmp->next = 0;	/* New one goes on the end */ +	  dirtmp->control_macro = 0; +	  if (i + 1 == argc) +	    fatal ("Directory name missing after `-idirafter' option"); +	  else +	    dirtmp->fname = argv[++i]; + +	  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 = argv[++i]; +	  pcp_outfile =  +	    ((pcp_fname[0] != '-' || pcp_fname[1] != '\0') +	     ? fopen (pcp_fname, "w") +	     : fdopen (dup (fileno (stdout)), "w")); +	  if (pcp_outfile == 0) +	    pfatal_with_name (pcp_fname); +	  no_precomp = 1; +	} +	break; + +      case 't': +	if (!strcmp (argv[i], "-traditional")) { +	  traditional = 1; +	  if (dollars_in_ident > 0) +	    dollars_in_ident = 1; +	} else if (!strcmp (argv[i], "-trigraphs")) { +	  no_trigraphs = 0; +	} +	break; + +      case 'l': +	if (! strcmp (argv[i], "-lang-c")) +	  cplusplus = 0, cplusplus_comments = 0, objc = 0; +	if (! strcmp (argv[i], "-lang-c++")) +	  cplusplus = 1, cplusplus_comments = 1, objc = 0; +	if (! strcmp (argv[i], "-lang-objc")) +	  objc = 1, cplusplus = 0, cplusplus_comments = 1; +	if (! strcmp (argv[i], "-lang-objc++")) +	  objc = 1, cplusplus = 1, cplusplus_comments = 1; + 	if (! strcmp (argv[i], "-lang-asm")) + 	  lang_asm = 1; + 	if (! strcmp (argv[i], "-lint")) + 	  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], "-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': +	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")) { +	  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; +	    } +	  } +	} +	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"); +	break; + +      case 'H': +	print_include_names = 1; +	break; + +      case 'D': +	{ +	  char *p, *p1; + +	  if (argv[i][2] != 0) +	    p = argv[i] + 2; +	  else if (i + 1 == argc) +	    fatal ("Macro name missing after -D option"); +	  else +	    p = argv[++i]; + +	  pend_defs[i] = p; +	} +	break; + +      case 'A': +	{ +	  char *p, *p1; + +	  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_commands = 1; +	break; + +      case '$':			/* Don't include $ in identifiers.  */ +	dollars_in_ident = 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 = (struct file_name_list *) +	      xmalloc (sizeof (struct file_name_list)); +	    dirtmp->next = 0;		/* New one goes on the end */ +	    dirtmp->control_macro = 0; +	    if (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]; +	    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 '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.  */ +  p = (char *) getenv ("CPATH"); +  if (p != 0 && ! no_standard_includes) +    path_include (p); + +  /* Now that dollars_in_ident is known, initialize is_idchar.  */ +  initialize_char_syntax (); + +  /* 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; + +  /* 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_command (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_command (fp, &outbuf, 0, same_file); +      make_undef (pend_undefs[i], &outbuf); +    } +    if (pend_defs[i]) { +      if (debug_output) +        output_line_command (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) { +      char *nstore = (char *) alloca (strlen (epath) + 2); +      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) { +        /* Handle cases like c:/usr/lib:d:/gcc/lib */ +        if ((*endp == PATH_SEPARATOR +#if 0 /* Obsolete, now that we use semicolons as the path separator.  */ +#ifdef __MSDOS__ +	     && (endp-startp != 1 || !isalpha (*startp)) +#endif +#endif +	     ) +            || *endp == 0) { +	  strncpy (nstore, startp, endp-startp); +	  if (endp == startp) +	    strcpy (nstore, "."); +	  else +	    nstore[endp-startp] = '\0'; + +	  include_defaults[num_dirs].fname = savestring (nstore); +	  include_defaults[num_dirs].cplusplus = cplusplus; +	  num_dirs++; +	  if (*endp == '\0') +	    break; +	  endp = startp = endp + 1; +	} else +	  endp++; +      } +      /* Put the usual defaults back in at the end.  */ +      bcopy (include_defaults_array, &include_defaults[num_dirs], +	     sizeof (include_defaults_array)); +    } +  } + +  first_system_include = 0; +  /* 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 +	      = (struct file_name_list *) xmalloc (sizeof (struct file_name_list)); +	    int this_len = strlen (specd_prefix) + strlen (p->fname) - default_len; +	    char *str = (char *) xmalloc (this_len + 1); +	    strcpy (str, specd_prefix); +	    strcat (str, p->fname + default_len); +	    new->fname = str; +	    new->control_macro = 0; +	    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 +	  = (struct file_name_list *) xmalloc (sizeof (struct file_name_list)); +	new->control_macro = 0; +	new->fname = p->fname; +	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; + +  /* 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++; +  for (i = 1; i < argc; i++) +    if (pend_files[i]) { +      int fd = open (pend_files[i], O_RDONLY, 0666); +      if (fd < 0) { +	perror_with_name (pend_files[i]); +	return FAILURE_EXIT_CODE; +      } +      finclude (fd, pend_files[i], &outbuf, 0, NULL_PTR); +    } +  no_output--; + +  /* 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; + +  /* 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 = (char *) 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 = (char *) xmalloc (deps_allocated_size); +    deps_buffer[0] = 0; +    deps_size = 0; +    deps_column = 0; + +    if (deps_target) { +      deps_output (deps_target, 0); +      deps_output (":", 0); +    } else if (*in_fname == 0) +      deps_output ("-: ", 0); +    else { +      int len; +      char *p = in_fname; +      char *p1 = p; +      /* Discard all directory prefixes from P.  */ +      while (*p1) { +	if (*p1 == '/') +	  p = p1 + 1; +	p1++; +      } +      /* Output P, but remove known suffixes.  */ +      len = strlen (p); +      if (p[len - 2] == '.' && p[len - 1] == 'c') +	deps_output (p, len - 2); +      else if (p[len - 2] == '.' && p[len - 1] == 'C') +	deps_output (p, len - 2); +      else if (p[len - 3] == '.' +	       && p[len - 2] == 'c' +	       && p[len - 1] == 'c') +	deps_output (p, len - 3); +      else if (p[len - 4] == '.' +	       && p[len - 3] == 'c' +	       && p[len - 2] == 'x' +	       && p[len - 1] == 'x') +	deps_output (p, len - 4); +      else if (p[len - 2] == '.' && p[len - 1] == 's') +	deps_output (p, len - 2); +      else if (p[len - 2] == '.' && p[len - 1] == 'S') +	deps_output (p, len - 2); +      else if (p[len - 2] == '.' && p[len - 1] == 'm') +	deps_output (p, len - 2); +      else +	deps_output (p, 0); +      /* Supply our own suffix.  */ +#ifndef VMS +      deps_output (".o : ", 0); +#else +      deps_output (".obj : ", 0); +#endif +      deps_output (in_fname, 0); +      deps_output (" ", 0); +    } +  } + +  file_size_and_mode (f, &st_mode, &st_size); +  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_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 perror;	/* 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 perror; +      } +      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') +      /* 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_command (fp, &outbuf, 0, same_file); + +  /* Scan the -include files before the main input.  */ + +  for (i = 1; i < argc; i++) +    if (pend_includes[i]) { +      int fd = open (pend_includes[i], O_RDONLY, 0666); +      if (fd < 0) { +	perror_with_name (pend_includes[i]); +	return FAILURE_EXIT_CODE; +      } +      finclude (fd, pend_includes[i], &outbuf, 0, NULL_PTR); +    } + +  /* 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 (FAILURE_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 *name; +      struct file_name_list *dirtmp; + +      /* Find the end of this name.  */ +      while (*q != 0 && *q != PATH_SEPARATOR) q++; +      if (p == q) { +	/* An empty name in the path stands for the current directory.  */ +	name = (char *) xmalloc (2); +	name[0] = '.'; +	name[1] = 0; +      } else { +	/* Otherwise use the directory that is named.  */ +	name = (char *) xmalloc (q - p + 1); +	bcopy (p, name, q - p); +	name[q - p] = 0; +      } + +      dirtmp = (struct file_name_list *) +	xmalloc (sizeof (struct file_name_list)); +      dirtmp->next = 0;		/* New one goes on the end */ +      dirtmp->control_macro = 0; +      dirtmp->fname = name; +      append_include_chain (dirtmp, dirtmp); + +      /* Advance past this name.  */ +      p = q; +      if (*p == 0) +	break; +      /* Skip the colon.  */ +      p++; +    } +} + +/* 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; +  int len; + +  fptr = bptr = sptr = buf->buf; +  while ((sptr = (U_CHAR *) index (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; +    if (bptr != fptr && len > 0) +      bcopy (fptr, bptr, len);	/* BSD doc says bcopy () works right +				   for overlapping strings.  In ANSI +				   C, this will be memmove (). */ +    bptr += len; +    *bptr++ = c; +    fptr = ++sptr; +  } +  len = buf->length - (fptr - buf->buf); +  if (bptr != fptr && len > 0) +    bcopy (fptr, bptr, len); +  buf->length -= fptr - bptr; +  buf->buf[buf->length] = '\0'; +  if (warn_trigraphs && fptr != bptr) +    warning ("%d trigraph(s) encountered", (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; +  register int count = 0; + +  /* First count the backslash-newline pairs here.  */ + +  while (1) { +    if (p[0] == '\\') { +      if (p[1] == '\n') +	p += 2, count++; +      else if (p[1] == '\r' && p[2] == '\n') +	p += 3, count++; +      else +	break; +    } else +      break; +  } + +  /* 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; +{ +  register U_CHAR *p = bp; +  register int count = 0; + +  /* First count the backslash-newline pairs here.  */ +  while (1) { +    if (p[0] == '\\') { +      if (p[1] == '\n') +	p += 2, count++; +      else if (p[1] == '\r' && p[2] == '\n') +	p += 3, count++; +      else +	break; +    } else +      break; +  } + +  /* 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'; +  } +} + +/* 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 */ +{ +  long 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) && !strncmp (ibp, "NOTREACHED", 10)) { +    *cmdlen = 10; +    return "NOTREACHED"; +  } +  if ((linsize >= 8) && !strncmp (ibp, "ARGSUSED", 8)) { +    *cmdlen = 8; +    return "ARGSUSED"; +  } +  if ((linsize >= 11) && !strncmp (ibp, "LINTLIBRARY", 11)) { +    *cmdlen = 11; +    return "LINTLIBRARY"; +  } +  if ((linsize >= 7) && !strncmp (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); + +  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 (assertions_flag) { +	/* Copy #foo (bar lose) without macro expansion.  */ +	SKIP_WHITE_SPACE (ibp); +	while (is_idchar[*ibp]) +	  *obp++ = *ibp++; +	SKIP_WHITE_SPACE (ibp); +	if (*ibp == '(') { +	  ip->bufp = ibp; +	  skip_paren_group (ip); +	  bcopy (ibp, obp, ip->bufp - ibp); +	  obp += ip->bufp - ibp; +	  ibp = ip->bufp; +	} +      } + +      /* If this is expanding a macro definition, don't recognize +	 preprocessor directives.  */ +      if (ip->macro != 0) +	goto randomchar; +      /* If this is expand_into_temp_buffer, recognize them +	 only after an actual newline at this level, +	 not at the beginning of the input level.  */ +      if (ip->fname == 0 && beg_of_line == ip->buf) +	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; +	    } +	    else if (cplusplus_comments && *bp == '/' && bp[1] == '/') { +	      bp += 2; +	      while (*bp++ != '\n') ; +	    } +	    else break; +	  } +	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); +	  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. +	       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 (pedantic && multiline_string_line == 0) { +	    pedwarn_with_line (line_for_error (start_line), +			       "string constant runs past end of line"); +	  } +	  if (multiline_string_line == 0) +	    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; + +	--ibp;			/* Back over the slash */ +	--obp; + +	/* Comments are equivalent to spaces. */ +	if (! put_out_comments) +	  *obp++ = ' '; +	else { +	  /* must fake up a comment here */ +	  *obp++ = '/'; +	  *obp++ = '/'; +	} +	{ +	  U_CHAR *before_bp = ibp+2; + +	  while (ibp < limit) { +	    if (*ibp++ == '\n') { +	      ibp--; +	      if (put_out_comments) { +		bcopy (before_bp, obp, ibp - before_bp); +		obp += ibp - before_bp; +	      } +	      break; +	    } +	  } +	  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 (lint) { +	U_CHAR *argbp; +	int cmdlen, arglen; +	char *lintcmd = get_lintcmd (ibp, limit, &argbp, &arglen, &cmdlen); + +	if (lintcmd != NULL) { +	  /* I believe it is always safe to emit this newline: */ +	  obp[-1] = '\n'; +	  bcopy ("#pragma lint ", obp, 13); +	  obp += 13; +	  bcopy (lintcmd, obp, cmdlen); +	  obp += cmdlen; + +	  if (arglen != 0) { +	    *(obp++) = ' '; +	    bcopy (argbp, obp, arglen); +	    obp += arglen; +	  } + +	  /* OK, now bring us back to the state we were in before we entered +	     this branch.  We need #line b/c the newline for the pragma +	     could fuck things up. */ +	  output_line_command (ip, op, 0, same_file); +	  *(obp++) = ' ';	/* just in case, if comments are copied thru */ +	  *(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; + +	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) { +	    bcopy (before_bp, obp, ibp - before_bp); +	    obp += ibp - before_bp; +	  } +	} +      } +      break; + +    case '$': +      if (!dollars_in_ident) +	goto randomchar; +      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) { +	while (ibp < limit) { +	  while (ibp < limit && ibp[0] == '\\' && ibp[1] == '\n') { +	    ++ip->lineno; +	    ibp += 2; +	  } +	  c = *ibp++; +	  /* ".." terminates a preprocessing number.  This is useless for C +	     code but useful for preprocessing other things.  */ +	  if (!isalnum (c) && (c != '.' || *ibp == '.') && 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++; +	      /* But traditional C does not let the token go past the sign.  */ +	      if (traditional) +		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++ = '-'; +	} +	break; +      } + +      /* If reprocessing a macro expansion, newline is a special marker.  */ +      else 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 whitespace character. +	   Newline - marks a recursive macro use that is not +	   supposed to be expandable.  */ + +	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 traditional, and we have an identifier that ends here, +	   process it now, so we get the right error for recursion.  */ +	if (traditional && 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) { +	register 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.  */ + +	if (!pcp_outfile || pcp_inside_if) { +startagain: +	  for (hp = hashtab[MAKE_POS (hash) % HASHSIZE]; hp != NULL; +	       hp = hp->next) { +	     +	    if (hp->length == ident_length) { +	      int obufp_before_macroname; +	      int op_lineno_before_macroname; +	      register int i = ident_length; +	      register U_CHAR *p = hp->name; +	      register U_CHAR *q = obp - i; +	      int disabled; +	       +	      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--; +	      } +	       +	      /* Save this as a displacement from the beginning of the output +		 buffer.  We can not save this as a position in the output +		 buffer, because it may get realloc'ed by RECACHE.  */ +	      obufp_before_macroname = (obp - op->buf) - ident_length; +	      op_lineno_before_macroname = op->lineno; +	       +	      if (hp->type == T_PCSTRING) { +		pcstring_used (hp); /* Mark the definition of this key +				       as needed, ensuring that it +				       will be output.  */ +		break;		/* Exit loop, since the key cannot have a +				   definition any longer.  */ +	      } + +	      /* Record whether the macro is disabled.  */ +	      disabled = hp->type == T_DISABLED; +	       +	      /* This looks like a macro ref, but if the macro was disabled, +		 just copy its name and put in a marker if requested.  */ +	       +	      if (disabled) { +#if 0 +		/* This error check caught useful cases such as +		   #define foo(x,y) bar (x (y,0), y) +		   foo (foo, baz)  */ +		if (traditional) +		  error ("recursive use of macro `%s'", hp->name); +#endif +		 +		if (output_marks) { +		  check_expand (op, limit - ibp + 2); +		  *obp++ = '\n'; +		  *obp++ = '-'; +		} +		break; +	      } +	       +	      /* 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->type == T_DISABLED) +		  && hp->value.defn->nargs >= 0) +		{ +		  U_CHAR *old_ibp = ibp; +		  U_CHAR *old_obp = obp; +		  int old_iln = ip->lineno; +		  int old_oln = op->lineno; +		   +		  while (1) { +		    /* Scan forward over whitespace, copying it to the output.  */ +		    if (ibp == limit && ip->macro != 0) { +		      POPMACRO; +		      RECACHE; +		      old_ibp = ibp; +		      old_obp = obp; +		      old_iln = ip->lineno; +		      old_oln = op->lineno; +		    } +		    /* A comment: copy it unchanged or discard it.  */ +		    else if (*ibp == '/' && ibp+1 != limit && ibp[1] == '*') { +		      if (put_out_comments) { +			*obp++ = '/'; +			*obp++ = '*'; +		      } else if (! traditional) { +			*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 != '(') { +		    /* It isn't a macro call. +		       Put back the space that we just skipped.  */ +		    ibp = old_ibp; +		    obp = old_obp; +		    ip->lineno = old_iln; +		    op->lineno = old_oln; +		    /* Exit the for loop.  */ +		    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 = op->buf + obufp_before_macroname; +	      op->lineno = op_lineno_before_macroname; + +	      /* Prevent accidental token-pasting with a character +		 before the macro call.  */ +	      if (!traditional && obp != op->buf +		  && (obp[-1] == '-' || obp[1] == '+' || obp[1] == '&' +		      || obp[-1] == '|' || obp[1] == '<' || obp[1] == '>')) { +		/* If we are expanding a macro arg, make a newline marker +		   to separate the tokens.  If we are making real output, +		   a plain space will do.  */ +		if (output_marks) +		  *obp++ = '\n'; +		*obp++ = ' '; +	      } + +	      /* 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) { +    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; +    } +    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, assertions) +     U_CHAR *buf, *limit; +     int output_marks, assertions; +{ +  register FILE_BUF *ip; +  FILE_BUF obuf; +  int length = limit - buf; +  U_CHAR *buf1; +  int odepth = indepth; +  int save_assertions_flag = assertions_flag; + +  assertions_flag = assertions; + +  if (length < 0) +    abort (); + +  /* Set up the input on the input stack.  */ + +  buf1 = (U_CHAR *) alloca (length + 1); +  { +    register U_CHAR *p1 = buf; +    register 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->nominal_fname = 0; +  ip->system_header_p = 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; + +  assertions_flag = save_assertions_flag; +  return obuf; +} + +/* + * Process a # directive.  Expects IP->bufp to point after 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; +{ +  register U_CHAR *bp, *cp; +  register struct directive *kt; +  register 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; + +  /* Record where the directive started.  do_xifdef needs this.  */ +  directive_start = bp - 1; + +  /* Skip whitespace and \-newline.  */ +  while (1) { +    if (is_hor_space[*bp]) { +      if ((*bp == '\f' || *bp == '\v') && pedantic) +	pedwarn ("%s in preprocessing directive", +		 *bp == '\f' ? "formfeed" : "vertical tab"); +      bp++; +    } else if (*bp == '/' && bp[1] == '*') { +      ip->bufp = bp; +      skip_to_end_of_comment (ip, &ip->lineno, 0); +      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; +  } + +  if (ident_length == 0 || !is_idstart[*ident]) { +    U_CHAR *p = ident; +    while (is_idchar[*p]) { +      if (*p < '0' || *p > '9') +	break; +      p++; +    } +    /* Handle # followed by a line number.  */ +    if (p != ident && !is_idchar[*p]) { +      static struct directive line_directive_table[] = { +	{  4, do_line, "line", T_LINE}, +      }; +      if (pedantic) +	pedwarn ("`#' followed by integer"); +      after_ident = ident; +      kt = line_directive_table; +      goto old_linenum; +    } + +    /* Avoid error for `###' and similar cases unless -pedantic.  */ +    if (p == ident) { +      while (*p == '#' || is_hor_space[*p]) p++; +      if (*p == '\n') { +	if (pedantic && !lang_asm) +	  warning ("invalid preprocessor directive"); +	return 0; +      } +    } + +    if (!lang_asm) +      error ("invalid preprocessor directive name"); + +    return 0; +  } + +  /* +   * 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, ident, ident_length)) { +      register U_CHAR *buf; +      register U_CHAR *limit; +      int unterminated; +      int junk; +      int *already_output; + +      /* Nonzero means do not delete comments within the directive. +	 #define needs this when -traditional.  */ +      int keep_comments; + +    old_linenum: + +      limit = ip->buf + ip->length; +      unterminated = 0; +      already_output = 0; +      keep_comments = traditional && kt->traditional_comments; +      /* #import is defined only in Objective C, or when on the NeXT.  */ +      if (kt->type == T_IMPORT && !(objc || lookup ("__NeXT__", -1, -1))) +	break; + +      /* 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) { +	register 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); +	  /* Don't bother calling the directive if we already got an error +	     message due to unterminated string.  Skip everything and pretend +	     we called the directive.  */ +	  if (unterminated) { +	    if (traditional) { +	      /* Traditional preprocessing permits unterminated strings.  */ +	      ip->bufp = bp; +	      goto endloop1; +	    } +	    ip->bufp = bp; +	    return 1; +	  } +	  break; + +	  /* <...> is special for #include.  */ +	case '<': +	  if (!kt->angle_brackets) +	    break; +	  while (*bp && *bp != '>') bp++; +	  break; + +	case '/': +	  if (*bp == '\\' && bp[1] == '\n') +	    newline_fix (bp); +	  if (*bp == '*' +	      || (cplusplus_comments && *bp == '/')) { +	    U_CHAR *obp = bp - 1; +	    ip->bufp = bp + 1; +	    skip_to_end_of_comment (ip, &ip->lineno, 0); +	    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 -traditional.  */ +	    if (! keep_comments) +	      copy_command++; +	  } +	  break; + +	case '\f': +	case '\v': +	  if (pedantic) +	    pedwarn ("%s in preprocessing directive", +		     c == '\f' ? "formfeed" : "vertical tab"); +	  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 a directive should be copied through, and -E was given, +	 pass it through before removing comments.  */ +      if (!no_output && kt->pass_thru && put_out_comments) { +        int len; + +	/* Output directive name.  */ +        check_expand (op, kt->length + 2); +	/* Make sure # is at the start of a line */ +	if (op->bufp > op->buf && op->bufp[-1] != '\n') { +	  op->lineno++; +	  *op->bufp++ = '\n'; +	} +        *op->bufp++ = '#'; +        bcopy (kt->name, op->bufp, kt->length); +        op->bufp += kt->length; + +	/* Output arguments.  */ +	len = (bp - buf); +	check_expand (op, len); +	bcopy (buf, op->bufp, len); +	op->bufp += len; +	/* Take account of any (escaped) newlines just output.  */ +	while (--len >= 0) +	  if (buf[len] == '\n') +	    op->lineno++; + +	already_output = &junk; +      }				/* Don't we need a newline or #line? */ + +      if (copy_command) { +	register 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) { +	  register U_CHAR c = *xp++; +	  *cp++ = c; + +	  switch (c) { +	  case '\n': +	    abort ();  /* A bare newline should never part of the line.  */ +	    break; + +	    /* <...> is special for #include.  */ +	  case '<': +	    if (!kt->angle_brackets) +	      break; +	    while (xp < bp && c != '>') { +	      c = *xp++; +	      if (c == '\\' && xp < bp && *xp == '\n') +		xp++; +	      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_space[*xp]) { +		*cp++ = *xp++; +		SKIP_WHITE_SPACE (xp); +	      } +	    } else { +	      *cp++ = *xp++; +	    } +	    break; + +	  case '\'': +	  case '\"': +	    { +	      register U_CHAR *bp1 +		= skip_quoted_string (xp - 1, bp, ip->lineno, +				      NULL_PTR, NULL_PTR, NULL_PTR); +	      while (xp != bp1) +		if (*xp == '\\') { +		  if (*++xp != '\n') +		    *cp++ = '\\'; +		  else +		    xp++; +		} else +		  *cp++ = *xp++; +	    } +	    break; + +	  case '/': +	    if (*xp == '*' +		|| (cplusplus_comments && *xp == '/')) { +	      ip->bufp = xp + 1; +	      /* If we already copied the command through, +		 already_output != 0 prevents outputting comment now.  */ +	      skip_to_end_of_comment (ip, already_output, 0); +	      if (keep_comments) +		while (xp != ip->bufp) +		  *cp++ = *xp++; +	      /* Delete or replace the slash.  */ +	      else if (traditional) +		cp--; +	      else +		cp[-1] = ' '; +	      xp = ip->bufp; +	    } +	  } +	} + +	/* Null-terminate the copy.  */ + +	*cp = 0; +      } else +	cp = bp; + +      ip->bufp = resume_p; + +      /* Some directives should be written out for cc1 to process, +	 just as if they were not defined.  And sometimes we're copying +	 definitions through.  */ + +      if (!no_output && already_output == 0 +	  && (kt->pass_thru +	      || (kt->type == T_DEFINE +		  && (dump_macros == dump_names +		      || dump_macros == dump_definitions)))) { +        int len; + +	/* Output directive name.  */ +        check_expand (op, kt->length + 1); +        *op->bufp++ = '#'; +        bcopy (kt->name, op->bufp, kt->length); +        op->bufp += kt->length; + +	if (kt->pass_thru || dump_macros == dump_definitions) { +	  /* Output arguments.  */ +	  len = (cp - buf); +	  check_expand (op, len); +	  bcopy (buf, op->bufp, len); +	  op->bufp += len; +	} else if (kt->type == T_DEFINE && dump_macros == dump_names) { +	  U_CHAR *xp = buf; +	  U_CHAR *yp; +	  SKIP_WHITE_SPACE (xp); +	  yp = xp; +	  while (is_idchar[*xp]) xp++; +	  len = (xp - yp); +	  check_expand (op, len + 1); +	  *op->bufp++ = ' '; +	  bcopy (yp, op->bufp, len); +	  op->bufp += len; +	} +      }				/* Don't we need a newline or #line? */ + +      /* 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, kt); +      check_expand (op, ip->length - (ip->bufp - ip->buf)); + +      return 1; +    } +  } + +  /* It is deliberate that we don't warn about undefined directives. +     That is the responsibility of cc1.  */ +  return 0; +} + +static struct tm * +timestamp () +{ +  static struct tm *timebuf; +  if (!timebuf) { +    time_t t = time (0); +    timebuf = localtime (&t); +  } +  return timebuf; +} + +static char *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; +{ +  char *buf; +  int i, len; +  int true_indepth; +  FILE_BUF *ip = NULL; +  struct tm *timebuf; + +  int paren = 0;		/* For special `defined' keyword */ + +  if (pcp_outfile && pcp_inside_if +      && hp->type != T_SPEC_DEFINED && hp->type != T_CONST) +    error ("Predefined macro `%s' used inside `#if' during precompilation", +	   hp->name); +     +  for (i = indepth; i >= 0; i--) +    if (instack[i].fname != NULL) { +      ip = &instack[i]; +      break; +    } +  if (ip == NULL) { +    error ("cccp error: not in any file?!"); +    return;			/* the show must go on */ +  } + +  switch (hp->type) { +  case T_FILE: +  case T_BASE_FILE: +    { +      char *string; +      if (hp->type == T_FILE) +	string = ip->nominal_fname; +      else +	string = instack[0].nominal_fname; + +      if (string) +	{ +	  buf = (char *) alloca (3 + strlen (string)); +	  sprintf (buf, "\"%s\"", string); +	} +      else +	buf = "\"\""; + +      break; +    } + +  case T_INCLUDE_LEVEL: +    true_indepth = 0; +    for (i = indepth; i >= 0; i--) +      if (instack[i].fname != NULL) +        true_indepth++; + +    buf = (char *) alloca (8);	/* Eight bytes ought to be more than enough */ +    sprintf (buf, "%d", true_indepth - 1); +    break; + +  case T_VERSION: +    buf = (char *) alloca (3 + strlen (version_string)); +    sprintf (buf, "\"%s\"", version_string); +    break; + +  case T_SIZE_TYPE: +    buf = (char *) alloca (3 + strlen (SIZE_TYPE)); +    sprintf (buf, "%s", SIZE_TYPE); +    break; + +  case T_PTRDIFF_TYPE: +    buf = (char *) alloca (3 + strlen (PTRDIFF_TYPE)); +    sprintf (buf, "%s", PTRDIFF_TYPE); +    break; + +  case T_WCHAR_TYPE: +    buf = (char *) alloca (3 + strlen (WCHAR_TYPE)); +    sprintf (buf, "%s", WCHAR_TYPE); +    break; + +  case T_USER_LABEL_PREFIX_TYPE: +    buf = (char *) alloca (3 + strlen (USER_LABEL_PREFIX)); +    sprintf (buf, "%s", USER_LABEL_PREFIX); +    break; + +  case T_REGISTER_PREFIX_TYPE: +    buf = (char *) alloca (3 + strlen (REGISTER_PREFIX)); +    sprintf (buf, "%s", REGISTER_PREFIX); +    break; + +  case T_CONST: +    buf = (char *) alloca (4 * sizeof (int)); +    sprintf (buf, "%d", hp->value.ival); +    if (pcp_inside_if && pcp_outfile) +      /* Output a precondition for this macro use */ +      fprintf (pcp_outfile, "#define %s %d\n", hp->name, hp->value.ival); +    break; + +  case T_SPECLINE: +    buf = (char *) alloca (10); +    sprintf (buf, "%d", ip->lineno); +    break; + +  case T_DATE: +  case T_TIME: +    buf = (char *) alloca (20); +    timebuf = timestamp (); +    if (hp->type == T_DATE) +      sprintf (buf, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon], +	      timebuf->tm_mday, timebuf->tm_year + 1900); +    else +      sprintf (buf, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min, +	      timebuf->tm_sec); +    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; +    if (hp = lookup (ip->bufp, -1, -1)) { +      if (pcp_outfile && pcp_inside_if +	  && hp->value.defn->predefined) +	/* Output a precondition for this macro use. */ +	fprintf (pcp_outfile, "#define %s\n", hp->name); +      buf = " 1 "; +    } +    else +      if (pcp_outfile && pcp_inside_if)	{ +	/* Output a precondition for this macro use */ +	U_CHAR *cp = ip->bufp; +	fprintf (pcp_outfile, "#undef "); +	while (is_idchar[*cp]) /* Ick! */ +	  fputc (*cp++, pcp_outfile); +	putc ('\n', pcp_outfile); +      } +    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' without an identifier"); +    break; + +  default: +    error ("cccp error: invalid special hash type"); /* time for gdb */ +    abort (); +  } +  len = strlen (buf); +  check_expand (op, len); +  bcopy (buf, op->bufp, len); +  op->bufp += len; + +  return; +} + + +/* Routines to handle #directives */ + +/* Handle #include and #import. +   This function expects to see "fname" or <fname> on the input.  */ + +static int +do_include (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  int importing = (keyword->type == T_IMPORT); +  int skip_dirs = (keyword->type == T_INCLUDE_NEXT); +  static int import_warning = 0; +  char *fname;		/* Dynamically allocated fname buffer */ +  char *pcftry; +  char *pcfname; +  U_CHAR *fbeg, *fend;		/* Beginning and end of fname */ + +  struct file_name_list *search_start = include; /* Chain of dirs to search */ +  struct file_name_list dsp[1];	/* First in chain, if #include "..." */ +  struct file_name_list *searchptr = 0; +  int flen; + +  int f;			/* file number */ + +  int retried = 0;		/* Have already tried macro +				   expanding the include line*/ +  FILE_BUF trybuf;		/* It got expanded into here */ +  int angle_brackets = 0;	/* 0 for "...", 1 for <...> */ +  int pcf = -1; +  char *pcfbuf; +  int pcfbuflimit; +  int pcfnum; +  f= -1;			/* JF we iz paranoid! */ + +  if (importing && warn_import && !inhibit_warnings +      && !instack[indepth].system_header_p && !import_warning) { +    import_warning = 1; +    warning ("using `#import' is not recommended"); +    fprintf (stderr, "The fact that a certain header file need not be processed more than once\n"); +    fprintf (stderr, "should be indicated in the header file, not where it is used.\n"); +    fprintf (stderr, "The best way to do this is with a conditional of this form:\n\n"); +    fprintf (stderr, "  #ifndef _FOO_H_INCLUDED\n"); +    fprintf (stderr, "  #define _FOO_H_INCLUDED\n"); +    fprintf (stderr, "  ... <real contents of file> ...\n"); +    fprintf (stderr, "  #endif /* Not _FOO_H_INCLUDED */\n\n"); +    fprintf (stderr, "Then users can use `#include' any number of times.\n"); +    fprintf (stderr, "GNU C automatically avoids processing the file more than once\n"); +    fprintf (stderr, "when it is equipped with such a conditional.\n"); +  } + +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_hor_space[limit[-1]]) limit--; + +  switch (*fbeg++) { +  case '\"': +    { +      FILE_BUF *fp; +      /* Copy the operand text, concatenating the strings.  */ +      { +	U_CHAR *fin = fbeg; +	fbeg = (U_CHAR *) alloca (limit - fbeg + 1); +	fend = fbeg; +	while (fin != limit) { +	  while (fin != limit && *fin != '\"') +	    *fend++ = *fin++; +	  fin++; +	  if (fin == limit) +	    break; +	  /* If not at the end, there had better be another string.  */ +	  /* Skip just horiz space, and don't go past limit.  */ +	  while (fin != limit && is_hor_space[*fin]) fin++; +	  if (fin != limit && *fin == '\"') +	    fin++; +	  else +	    goto fail; +	} +      } +      *fend = 0; + +      /* 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--) +	{ +	  int n; +	  char *ep,*nam; + +	  if ((nam = fp->nominal_fname) != NULL) { +	    /* Found a named file.  Figure out dir of the file, +	       and put it in front of the search list.  */ +	    dsp[0].next = search_start; +	    search_start = dsp; +#ifndef VMS +	    ep = rindex (nam, '/'); +#else				/* VMS */ +	    ep = rindex (nam, ']'); +	    if (ep == NULL) ep = rindex (nam, '>'); +	    if (ep == NULL) ep = rindex (nam, ':'); +	    if (ep != NULL) ep++; +#endif				/* VMS */ +	    if (ep != NULL) { +	      n = ep - nam; +	      dsp[0].fname = (char *) alloca (n + 1); +	      strncpy (dsp[0].fname, nam, n); +	      dsp[0].fname[n] = '\0'; +	      if (n + INCLUDE_LEN_FUDGE > max_include_len) +		max_include_len = n + INCLUDE_LEN_FUDGE; +	    } else { +	      dsp[0].fname = 0; /* Current directory */ +	    } +	    break; +	  } +	} +      break; +    } + +  case '<': +    fend = fbeg; +    while (fend != limit && *fend != '>') fend++; +    if (*fend == '>' && fend + 1 == limit) { +      angle_brackets = 1; +      /* If -I-, start with the first -I dir after the -I-.  */ +      if (first_bracket_include) +	search_start = first_bracket_include; +      break; +    } +    goto fail; + +  default: +  fail: +    if (retried) { +      error ("`#%s' expects \"FILENAME\" or <FILENAME>", keyword->name); +      return 0; +    } else { +      trybuf = expand_to_temp_buffer (buf, limit, 0, 0); +      buf = (U_CHAR *) alloca (trybuf.bufp - trybuf.buf + 1); +      bcopy (trybuf.buf, buf, trybuf.bufp - trybuf.buf); +      limit = buf + (trybuf.bufp - trybuf.buf); +      free (trybuf.buf); +      retried++; +      goto get_filename; +    } +  } + +  /* For #include_next, skip in the search path +     past the dir in which the containing file was found.  */ +  if (skip_dirs) { +    FILE_BUF *fp; +    for (fp = &instack[indepth]; fp >= instack; fp--) +      if (fp->fname != NULL) { +	/* fp->dir is null if the containing file was specified +	   with an absolute file name.  In that case, don't skip anything.  */ +	if (fp->dir) +	  search_start = fp->dir->next; +	break; +      } +  } + +  flen = fend - fbeg; + +  if (flen == 0) +    { +      error ("empty file name in `#%s'", keyword->name); +      return 0; +    } + +  /* Allocate this permanently, because it gets stored in the definitions +     of macros.  */ +  fname = (char *) xmalloc (max_include_len + flen + 2); +  /* + 2 above for slash and terminating null.  */ + +  /* If specified file name is absolute, just open it.  */ + +  if (*fbeg == '/') { +    strncpy (fname, fbeg, flen); +    fname[flen] = 0; +    if (redundant_include_p (fname)) +      return 0; +    if (importing) +      f = lookup_import (fname); +    else +      f = open (fname, O_RDONLY, 0666); +    if (f == -2) +      return 0;		/* Already included this file */ +  } else { +    /* Search directory path, trying to open the file. +       Copy each filename tried into FNAME.  */ + +    for (searchptr = search_start; searchptr; searchptr = searchptr->next) { +      if (searchptr->fname) { +	/* The empty string in a search path is ignored. +	   This makes it possible to turn off entirely +	   a standard piece of the list.  */ +	if (searchptr->fname[0] == 0) +	  continue; +	strcpy (fname, searchptr->fname); +	strcat (fname, "/"); +	fname[strlen (fname) + flen] = 0; +      } else { +	fname[0] = 0; +      } +      strncat (fname, fbeg, flen); +#ifdef VMS +      /* Change this 1/2 Unix 1/2 VMS file specification into a +         full VMS file specification */ +      if (searchptr->fname && (searchptr->fname[0] != 0)) { +	/* Fix up the filename */ +	hack_vms_include_specification (fname); +      } else { +      	/* This is a normal VMS filespec, so use it unchanged.  */ +	strncpy (fname, fbeg, flen); +	fname[flen] = 0; +      } +#endif /* VMS */ +      if (importing) +	f = lookup_import (fname); +      else +	f = open (fname, O_RDONLY, 0666); +      if (f == -2) +	return 0;			/* Already included this file */ +#ifdef EACCES +      else if (f == -1 && errno == EACCES) +	warning ("Header file %s exists, but is not readable", fname); +#endif +      if (redundant_include_p (fname)) { +	close (f); +	return 0; +      } +      if (f >= 0) +	break; +    } +  } + +  if (f < 0) { +    /* A file that was not found.  */ + +    strncpy (fname, fbeg, flen); +    fname[flen] = 0; +    /* If -M was specified, and this header file won't be added to the +       dependency list, then don't count this as an error, because we can +       still produce correct output.  Otherwise, we can't produce correct +       output, because there may be dependencies we need inside the missing +       file, and we don't know what directory this missing file exists in.  */ +    if (print_deps +	&& (print_deps <= (angle_brackets || (system_include_depth > 0)))) +      warning ("No include path in which to find %s", fname); +    else if (search_start) +      error_from_errno (fname); +    else +      error ("No include path in which to find %s", fname); +  } else { +    struct stat stat_f; + +    /* 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 0;				/* 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->control_macro = 0; +      ptr->next = all_include_files; +      all_include_files = ptr; +      ptr->fname = savestring (fname); + +      /* For -M, add this file to the dependencies.  */ +      if (print_deps > (angle_brackets || (system_include_depth > 0))) { +	deps_output ("", 0); +	deps_output (fname, 0); +	deps_output (" ", 0); +      } +    }    + +    /* Handle -H option.  */ +    if (print_include_names) +      fprintf (stderr, "%s\n", fname); + +    if (angle_brackets) +      system_include_depth++; + +    /* Actually process the file.  */ +    add_import (f, fname);	/* Record file on "seen" list for #import. */ + +    pcftry = (char *) alloca (strlen (fname) + 30); +    pcfbuf = 0; +    pcfnum = 0; + +    fstat (f, &stat_f); + +    if (!no_precomp) +      do { +	sprintf (pcftry, "%s%d", fname, pcfnum++); +	 +	pcf = open (pcftry, O_RDONLY, 0666); +	if (pcf != -1) +	  { +	    struct stat s; + +	    fstat (pcf, &s); +	    if (bcmp (&stat_f.st_ino, &s.st_ino, sizeof (s.st_ino)) +		|| stat_f.st_dev != s.st_dev) +	      { +		pcfbuf = check_precompiled (pcf, fname, &pcfbuflimit); +		/* Don't need it any more.  */ +		close (pcf); +	      } +	    else +	      { +		/* Don't need it at all.  */ +		close (pcf); +		break; +	      } +	  } +      } while (pcf != -1 && !pcfbuf); +     +    /* Actually process the file */ +    if (pcfbuf) { +      pcfname = xmalloc (strlen (pcftry) + 1); +      strcpy (pcfname, pcftry); +      pcfinclude (pcfbuf, pcfbuflimit, fname, op); +    } +    else +      finclude (f, fname, op, is_system_include (fname), searchptr); + +    if (angle_brackets) +      system_include_depth--; +  } +  return 0; +} + +/* Return nonzero if there is no need to include file NAME +   because it has already been included and it contains a conditional +   to make a repeated include do nothing.  */ + +static int +redundant_include_p (name) +     char *name; +{ +  struct file_name_list *l = all_include_files; +  for (; l; l = l->next) +    if (! strcmp (name, l->fname) +	&& l->control_macro +	&& lookup (l->control_macro, -1, -1)) +      return 1; +  return 0; +} + +/* Return nonzero if the given FILENAME is an absolute pathname which +   designates a file within one of the known "system" include file +   directories.  We assume here that if the given FILENAME looks like +   it is the name of a file which resides either directly in a "system" +   include file directory, or within any subdirectory thereof, then the +   given file must be a "system" include file.  This function tells us +   if we should suppress pedantic errors/warnings for the given FILENAME.  */ + +static int +is_system_include (filename) +    register char *filename; +{ +  struct file_name_list *searchptr; + +  for (searchptr = first_system_include; searchptr; +       searchptr = searchptr->next) +    if (searchptr->fname) { +      register char *sys_dir = searchptr->fname; +      register unsigned length = strlen (sys_dir); + +      if (! strncmp (sys_dir, filename, length) && filename[length] == '/') +	return 1; +    } +  return 0; +} + +/* Process the contents of include file FNAME, already open on descriptor F, +   with output to OP. +   SYSTEM_HEADER_P is 1 if this file resides in any one of the known +   "system" include directories (as decided by the `is_system_include' +   function above). +   DIRPTR is the link in the dir path through which this file was found, +   or 0 if the file name was absolute.  */ + +static void +finclude (f, fname, op, system_header_p, dirptr) +     int f; +     char *fname; +     FILE_BUF *op; +     int system_header_p; +     struct file_name_list *dirptr; +{ +  int st_mode; +  long st_size; +  long i; +  FILE_BUF *fp;			/* For input stack frame */ +  int missing_newline = 0; + +  CHECK_DEPTH (return;); + +  if (file_size_and_mode (f, &st_mode, &st_size) < 0) +    { +      perror_with_name (fname); +      close (f); +      return; +    } + +  fp = &instack[indepth + 1]; +  bzero (fp, sizeof (FILE_BUF)); +  fp->nominal_fname = fp->fname = fname; +  fp->length = 0; +  fp->lineno = 1; +  fp->if_stack = if_stack; +  fp->system_header_p = system_header_p; +  fp->dir = dirptr; + +  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 if (S_ISDIR (st_mode)) { +    error ("directory `%s' specified in #include", fname); +    close (f); +    return; +  } else { +    /* Cannot count its file size before reading. +       First read the entire file into heap and +       copy them into buffer on stack. */ + +    U_CHAR *bufp; +    U_CHAR *basep; +    int bsize = 2000; + +    st_size = 0; +    basep = (U_CHAR *) xmalloc (bsize + 2); +    fp->buf = basep; /* So it will get freed, on error.  */ +    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); +	  fp->buf = basep; +	  bufp = basep + st_size;	/* May have moved */ +	} +    } +    fp->bufp = fp->buf; +    fp->length = st_size; +  } + +  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'; + +  /* Close descriptor now, so nesting does not use lots of descriptors.  */ +  close (f); + +  /* Must do this before calling trigraph_pcp, so that the correct file name +     will be printed in warning messages.  */ + +  indepth++; +  input_file_stack_tick++; + +  if (!no_trigraphs) +    trigraph_pcp (fp); + +  output_line_command (fp, op, 0, enter_file); +  rescan (op, 0); + +  if (missing_newline) +    fp->lineno--; + +  if (pedantic && missing_newline) +    pedwarn ("file does not end in newline"); + +  indepth--; +  input_file_stack_tick++; +  output_line_command (&instack[indepth], op, 0, leave_file); +  free (fp->buf); +  return; + + nope: + +  perror_with_name (fname); +  close (f); +  free (fp->buf); +} + +/* Record that inclusion of the file named FILE +   should be controlled by the macro named MACRO_NAME. +   This means that trying to include the file again +   will do something if that macro is defined.  */ + +static void +record_control_macro (file, macro_name) +     char *file; +     U_CHAR *macro_name; +{ +  struct file_name_list *new; + +  for (new = all_include_files; new; new = new->next) { +    if (!strcmp (new->fname, file)) { +      new->control_macro = macro_name; +      return; +    } +  } + +  /* If the file is not in all_include_files, something's wrong.  */ +  abort (); +} + +/* Maintain and search list of included files, for #import.  */ + +#define IMPORT_HASH_SIZE 31 + +struct import_file { +  char *name; +  ino_t inode; +  dev_t dev; +  struct import_file *next; +}; + +/* Hash table of files already included with #include or #import.  */ + +static struct import_file *import_hash_table[IMPORT_HASH_SIZE]; + +/* Hash a file name for import_hash_table.  */ + +static int  +import_hash (f) +     char *f; +{ +  int val = 0; + +  while (*f) val += *f++; +  return (val%IMPORT_HASH_SIZE); +} + +/* Search for file FILENAME in import_hash_table. +   Return -2 if found, either a matching name or a matching inode. +   Otherwise, open the file and return a file descriptor if successful +   or -1 if unsuccessful.  */ + +static int +lookup_import (filename) +     char *filename; +{ +  struct import_file *i; +  int h; +  int hashval; +  struct stat sb; +  int fd; + +  hashval = import_hash (filename); + +  /* Attempt to find file in list of already included files */ +  i = import_hash_table[hashval]; + +  while (i) { +    if (!strcmp (filename, i->name)) +      return -2;		/* return found */ +    i = i->next; +  } +  /* Open it and try a match on inode/dev */ +  fd = open (filename, O_RDONLY, 0666); +  if (fd < 0) +    return fd; +  fstat (fd, &sb); +  for (h = 0; h < IMPORT_HASH_SIZE; h++) { +    i = import_hash_table[h]; +    while (i) { +      /* Compare the inode and the device. +	 Supposedly on some systems the inode is not a scalar.  */ +      if (!bcmp (&i->inode, &sb.st_ino, sizeof (sb.st_ino)) +	  && i->dev == sb.st_dev) { +        close (fd); +        return -2;		/* return found */ +      } +      i = i->next; +    } +  } +  return fd;			/* Not found, return open file */ +} + +/* Add the file FNAME, open on descriptor FD, to import_hash_table.  */ + +static void +add_import (fd, fname) +     int fd; +     char *fname; +{ +  struct import_file *i; +  int hashval; +  struct stat sb; + +  hashval = import_hash (fname); +  fstat (fd, &sb); +  i = (struct import_file *)xmalloc (sizeof (struct import_file)); +  i->name = (char *)xmalloc (strlen (fname)+1); +  strcpy (i->name, fname); +  bcopy (&sb.st_ino, &i->inode, sizeof (sb.st_ino)); +  i->dev = sb.st_dev; +  i->next = import_hash_table[hashval]; +  import_hash_table[hashval] = i; +} + +/* Load the specified precompiled header into core, and verify its +   preconditions.  PCF indicates the file descriptor to read, which must +   be a regular file.  FNAME indicates the file name of the original  +   header.  *LIMIT will be set to an address one past the end of the file. +   If the preconditions of the file are not satisfied, the buffer is  +   freed and we return 0.  If the preconditions are satisfied, return +   the address of the buffer following the preconditions.  The buffer, in +   this case, should never be freed because various pieces of it will +   be referred to until all precompiled strings are output at the end of +   the run. +*/ +static char * +check_precompiled (pcf, fname, limit) +     int pcf; +     char *fname; +     char **limit; +{ +  int st_mode; +  long st_size; +  int length = 0; +  char *buf; +  char *dollar_loc; +  int i; +  char *cp; + +  if (pcp_outfile) +    return 0; +   +  if (file_size_and_mode (pcf, &st_mode, &st_size) < 0) +    return 0; + +  if (S_ISREG (st_mode)) +    { +      buf = xmalloc (st_size + 2); +      while (st_size > 0) +	{ +	  i = read (pcf, buf + length, st_size); +	  if (i < 0) +	    goto nope; +	  if (i == 0) +	    break; +	  length += i; +	  st_size -= i; +	}	   +    } +  else +    abort (); +     +  if (length > 0 && buf[length-1] != '\n') +    buf[length++] = '\n'; +  buf[length] = '\0'; +   +  *limit = buf + length; + +  /* File is in core.  Check the preconditions. */ +  if (!check_preconditions (buf)) +    goto nope; +  for (cp = buf; *cp; cp++) +    ; +#ifdef DEBUG_PCP +  fprintf (stderr, "Using preinclude %s\n", fname); +#endif +  return cp + 1; + + nope: +#ifdef DEBUG_PCP +  fprintf (stderr, "Cannot use preinclude %s\n", fname); +#endif +  free (buf); +  return 0; +} + +/* PREC (null terminated) points to the preconditions of a +   precompiled header.  These are a series of #define and #undef +   lines which must match the current contents of the hash +   table.  */ +static int  +check_preconditions (prec) +     char *prec; +{ +  MACRODEF mdef; +  char *lineend; +   +  while (*prec) { +    lineend = (char *) index (prec, '\n'); +     +    if (*prec++ != '#') { +      error ("Bad format encountered while reading precompiled file"); +      return 0; +    } +    if (!strncmp (prec, "define", 6)) { +      HASHNODE *hp; +       +      prec += 6; +      mdef = create_definition (prec, lineend, NULL_PTR); + +      if (mdef.defn == 0) +	abort (); +       +      if ((hp = lookup (mdef.symnam, mdef.symlen, -1)) == NULL +	  || (hp->type != T_MACRO && hp->type != T_CONST) +	  || (hp->type == T_MACRO +	      && !compare_defs (mdef.defn, hp->value.defn) +	      && (mdef.defn->length != 2 +		  || mdef.defn->expansion[0] != '\n' +		  || mdef.defn->expansion[1] != ' '))) +	return 0; +    } else if (!strncmp (prec, "undef", 5)) { +      char *name; +      int len; +       +      prec += 5; +      while (is_hor_space[(U_CHAR) *prec]) +	prec++; +      name = prec; +      while (is_idchar[(U_CHAR) *prec]) +	prec++; +      len = prec - name; +       +      if (lookup (name, len, -1)) +	return 0; +    } else { +      error ("Bad format encountered while reading precompiled file"); +      return 0; +    } +    prec = lineend + 1; +  } +  /* They all passed successfully */ +  return 1; +} + +/* Process the main body of a precompiled file.  BUF points to the +   string section of the file, following the preconditions.  LIMIT is one +   character past the end.  NAME is the name of the file being read +   in.  OP is the main output buffer */ +static void +pcfinclude (buf, limit, name, op) +     U_CHAR *buf, *limit, *name; +     FILE_BUF *op; +{ +  FILE_BUF tmpbuf; +  int nstrings; +  U_CHAR *cp = buf; + +  /* First in the file comes 4 bytes indicating the number of strings, */ +  /* in network byte order. (MSB first).  */ +  nstrings = *cp++; +  nstrings = (nstrings << 8) | *cp++; +  nstrings = (nstrings << 8) | *cp++; +  nstrings = (nstrings << 8) | *cp++; +   +  /* Looping over each string... */ +  while (nstrings--) { +    U_CHAR *string_start; +    U_CHAR *endofthiskey; +    STRINGDEF *str; +    int nkeys; +     +    /* Each string starts with a STRINGDEF structure (str), followed */ +    /* by the text of the string (string_start) */ + +    /* First skip to a longword boundary */ +    /* ??? Why a 4-byte boundary?  On all machines? */ +    /* NOTE: This works correctly even if HOST_WIDE_INT +       is narrower than a pointer. +       Do not try risky measures here to get another type to use! +       Do not include gstddef.h or stddef.h--either one will fail!  */ +    if ((HOST_WIDE_INT) cp & 3) +      cp += 4 - ((HOST_WIDE_INT) cp & 3); +     +    /* Now get the string. */ +    str = (STRINGDEF *) cp; +    string_start = cp += sizeof (STRINGDEF); +     +    for (; *cp; cp++)		/* skip the string */ +      ; +     +    /* We need to macro expand the string here to ensure that the +       proper definition environment is in place.  If it were only +       expanded when we find out it is needed, macros necessary for +       its proper expansion might have had their definitions changed. */ +    tmpbuf = expand_to_temp_buffer (string_start, cp++, 0, 0); +    /* Lineno is already set in the precompiled file */ +    str->contents = tmpbuf.buf; +    str->len = tmpbuf.length; +    str->writeflag = 0; +    str->filename = name; +    str->output_mark = outbuf.bufp - outbuf.buf; +     +    str->chain = 0; +    *stringlist_tailp = str; +    stringlist_tailp = &str->chain; +     +    /* Next comes a fourbyte number indicating the number of keys */ +    /* for this string. */ +    nkeys = *cp++; +    nkeys = (nkeys << 8) | *cp++; +    nkeys = (nkeys << 8) | *cp++; +    nkeys = (nkeys << 8) | *cp++; + +    /* If this number is -1, then the string is mandatory. */ +    if (nkeys == -1) +      str->writeflag = 1; +    else +      /* Otherwise, for each key, */ +      for (; nkeys--; free (tmpbuf.buf), cp = endofthiskey + 1) { +	KEYDEF *kp = (KEYDEF *) cp; +	HASHNODE *hp; +	 +	/* It starts with a KEYDEF structure */ +	cp += sizeof (KEYDEF); +	 +	/* Find the end of the key.  At the end of this for loop we +	   advance CP to the start of the next key using this variable. */ +	endofthiskey = cp + strlen (cp); +	kp->str = str; +	 +	/* Expand the key, and enter it into the hash table. */ +	tmpbuf = expand_to_temp_buffer (cp, endofthiskey, 0, 0); +	tmpbuf.bufp = tmpbuf.buf; +	 +	while (is_hor_space[*tmpbuf.bufp]) +	  tmpbuf.bufp++; +	if (!is_idstart[*tmpbuf.bufp] +	    || tmpbuf.bufp == tmpbuf.buf + tmpbuf.length) { +	  str->writeflag = 1; +	  continue; +	} +	     +	hp = lookup (tmpbuf.bufp, -1, -1); +	if (hp == NULL) { +	  kp->chain = 0; +	  install (tmpbuf.bufp, -1, T_PCSTRING, 0, (char *) kp, -1); +	} +	else if (hp->type == T_PCSTRING) { +	  kp->chain = hp->value.keydef; +	  hp->value.keydef = kp; +	} +	else +	  str->writeflag = 1; +      } +  } +  /* This output_line_command serves to switch us back to the current +     input file in case some of these strings get output (which will  +     result in line commands for the header file being output). */ +  output_line_command (&instack[indepth], op, 0, enter_file); +} + +/* Called from rescan when it hits a key for strings.  Mark them all */ + /* used and clean up. */ +static void +pcstring_used (hp) +     HASHNODE *hp; +{ +  KEYDEF *kp, *tmp; +   +  for (kp = hp->value.keydef; kp; kp = kp->chain) +    kp->str->writeflag = 1; +  delete_macro (hp); +} + +/* Write the output, interspersing precompiled strings in their */ + /* appropriate places. */ +static void +write_output () +{ +  STRINGDEF *next_string; +  U_CHAR *cur_buf_loc; +  int line_command_len = 80; +  char *line_command = xmalloc (line_command_len); +  int len; + +  /* In each run through the loop, either cur_buf_loc == */ +  /* next_string_loc, in which case we print a series of strings, or */ +  /* it is less than next_string_loc, in which case we write some of */ +  /* the buffer. */ +  cur_buf_loc = outbuf.buf;  +  next_string = stringlist; +   +  while (cur_buf_loc < outbuf.bufp || next_string) { +    if (next_string +	&& cur_buf_loc - outbuf.buf == next_string->output_mark) { +      if (next_string->writeflag) { +	len = strlen (next_string->filename); +	if (len > line_command_len) +	  line_command = xrealloc (line_command,  +				   line_command_len *= 2); +	sprintf (line_command, "\n# %d \"%s\"\n", +		 next_string->lineno, next_string->filename); +	if (write (fileno (stdout), line_command, strlen (line_command)) < 0) +	  pfatal_with_name (out_fname); +	if (write (fileno (stdout), next_string->contents, next_string->len) < 0) +	  pfatal_with_name (out_fname); +      }	       +      next_string = next_string->chain; +    } +    else { +      len = (next_string +	     ? (next_string->output_mark  +		- (cur_buf_loc - outbuf.buf)) +	     : outbuf.bufp - cur_buf_loc); +       +      if (write (fileno (stdout), cur_buf_loc, len) < len) +	pfatal_with_name (out_fname); +      cur_buf_loc += len; +    } +  } +  free (line_command); +} + +/* Pass a directive through to the output file. +   BUF points to the contents of the directive, as a contiguous string. +   LIMIT points to the first character past the end of the directive. +   KEYWORD is the keyword-table entry for the directive.  */ + +static void +pass_thru_directive (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  register unsigned keyword_length = keyword->length; + +  check_expand (op, 1 + keyword_length + (limit - buf)); +  *op->bufp++ = '#'; +  bcopy (keyword->name, op->bufp, keyword_length); +  op->bufp += keyword_length; +  if (limit != buf && buf[0] != ' ') +    *op->bufp++ = ' '; +  bcopy (buf, op->bufp, limit - buf); +  op->bufp += (limit - buf); +#if 0 +  *op->bufp++ = '\n'; +  /* Count the line we have just made in the output, +     to get in sync properly.  */ +  op->lineno++; +#endif +} + +/* 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; +  char rest_args; +}; + +/* Create a DEFINITION node from a #define directive.  Arguments are  +   as for do_define. */ +static MACRODEF +create_definition (buf, limit, op) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +{ +  U_CHAR *bp;			/* temp ptr into input buffer */ +  U_CHAR *symname;		/* remember where symbol name starts */ +  int sym_length;		/* and how long it is */ +  int line = instack[indepth].lineno; +  char *file = instack[indepth].nominal_fname; +  int rest_args = 0; + +  DEFINITION *defn; +  int arglengths = 0;		/* Accumulate lengths of arg names +				   plus number of args.  */ +  MACRODEF mdef; + +  bp = buf; + +  while (is_hor_space[*bp]) +    bp++; + +  symname = bp;			/* remember where it starts */ +  sym_length = check_macro_name (bp, "macro"); +  bp += sym_length; + +  /* 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++; +      temp->rest_args = 0; +      arg_ptrs = temp; + +      if (rest_args) +	pedwarn ("another parameter follows `%s'", +		 rest_extension); + +      if (!is_idstart[*bp]) +	pedwarn ("invalid character in macro parameter name"); +       +      /* Find the end of the arg name.  */ +      while (is_idchar[*bp]) { +	bp++; +	/* do we have a "special" rest-args extension here? */ +	if (limit - bp > REST_EXTENSION_LENGTH && +	    strncmp (rest_extension, bp, REST_EXTENSION_LENGTH) == 0) { +	  rest_args = 1; +	  temp->rest_args = 1; +	  break; +	} +      } +      temp->length = bp - temp->name; +      if (rest_args == 1) +	bp += REST_EXTENSION_LENGTH; +      arglengths += temp->length + 2; +      SKIP_WHITE_SPACE (bp); +      if (temp->length == 0 || (*bp != ',' && *bp != ')')) { +	error ("badly punctuated parameter list in `#define'"); +	goto nope; +      } +      if (*bp == ',') { +	bp++; +	SKIP_WHITE_SPACE (bp); +      } +      if (bp >= limit) { +	error ("unterminated parameter list in `#define'"); +	goto nope; +      } +      { +	struct arglist *otemp; + +	for (otemp = temp->next; otemp != NULL; otemp = otemp->next) +	  if (temp->length == otemp->length && +	    strncmp (temp->name, otemp->name, temp->length) == 0) { +	      U_CHAR *name; + +	      name = (U_CHAR *) alloca (temp->length + 1); +	      (void) strncpy (name, temp->name, temp->length); +	      name[temp->length] = '\0'; +	      error ("duplicate argument name `%s' in `#define'", name); +	      goto nope; +	  } +      } +    } + +    ++bp;			/* skip paren */ +    /* Skip exactly one space or tab if any.  */ +    if (bp < limit && (*bp == ' ' || *bp == '\t')) ++bp; +    /* now everything from bp before limit is the definition. */ +    defn = collect_expansion (bp, limit, argno, arg_ptrs); +    defn->rest_args = rest_args; + +    /* Now set defn->args.argnames to the result of concatenating +       the argument names in reverse order +       with comma-space between them.  */ +    defn->args.argnames = (U_CHAR *) xmalloc (arglengths + 1); +    { +      struct arglist *temp; +      int i = 0; +      for (temp = arg_ptrs; temp; temp = temp->next) { +	bcopy (temp->name, &defn->args.argnames[i], temp->length); +	i += temp->length; +	if (temp->next != 0) { +	  defn->args.argnames[i++] = ','; +	  defn->args.argnames[i++] = ' '; +	} +      } +      defn->args.argnames[i] = 0; +    } +  } else { +    /* simple expansion or empty definition; gobble it */ +    if (is_hor_space[*bp]) +      ++bp;		/* skip exactly one blank/tab char */ +    /* now everything from bp before limit is the definition. */ +    defn = collect_expansion (bp, limit, -1, NULL_PTR); +    defn->args.argnames = (U_CHAR *) ""; +  } + +  defn->line = line; +  defn->file = file; + +  /* OP is null if this is a predefinition */ +  defn->predefined = !op; +  mdef.defn = defn; +  mdef.symnam = symname; +  mdef.symlen = sym_length; + +  return mdef; + + nope: +  mdef.defn = 0; +  return mdef; +} +  +/* Process a #define command. +BUF points to the contents of the #define command, as a contiguous string. +LIMIT points to the first character past the end of the definition. +KEYWORD is the keyword-table entry for #define.  */ + +static int +do_define (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  int hashcode; +  MACRODEF mdef; + +  /* If this is a precompiler run (with -pcp) pass thru #define commands.  */ +  if (pcp_outfile && op) +    pass_thru_directive (buf, limit, op, keyword); + +  mdef = create_definition (buf, limit, op); +  if (mdef.defn == 0) +    goto nope; + +  hashcode = hashf (mdef.symnam, mdef.symlen, HASHSIZE); + +  { +    HASHNODE *hp; +    if ((hp = lookup (mdef.symnam, mdef.symlen, hashcode)) != NULL) { +      int ok = 0; +      /* Redefining a precompiled key is ok.  */ +      if (hp->type == T_PCSTRING) +	ok = 1; +      /* Redefining a macro is ok if the definitions are the same.  */ +      else if (hp->type == T_MACRO) +	ok = ! compare_defs (mdef.defn, hp->value.defn); +      /* Redefining a constant is ok with -D.  */ +      else if (hp->type == T_CONST) +        ok = ! done_initializing; +      /* Print the warning if it's not ok.  */ +      if (!ok) { +	U_CHAR *msg;		/* what pain... */ + +        /* If we are passing through #define and #undef directives, do +	   that for this re-definition now.  */ +        if (debug_output && op) +	  pass_thru_directive (buf, limit, op, keyword); + +	msg = (U_CHAR *) alloca (mdef.symlen + 22); +	*msg = '`'; +	bcopy (mdef.symnam, msg + 1, mdef.symlen); +	strcpy ((char *) (msg + mdef.symlen + 1), "' redefined"); +	pedwarn (msg); +	if (hp->type == T_MACRO) +	  pedwarn_with_file_and_line (hp->value.defn->file, hp->value.defn->line, +				      "this is the location of the previous definition"); +      } +      /* Replace the old definition.  */ +      hp->type = T_MACRO; +      hp->value.defn = mdef.defn; +    } else { +      /* If we are passing through #define and #undef directives, do +	 that for this new definition now.  */ +      if (debug_output && op) +	pass_thru_directive (buf, limit, op, keyword); +      install (mdef.symnam, mdef.symlen, T_MACRO, 0, +	       (char *) mdef.defn, hashcode); +    } +  } + +  return 0; + +nope: + +  return 1; +} + +/* Check a purported macro name SYMNAME, and yield its length. +   USAGE is the kind of name this is intended for.  */ + +static int +check_macro_name (symname, usage) +     U_CHAR *symname; +     char *usage; +{ +  U_CHAR *p; +  int sym_length; + +  for (p = symname; is_idchar[*p]; p++) +    ; +  sym_length = p - symname; +  if (sym_length == 0) +    error ("invalid %s name", usage); +  else if (!is_idstart[*symname]) { +    U_CHAR *msg;			/* what pain... */ +    msg = (U_CHAR *) alloca (sym_length + 1); +    bcopy (symname, msg, sym_length); +    msg[sym_length] = 0; +    error ("invalid %s name `%s'", usage, msg); +  } else { +    if (! strncmp (symname, "defined", 7) && sym_length == 7) +      error ("invalid %s name `defined'", usage); +  } +  return sym_length; +} + +/* + * return zero if two DEFINITIONs are isomorphic + */ +static int +compare_defs (d1, d2) +     DEFINITION *d1, *d2; +{ +  register struct reflist *a1, *a2; +  register U_CHAR *p1 = d1->expansion; +  register U_CHAR *p2 = d2->expansion; +  int first = 1; + +  if (d1->nargs != d2->nargs) +    return 1; +  if (strcmp ((char *)d1->args.argnames, (char *)d2->args.argnames)) +    return 1; +  for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2; +       a1 = a1->next, a2 = a2->next) { +    if (!((a1->nchars == a2->nchars && ! strncmp (p1, 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; +     U_CHAR *beg1, *beg2; +     int len1, len2; +     int last; +{ +  register U_CHAR *end1 = beg1 + len1; +  register 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 END. +   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; +  register 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++; +  } + +  if (limit - p >= 2 && p[0] == '#' && p[1] == '#') { +    error ("`##' at start of macro definition"); +    p += 2; +  } + +  /* Process the main body of the definition.  */ +  while (p < limit) { +    int skipped_arg = 0; +    register U_CHAR c = *p++; + +    *exp_p++ = c; + +    if (!traditional) { +      switch (c) { +      case '\'': +      case '\"': +        if (expected_delimiter != '\0') { +          if (c == expected_delimiter) +            expected_delimiter = '\0'; +        } else +          expected_delimiter = c; +	break; + +	/* Special hack: if a \# is written in the #define +	   include a # in the definition.  This is useless for C code +	   but useful for preprocessing other things.  */ + +      case '\\': +	/* \# quotes a # even outside of strings.  */ +	if (p < limit && *p == '#' && !expected_delimiter) { +	  exp_p--; +	  *exp_p++ = *p++; +	} else if (p < limit && expected_delimiter) { +	  /* In a string, backslash goes through +	     and makes next char ordinary.  */ +	  *exp_p++ = *p++; +	} +	break; + +      case '#': +	/* # is ordinary inside a string.  */ +	if (expected_delimiter) +	  break; +	if (p < limit && *p == '#') { +	  /* ##: concatenate preceding and following tokens.  */ +	  /* Take out the first #, discard preceding whitespace.  */ +	  exp_p--; +	  while (exp_p > lastp && is_hor_space[exp_p[-1]]) +	    --exp_p; +	  /* Skip the second #.  */ +	  p++; +	  /* Discard following whitespace.  */ +	  SKIP_WHITE_SPACE (p); +	  concat = p; +	  if (p == limit) +	    error ("`##' at end of macro definition"); +	} else if (nargs >= 0) { +	  /* Single #: stringify following argument ref. +	     Don't leave the # in the expansion.  */ +	  exp_p--; +	  SKIP_WHITE_SPACE (p); +	  if (p == limit || ! is_idstart[*p]) +	    error ("`#' operator is not followed by a macro argument name"); +	  else +	    stringify = p; +	} +	break; +      } +    } else { +      /* 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++; +#if 0 +	  /* Mark this as a concatenation-point, as if it had been ##.  */ +	  concat = p; +#endif +	} +	break; +      } +    } + +    /* Handle the start of a symbol.  */ +    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]) { +	register struct arglist *arg; + +	for (arg = arglist; arg != NULL; arg = arg->next) { +	  struct reflist *tpat; + +	  if (arg->name[0] == c +	      && arg->length == id_len +	      && strncmp (arg->name, id_beg, id_len) == 0) { +	    if (expected_delimiter && warn_stringify) { +	      if (traditional) { +		warning ("macro argument `%.*s' is stringified.", +			 id_len, arg->name); +	      } else { +		warning ("macro arg `%.*s' would be stringified with -traditional.", +			 id_len, arg->name); +	      } +	    } +	    /* If ANSI, don't actually substitute inside a string.  */ +	    if (!traditional && expected_delimiter) +	      break; +	    /* 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->rest_args = arg->rest_args; +	    tpat->stringify = (traditional ? expected_delimiter != '\0' +			       : stringify == id_beg); + +	    if (endpat == NULL) +	      defn->pattern = tpat; +	    else +	      endpat->next = tpat; +	    endpat = tpat; + +	    tpat->argno = arg->argno; +	    tpat->nchars = exp_p - lastp; +	    { +	      register 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) { +	register 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++; +    } +  } else if (!traditional && expected_delimiter == 0) { +    /* There is no trailing whitespace, so invent some in ANSI mode. +       But not if "inside a string" (which in ANSI mode +       happens only for -D option).  */ +    *exp_p++ = '\n'; +    *exp_p++ = ' '; +  } + +  *exp_p = '\0'; + +  defn->length = exp_p - defn->expansion; + +  /* Crash now if we overrun the allocated size.  */ +  if (defn->length + 1 > maxsize) +    abort (); + +#if 0 +/* This isn't worth the time it takes.  */ +  /* give back excess storage */ +  defn->expansion = (U_CHAR *) xrealloc (defn->expansion, defn->length + 1); +#endif + +  return defn; +} + +static int +do_assert (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  U_CHAR *bp;			/* temp ptr into input buffer */ +  U_CHAR *symname;		/* remember where symbol name starts */ +  int sym_length;		/* and how long it is */ +  struct arglist *tokens = NULL; + +  if (pedantic && done_initializing && !instack[indepth].system_header_p) +    pedwarn ("ANSI C does not allow `#assert'"); + +  bp = buf; + +  while (is_hor_space[*bp]) +    bp++; + +  symname = bp;			/* remember where it starts */ +  sym_length = check_macro_name (bp, "assertion"); +  bp += sym_length; +  /* #define doesn't do this, but we should.  */ +  SKIP_WHITE_SPACE (bp); + +  /* Lossage will occur if identifiers or control tokens are broken +     across lines using backslash.  This is not the right place to take +     care of that. */ + +  if (*bp != '(') { +    error ("missing token-sequence in `#assert'"); +    return 1; +  } + +  { +    int error_flag = 0; + +    bp++;			/* skip '(' */ +    SKIP_WHITE_SPACE (bp); + +    tokens = read_token_list (&bp, limit, &error_flag); +    if (error_flag) +      return 1; +    if (tokens == 0) { +      error ("empty token-sequence in `#assert'"); +      return 1; +    } + +    ++bp;			/* skip paren */ +    SKIP_WHITE_SPACE (bp); +  } + +  /* If this name isn't already an assertion name, make it one. +     Error if it was already in use in some other way.  */ + +  { +    ASSERTION_HASHNODE *hp; +    int hashcode = hashf (symname, sym_length, ASSERTION_HASHSIZE); +    struct tokenlist_list *value +      = (struct tokenlist_list *) xmalloc (sizeof (struct tokenlist_list)); + +    hp = assertion_lookup (symname, sym_length, hashcode); +    if (hp == NULL) { +      if (sym_length == 7 && ! strncmp (symname, "defined", sym_length)) +	error ("`defined' redefined as assertion"); +      hp = assertion_install (symname, sym_length, hashcode); +    } + +    /* Add the spec'd token-sequence to the list of such.  */ +    value->tokens = tokens; +    value->next = hp->value; +    hp->value = value; +  } + +  return 0; +} + +static int +do_unassert (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  U_CHAR *bp;			/* temp ptr into input buffer */ +  U_CHAR *symname;		/* remember where symbol name starts */ +  int sym_length;		/* and how long it is */ + +  struct arglist *tokens = NULL; +  int tokens_specified = 0; + +  if (pedantic && done_initializing && !instack[indepth].system_header_p) +    pedwarn ("ANSI C does not allow `#unassert'"); + +  bp = buf; + +  while (is_hor_space[*bp]) +    bp++; + +  symname = bp;			/* remember where it starts */ +  sym_length = check_macro_name (bp, "assertion"); +  bp += sym_length; +  /* #define doesn't do this, but we should.  */ +  SKIP_WHITE_SPACE (bp); + +  /* Lossage will occur if identifiers or control tokens are broken +     across lines using backslash.  This is not the right place to take +     care of that. */ + +  if (*bp == '(') { +    int error_flag = 0; + +    bp++;			/* skip '(' */ +    SKIP_WHITE_SPACE (bp); + +    tokens = read_token_list (&bp, limit, &error_flag); +    if (error_flag) +      return 1; +    if (tokens == 0) { +      error ("empty token list in `#unassert'"); +      return 1; +    } + +    tokens_specified = 1; + +    ++bp;			/* skip paren */ +    SKIP_WHITE_SPACE (bp); +  } + +  { +    ASSERTION_HASHNODE *hp; +    int hashcode = hashf (symname, sym_length, ASSERTION_HASHSIZE); +    struct tokenlist_list *tail, *prev; + +    hp = assertion_lookup (symname, sym_length, hashcode); +    if (hp == NULL) +      return 1; + +    /* If no token list was specified, then eliminate this assertion +       entirely.  */ +    if (! tokens_specified) { +      struct tokenlist_list *next; +      for (tail = hp->value; tail; tail = next) { +	next = tail->next; +	free_token_list (tail->tokens); +	free (tail); +      } +      delete_assertion (hp); +    } else { +      /* If a list of tokens was given, then delete any matching list.  */ + +      tail = hp->value; +      prev = 0; +      while (tail) { +	struct tokenlist_list *next = tail->next; +	if (compare_token_lists (tail->tokens, tokens)) { +	  if (prev) +	    prev->next = next; +	  else +	    hp->value = tail->next; +	  free_token_list (tail->tokens); +	  free (tail); +	} else { +	  prev = tail; +	} +	tail = next; +      } +    } +  } + +  return 0; +} + +/* Test whether there is an assertion named NAME +   and optionally whether it has an asserted token list TOKENS. +   NAME is not null terminated; its length is SYM_LENGTH. +   If TOKENS_SPECIFIED is 0, then don't check for any token list.  */ + +int +check_assertion (name, sym_length, tokens_specified, tokens) +     U_CHAR *name; +     int sym_length; +     int tokens_specified; +     struct arglist *tokens; +{ +  ASSERTION_HASHNODE *hp; +  int hashcode = hashf (name, sym_length, ASSERTION_HASHSIZE); + +  if (pedantic && !instack[indepth].system_header_p) +    pedwarn ("ANSI C does not allow testing assertions"); + +  hp = assertion_lookup (name, sym_length, hashcode); +  if (hp == NULL) +    /* It is not an assertion; just return false.  */ +    return 0; + +  /* If no token list was specified, then value is 1.  */ +  if (! tokens_specified) +    return 1; + +  { +    struct tokenlist_list *tail; + +    tail = hp->value; + +    /* If a list of tokens was given, +       then succeed if the assertion records a matching list.  */ + +    while (tail) { +      if (compare_token_lists (tail->tokens, tokens)) +	return 1; +      tail = tail->next; +    } + +    /* Fail if the assertion has no matching list.  */ +    return 0; +  } +} + +/* Compare two lists of tokens for equality including order of tokens.  */ + +static int +compare_token_lists (l1, l2) +     struct arglist *l1, *l2; +{ +  while (l1 && l2) { +    if (l1->length != l2->length) +      return 0; +    if (strncmp (l1->name, l2->name, l1->length)) +      return 0; +    l1 = l1->next; +    l2 = l2->next; +  } + +  /* Succeed if both lists end at the same time.  */ +  return l1 == l2; +} + +/* Read a space-separated list of tokens ending in a close parenthesis. +   Return a list of strings, in the order they were written. +   (In case of error, return 0 and store -1 in *ERROR_FLAG.) +   Parse the text starting at *BPP, and update *BPP. +   Don't parse beyond LIMIT.  */ + +static struct arglist * +read_token_list (bpp, limit, error_flag) +     U_CHAR **bpp; +     U_CHAR *limit; +     int *error_flag; +{ +  struct arglist *token_ptrs = 0; +  U_CHAR *bp = *bpp; +  int depth = 1; + +  *error_flag = 0; + +  /* Loop over the assertion value tokens.  */ +  while (depth > 0) { +    struct arglist *temp; +    int eofp = 0; +    U_CHAR *beg = bp; + +    /* Find the end of the token.  */ +    if (*bp == '(') { +      bp++; +      depth++; +    } else if (*bp == ')') { +      depth--; +      if (depth == 0) +	break; +      bp++; +    } else if (*bp == '"' || *bp == '\'') +      bp = skip_quoted_string (bp, limit, 0, NULL_PTR, NULL_PTR, &eofp); +    else +      while (! is_hor_space[*bp] && *bp != '(' && *bp != ')' +	     && *bp != '"' && *bp != '\'' && bp != limit) +	bp++; + +    temp = (struct arglist *) xmalloc (sizeof (struct arglist)); +    temp->name = (U_CHAR *) xmalloc (bp - beg + 1); +    bcopy (beg, temp->name, bp - beg); +    temp->name[bp - beg] = 0; +    temp->next = token_ptrs; +    token_ptrs = temp; +    temp->length = bp - beg; + +    SKIP_WHITE_SPACE (bp); + +    if (bp >= limit) { +      error ("unterminated token sequence in `#assert' or `#unassert'"); +      *error_flag = -1; +      return 0; +    } +  } +  *bpp = bp; + +  /* We accumulated the names in reverse order. +     Now reverse them to get the proper order.  */ +  { +    register struct arglist *prev = 0, *this, *next; +    for (this = token_ptrs; this; this = next) { +      next = this->next; +      this->next = prev; +      prev = this; +    } +    return prev; +  } +} + +static void +free_token_list (tokens) +     struct arglist *tokens; +{ +  while (tokens) { +    struct arglist *next = tokens->next; +    free (tokens->name); +    free (tokens); +    tokens = next; +  } +} + +/* + * Install a name in the assertion hash table. + * + * 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. + */ +static ASSERTION_HASHNODE * +assertion_install (name, len, hash) +     U_CHAR *name; +     int len; +     int hash; +{ +  register ASSERTION_HASHNODE *hp; +  register int i, bucket; +  register U_CHAR *p, *q; + +  i = sizeof (ASSERTION_HASHNODE) + len + 1; +  hp = (ASSERTION_HASHNODE *) xmalloc (i); +  bucket = hash; +  hp->bucket_hdr = &assertion_hashtab[bucket]; +  hp->next = assertion_hashtab[bucket]; +  assertion_hashtab[bucket] = hp; +  hp->prev = NULL; +  if (hp->next != NULL) +    hp->next->prev = hp; +  hp->length = len; +  hp->value = 0; +  hp->name = ((U_CHAR *) hp) + sizeof (ASSERTION_HASHNODE); +  p = hp->name; +  q = name; +  for (i = 0; i < len; i++) +    *p++ = *q++; +  hp->name[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. + */ +static ASSERTION_HASHNODE * +assertion_lookup (name, len, hash) +     U_CHAR *name; +     int len; +     int hash; +{ +  register U_CHAR *bp; +  register ASSERTION_HASHNODE *bucket; + +  bucket = assertion_hashtab[hash]; +  while (bucket) { +    if (bucket->length == len && strncmp (bucket->name, name, len) == 0) +      return bucket; +    bucket = bucket->next; +  } +  return NULL; +} + +static void +delete_assertion (hp) +     ASSERTION_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); +} + +/* + * interpret #line command.  Remembers previously seen fnames + * in its very own hash table. + */ +#define FNAME_HASHSIZE 37 + +static int +do_line (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  register 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, 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 0; +  } + +  /* 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 (bp) - 1; + +  /* NEW_LINENO is one less than the actual line number here.  */ +  if (pedantic && new_lineno < 0) +    pedwarn ("line number out of range in `#line' command"); + +  /* skip over the line number.  */ +  while (isdigit (*bp)) +    bp++; + +#if 0 /* #line 10"foo.c" is supposed to be allowed.  */ +  if (*bp && !is_space[*bp]) { +    error ("invalid format `#line' command"); +    return; +  } +#endif + +  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 0; +    } + +    fname_length = bp - fname; + +    bp++; +    SKIP_WHITE_SPACE (bp); +    if (*bp) { +      if (pedantic) +	pedwarn ("garbage at end of `#line' command"); +      if (*bp == '1') +	file_change = enter_file; +      else if (*bp == '2') +	file_change = leave_file; +      else if (*bp == '3') +	ip->system_header_p = 1; +      else { +	error ("invalid format `#line' command"); +	return 0; +      } + +      bp++; +      SKIP_WHITE_SPACE (bp); +      if (*bp == '3') { +	ip->system_header_p = 1; +	bp++; +	SKIP_WHITE_SPACE (bp); +      } +      if (*bp) { +	error ("invalid format `#line' command"); +	return 0; +      } +    } + +    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, fname, fname_length) == 0) { +	ip->nominal_fname = hp->value.cpval; +	break; +      } +    if (hp == 0) { +      /* 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->nominal_fname = hp->value.cpval = ((char *) hp) + sizeof (HASHNODE); +      bcopy (fname, hp->value.cpval, fname_length); +    } +  } else if (*bp) { +    error ("invalid format `#line' command"); +    return 0; +  } + +  ip->lineno = new_lineno; +  output_line_command (ip, op, 0, file_change); +  check_expand (op, ip->length - (ip->bufp - ip->buf)); +  return 0; +} + +/* + * remove the definition of a symbol from the 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 int +do_undef (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  int sym_length; +  HASHNODE *hp; +  U_CHAR *orig_buf = buf; + +  /* If this is a precompiler run (with -pcp) pass thru #undef commands.  */ +  if (pcp_outfile && op) +    pass_thru_directive (buf, limit, op, keyword); + +  SKIP_WHITE_SPACE (buf); +  sym_length = check_macro_name (buf, "macro"); + +  while ((hp = lookup (buf, sym_length, -1)) != NULL) { +    /* If we are generating additional info for debugging (with -g) we +       need to pass through all effective #undef commands.  */ +    if (debug_output && op) +      pass_thru_directive (orig_buf, limit, op, keyword); +    if (hp->type != T_MACRO) +      warning ("undefining `%s'", hp->name); +    delete_macro (hp); +  } + +  if (pedantic) { +    buf += sym_length; +    SKIP_WHITE_SPACE (buf); +    if (buf != limit) +      pedwarn ("garbage after `#undef' directive"); +  } +  return 0; +} + +/* + * Report a fatal error detected by the program we are processing. + * Use the text of the line in the error message, then terminate. + * (We use error because it prints the filename & line#.) + */ + +static int +do_error (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  int length = limit - buf; +  U_CHAR *copy = (U_CHAR *) xmalloc (length + 1); +  bcopy (buf, copy, length); +  copy[length] = 0; +  SKIP_WHITE_SPACE (copy); +  error ("#error %s", copy); +  exit (FAILURE_EXIT_CODE); +  /* NOTREACHED */ +  return 0; +} + +/* + * Report a warning detected by the program we are processing. + * Use the text of the line in the warning message, then continue. + * (We use error because it prints the filename & line#.) + */ + +static int +do_warning (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  int length = limit - buf; +  U_CHAR *copy = (U_CHAR *) xmalloc (length + 1); +  bcopy (buf, copy, length); +  copy[length] = 0; +  SKIP_WHITE_SPACE (copy); +  warning ("#warning %s", copy); +  return 0; +} + +/* Remember the name of the current file being read from so that we can +   avoid ever including it again.  */ + +static int +do_once () +{ +  int i; +  FILE_BUF *ip = NULL; + +  for (i = indepth; i >= 0; i--) +    if (instack[i].fname != NULL) { +      ip = &instack[i]; +      break; +    } + +  if (ip != NULL) { +    struct file_name_list *new; +     +    new = (struct file_name_list *) xmalloc (sizeof (struct file_name_list)); +    new->next = dont_repeat_files; +    dont_repeat_files = new; +    new->fname = savestring (ip->fname); +    new->control_macro = 0; +  } +  return 0; +} + +/* #ident has already been copied to the output file, so just ignore it.  */ + +static int +do_ident (buf, limit) +     U_CHAR *buf, *limit; +{ +  /* Allow #ident in system headers, since that's not user's fault.  */ +  if (pedantic && !instack[indepth].system_header_p) +    pedwarn ("ANSI C does not allow `#ident'"); +  return 0; +} + +/* #pragma and its argument line have already been copied to the output file. +   Just check for some recognized pragmas that need validation here.  */ + +static int +do_pragma (buf, limit) +     U_CHAR *buf, *limit; +{ +  while (*buf == ' ' || *buf == '\t') +    buf++; +  if (!strncmp (buf, "once", 4)) { +    /* Allow #pragma once in system headers, since that's not the user's +       fault.  */ +    if (!instack[indepth].system_header_p) +      warning ("`#pragma once' is obsolete"); +    do_once (); +  } + +  if (!strncmp (buf, "implementation", 14)) { +    /* Be quiet about `#pragma implementation' for a file only if it hasn't +       been included yet.  */ +    struct file_name_list *ptr; +    U_CHAR *p = buf + 14, *fname, *inc_fname; +    SKIP_WHITE_SPACE (p); +    if (*p == '\n' || *p != '\"') +      return 0; + +    fname = p + 1; +    if (p = (U_CHAR *) index (fname, '\"')) +      *p = '\0'; +     +    for (ptr = all_include_files; ptr; ptr = ptr->next) { +      inc_fname = (U_CHAR *) rindex (ptr->fname, '/'); +      inc_fname = inc_fname ? inc_fname + 1 : (U_CHAR *) ptr->fname; +      if (inc_fname && !strcmp (inc_fname, fname)) +	warning ("`#pragma implementation' for `%s' appears after file is included", +		 fname); +    } +  } + +  return 0; +} + +#if 0 +/* This was a fun hack, but #pragma seems to start to be useful. +   By failing to recognize it, we pass it through unchanged to cc1.  */ + +/* + * the behavior of the #pragma directive is implementation defined. + * this implementation defines it as follows. + */ + +static int +do_pragma () +{ +  close (0); +  if (open ("/dev/tty", O_RDONLY, 0666) != 0) +    goto nope; +  close (1); +  if (open ("/dev/tty", O_WRONLY, 0666) != 1) +    goto nope; +  execl ("/usr/games/hack", "#pragma", 0); +  execl ("/usr/games/rogue", "#pragma", 0); +  execl ("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0); +  execl ("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0); +nope: +  fatal ("You are in a maze of twisty compiler features, all different"); +} +#endif + +/* Just ignore #sccs, on systems where we define it at all.  */ + +static int +do_sccs () +{ +  if (pedantic) +    pedwarn ("ANSI C does not allow `#sccs'"); +  return 0; +} + +/* + * 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 int +do_if (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  int value; +  FILE_BUF *ip = &instack[indepth]; + +  value = eval_if_expression (buf, limit - buf); +  conditional_skip (ip, value == 0, T_IF, NULL_PTR); +  return 0; +} + +/* + * handle a #elif directive by not changing  if_stack  either. + * see the comment above do_else. + */ + +static int +do_elif (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  int value; +  FILE_BUF *ip = &instack[indepth]; + +  if (if_stack == instack[indepth].if_stack) { +    error ("`#elif' not within a conditional"); +    return 0; +  } 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->nominal_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); +    } +  } +  return 0; +} + +/* + * 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) +     U_CHAR *buf; +     int length; +{ +  FILE_BUF temp_obuf; +  HASHNODE *save_defined; +  int value; + +  save_defined = install ("defined", -1, T_SPEC_DEFINED, 0, 0, -1); +  pcp_inside_if = 1; +  temp_obuf = expand_to_temp_buffer (buf, buf + length, 0, 1); +  pcp_inside_if = 0; +  delete_macro (save_defined);	/* clean up special symbol */ + +  value = parse_c_expression (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 int +do_xifdef (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  int skip; +  FILE_BUF *ip = &instack[indepth]; +  U_CHAR *end;  +  int start_of_file = 0; +  U_CHAR *control_macro = 0; + +  /* Detect a #ifndef at start of file (not counting comments).  */ +  if (ip->fname != 0 && keyword->type == T_IFNDEF) { +    U_CHAR *p = ip->buf; +    while (p != directive_start) { +      U_CHAR c = *p++; +      if (is_space[c]) +	; +      else if (c == '/' && p != ip->bufp && *p == '*') { +	/* Skip this comment.  */ +	int junk; +	U_CHAR *save_bufp = ip->bufp; +	ip->bufp = p + 1; +	p = skip_to_end_of_comment (ip, &junk, 1); +	ip->bufp = save_bufp; +      } else { +	goto fail; +      } +    } +    /* If we get here, this conditional is the beginning of the file.  */ +    start_of_file = 1; +  fail: ; +  } + +  /* Discard leading and trailing whitespace.  */ +  SKIP_WHITE_SPACE (buf); +  while (limit != buf && is_hor_space[limit[-1]]) limit--; + +  /* Find the end of the identifier at the beginning.  */ +  for (end = buf; is_idchar[*end]; end++); + +  if (end == buf) { +    skip = (keyword->type == T_IFDEF); +    if (! traditional) +      pedwarn (end == limit ? "`#%s' with no argument" +	       : "`#%s' argument starts with punctuation", +	       keyword->name); +  } else { +    HASHNODE *hp; + +    if (pedantic && buf[0] >= '0' && buf[0] <= '9') +      pedwarn ("`#%s' argument starts with a digit", keyword->name); +    else if (end != limit && !traditional) +      pedwarn ("garbage at end of `#%s' argument", keyword->name); + +    hp = lookup (buf, end-buf, -1); + +    if (pcp_outfile) { +      /* Output a precondition for this macro.  */ +      if (hp && hp->value.defn->predefined) +	fprintf (pcp_outfile, "#define %s\n", hp->name); +      else { +	U_CHAR *cp = buf; +	fprintf (pcp_outfile, "#undef "); +	while (is_idchar[*cp]) /* Ick! */ +	  fputc (*cp++, pcp_outfile); +	putc ('\n', pcp_outfile); +      } +    } + +    skip = (hp == NULL) ^ (keyword->type == T_IFNDEF); +    if (start_of_file && !skip) { +      control_macro = (U_CHAR *) xmalloc (end - buf + 1); +      bcopy (buf, control_macro, end - buf); +      control_macro[end - buf] = 0; +    } +  } +   +  conditional_skip (ip, skip, T_IF, control_macro); +  return 0; +} + +/* Push TYPE on stack; then, if SKIP is nonzero, skip ahead. +   If this is a #ifndef starting at the beginning of a file, +   CONTROL_MACRO is the macro name tested by the #ifndef. +   Otherwise, CONTROL_MACRO is 0.  */ + +static void +conditional_skip (ip, skip, type, control_macro) +     FILE_BUF *ip; +     int skip; +     enum node_type type; +     U_CHAR *control_macro; +{ +  IF_STACK_FRAME *temp; + +  temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME)); +  temp->fname = ip->nominal_fname; +  temp->lineno = ip->lineno; +  temp->next = if_stack; +  temp->control_macro = control_macro; +  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; +{ +  register U_CHAR *bp = ip->bufp, *cp; +  register U_CHAR *endb = ip->buf + ip->length; +  struct directive *kt; +  IF_STACK_FRAME *save_if_stack = if_stack; /* don't pop past here */ +  U_CHAR *beg_of_line = bp; +  register int ident_length; +  U_CHAR *ident, *after_ident; + +  while (bp < endb) { +    switch (*bp++) { +    case '/':			/* possible comment */ +      if (*bp == '\\' && bp[1] == '\n') +	newline_fix (bp); +      if (*bp == '*' +	  || (cplusplus_comments && *bp == '/')) { +	ip->bufp = ++bp; +	bp = skip_to_end_of_comment (ip, &ip->lineno, 0); +      } +      break; +    case '\"': +    case '\'': +      bp = skip_quoted_string (bp - 1, endb, ip->lineno, &ip->lineno, +			       NULL_PTR, NULL_PTR); +      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_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; +	} else if (cplusplus_comments && *bp == '/' && bp[1] == '/') { +	  bp += 2; +	  while (*bp++ != '\n') ; +        } +	else break; +      } +      if (bp != ip->bufp) { +	bp = ip->bufp + 1;	/* Reset bp to after the #.  */ +	break; +      } + +      bp = ip->bufp + 1;	/* Point after the '#' */ + +      /* Skip whitespace and \-newline.  */ +      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] == '/')) { +	    if (*bp == '\n') +	      ip->lineno++; +	    bp++; +	  } +	  bp += 2; +	} else if (cplusplus_comments && *bp == '/' && bp[1] == '/') { +	  bp += 2; +	  while (*bp++ != '\n') ; +        } +	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; +	} +      } +      ident_length = bp - cp; +      ident = cp; +      after_ident = bp; + +      /* A line of just `#' becomes blank.  */ + +      if (ident_length == 0 && *after_ident == '\n') { +	continue; +      } + +      if (ident_length == 0 || !is_idstart[*ident]) { +	U_CHAR *p = ident; +	while (is_idchar[*p]) { +	  if (*p < '0' || *p > '9') +	    break; +	  p++; +	} +	/* Handle # followed by a line number.  */ +	if (p != ident && !is_idchar[*p]) { +	  if (pedantic) +	    pedwarn ("`#' followed by integer"); +	  continue; +	} + +	/* Avoid error for `###' and similar cases unless -pedantic.  */ +	if (p == ident) { +	  while (*p == '#' || is_hor_space[*p]) p++; +	  if (*p == '\n') { +	    if (pedantic && !lang_asm) +	      pedwarn ("invalid preprocessor directive"); +	    continue; +	  } +	} + +	if (!lang_asm && pedantic) +	  pedwarn ("invalid preprocessor directive name"); +	continue; +      } + +      for (kt = directive_table; kt->length >= 0; kt++) { +	IF_STACK_FRAME *temp; +	if (ident_length == kt->length +	    && strncmp (cp, kt->name, kt->length) == 0) { +	  /* 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->nominal_fname; +	    temp->type = kt->type; +	    break; +	  case T_ELSE: +	  case T_ENDIF: +	    if (pedantic && if_stack != save_if_stack) +	      validate_else (bp); +	  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; +	  } +	  break; +	} +      } +      /* Don't let erroneous code go by.  */ +      if (kt->length < 0 && !lang_asm && pedantic) +	pedwarn ("invalid preprocessor directive name"); +    } +  } +  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 int +do_else (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  FILE_BUF *ip = &instack[indepth]; + +  if (pedantic) { +    SKIP_WHITE_SPACE (buf); +    if (buf != limit) +      pedwarn ("text following `#else' violates ANSI standard"); +  } + +  if (if_stack == instack[indepth].if_stack) { +    error ("`#else' not within a conditional"); +    return 0; +  } else { +    /* #ifndef can't have its special treatment for containing the whole file +       if it has a #else clause.  */ +    if_stack->control_macro = 0; + +    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->nominal_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); +  } +  return 0; +} + +/* + * unstack after #endif command + */ + +static int +do_endif (buf, limit, op, keyword) +     U_CHAR *buf, *limit; +     FILE_BUF *op; +     struct directive *keyword; +{ +  if (pedantic) { +    SKIP_WHITE_SPACE (buf); +    if (buf != limit) +      pedwarn ("text following `#endif' violates ANSI standard"); +  } + +  if (if_stack == instack[indepth].if_stack) +    error ("unbalanced `#endif'"); +  else { +    IF_STACK_FRAME *temp = if_stack; +    if_stack = if_stack->next; +    if (temp->control_macro != 0) { +      /* This #endif matched a #ifndef at the start of the file. +	 See if it is at the end of the file.  */ +      FILE_BUF *ip = &instack[indepth]; +      U_CHAR *p = ip->bufp; +      U_CHAR *ep = ip->buf + ip->length; + +      while (p != ep) { +	U_CHAR c = *p++; +	switch (c) { +	case ' ': +	case '\t': +	case '\n': +	  break; +	case '/': +	  if (p != ep && *p == '*') { +	    /* Skip this comment.  */ +	    int junk; +	    U_CHAR *save_bufp = ip->bufp; +	    ip->bufp = p + 1; +	    p = skip_to_end_of_comment (ip, &junk, 1); +	    ip->bufp = save_bufp; +	  } +	  break; +	default: +	  goto fail; +	} +      } +      /* If we get here, this #endif ends a #ifndef +	 that contains all of the file (aside from whitespace). +	 Arrange not to include the file again +	 if the macro that was tested is defined.  */ +      if (indepth != 0) +	record_control_macro (ip->fname, temp->control_macro); +    fail: ; +    } +    free (temp); +    output_line_command (&instack[indepth], op, 1, same_file); +  } +  return 0; +} + +/* When an #else or #endif is found while skipping failed conditional, +   if -pedantic was specified, this is called to warn about text after +   the command name.  P points to the first char after the command name.  */ + +static void +validate_else (p) +     register U_CHAR *p; +{ +  /* Advance P over whitespace and comments.  */ +  while (1) { +    if (*p == '\\' && p[1] == '\n') +      p += 2; +    if (is_hor_space[*p]) +      p++; +    else if (*p == '/') { +      if (p[1] == '\\' && p[2] == '\n') +	newline_fix (p + 1); +      if (p[1] == '*') { +	p += 2; +	/* Don't bother warning about unterminated comments +	   since that will happen later.  Just be sure to exit.  */ +	while (*p) { +	  if (p[1] == '\\' && p[2] == '\n') +	    newline_fix (p + 1); +	  if (*p == '*' && p[1] == '/') { +	    p += 2; +	    break; +	  } +	  p++; +	} +      } +      else if (cplusplus_comments && p[1] == '/') { +	p += 2; +	while (*p && *p++ != '\n') ; +      } +    } else break; +  } +  if (*p && *p != '\n') +    pedwarn ("text following `#else' or `#endif' violates ANSI standard"); +} + +/* Skip a comment, assuming the input ptr immediately follows the +   initial slash-star.  Bump *LINE_COUNTER for each newline. +   (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. + +   If NOWARN is nonzero, don't warn about slash-star inside a comment. +   This feature is useful when processing a comment that is going to be +   processed or was processed at another point in the preprocessor, +   to avoid a duplicate warning.  */ +static U_CHAR * +skip_to_end_of_comment (ip, line_counter, nowarn) +     register FILE_BUF *ip; +     int *line_counter;		/* place to remember newlines, or NULL */ +     int nowarn; +{ +  register U_CHAR *limit = ip->buf + ip->length; +  register 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++ = '*'; +  } +  if (cplusplus_comments && bp[-1] == '/') { +    if (output) { +      while (bp < limit) +	if ((*op->bufp++ = *bp++) == '\n') { +	  bp--; +	  break; +	} +      op->bufp[-1] = '*'; +      *op->bufp++ = '/'; +      *op->bufp++ = '\n'; +    } else { +      while (bp < limit) { +	if (*bp++ == '\n') { +	  bp--; +	  break; +	} +      } +    } +    ip->bufp = bp; +    return bp; +  } +  while (bp < limit) { +    if (output) +      *op->bufp++ = *bp; +    switch (*bp++) { +    case '/': +      if (warn_comments && !nowarn && 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) +     register U_CHAR *bp; +     register U_CHAR *limit; +     int start_line; +     int *count_newlines; +     int *backslash_newlines_p; +     int *eofp; +{ +  register U_CHAR c, match; + +  match = *bp++; +  while (1) { +    if (bp >= limit) { +      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; +      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') { +      if (traditional) { + 	/* Unterminated strings and character constants are 'legal'.  */ + 	bp--;	/* Don't consume the newline. */ + 	if (eofp) + 	  *eofp = 1; + 	break; +      } +      if (pedantic || match == '\'') { +	error_with_line (line_for_error (start_line), +			 "unterminated string or character constant"); +	bp--; +	if (eofp) +	  *eofp = 1; +	break; +      } +      /* If not traditional, then allow newlines inside strings.  */ +      if (count_newlines) +	++*count_newlines; +      if (multiline_string_line == 0) +	multiline_string_line = start_line; +    } else if (c == match) +      break; +  } +  return bp; +} + +/* Skip across a group of balanced parens, starting from IP->bufp. +   IP->bufp is updated.  Use this with IP->bufp pointing at an open-paren. + +   This does not handle newlines, because it's used for the arg of #if, +   where there aren't any newlines.  Also, backslash-newline can't appear.  */ + +static U_CHAR * +skip_paren_group (ip) +     register FILE_BUF *ip; +{ +  U_CHAR *limit = ip->buf + ip->length; +  U_CHAR *p = ip->bufp; +  int depth = 0; +  int lines_dummy = 0; + +  while (p != limit) { +    int c = *p++; +    switch (c) { +    case '(': +      depth++; +      break; + +    case ')': +      depth--; +      if (depth == 0) +	return ip->bufp = p; +      break; + +    case '/': +      if (*p == '*') { +	ip->bufp = p; +	p = skip_to_end_of_comment (ip, &lines_dummy, 0); +	p = ip->bufp; +      } + +    case '"': +    case '\'': +      { +	int eofp = 0; +	p = skip_quoted_string (p - 1, limit, 0, NULL_PTR, NULL_PTR, &eofp); +	if (eofp) +	  return ip->bufp = p; +      } +      break; +    } +  } + +  ip->bufp = p; +  return p; +} + +/* + * 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; + +  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; +    } +  } + +  /* Don't output a line number of 0 if we can help it.  */ +  if (ip->lineno == 0 && ip->bufp - ip->buf < ip->length +      && *ip->bufp == '\n') { +    ip->lineno++; +    ip->bufp++; +  } + +  line_cmd_buf = (char *) alloca (strlen (ip->nominal_fname) + 100); +#ifdef OUTPUT_LINE_COMMANDS +  sprintf (line_cmd_buf, "#line %d \"%s\"", ip->lineno, ip->nominal_fname); +#else +  sprintf (line_cmd_buf, "# %d \"%s\"", ip->lineno, ip->nominal_fname); +#endif +  if (file_change != same_file) +    strcat (line_cmd_buf, file_change == enter_file ? " 1" : " 2"); +  /* Tell cc1 if following text comes from a system header file.  */ +  if (ip->system_header_p) +    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'; +  bcopy (line_cmd_buf, op->bufp, len); +  op->bufp += len; +  op->lineno = ip->lineno; +} + +/* 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. +   `use_count' is the number of times this macro arg is substituted +   into the macro.  If the actual use count exceeds 10,  +   the value stored is 10. +   `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; +  char use_count; +}; + +/* 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; +  register U_CHAR *xbuf; +  int xbuf_len; +  int start_line = instack[indepth].lineno; +  int rest_args, rest_zero; + +  CHECK_DEPTH (return;); + +  /* it might not actually be a macro.  */ +  if (hp->type != T_MACRO) { +    special_symbol (hp, op); +    return; +  } + +  /* This macro is being used inside a #if, which means it must be */ +  /* recorded as a precondition.  */ +  if (pcp_inside_if && pcp_outfile && defn->predefined) +    dump_single_macro (hp, pcp_outfile); +   +  nargs = defn->nargs; + +  if (nargs >= 0) { +    register int i; +    struct argdata *args; +    char *parse_error = 0; + +    args = (struct argdata *) alloca ((nargs + 1) * sizeof (struct argdata)); + +    for (i = 0; i < nargs; i++) { +      args[i].raw = (U_CHAR *) ""; +      args[i].expanded = 0; +      args[i].raw_length = args[i].expand_length +	= args[i].stringified_length = 0; +      args[i].free1 = args[i].free2 = 0; +      args[i].use_count = 0; +    } + +    /* Parse all the macro args that are supplied.  I counts them. +       The first NARGS args are stored in ARGS. +       The rest are discarded. +       If rest_args is set then we assume macarg absorbed the rest of the args. +       */ +    i = 0; +    rest_args = 0; +    do { +      /* Discard the open-parenthesis or comma before the next arg.  */ +      ++instack[indepth].bufp; +      if (rest_args) +	continue; +      if (i < nargs || (nargs == 0 && i == 0)) { +	/* if we are working on last arg which absorbs rest of args... */ +	if (i == nargs - 1 && defn->rest_args) +	  rest_args = 1; +	parse_error = macarg (&args[i], rest_args); +      } +      else +	parse_error = macarg (NULL_PTR, 0); +      if (parse_error) { +	error_with_line (line_for_error (start_line), 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) { +      register U_CHAR *bp = args[0].raw; +      register U_CHAR *lim = bp + args[0].raw_length; +      /* cpp.texi says for foo ( ) we provide one argument. +	 However, if foo wants just 0 arguments, treat this as 0.  */ +      if (nargs == 0) +	while (bp != lim && is_space[*bp]) bp++; +      if (bp == lim) +	i = 0; +    } + +    /* Don't output an error message if we have already output one for +       a parse error above.  */ +    rest_zero = 0; +    if (nargs == 0 && i > 0) { +      if (! parse_error) +	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 && traditional) +	; +      /* the rest args token is allowed to absorb 0 tokens */ +      else if (i == nargs - 1 && defn->rest_args) +	rest_zero = 1; +      else if (parse_error) +	; +      else if (i == 0) +	error ("macro `%s' used without args", hp->name); +      else if (i == 1) +	error ("macro `%s' used with just one arg", hp->name); +      else +	error ("macro `%s' used with only %d args", hp->name, i); +    } else if (i > nargs) { +      if (! parse_error) +	error ("macro `%s' used with too many (%d) args", hp->name, i); +    } + +    /* 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 { +      register U_CHAR *exp = defn->expansion; +      register int offset;	/* offset in expansion, +				   copied a piece at a time */ +      register int totlen;	/* total amount of exp buffer filled so far */ + +      register struct reflist *ap, *last_ap; + +      /* Macro really takes args.  Compute the expansion of this call.  */ + +      /* Compute length in characters of the macro's expansion. +	 Also count number of times each arg is used.  */ +      xbuf_len = defn->length; +      for (ap = defn->pattern; ap != NULL; ap = ap->next) { +	if (ap->stringify) +	  xbuf_len += args[ap->argno].stringified_length; +	else if (ap->raw_before || ap->raw_after || traditional) +	  /* Add 4 for two newline-space markers to prevent +	     token concatenation.  */ +	  xbuf_len += args[ap->argno].raw_length + 4; +	else { +	  /* We have an ordinary (expanded) occurrence of the arg. +	     So compute its expansion, if we have not already.  */ +	  if (args[ap->argno].expanded == 0) { +	    FILE_BUF obuf; +	    obuf = expand_to_temp_buffer (args[ap->argno].raw, +					  args[ap->argno].raw + args[ap->argno].raw_length, +					  1, 0); + +	    args[ap->argno].expanded = obuf.buf; +	    args[ap->argno].expand_length = obuf.length; +	    args[ap->argno].free2 = obuf.buf; +	  } + +	  /* Add 4 for two newline-space markers to prevent +	     token concatenation.  */ +	  xbuf_len += args[ap->argno].expand_length + 4; +	} +	if (args[ap->argno].use_count < 10) +	  args[ap->argno].use_count++; +      } + +      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 (last_ap = NULL, ap = defn->pattern; ap != NULL; +	   last_ap = ap, ap = ap->next) { +	register struct argdata *arg = &args[ap->argno]; +	int count_before = totlen; + +	/* Add chars to XBUF.  */ +	for (i = 0; i < ap->nchars; i++, offset++) +	  xbuf[totlen++] = exp[offset]; + +	/* If followed by an empty rest arg with concatenation, +	   delete the last run of nonwhite chars.  */ +	if (rest_zero && totlen > count_before +	    && ((ap->rest_args && ap->raw_before) +		|| (last_ap != NULL && last_ap->rest_args +		    && last_ap->raw_after))) { +	  /* Delete final whitespace.  */ +	  while (totlen > count_before && is_space[xbuf[totlen - 1]]) { +	    totlen--; +	  } + +	  /* Delete the nonwhites before them.  */ +	  while (totlen > count_before && ! is_space[xbuf[totlen - 1]]) { +	    totlen--; +	  } +	} + +	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--; +	  if (!traditional) +	    xbuf[totlen++] = '\"'; /* insert beginning quote */ +	  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; +	    } +	  } +	  if (!traditional) +	    xbuf[totlen++] = '\"'; /* insert ending quote */ +	} else if (ap->raw_before || ap->raw_after || traditional) { +	  U_CHAR *p1 = arg->raw; +	  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; +	  } else if (!traditional) { +	  /* Ordinary expanded use of the argument. +	     Put in newline-space markers to prevent token pasting.  */ +	    xbuf[totlen++] = '\n'; +	    xbuf[totlen++] = ' '; +	  } +	  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] == '-') { +		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; +	    } +	  } + +	  bcopy (p1, xbuf + totlen, l1 - p1); +	  totlen += l1 - p1; +	  if (!traditional && !ap->raw_after) { +	    /* Ordinary expanded use of the argument. +	       Put in newline-space markers to prevent token pasting.  */ +	    xbuf[totlen++] = '\n'; +	    xbuf[totlen++] = ' '; +	  } +	} else { +	  /* Ordinary expanded use of the argument. +	     Put in newline-space markers to prevent token pasting.  */ +	  if (!traditional) { +	    xbuf[totlen++] = '\n'; +	    xbuf[totlen++] = ' '; +	  } +	  bcopy (arg->expanded, xbuf + totlen, arg->expand_length); +	  totlen += arg->expand_length; +	  if (!traditional) { +	    xbuf[totlen++] = '\n'; +	    xbuf[totlen++] = ' '; +	  } +	  /* If a macro argument with newlines is used multiple times, +	     then only expand the newlines once.  This avoids creating output +	     lines which don't correspond to any input line, which confuses +	     gdb and gcov.  */ +	  if (arg->use_count > 1 && arg->newlines > 0) { +	    /* Don't bother doing change_newlines for subsequent +	       uses of arg.  */ +	    arg->use_count = 1; +	    arg->expand_length +	      = change_newlines (arg->expanded, arg->expand_length); +	  } +	} + +	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++) { +	/* if we've reached the end of the macro */ +	if (exp[i] == ')') +	  rest_zero = 0; +	if (! (rest_zero && last_ap != NULL && last_ap->rest_args +	       && last_ap->raw_after)) +	  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.  */ +  { +    register FILE_BUF *ip2; + +    ip2 = &instack[++indepth]; + +    ip2->fname = 0; +    ip2->nominal_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; +    ip2->system_header_p = 0; + +    /* Recursive macro use sometimes works traditionally. +       #define foo(x,y) bar (x (y,0), y) +       foo (foo, baz)  */ + +    if (!traditional) +      hp->type = T_DISABLED; +  } +} + +/* + * Parse a macro argument and store the info on it into *ARGPTR. + * REST_ARGS is passed to macarg1 to make it absorb the rest of the args. + * Return nonzero to indicate a syntax error. + */ + +static char * +macarg (argptr, rest_args) +     register struct argdata *argptr; +     int rest_args; +{ +  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, rest_args); + +  /* 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; +      argptr->newlines = newlines; +    } +    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; + +    bcopy (ip->bufp, buffer, 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, rest_args); +      final_start = bufsize; +      bufsize += bp - ip->bufp; +      extra += newlines; +      buffer = (U_CHAR *) xrealloc (buffer, bufsize + extra + 1); +      bcopy (ip->bufp, buffer + bufsize - (bp - 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) { +    register U_CHAR *buf, *lim; +    register int totlen; + +    buf = argptr->raw; +    lim = buf + argptr->raw_length; + +    while (buf != lim && is_space[*buf]) +      buf++; +    while (buf != lim && is_space[lim[-1]]) +      lim--; +    totlen = traditional ? 0 : 2;	/* Count opening and closing quote.  */ +    while (buf != lim) { +      register 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 0 +      if (is_space[c]) +	SKIP_ALL_WHITE_SPACE (buf); +      else +#endif +      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. +   REST_ARGS notifies macarg1 that it should absorb the rest of the args. +   Set *COMMENTS to 1 if a comment is seen.  */ + +static U_CHAR * +macarg1 (start, limit, depthptr, newlines, comments, rest_args) +     U_CHAR *start; +     register U_CHAR *limit; +     int *depthptr, *newlines, *comments; +     int rest_args; +{ +  register 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 && traditional) +	{ +	  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 (cplusplus_comments && bp[1] == '/') { +	*comments = 1; +	bp += 2; +	while (bp < limit && *bp++ != '\n') ; +	++*newlines; +	break; +      } +      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++; +      } +      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 we've returned to lowest level and we aren't absorbing all args */ +      if ((*depthptr) == 0 && rest_args == 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; +{ +  register U_CHAR *ibp; +  register U_CHAR *obp; +  register U_CHAR *limit; +  register 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 (cplusplus_comments && ibp[0] == '/') { +	obp--; +	ibp++; +	while (ibp < limit && *ibp++ != '\n') ; +	break; +      } +      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; +} + +/* Turn newlines to spaces in the string of length LENGTH at START, +   except inside of string constants. +   The string is copied into itself with its beginning staying fixed.  */ + +static int +change_newlines (start, length) +     U_CHAR *start; +     int length; +{ +  register U_CHAR *ibp; +  register U_CHAR *obp; +  register U_CHAR *limit; +  register int c; + +  ibp = start; +  limit = start + length; +  obp = start; + +  while (ibp < limit) { +    *obp++ = c = *ibp++; +    switch (c) { +    case '\n': +      /* If this is a NEWLINE NEWLINE, then this is a real newline in the +	 string.  Skip past the newline and its duplicate. +	 Put a space in the output.  */ +      if (*ibp == '\n') +	{ +	  ibp++; +	  obp--; +	  *obp++ = ' '; +	} +      break; + +    case '\'': +    case '\"': +      /* Notice and skip strings, so that we don't delete newlines in them.  */ +      { +	int quotec = c; +	while (ibp < limit) { +	  *obp++ = c = *ibp++; +	  if (c == quotec) +	    break; +	  if (c == '\n' && quotec == '\'') +	    break; +	} +      } +      break; +    } +  } + +  return obp - start; +} + +/* + * error - print error message and increment count of errors. + */ + +void +error (msg, arg1, arg2, arg3) +     char *msg; +     char *arg1, *arg2, *arg3; +{ +  int i; +  FILE_BUF *ip = NULL; + +  print_containing_files (); + +  for (i = indepth; i >= 0; i--) +    if (instack[i].fname != NULL) { +      ip = &instack[i]; +      break; +    } + +  if (ip != NULL) +    fprintf (stderr, "%s:%d: ", ip->nominal_fname, ip->lineno); +  fprintf (stderr, msg, arg1, arg2, arg3); +  fprintf (stderr, "\n"); +  errors++; +} + +/* Error including a message from `errno'.  */ + +static void +error_from_errno (name) +     char *name; +{ +  int i; +  FILE_BUF *ip = NULL; + +  print_containing_files (); + +  for (i = indepth; i >= 0; i--) +    if (instack[i].fname != NULL) { +      ip = &instack[i]; +      break; +    } + +  if (ip != NULL) +    fprintf (stderr, "%s:%d: ", ip->nominal_fname, ip->lineno); + +  if (errno < sys_nerr) +    fprintf (stderr, "%s: %s\n", name, sys_errlist[errno]); +  else +    fprintf (stderr, "%s: undocumented I/O error\n", name); + +  errors++; +} + +/* Print error message but don't count it.  */ + +void +warning (msg, arg1, arg2, arg3) +     char *msg; +     char *arg1, *arg2, *arg3; +{ +  int i; +  FILE_BUF *ip = NULL; + +  if (inhibit_warnings) +    return; + +  if (warnings_are_errors) +    errors++; + +  print_containing_files (); + +  for (i = indepth; i >= 0; i--) +    if (instack[i].fname != NULL) { +      ip = &instack[i]; +      break; +    } + +  if (ip != NULL) +    fprintf (stderr, "%s:%d: ", ip->nominal_fname, ip->lineno); +  fprintf (stderr, "warning: "); +  fprintf (stderr, msg, arg1, arg2, arg3); +  fprintf (stderr, "\n"); +} + +static void +error_with_line (line, msg, arg1, arg2, arg3) +     int line; +     char *msg; +     char *arg1, *arg2, *arg3; +{ +  int i; +  FILE_BUF *ip = NULL; + +  print_containing_files (); + +  for (i = indepth; i >= 0; i--) +    if (instack[i].fname != NULL) { +      ip = &instack[i]; +      break; +    } + +  if (ip != NULL) +    fprintf (stderr, "%s:%d: ", ip->nominal_fname, line); +  fprintf (stderr, msg, arg1, arg2, arg3); +  fprintf (stderr, "\n"); +  errors++; +} + +static void +warning_with_line (line, msg, arg1, arg2, arg3) +     int line; +     char *msg; +     char *arg1, *arg2, *arg3; +{ +  int i; +  FILE_BUF *ip = NULL; + +  if (inhibit_warnings) +    return; + +  if (warnings_are_errors) +    errors++; + +  print_containing_files (); + +  for (i = indepth; i >= 0; i--) +    if (instack[i].fname != NULL) { +      ip = &instack[i]; +      break; +    } + +  if (ip != NULL) +    fprintf (stderr, "%s:%d: ", ip->nominal_fname, line); +  fprintf (stderr, "warning: "); +  fprintf (stderr, msg, arg1, arg2, arg3); +  fprintf (stderr, "\n"); +} + +/* print an error message and maybe count it.  */ + +void +pedwarn (msg, arg1, arg2, arg3) +     char *msg; +     char *arg1, *arg2, *arg3; +{ +  if (pedantic_errors) +    error (msg, arg1, arg2, arg3); +  else +    warning (msg, arg1, arg2, arg3); +} + +void +pedwarn_with_line (line, msg, arg1, arg2, arg3) +     int line; +     char *msg; +     char *arg1, *arg2, *arg3; +{ +  if (pedantic_errors) +    error_with_line (line, msg, arg1, arg2, arg3); +  else +    warning_with_line (line, msg, arg1, arg2, arg3); +} + +/* Report a warning (or an error if pedantic_errors) +   giving specified file name and line number, not current.  */ + +static void +pedwarn_with_file_and_line (file, line, msg, arg1, arg2, arg3) +     char *file; +     int line; +     char *msg; +     char *arg1, *arg2, *arg3; +{ +  int i; +  if (!pedantic_errors && inhibit_warnings) +    return; +  if (file != NULL) +    fprintf (stderr, "%s:%d: ", file, line); +  if (pedantic_errors) +    errors++; +  if (!pedantic_errors) +    fprintf (stderr, "warning: "); +  fprintf (stderr, msg, arg1, arg2, arg3); +  fprintf (stderr, "\n"); +} + +/* Print the file names and line numbers of the #include +   commands which led to the current file.  */ + +static void +print_containing_files () +{ +  FILE_BUF *ip = NULL; +  int i; +  int first = 1; + +  /* If stack of files hasn't changed since we last printed +     this info, don't repeat it.  */ +  if (last_error_tick == input_file_stack_tick) +    return; + +  for (i = indepth; i >= 0; i--) +    if (instack[i].fname != NULL) { +      ip = &instack[i]; +      break; +    } + +  /* Give up if we don't find a source file.  */ +  if (ip == NULL) +    return; + +  /* Find the other, outer source files.  */ +  for (i--; i >= 0; i--) +    if (instack[i].fname != NULL) { +      ip = &instack[i]; +      if (first) { +	first = 0; +	fprintf (stderr, "In file included"); +      } else { +	fprintf (stderr, ","); +      } + +      fprintf (stderr, " from %s:%d", ip->nominal_fname, ip->lineno); +    } +  if (! first) +    fprintf (stderr, ":\n"); + +  /* Record we have printed the status as of this time.  */ +  last_error_tick = input_file_stack_tick; +} + +/* 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; +  } +  abort (); +  /*NOTREACHED*/ +  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. + */ + +/* You might think void was cleaner for the return type, +   but that would get type mismatch in check_expand in strict ANSI.  */ +static int +grow_outbuf (obuf, needed) +     register FILE_BUF *obuf; +     register int needed; +{ +  register U_CHAR *p; +  int minsize; + +  if (obuf->length - (obuf->bufp - obuf->buf) > needed) +    return 0; + +  /* 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; + +  if ((p = (U_CHAR *) xrealloc (obuf->buf, obuf->length)) == NULL) +    memory_full (); + +  obuf->bufp = p + (obuf->bufp - obuf->buf); +  obuf->buf = p; + +  return 0; +} + +/* 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. + */ +static HASHNODE * +install (name, len, type, ivalue, value, hash) +     U_CHAR *name; +     int len; +     enum node_type type; +     int ivalue; +     char *value; +     int hash; +{ +  register HASHNODE *hp; +  register int i, bucket; +  register U_CHAR *p, *q; + +  if (len < 0) { +    p = name; +    while (is_idchar[*p]) +      p++; +    len = p - name; +  } + +  if (hash < 0) +    hash = hashf (name, len, HASHSIZE); + +  i = sizeof (HASHNODE) + len + 1; +  hp = (HASHNODE *) xmalloc (i); +  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; +  if (hp->type == T_CONST) +    hp->value.ival = ivalue; +  else +    hp->value.cpval = value; +  hp->name = ((U_CHAR *) hp) + sizeof (HASHNODE); +  p = hp->name; +  q = name; +  for (i = 0; i < len; i++) +    *p++ = *q++; +  hp->name[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) +     U_CHAR *name; +     int len; +     int hash; +{ +  register U_CHAR *bp; +  register 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 (bucket->name, 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; + +#if 0 +  if (hp->type == T_MACRO) { +    DEFINITION *d = hp->value.defn; +    struct reflist *ap, *nextap; + +    for (ap = d->pattern; ap != NULL; ap = nextap) { +      nextap = ap->next; +      free (ap); +    } +    free (d); +  } +#endif +  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) +     register U_CHAR *name; +     register int len; +     int hashsize; +{ +  register int r = 0; + +  while (len--) +    r = HASHSTEP (r, *name++); + +  return MAKE_POS (r) % hashsize; +} + + +/* Dump the definition of a single macro HP to OF.  */ +static void +dump_single_macro (hp, of) +     register HASHNODE *hp; +     FILE *of; +{ +  register DEFINITION *defn = hp->value.defn; +  struct reflist *ap; +  int offset; +  int concat; + + +  /* Print the definition of the macro HP.  */ + +  fprintf (of, "#define %s", hp->name); + +  if (defn->nargs >= 0) { +    int i; + +    fprintf (of, "("); +    for (i = 0; i < defn->nargs; i++) { +      dump_arg_n (defn, i, of); +      if (i + 1 < defn->nargs) +	fprintf (of, ", "); +    } +    fprintf (of, ")"); +  } + +  fprintf (of, " "); + +  offset = 0; +  concat = 0; +  for (ap = defn->pattern; ap != NULL; ap = ap->next) { +    dump_defn_1 (defn->expansion, offset, ap->nchars, of); +    if (ap->nchars != 0) +      concat = 0; +    offset += ap->nchars; +    if (ap->stringify) +      fprintf (of, " #"); +    if (ap->raw_before && !concat) +      fprintf (of, " ## "); +    concat = 0; +    dump_arg_n (defn, ap->argno, of); +    if (ap->raw_after) { +      fprintf (of, " ## "); +      concat = 1; +    } +  } +  dump_defn_1 (defn->expansion, offset, defn->length - offset, of); +  fprintf (of, "\n"); +} + +/* Dump all macro definitions as #defines to stdout.  */ + +static void +dump_all_macros () +{ +  int bucket; + +  for (bucket = 0; bucket < HASHSIZE; bucket++) { +    register HASHNODE *hp; + +    for (hp = hashtab[bucket]; hp; hp= hp->next) { +      if (hp->type == T_MACRO) +	dump_single_macro (hp, stdout); +    } +  } +} + +/* Output to OF 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, of) +     U_CHAR *base; +     int start; +     int length; +     FILE *of; +{ +  U_CHAR *p = base + start; +  U_CHAR *limit = base + start + length; + +  while (p < limit) { +    if (*p != '\n') +      putc (*p, of); +    else if (*p == '\"' || *p =='\'') { +      U_CHAR *p1 = skip_quoted_string (p, limit, 0, NULL_PTR, +				       NULL_PTR, NULL_PTR); +      fwrite (p, p1 - p, 1, of); +      p = p1 - 1; +    } +    p++; +  } +} + +/* Print the name of argument number ARGNUM of macro definition DEFN +   to OF. +   Recall that DEFN->args.argnames contains all the arg names +   concatenated in reverse order with comma-space in between.  */ + +static void +dump_arg_n (defn, argnum, of) +     DEFINITION *defn; +     int argnum; +     FILE *of; +{ +  register U_CHAR *p = defn->args.argnames; +  while (argnum + 1 < defn->nargs) { +    p = (U_CHAR *) index (p, ' ') + 1; +    argnum++; +  } + +  while (*p && *p != ',') { +    putc (*p, of); +    p++; +  } +} + +/* Initialize syntactic classifications of characters.  */ + +static void +initialize_char_syntax () +{ +  register int i; + +  /* +   * Set up is_idchar and is_idstart tables.  These should be +   * faster than saying (is_alpha (c) || c == '_'), etc. +   * Set up these things before calling any routines tthat +   * refer to them. +   */ +  for (i = 'a'; i <= 'z'; i++) { +    is_idchar[i - 'a' + 'A'] = 1; +    is_idchar[i] = 1; +    is_idstart[i - 'a' + 'A'] = 1; +    is_idstart[i] = 1; +  } +  for (i = '0'; i <= '9'; i++) +    is_idchar[i] = 1; +  is_idchar['_'] = 1; +  is_idstart['_'] = 1; +  is_idchar['$'] = dollars_in_ident; +  is_idstart['$'] = dollars_in_ident; + +  /* horizontal space table */ +  is_hor_space[' '] = 1; +  is_hor_space['\t'] = 1; +  is_hor_space['\v'] = 1; +  is_hor_space['\f'] = 1; +  is_hor_space['\r'] = 1; + +  is_space[' '] = 1; +  is_space['\t'] = 1; +  is_space['\v'] = 1; +  is_space['\f'] = 1; +  is_space['\n'] = 1; +  is_space['\r'] = 1; +} + +/* Initialize the built-in macros.  */ + +static void +initialize_builtins (inp, outp) +     FILE_BUF *inp; +     FILE_BUF *outp; +{ +  install ("__LINE__", -1, T_SPECLINE, 0, 0, -1); +  install ("__DATE__", -1, T_DATE, 0, 0, -1); +  install ("__FILE__", -1, T_FILE, 0, 0, -1); +  install ("__BASE_FILE__", -1, T_BASE_FILE, 0, 0, -1); +  install ("__INCLUDE_LEVEL__", -1, T_INCLUDE_LEVEL, 0, 0, -1); +  install ("__VERSION__", -1, T_VERSION, 0, 0, -1); +#ifndef NO_BUILTIN_SIZE_TYPE +  install ("__SIZE_TYPE__", -1, T_SIZE_TYPE, 0, 0, -1); +#endif +#ifndef NO_BUILTIN_PTRDIFF_TYPE +  install ("__PTRDIFF_TYPE__ ", -1, T_PTRDIFF_TYPE, 0, 0, -1); +#endif +  install ("__WCHAR_TYPE__", -1, T_WCHAR_TYPE, 0, 0, -1); +  install ("__USER_LABEL_PREFIX__", -1, T_USER_LABEL_PREFIX_TYPE, 0, 0, -1); +  install ("__REGISTER_PREFIX__", -1, T_REGISTER_PREFIX_TYPE, 0, 0, -1); +  install ("__TIME__", -1, T_TIME, 0, 0, -1); +  if (!traditional) +    install ("__STDC__", -1, T_CONST, STDC_VALUE, 0, -1); +  if (objc) +    install ("__OBJC__", -1, T_CONST, 1, 0, -1); +/*  This is supplied using a -D by the compiler driver +    so that it is present only when truly compiling with GNU C.  */ +/*  install ("__GNUC__", -1, T_CONST, 2, 0, -1);  */ + +  if (debug_output) +    { +      char directive[2048]; +      register struct directive *dp = &directive_table[0]; +      struct tm *timebuf = timestamp (); + +      sprintf (directive, " __BASE_FILE__ \"%s\"\n", +	       instack[0].nominal_fname); +      output_line_command (inp, outp, 0, same_file); +      pass_thru_directive (directive, &directive[strlen (directive)], outp, dp); + +      sprintf (directive, " __VERSION__ \"%s\"\n", version_string); +      output_line_command (inp, outp, 0, same_file); +      pass_thru_directive (directive, &directive[strlen (directive)], outp, dp); + +      sprintf (directive, " __SIZE_TYPE__ %s\n", SIZE_TYPE); +      output_line_command (inp, outp, 0, same_file); +      pass_thru_directive (directive, &directive[strlen (directive)], outp, dp); + +      sprintf (directive, " __PTRDIFF_TYPE__ %s\n", PTRDIFF_TYPE); +      output_line_command (inp, outp, 0, same_file); +      pass_thru_directive (directive, &directive[strlen (directive)], outp, dp); + +      sprintf (directive, " __WCHAR_TYPE__ %s\n", WCHAR_TYPE); +      output_line_command (inp, outp, 0, same_file); +      pass_thru_directive (directive, &directive[strlen (directive)], outp, dp); + +      sprintf (directive, " __DATE__ \"%s %2d %4d\"\n", +	       monthnames[timebuf->tm_mon], +	       timebuf->tm_mday, timebuf->tm_year + 1900); +      output_line_command (inp, outp, 0, same_file); +      pass_thru_directive (directive, &directive[strlen (directive)], outp, dp); + +      sprintf (directive, " __TIME__ \"%02d:%02d:%02d\"\n", +	       timebuf->tm_hour, timebuf->tm_min, timebuf->tm_sec); +      output_line_command (inp, outp, 0, same_file); +      pass_thru_directive (directive, &directive[strlen (directive)], outp, dp); + +      if (!traditional) +	{ +          sprintf (directive, " __STDC__ 1"); +          output_line_command (inp, outp, 0, same_file); +          pass_thru_directive (directive, &directive[strlen (directive)], +			       outp, dp); +	} +      if (objc) +	{ +          sprintf (directive, " __OBJC__ 1"); +          output_line_command (inp, outp, 0, same_file); +          pass_thru_directive (directive, &directive[strlen (directive)], +			       outp, dp); +	} +    } +} + +/* + * process a given definition string, for initialization + * If STR is just an identifier, define it with value 1. + * If STR has anything after the identifier, then it should + * be identifier=definition. + */ + +static void +make_definition (str, op) +     U_CHAR *str; +     FILE_BUF *op; +{ +  FILE_BUF *ip; +  struct directive *kt; +  U_CHAR *buf, *p; + +  buf = str; +  p = str; +  if (!is_idstart[*p]) { +    error ("malformed option `-D %s'", str); +    return; +  } +  while (is_idchar[*++p]) +    ; +  if (*p == 0) { +    buf = (U_CHAR *) alloca (p - buf + 4); +    strcpy ((char *)buf, str); +    strcat ((char *)buf, " 1"); +  } else if (*p != '=') { +    error ("malformed option `-D %s'", str); +    return; +  } else { +    U_CHAR *q; +    /* Copy the entire option so we can modify it.  */ +    buf = (U_CHAR *) alloca (2 * strlen (str) + 1); +    strncpy (buf, str, p - str); +    /* Change the = to a space.  */ +    buf[p - str] = ' '; +    /* Scan for any backslash-newline and remove it.  */ +    p++; +    q = &buf[p - str]; +    while (*p) { +      if (*p == '\\' && p[1] == '\n') +	p += 2; +      /* Change newline chars into newline-markers.  */ +      else if (*p == '\n') +	{ +	  *q++ = '\n'; +	  *q++ = '\n'; +	  p++; +	} +      else +	*q++ = *p++; +    } +    *q = 0; +  } +   +  ip = &instack[++indepth]; +  ip->nominal_fname = ip->fname = "*Initialization*"; + +  ip->buf = ip->bufp = buf; +  ip->length = strlen (buf); +  ip->lineno = 1; +  ip->macro = 0; +  ip->free_ptr = 0; +  ip->if_stack = if_stack; +  ip->system_header_p = 0; + +  for (kt = directive_table; kt->type != T_DEFINE; kt++) +    ; + +  /* Pass NULL instead of OP, since this is a "predefined" macro.  */ +  do_define (buf, buf + strlen (buf), NULL, kt); +  --indepth; +} + +/* JF, this does the work for the -U option */ + +static void +make_undef (str, op) +     U_CHAR *str; +     FILE_BUF *op; +{ +  FILE_BUF *ip; +  struct directive *kt; + +  ip = &instack[++indepth]; +  ip->nominal_fname = ip->fname = "*undef*"; + +  ip->buf = ip->bufp = str; +  ip->length = strlen (str); +  ip->lineno = 1; +  ip->macro = 0; +  ip->free_ptr = 0; +  ip->if_stack = if_stack; +  ip->system_header_p = 0; + +  for (kt = directive_table; kt->type != T_UNDEF; kt++) +    ; + +  do_undef (str, str + strlen (str), op, kt); +  --indepth; +} + +/* Process the string STR as if it appeared as the body of a #assert. +   OPTION is the option name for which STR was the argument.  */ + +static void +make_assertion (option, str) +     char *option; +     U_CHAR *str; +{ +  FILE_BUF *ip; +  struct directive *kt; +  U_CHAR *buf, *p, *q; + +  /* Copy the entire option so we can modify it.  */ +  buf = (U_CHAR *) alloca (strlen (str) + 1); +  strcpy ((char *) buf, str); +  /* Scan for any backslash-newline and remove it.  */ +  p = q = buf; +  while (*p) { +    if (*p == '\\' && p[1] == '\n') +      p += 2; +    else +      *q++ = *p++; +  } +  *q = 0; + +  p = buf; +  if (!is_idstart[*p]) { +    error ("malformed option `%s %s'", option, str); +    return; +  } +  while (is_idchar[*++p]) +    ; +  while (*p == ' ' || *p == '\t') p++; +  if (! (*p == 0 || *p == '(')) { +    error ("malformed option `%s %s'", option, str); +    return; +  } +   +  ip = &instack[++indepth]; +  ip->nominal_fname = ip->fname = "*Initialization*"; + +  ip->buf = ip->bufp = buf; +  ip->length = strlen (buf); +  ip->lineno = 1; +  ip->macro = 0; +  ip->free_ptr = 0; +  ip->if_stack = if_stack; +  ip->system_header_p = 0; + +  for (kt = directive_table; kt->type != T_ASSERT; kt++) +    ; + +  /* pass NULL as output ptr to do_define since we KNOW it never +     does any output.... */ +  do_assert (buf, buf + strlen (buf) , NULL_PTR, kt); +  --indepth; +} + +/* Append a chain of `struct file_name_list's +   to the end of the main include chain. +   FIRST is the beginning of the chain to append, and LAST is the end.  */ + +static void +append_include_chain (first, last) +     struct file_name_list *first, *last; +{ +  struct file_name_list *dir; + +  if (!first || !last) +    return; + +  if (include == 0) +    include = first; +  else +    last_include->next = first; + +  if (first_bracket_include == 0) +    first_bracket_include = first; + +  for (dir = first; ; dir = dir->next) { +    int len = strlen (dir->fname) + INCLUDE_LEN_FUDGE; +    if (len > max_include_len) +      max_include_len = len; +    if (dir == last) +      break; +  } + +  last->next = NULL; +  last_include = last; +} + +/* Add output to `deps_buffer' for the -M switch. +   STRING points to the text to be output. +   SIZE is the number of bytes, or 0 meaning output until a null. +   Outputting the empty string breaks the line if it is long enough.  */ + +static void +deps_output (string, size) +     char *string; +     unsigned size; +{ +  if (size == 0) +    size = strlen (string); + +#ifndef MAX_OUTPUT_COLUMNS +#define MAX_OUTPUT_COLUMNS 75 +#endif +  if (size == 0 && deps_column != 0 +      && size + deps_column > MAX_OUTPUT_COLUMNS) { +    deps_output ("\\\n  ", 0); +    deps_column = 0; +  } + +  if (deps_size + size + 1 > deps_allocated_size) { +    deps_allocated_size = deps_size + size + 50; +    deps_allocated_size *= 2; +    deps_buffer = (char *) xrealloc (deps_buffer, deps_allocated_size); +  } +  bcopy (string, &deps_buffer[deps_size], size); +  deps_size += size; +  deps_column += size; +  deps_buffer[deps_size] = 0; +} + +#if defined(USG) || defined(VMS) +#ifndef BSTRING + +void +bzero (b, length) +     register char *b; +     register unsigned length; +{ +  while (length-- > 0) +    *b++ = 0; +} + +void +bcopy (b1, b2, length) +     register char *b1; +     register char *b2; +     register unsigned length; +{ +  while (length-- > 0) +    *b2++ = *b1++; +} + +int +bcmp (b1, b2, length)	/* This could be a macro! */ +     register char *b1; +     register char *b2; +     register unsigned length; +{ +   while (length-- > 0) +     if (*b1++ != *b2++) +       return 1; + +   return 0; +} +#endif /* not BSTRING */ +#endif /* USG or VMS */ + + +static void +fatal (str, arg) +     char *str, *arg; +{ +  fprintf (stderr, "%s: ", progname); +  fprintf (stderr, str, arg); +  fprintf (stderr, "\n"); +  exit (FAILURE_EXIT_CODE); +} + +/* More 'friendly' abort that prints the line and file. +   config.h can #define abort fancy_abort if you like that sort of thing.  */ + +void +fancy_abort () +{ +  fatal ("Internal gcc abort."); +} + +static void +perror_with_name (name) +     char *name; +{ +  fprintf (stderr, "%s: ", progname); +  if (errno < sys_nerr) +    fprintf (stderr, "%s: %s\n", name, sys_errlist[errno]); +  else +    fprintf (stderr, "%s: undocumented I/O error\n", name); +  errors++; +} + +static void +pfatal_with_name (name) +     char *name; +{ +  perror_with_name (name); +#ifdef VMS +  exit (vaxc$errno); +#else +  exit (FAILURE_EXIT_CODE); +#endif +} + +/* Handler for SIGPIPE.  */ + +static void +pipe_closed (signo) +     /* If this is missing, some compilers complain.  */ +     int signo; +{ +  fatal ("output pipe has been closed"); +} + +static void +memory_full () +{ +  fatal ("Memory exhausted."); +} + + +char * +xmalloc (size) +     unsigned size; +{ +  register char *ptr = (char *) malloc (size); +  if (ptr != 0) return (ptr); +  memory_full (); +  /*NOTREACHED*/ +  return 0; +} + +static char * +xrealloc (old, size) +     char *old; +     unsigned size; +{ +  register char *ptr = (char *) realloc (old, size); +  if (ptr != 0) return (ptr); +  memory_full (); +  /*NOTREACHED*/ +  return 0; +} + +static char * +xcalloc (number, size) +     unsigned number, size; +{ +  register unsigned total = number * size; +  register char *ptr = (char *) malloc (total); +  if (ptr != 0) { +    if (total > 100) +      bzero (ptr, total); +    else { +      /* It's not too long, so loop, zeroing by longs. +	 It must be safe because malloc values are always well aligned.  */ +      register long *zp = (long *) ptr; +      register long *zl = (long *) (ptr + total - 4); +      register int i = total - 4; +      while (zp < zl) +	*zp++ = 0; +      if (i < 0) +	i = 0; +      while (i < total) +	ptr[i++] = 0; +    } +    return ptr; +  } +  memory_full (); +  /*NOTREACHED*/ +  return 0; +} + +static char * +savestring (input) +     char *input; +{ +  unsigned size = strlen (input); +  char *output = xmalloc (size + 1); +  strcpy (output, input); +  return output; +} + +/* 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 int *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; +} + +#ifdef VMS + +/* Under VMS we need to fix up the "include" specification +   filename so that everything following the 1st slash is +   changed into its correct VMS file specification. */ + +static void +hack_vms_include_specification (fname) +     char *fname; +{ +  register char *cp, *cp1, *cp2; +  int f, check_filename_before_returning, no_prefix_seen; +  char Local[512]; + +  check_filename_before_returning = 0; +  no_prefix_seen = 0; + +  /* Ignore leading "./"s */ +  while (fname[0] == '.' && fname[1] == '/') { +    strcpy (fname, fname+2); +    no_prefix_seen = 1;		/* mark this for later */ +  } +  /* Look for the boundary between the VMS and UNIX filespecs */ +  cp = rindex (fname, ']');	/* Look for end of dirspec. */ +  if (cp == 0) cp = rindex (fname, '>'); /* ... Ditto		    */ +  if (cp == 0) cp = rindex (fname, ':'); /* Look for end of devspec. */ +  if (cp) { +    cp++; +  } else { +    cp = index (fname, '/');	/* Look for the "/" */ +  } + +  cp2 = Local;			/* initialize */ + +  /* We are trying to do a number of things here.  First of all, we are +     trying to hammer the filenames into a standard format, such that later +     processing can handle them. +      +     If the file name contains something like [dir.], then it recognizes this +     as a root, and strips the ".]".  Later processing will add whatever is +     needed to get things working properly. +      +     If no device is specified, then the first directory name is taken to be +     a device name (or a rooted logical). */ + +  /* See if we found that 1st slash */ +  if (cp == 0) return;		/* Nothing to do!!! */ +  if (*cp != '/') return;	/* Nothing to do!!! */ +  /* Point to the UNIX filename part (which needs to be fixed!) */ +  cp1 = cp+1; +  /* If the directory spec is not rooted, we can just copy +     the UNIX filename part and we are done */ +  if (((cp - fname) > 1) && ((cp[-1] == ']') || (cp[-1] == '>'))) { +    if (cp[-2] != '.') { +      /* +       * The VMS part ends in a `]', and the preceding character is not a `.'. +       * We strip the `]', and then splice the two parts of the name in the +       * usual way.  Given the default locations for include files in cccp.c, +       * we will only use this code if the user specifies alternate locations +       * with the /include (-I) switch on the command line.  */ +      cp -= 1;			/* Strip "]" */ +      cp1--;			/* backspace */ +    } else { +      /* +       * The VMS part has a ".]" at the end, and this will not do.  Later +       * processing will add a second directory spec, and this would be a syntax +       * error.  Thus we strip the ".]", and thus merge the directory specs. +       * We also backspace cp1, so that it points to a '/'.  This inhibits the +       * generation of the 000000 root directory spec (which does not belong here +       * in this case). +       */ +      cp -= 2;			/* Strip ".]" */ +      cp1--; };			/* backspace */ +  } else { + +    /* We drop in here if there is no VMS style directory specification yet. +     * If there is no device specification either, we make the first dir a +     * device and try that.  If we do not do this, then we will be essentially +     * searching the users default directory (as if they did a #include "asdf.h"). +     * +     * Then all we need to do is to push a '[' into the output string. Later +     * processing will fill this in, and close the bracket. +     */ +    if (cp[-1] != ':') *cp2++ = ':'; /* dev not in spec.  take first dir */ +    *cp2++ = '[';		/* Open the directory specification */ +  } + +  /* at this point we assume that we have the device spec, and (at least +     the opening "[" for a directory specification.  We may have directories +     specified already */ + +  /* If there are no other slashes then the filename will be +     in the "root" directory.  Otherwise, we need to add +     directory specifications. */ +  if (index (cp1, '/') == 0) { +    /* Just add "000000]" as the directory string */ +    strcpy (cp2, "000000]"); +    cp2 += strlen (cp2); +    check_filename_before_returning = 1; /* we might need to fool with this later */ +  } else { +    /* As long as there are still subdirectories to add, do them. */ +    while (index (cp1, '/') != 0) { +      /* If this token is "." we can ignore it */ +      if ((cp1[0] == '.') && (cp1[1] == '/')) { +	cp1 += 2; +	continue; +      } +      /* Add a subdirectory spec. Do not duplicate "." */ +      if (cp2[-1] != '.' && cp2[-1] != '[' && cp2[-1] != '<') +	*cp2++ = '.'; +      /* If this is ".." then the spec becomes "-" */ +      if ((cp1[0] == '.') && (cp1[1] == '.') && (cp[2] == '/')) { +	/* Add "-" and skip the ".." */ +	*cp2++ = '-'; +	cp1 += 3; +	continue; +      } +      /* Copy the subdirectory */ +      while (*cp1 != '/') *cp2++= *cp1++; +      cp1++;			/* Skip the "/" */ +    } +    /* Close the directory specification */ +    if (cp2[-1] == '.')		/* no trailing periods */ +      cp2--; +    *cp2++ = ']'; +  } +  /* Now add the filename */ +  while (*cp1) *cp2++ = *cp1++; +  *cp2 = 0; +  /* Now append it to the original VMS spec. */ +  strcpy (cp, Local); + +  /* If we put a [000000] in the filename, try to open it first. If this fails, +     remove the [000000], and return that name.  This provides flexibility +     to the user in that they can use both rooted and non-rooted logical names +     to point to the location of the file.  */ + +  if (check_filename_before_returning && no_prefix_seen) { +    f = open (fname, O_RDONLY, 0666); +    if (f >= 0) { +      /* The file name is OK as it is, so return it as is.  */ +      close (f); +      return; +    } +    /* The filename did not work.  Try to remove the [000000] from the name, +       and return it.  */ +    cp = index (fname, '['); +    cp2 = index (fname, ']') + 1; +    strcpy (cp, cp2);		/* this gets rid of it */ +  } +  return; +} +#endif	/* VMS */ + +#ifdef	VMS + +/* These are the read/write replacement routines for +   VAX-11 "C".  They make read/write behave enough +   like their UNIX counterparts that CCCP will work */ + +static int +read (fd, buf, size) +     int fd; +     char *buf; +     int size; +{ +#undef	read	/* Get back the REAL read routine */ +  register int i; +  register int total = 0; + +  /* Read until the buffer is exhausted */ +  while (size > 0) { +    /* Limit each read to 32KB */ +    i = (size > (32*1024)) ? (32*1024) : size; +    i = read (fd, buf, i); +    if (i <= 0) { +      if (i == 0) return (total); +      return (i); +    } +    /* Account for this read */ +    total += i; +    buf += i; +    size -= i; +  } +  return (total); +} + +static int +write (fd, buf, size) +     int fd; +     char *buf; +     int size; +{ +#undef	write	/* Get back the REAL write routine */ +  int i; +  int j; + +  /* Limit individual writes to 32Kb */ +  i = size; +  while (i > 0) { +    j = (i > (32*1024)) ? (32*1024) : i; +    if (write (fd, buf, j) < 0) return (-1); +    /* Account for the data written */ +    buf += j; +    i -= j; +  } +  return (size); +} + +/* The following wrapper functions supply additional arguments to the VMS +   I/O routines to optimize performance with file handling.  The arguments +   are: +     "mbc=16" - Set multi-block count to 16 (use a 8192 byte buffer). +     "deq=64" - When extending the file, extend it in chunks of 32Kbytes. +     "fop=tef"- Truncate unused portions of file when closing file. +     "shr=nil"- Disallow file sharing while file is open. + */ + +static FILE * +freopen (fname, type, oldfile) +     char *fname; +     char *type; +     FILE *oldfile; +{ +#undef	freopen	/* Get back the REAL fopen routine */ +  if (strcmp (type, "w") == 0) +    return freopen (fname, type, oldfile, "mbc=16", "deq=64", "fop=tef", "shr=nil"); +  return freopen (fname, type, oldfile, "mbc=16"); +} + +static FILE * +fopen (fname, type) +     char *fname; +     char *type; +{ +#undef fopen	/* Get back the REAL fopen routine */ +  if (strcmp (type, "w") == 0) +    return fopen (fname, type, "mbc=16", "deq=64", "fop=tef", "shr=nil"); +  return fopen (fname, type, "mbc=16"); +} + +static int  +open (fname, flags, prot) +     char *fname; +     int flags; +     int prot; +{ +#undef open	/* Get back the REAL open routine */ +  return open (fname, flags, prot, "mbc=16", "deq=64", "fop=tef"); +} + +/* Avoid run-time library bug, where copying M out of N+M characters with +   N >= 65535 results in VAXCRTL's strncat falling into an infinite loop. +   gcc-cpp exercises this particular bug.  */ + +static char * +strncat (dst, src, cnt) +     char *dst; +     const char *src; +     unsigned cnt; +{ +  register char *d = dst, *s = (char *) src; +  register int n = cnt;	/* convert to _signed_ type */ + +  while (*d) d++;	/* advance to end */ +  while (--n >= 0) +    if (!(*d++ = *s++)) break; +  if (n < 0) *d = '\0'; +  return dst; +} +#endif /* VMS */ | 
