/* CPP Library.
 * Copyright (C) 1986, 87, 89, 92, 93, 94, 1995 Free Software Foundation, Inc.
 * Written by Per Bothner, 1994-95.
 * Based on CCCP program by by Paul Rubin, June 1986
 * Adapted to ANSI C, Richard Stallman, Jan 1987
 * Copyright (C) 2003-2011 Kim Woelders
 * 
 * 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!  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef HAVE_ALLOCA_H
# include <alloca.h>
#elif defined __GNUC__
# define alloca __builtin_alloca
#elif defined _AIX
# define alloca __alloca
#elif defined _MSC_VER
# include <malloc.h>
# define alloca _alloca
#else
# include <stddef.h>
void *alloca (size_t);
#endif

#ifdef __EMX__
#include <strings.h>
#endif

#ifndef STANDARD_INCLUDE_DIR
#define STANDARD_INCLUDE_DIR "/usr/include"
#endif

#ifndef LOCAL_INCLUDE_DIR
#define LOCAL_INCLUDE_DIR "/usr/local/include"
#endif

#include "cpplib.h"
#include "cpphash.h"

const char         *version_string = "0.0.0";

#ifndef STDC_VALUE
#define STDC_VALUE 1
#endif

/* By default, colon separates directories in a path.  */
#ifndef PATH_SEPARATOR
#define PATH_SEPARATOR ':'
#endif

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#ifndef VMS
#ifndef USG
#include <time.h>
#include <sys/time.h>		/* for __DATE__ and __TIME__ */
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
#else
#include <sys/param.h>		/* CYGNUS LOCAL: shebs -noquiet */
#include <sys/times.h>
#include <time.h>
#include <fcntl.h>
#endif /* USG */
#endif /* not VMS */

/* This defines "errno" properly for VMS, and gives us EACCES. */
#include <errno.h>

#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))

#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 GENERIC_PTR
#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
#define GENERIC_PTR void *
#else
#define GENERIC_PTR char *
#endif
#endif

#ifndef INCLUDE_LEN_FUDGE
#define INCLUDE_LEN_FUDGE 0
#endif

#define USE_FILE_NAME_MAPS 0

/* Symbols to predefine.  */

#ifdef CPP_PREDEFINES
static const char  *predefs = CPP_PREDEFINES;

#else
static const 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
#define CPP_WCHAR_TYPE(PFILE) \
	(CPP_OPTIONS (PFILE)->cplusplus ? "__wchar_t" : WCHAR_TYPE)

/* The string value for __USER_LABEL_PREFIX__ */

#ifndef USER_LABEL_PREFIX
#define USER_LABEL_PREFIX ""
#endif

/* The string value for __REGISTER_PREFIX__ */

#ifndef REGISTER_PREFIX
#define REGISTER_PREFIX ""
#endif

struct directive {
   int                 length;
   int                 (*func) (cpp_reader * pfile, struct directive * keyword,
				unsigned char *buf, unsigned char *limit);
   const char         *name;
   enum node_type      type;
   char                command_reads_line;
   char                traditional_comments;
   char                pass_thru;
};

/* 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 */
   char               *name;	/* the actual name */
   /* List of token-sequences.  */
   struct tokenlist_list *value;
};

#define SKIP_WHITE_SPACE(p) do { while (is_hor_space[(unsigned char)(*p)]) p++; } while (0)
#define SKIP_ALL_WHITE_SPACE(p) do { while (is_space[(unsigned char)(*p)]) p++; } while (0)

#define PEEKN(N) (CPP_BUFFER (pfile)->rlimit - CPP_BUFFER (pfile)->cur >= (N) ? CPP_BUFFER (pfile)->cur[N] : EOF)
#define FORWARD(N) CPP_FORWARD (CPP_BUFFER (pfile), (N))
#define GETC() CPP_BUF_GET (CPP_BUFFER (pfile))
#define PEEKC() CPP_BUF_PEEK (CPP_BUFFER (pfile))
/* CPP_IS_MACRO_BUFFER is true if the buffer contains macro expansion.
 * (Note that it is false while we're expanding marco *arguments*.) */
#define CPP_IS_MACRO_BUFFER(PBUF) ((PBUF)->cleanup == macro_cleanup)

/* 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).  */

#define NEWLINE_FIX \
  do {while (PEEKC() == '\\' && PEEKN(1) == '\n') FORWARD(2); } while(0)

/* Same, but assume we've already read the potential '\\' into C. */
#define NEWLINE_FIX1(C) do { \
    while ((C) == '\\' && PEEKC() == '\n') { FORWARD(1); (C) = GETC(); }\
  } while(0)

/* Name under which this program was invoked.  */

char               *progname;

struct cpp_pending {
   struct cpp_pending *next;
   const char         *cmd;
   const char         *arg;
};

/* Structure returned by create_definition */
typedef struct {
   struct definition  *defn;
   char               *symnam;
   int                 symlen;
} MACRODEF;

/* Forward declarations.  */
typedef struct file_name_list file_name_list;

static void         add_import(cpp_reader * pfile, int fd, char *fname);
static int          finclude(cpp_reader * pfile, int f, const char *fname,
			     int system_header_p, file_name_list * dirptr);
static void         validate_else(cpp_reader * pfile, const char *directive);
static int          comp_def_part(int first, unsigned char *beg1, int len1,
				  unsigned char *beg2, int len2, int last);
static int          lookup_import(cpp_reader * pfile, char *filename,
				  file_name_list * searchptr);
static int          redundant_include_p(cpp_reader * pfile, char *name);

static int          is_system_include(cpp_reader * pfile, char *filename);

static int          open_include_file(cpp_reader * pfile, char *filename,
				      file_name_list * searchptr);
static int          check_macro_name(cpp_reader * pfile, unsigned char *symname,
				     const char *usage);

static int          compare_token_lists(struct arglist *l1, struct arglist *l2);
static HOST_WIDE_INT eval_if_expression(cpp_reader * pfile, unsigned char *buf,
					int length);

static int          file_size_and_mode(int fd, int *mode_pointer,
				       long int *size_pointer);
static struct arglist *read_token_list(cpp_reader * pfile, int *error_flag);
static void         free_token_list(struct arglist *tokens);
static int          safe_read(int desc, char *ptr, int len);
static void         push_macro_expansion(cpp_reader * pfile,
					 unsigned char *x,
					 int xbuf_len, HASHNODE * hp);

static struct cpp_pending *nreverse_pending(struct cpp_pending *list);
static char        *savestring(const char *input);

static void         conditional_skip(cpp_reader * pfile, int skip,
				     enum node_type type,
				     unsigned char *control_macro);
static void         skip_if_group(cpp_reader * pfile, int any);

static void         cpp_error_with_line(cpp_reader * pfile, int line,
					int column, const char *msg);
static void         cpp_pedwarn_with_line(cpp_reader * pfile, int line,
					  int column, const char *msg);
static void         cpp_pedwarn_with_file_and_line(cpp_reader * pfile,
						   const char *file, int line,
						   const char *msg,
						   const char *arg1,
						   const char *arg2,
						   const char *arg3);
static void         cpp_error_from_errno(cpp_reader * pfile, const char *name);

static cpp_buffer  *cpp_push_buffer(cpp_reader * pfile, unsigned char *buffer,
				    long length);
static cpp_buffer  *cpp_pop_buffer(cpp_reader * pfile);

/* Last arg to output_line_command.  */
enum file_change_code {
   same_file, enter_file, leave_file
};

/* 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(cpp_reader * pfile, struct directive *keyword,
			      unsigned char *buf, unsigned char *limit);

static int          do_line(cpp_reader * pfile, struct directive *keyword,
			    unsigned char *unused1, unsigned char *unused2);

static int          do_include(cpp_reader * pfile, struct directive *keyword,
			       unsigned char *unused1, unsigned char *unused2);

static int          do_undef(cpp_reader * pfile, struct directive *keyword,
			     unsigned char *buf, unsigned char *limit);

static int          do_error(cpp_reader * pfile, struct directive *keyword,
			     unsigned char *buf, unsigned char *limit);

static int          do_pragma(cpp_reader * pfile, struct directive *keyword,
			      unsigned char *buf, unsigned char *limit);

static int          do_ident(cpp_reader * pfile, struct directive *keyword,
			     unsigned char *buf, unsigned char *limit);

static int          do_if(cpp_reader * pfile, struct directive *keyword,
			  unsigned char *buf, unsigned char *limit);

static int          do_xifdef(cpp_reader * pfile, struct directive *keyword,
			      unsigned char *buf, unsigned char *limit);

static int          do_else(cpp_reader * pfile, struct directive *keyword,
			    unsigned char *buf, unsigned char *limit);

static int          do_elif(cpp_reader * pfile, struct directive *keyword,
			    unsigned char *buf, unsigned char *limit);

static int          do_endif(cpp_reader * pfile, struct directive *keyword,
			     unsigned char *buf, unsigned char *limit);

static int          do_assert(cpp_reader * pfile, struct directive *keyword,
			      unsigned char *buf, unsigned char *limit);

static int          do_unassert(cpp_reader * pfile, struct directive *keyword,
				unsigned char *buf, unsigned char *limit);

static int          do_warning(cpp_reader * pfile, struct directive *keyword,
			       unsigned char *buf, unsigned char *limit);

struct arglist     *reverse_token_list(struct arglist *tokens);

static int          parse_name(cpp_reader * pfile, int c);

static void         parse_set_mark(struct parse_marker *pmark,
				   cpp_reader * pfile);
static void         parse_clear_mark(struct parse_marker *pmark);
static void         parse_goto_mark(struct parse_marker *pmark,
				    cpp_reader * pfile);
static void         parse_move_mark(struct parse_marker *pmark,
				    cpp_reader * pfile);

struct file_name_list {
   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.  */
   unsigned char      *control_macro;
   /* If the following is nonzero, it is a C-language system include
    * directory.  */
   int                 c_system_include_path;
   /* Mapping of file names for this directory.  */
   struct file_name_map *name_map;
   /* Non-zero if name_map is valid.  */
   int                 got_name_map;
};

/* If a buffer's dir field is SELF_DIR_DUMMY, it means the file was found
 * via the same directory as the file that #included it. */
#define SELF_DIR_DUMMY ((file_name_list*)(~0))

/* #include "file" looks in source file dir, then stack. */
/* #include <file> just looks in the stack. */
/* -I directories are added to the end, then the defaults are added. */
/* The */
static struct default_include {
   const char         *fname;	/* The name of the directory.  */
   int                 cplusplus;	/* Only look here if we're compiling C++.  */
   int                 cxx_aware;	/* Includes in this directory don't need to
					 * be wrapped in extern "C" when compiling
					 * C++.  */
} include_defaults_array[]
#ifdef INCLUDE_DEFAULTS
   = INCLUDE_DEFAULTS;

#else
   =
{
   /* Pick up GNU C++ specific include files.  */
   {
   GPLUSPLUS_INCLUDE_DIR, 1, 1}
   ,
#ifdef CROSS_COMPILE
      /* This is the dir for fixincludes.  Put it just before
       * the files that we fix.  */
   {
   GCC_INCLUDE_DIR, 0, 0}
   ,
      /* For cross-compilation, this dir name is generated
       * automatically in Makefile.in.  */
   {
   CROSS_INCLUDE_DIR, 0, 0}
   ,
      /* This is another place that the target system's headers might be.  */
   {
   TOOL_INCLUDE_DIR, 0, 1}
   ,
#else /* not CROSS_COMPILE */
      /* This should be /usr/local/include and should come before
       * the fixincludes-fixed header files.  */
   {
   LOCAL_INCLUDE_DIR, 0, 1}
   ,
      /* This is here ahead of GCC_INCLUDE_DIR because assert.h goes here.
       * Likewise, behind LOCAL_INCLUDE_DIR, where glibc puts its assert.h.  */
   {
   TOOL_INCLUDE_DIR, 0, 1}
   ,
      /* This is the dir for fixincludes.  Put it just before
       * the files that we fix.  */
   {
   GCC_INCLUDE_DIR, 0, 0}
   ,
      /* Some systems have an extra dir of include files.  */
#ifdef SYSTEM_INCLUDE_DIR
   {
   SYSTEM_INCLUDE_DIR, 0, 0}
   ,
#endif
   {
   STANDARD_INCLUDE_DIR, 0, 0}
   ,
#endif /* not CROSS_COMPILE */
   {
   0, 0, 0}
};

#endif /* no INCLUDE_DEFAULTS */

/* Here is the actual list of #-directives, most-often-used first.
 * The initialize_builtins function assumes #define is the very first.  */

static struct directive directive_table[] = {
   {6, do_define, "define", T_DEFINE, 0, 1, 0},
   {5, do_xifdef, "ifdef", T_IFDEF, 1, 0, 0},
   {6, do_xifdef, "ifndef", T_IFNDEF, 1, 0, 0},
   {7, do_include, "include", T_INCLUDE, 1, 0, 0},
   {12, do_include, "include_next", T_INCLUDE_NEXT, 1, 0, 0},
   {6, do_include, "import", T_IMPORT, 1, 0, 0},
   {5, do_endif, "endif", T_ENDIF, 1, 0, 0},
   {4, do_else, "else", T_ELSE, 1, 0, 0},
   {2, do_if, "if", T_IF, 1, 0, 0},
   {4, do_elif, "elif", T_ELIF, 1, 0, 0},
   {5, do_undef, "undef", T_UNDEF, 0, 0, 0},
   {5, do_error, "error", T_ERROR, 0, 0, 0},
   {7, do_warning, "warning", T_WARNING, 0, 0, 0},
   {6, do_pragma, "pragma", T_PRAGMA, 0, 0, 1},
   {4, do_line, "line", T_LINE, 1, 0, 0},
   {5, do_ident, "ident", T_IDENT, 1, 0, 1},
#ifdef SCCS_DIRECTIVE
   {4, do_sccs, "sccs", T_SCCS, 0, 0, 0},
#endif
   {6, do_assert, "assert", T_ASSERT, 1, 0, 0},
   {8, do_unassert, "unassert", T_UNASSERT, 1, 0, 0},
   {-1, 0, "", T_UNUSED, 0, 0, 0},
};

/* table to tell if char can be part of a C identifier. */
unsigned char       is_idchar[256];

/* table to tell if char can be first char of a c identifier. */
unsigned char       is_idstart[256];

/* table to tell if c is horizontal space.  */
unsigned char       is_hor_space[256];

/* table to tell if c is horizontal or vertical space.  */
static unsigned char is_space[256];

/* Initialize syntactic classifications of characters.  */

static void
initialize_char_syntax(struct cpp_options *opts)
{
   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[(unsigned char)'_'] = 1;
   is_idstart[(unsigned char)'_'] = 1;
   is_idchar[(unsigned char)'$'] = opts->dollars_in_ident;
   is_idstart[(unsigned char)'$'] = opts->dollars_in_ident;

   /* horizontal space table */
   is_hor_space[(unsigned char)' '] = 1;
   is_hor_space[(unsigned char)'\t'] = 1;
   is_hor_space[(unsigned char)'\v'] = 1;
   is_hor_space[(unsigned char)'\f'] = 1;
   is_hor_space[(unsigned char)'\r'] = 1;

   is_space[(unsigned char)' '] = 1;
   is_space[(unsigned char)'\t'] = 1;
   is_space[(unsigned char)'\v'] = 1;
   is_space[(unsigned char)'\f'] = 1;
   is_space[(unsigned char)'\n'] = 1;
   is_space[(unsigned char)'\r'] = 1;
}

/* Place into PFILE a quoted string representing the string SRC.
 * Caller must reserve enough space in pfile->token_buffer. */
static void
quote_string(cpp_reader * pfile, const char *src)
{
   unsigned char       c;

   CPP_PUTC_Q(pfile, '\"');
   for (;;)
      switch ((c = *src++))
	{
	default:
	   if (isprint(c))
	      CPP_PUTC_Q(pfile, c);
	   else
	     {
		sprintf((char *)CPP_PWRITTEN(pfile), "\\%03o", c);
		CPP_ADJUST_WRITTEN(pfile, 4);
	     }
	   break;

	case '\"':
	case '\\':
	   CPP_PUTC_Q(pfile, '\\');
	   CPP_PUTC_Q(pfile, c);
	   break;

	case '\0':
	   CPP_PUTC_Q(pfile, '\"');
	   CPP_NUL_TERMINATE_Q(pfile);
	   return;
	}
}

/* Make sure PFILE->token_buffer will hold at least N more chars. */

void
cpp_grow_buffer(cpp_reader * pfile, long n)
{
   long                old_written = CPP_WRITTEN(pfile);

   pfile->token_buffer_size = n + 2 * pfile->token_buffer_size;
   pfile->token_buffer =
      (unsigned char *)xrealloc(pfile->token_buffer, pfile->token_buffer_size);
   CPP_SET_WRITTEN(pfile, old_written);
}

/*
 * 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.
 */

void
cpp_define(cpp_reader * pfile, unsigned char *str)
{
   unsigned char      *buf, *p;

   buf = str;
   p = str;
   if (!is_idstart[*p])
     {
	cpp_error(pfile, "malformed option `-D %s'", str);
	return;
     }
   while (is_idchar[*++p])
      ;
   if (*p == 0)
     {
	buf = (unsigned char *)alloca(p - buf + 4);
	strcpy((char *)buf, (const char *)str);
	strcat((char *)buf, " 1");
     }
   else if (*p != '=')
     {
	cpp_error(pfile, "malformed option `-D %s'", str);
	return;
     }
   else
     {
	unsigned char      *q;

	/* Copy the entire option so we can modify it.  */
	buf = (unsigned char *)alloca(2 * strlen((char *)str) + 1);
	strncpy((char *)buf, (const char *)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;
	     else
		*q++ = *p++;
	  }
	*q = 0;
     }

   do_define(pfile, NULL, buf, buf + strlen((char *)buf));
}

/* 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(cpp_reader * pfile, const char *option, const char *str)
{
   unsigned char      *buf, *p, *q;

   /* Copy the entire option so we can modify it.  */
   buf = (unsigned char *)alloca(strlen((char *)str) + 1);
   strcpy((char *)buf, (const char *)str);
   /* Scan for any backslash-newline and remove it.  */
   p = q = buf;
   while (*p)
     {
	*q++ = *p++;
     }
   *q = 0;

   p = buf;
   if (!is_idstart[*p])
     {
	cpp_error(pfile, "malformed option `%s %s'", option, str);
	return;
     }
   while (is_idchar[*++p])
      ;
   while (*p == ' ' || *p == '\t')
      p++;
   if (!(*p == 0 || *p == '('))
     {
	cpp_error(pfile, "malformed option `%s %s'", option, str);
	return;
     }
   cpp_push_buffer(pfile, buf, strlen((char *)buf));
   do_assert(pfile, NULL, NULL, NULL);
   cpp_pop_buffer(pfile);
}

/* Append a chain of `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(cpp_reader * pfile, file_name_list * first,
		     file_name_list * last)
{
   struct cpp_options *opts = CPP_OPTIONS(pfile);
   file_name_list     *dir;

   if (!first || !last)
      return;

   if (!opts->include)
      opts->include = first;
   else
      opts->last_include->next = first;

   if (!opts->first_bracket_include)
      opts->first_bracket_include = first;

   for (dir = first;; dir = dir->next)
     {
	int                 len = strlen(dir->fname) + INCLUDE_LEN_FUDGE;

	if (len > pfile->max_include_len)
	   pfile->max_include_len = len;
	if (dir == last)
	   break;
     }

   last->next = NULL;
   opts->last_include = last;
}

/* Add output to `deps_buffer' for the -M switch.
 * STRING points to the text to be output.
 * SPACER is ':' for targets, ' ' for dependencies, zero for text
 * to be inserted literally.  */

static void
deps_output(cpp_reader * pfile, const char *string, int spacer)
{
   int                 size = strlen(string);

   if (size == 0)
      return;

#ifndef MAX_OUTPUT_COLUMNS
#define MAX_OUTPUT_COLUMNS 72
#endif
   if (spacer
       && pfile->deps_column > 0
       && (pfile->deps_column + size) > MAX_OUTPUT_COLUMNS)
     {
	deps_output(pfile, " \\\n  ", 0);
	pfile->deps_column = 0;
     }
   if (pfile->deps_size + size + 8 > pfile->deps_allocated_size)
     {
	pfile->deps_allocated_size = (pfile->deps_size + size + 50) * 2;
	pfile->deps_buffer = (char *)xrealloc(pfile->deps_buffer,
					      pfile->deps_allocated_size);
     }
   if (spacer == ' ' && pfile->deps_column > 0)
      pfile->deps_buffer[pfile->deps_size++] = ' ';
   memcpy(&pfile->deps_buffer[pfile->deps_size], string, size);
   pfile->deps_size += size;
   pfile->deps_column += size;
   if (spacer == ':')
      pfile->deps_buffer[pfile->deps_size++] = ':';
   pfile->deps_buffer[pfile->deps_size] = 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(cpp_reader * pfile, char *path)
{
   char               *p;

   p = path;

   if (*p)
      while (1)
	{
	   char               *q = p;
	   char               *name;
	   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);
		memcpy(name, p, q - p);
		name[q - p] = 0;
	     }

	   dirtmp = (file_name_list *) xmalloc(sizeof(file_name_list));

	   dirtmp->next = 0;	/* New one goes on the end */
	   dirtmp->control_macro = 0;
	   dirtmp->c_system_include_path = 0;
	   dirtmp->fname = name;
	   dirtmp->got_name_map = 0;
	   append_include_chain(pfile, dirtmp, dirtmp);

	   /* Advance past this name.  */
	   p = q;
	   if (*p == 0)
	      break;
	   /* Skip the colon.  */
	   p++;
	}
}

void
init_parse_options(struct cpp_options *opts)
{
   memset((char *)opts, 0, sizeof *opts);
   opts->in_fname = NULL;
   opts->out_fname = NULL;

   /* Initialize is_idchar to allow $.  */
   opts->dollars_in_ident = 1;
   initialize_char_syntax(opts);
   opts->dollars_in_ident = DOLLARS_IN_IDENTIFIERS > 0;

   opts->no_line_commands = 0;
   opts->no_trigraphs = 1;
   opts->put_out_comments = 0;
   opts->print_include_names = 0;
   opts->dump_macros = dump_none;
   opts->no_output = 0;
   opts->cplusplus = 0;
   opts->cplusplus_comments = 1;

   opts->verbose = 0;
   opts->objc = 0;
   opts->lang_asm = 0;
   opts->for_lint = 0;
   opts->chill = 0;
   opts->pedantic_errors = 0;
   opts->inhibit_warnings = 0;
   opts->warn_comments = 0;
   opts->warn_import = 1;
   opts->warnings_are_errors = 0;
}

static enum cpp_token
null_underflow(cpp_reader * pfile __UNUSED__)
{
   return CPP_EOF;
}

static int
null_cleanup(cpp_buffer * pbuf __UNUSED__, cpp_reader * pfile __UNUSED__)
{
   return 0;
}

static int
macro_cleanup(cpp_buffer * pbuf, cpp_reader * pfile __UNUSED__)
{
   HASHNODE           *macro = (HASHNODE *) pbuf->data;

   if (macro->type == T_DISABLED)
      macro->type = T_MACRO;
   if (macro->type != T_MACRO || pbuf->buf != macro->value.defn->expansion)
      free(pbuf->buf);
   return 0;
}

static int
file_cleanup(cpp_buffer * pbuf, cpp_reader * pfile __UNUSED__)
{
   if (pbuf->buf)
     {
	free(pbuf->buf);
	pbuf->buf = 0;
     }
   return 0;
}

/* Assuming we have read '/'.
 * If this is the start of a comment (followed by '*' or '/'),
 * skip to the end of the comment, and return ' '.
 * Return EOF if we reached the end of file before the end of the comment.
 * If not the start of a comment, return '/'. */

static int
skip_comment(cpp_reader * pfile, long *linep)
{
   int                 c = 0;

   while (PEEKC() == '\\' && PEEKN(1) == '\n')
     {
	if (linep)
	   (*linep)++;
	FORWARD(2);
     }
   if (PEEKC() == '*')
     {
	FORWARD(1);
	for (;;)
	  {
	     int                 prev_c = c;

	     c = GETC();
	     if (c == EOF)
		return EOF;
	     while (c == '\\' && PEEKC() == '\n')
	       {
		  if (linep)
		     (*linep)++;
		  FORWARD(1), c = GETC();
	       }
	     if (prev_c == '*' && c == '/')
		return ' ';
	     if (c == '\n' && linep)
		(*linep)++;
	  }
     }
   else if (PEEKC() == '/' && CPP_OPTIONS(pfile)->cplusplus_comments)
     {
	FORWARD(1);
	for (;;)
	  {
	     c = GETC();
	     if (c == EOF)
		return ' ';	/* Allow // to be terminated by EOF. */
	     while (c == '\\' && PEEKC() == '\n')
	       {
		  FORWARD(1);
		  c = GETC();
		  if (linep)
		     (*linep)++;
	       }
	     if (c == '\n')
	       {
		  /* Don't consider final '\n' to be part of comment. */
		  FORWARD(-1);
		  return ' ';
	       }
	  }
     }
   else
      return '/';
}

/* Skip whitespace \-newline and comments.  Does not macro-expand.  */
void
cpp_skip_hspace(cpp_reader * pfile)
{
   while (1)
     {
	int                 c = PEEKC();

	if (c == EOF)
	   return;		/* FIXME */
	if (is_hor_space[c])
	  {
	     if ((c == '\f' || c == '\v') && CPP_PEDANTIC(pfile))
		cpp_pedwarn(pfile, "%s in preprocessing directive",
			    c == '\f' ? "formfeed" : "vertical tab");
	     FORWARD(1);
	  }
	else if (c == '/')
	  {
	     FORWARD(1);
	     c = skip_comment(pfile, NULL);
	     if (c == '/')
		FORWARD(-1);
	     if (c == EOF || c == '/')
		return;
	  }
	else if (c == '\\' && PEEKN(1) == '\n')
	  {
	     FORWARD(2);
	  }
	else if (c == '@' && CPP_BUFFER(pfile)->has_escapes
		 && is_hor_space[PEEKN(1)])
          {
             FORWARD(1);
          }
	else
	   return;
     }
}

/* Read the rest of the current line.
 * The line is appended to PFILE's output buffer. */

static void
copy_rest_of_line(cpp_reader * pfile)
{
   struct cpp_options *opts = CPP_OPTIONS(pfile);

   for (;;)
     {
	int                 c = GETC();
	int                 nextc;

	switch (c)
	  {
	  case EOF:
	     goto end_directive;
	  case '\\':
	     if (PEEKC() == '\n')
	       {
		  FORWARD(1);
		  continue;
	       }
	  case '\'':
	  case '\"':
	     goto scan_directive_token;
	     break;
	  case '/':
	     nextc = PEEKC();
	     if (nextc == '*' || (opts->cplusplus_comments && nextc == '*'))
		goto scan_directive_token;
	     break;
	  case '\f':
	  case '\v':
	     if (CPP_PEDANTIC(pfile))
		cpp_pedwarn(pfile, "%s in preprocessing directive",
			    c == '\f' ? "formfeed" : "vertical tab");
	     break;

	  case '\n':
	     FORWARD(-1);
	     goto end_directive;
	   scan_directive_token:
	     FORWARD(-1);
	     cpp_get_token(pfile);
	     continue;
	  }
	CPP_PUTC(pfile, c);
     }
 end_directive:;
   CPP_NUL_TERMINATE(pfile);
}

void
skip_rest_of_line(cpp_reader * pfile)
{
   long                old = CPP_WRITTEN(pfile);

   copy_rest_of_line(pfile);
   CPP_SET_WRITTEN(pfile, old);
}

/* Handle a possible # directive.
 * '#' has already been read.  */

static int
handle_directive(cpp_reader * pfile)
{
   int                 c;
   struct directive   *kt;
   int                 ident_length;
   long                after_ident = 0;
   unsigned char      *ident, *line_end;
   long                old_written = CPP_WRITTEN(pfile);

   cpp_skip_hspace(pfile);

   c = PEEKC();
   if (c >= '0' && c <= '9')
     {
	/* Handle # followed by a line number.  */
	if (CPP_PEDANTIC(pfile))
	   cpp_pedwarn(pfile, "`#' followed by integer");
	do_line(pfile, NULL, NULL, NULL);
	goto done_a_directive;
     }
   /* Now find the directive name. */
   CPP_PUTC(pfile, '#');
   parse_name(pfile, GETC());
   ident = pfile->token_buffer + old_written + 1;
   ident_length = CPP_PWRITTEN(pfile) - ident;
   if (ident_length == 0 && PEEKC() == '\n')
     {
	/* A line of just `#' becomes blank.  */
	goto done_a_directive;
     }
   /*
    * Decode the keyword and call the appropriate expansion
    * routine, after moving the input pointer up to the next line.
    */
   for (kt = directive_table;; kt++)
     {
	if (kt->length <= 0)
	   goto not_a_directive;
	if (kt->length == ident_length
	    && !strncmp(kt->name, (const char *)ident, ident_length))
	   break;
     }

   if (!kt->command_reads_line)
     {
	/* Nonzero means do not delete comments within the directive.
	 * #define needs this when -traditional.  */
	int                 comments = 0;
	int                 save_put_out_comments =
	   CPP_OPTIONS(pfile)->put_out_comments;

	CPP_OPTIONS(pfile)->put_out_comments = comments;
	after_ident = CPP_WRITTEN(pfile);
	copy_rest_of_line(pfile);
	CPP_OPTIONS(pfile)->put_out_comments = save_put_out_comments;
     }
   /* For #pragma and #define, we may want to pass through the directive.
    * Other directives may create output, but we don't want the directive
    * itself out, so we pop it now.  For example #include may write a #line
    * command (see comment in do_include), and conditionals may emit
    * #failed ... #endfailed stuff.  But note that popping the buffer
    * means the parameters to kt->func may point after pfile->limit
    * so these parameters are invalid as soon as something gets appended
    * to the token_buffer.  */

   line_end = CPP_PWRITTEN(pfile);
   if (!kt->pass_thru && kt->type != T_DEFINE)
      CPP_SET_WRITTEN(pfile, old_written);

   (*kt->func) (pfile, kt, pfile->token_buffer + after_ident, line_end);
   if (kt->pass_thru
       || (kt->type == T_DEFINE
	   && CPP_OPTIONS(pfile)->dump_macros == dump_definitions))
     {
	/* Just leave the entire #define in the output stack. */
     }
   else if (kt->type == T_DEFINE
	    && CPP_OPTIONS(pfile)->dump_macros == dump_names)
     {
	unsigned char      *p = pfile->token_buffer + old_written + 7;	/* Skip "#define". */

	SKIP_WHITE_SPACE(p);
	while (is_idchar[*p])
	   p++;
	pfile->limit = p;
	CPP_PUTC(pfile, '\n');
     }
   else if (kt->type == T_DEFINE)
      CPP_SET_WRITTEN(pfile, old_written);
 done_a_directive:
   return 1;

 not_a_directive:
   return 0;
}

/* 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(char *buf, char *limit, cpp_reader * pfile,
		    struct directive *keyword)
{
   unsigned            keyword_length = keyword->length;

   CPP_RESERVE(pfile, 1 + keyword_length + (limit - buf));
   CPP_PUTC_Q(pfile, '#');
   CPP_PUTS_Q(pfile, keyword->name, keyword_length);
   if (limit != buf && buf[0] != ' ')
      CPP_PUTC_Q(pfile, ' ');
   CPP_PUTS_Q(pfile, buf, limit - buf);
}

/* 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;
   char               *name;
   int                 length;
   int                 argno;
   char                rest_args;
};

/* 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, backslash-newlines, and leading white space
 * have already been deleted from the argument.  */

static DEFINITION  *
collect_expansion(cpp_reader * pfile, unsigned char *buf, unsigned char *limit,
		  int nargs, struct arglist *arglist)
{
   DEFINITION         *defn;
   unsigned char      *p, *lastp, *exp_p;
   reflist            *endpat = NULL;

   /* Pointer to first nonspace after last ## seen.  */
   unsigned char      *concat = 0;

   /* Pointer to first nonspace after last single-# seen.  */
   unsigned 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 (limit < buf)
      abort();

   /* Find the beginning of the trailing whitespace.  */
   p = buf;
   while (p < limit && is_space[limit[-1]])
      limit--;

   /* 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 5 are for invented
    * leading and trailing newline-marker and final null.  */
   maxsize = (sizeof(DEFINITION) + (limit - p) + 5);
   /* Occurrences of '@' get doubled, so allocate extra space for them. */
   while (p < limit)
      if (*p++ == '@')
	 maxsize++;
   defn = (DEFINITION *) xcalloc(1, maxsize);

   defn->nargs = nargs;
   exp_p = defn->expansion = (unsigned char *)defn + sizeof(DEFINITION);
   lastp = exp_p;

   p = buf;

   /* Add one initial space escape-marker to prevent accidental
    * token-pasting (often removed by macroexpand). */
   *exp_p++ = '@';
   *exp_p++ = ' ';

   if (limit - p >= 2 && p[0] == '#' && p[1] == '#')
     {
	cpp_error(pfile, "`##' at start of macro definition");
	p += 2;
     }
   /* Process the main body of the definition.  */
   while (p < limit)
     {
	int                 skipped_arg = 0;
	unsigned char       c = *p++;

	*exp_p++ = c;

        switch (c)
          {
           case '\'':
           case '\"':
             if (expected_delimiter != '\0')
               {
                  if (c == expected_delimiter)
                     expected_delimiter = '\0';
               }
             else
                expected_delimiter = c;
             break;
             
           case '\\':
             if (p < limit && expected_delimiter)
               {
                  /* In a string, backslash goes through
                   * and makes next char ordinary.  */
                  *exp_p++ = *p++;
               }
             break;
             
           case '@':
             /* An '@' in a string or character constant stands for itself,
              * and does not need to be escaped. */
             if (!expected_delimiter)
                *exp_p++ = c;
             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)
                     cpp_error(pfile, "`##' 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])
                     cpp_error(pfile,
                               "`#' operator is not followed by a macro argument name");
                  else
                     stringify = p;
               }
             break;
          }

	/* Handle the start of a symbol.  */
	if (is_idchar[c] && nargs > 0)
	  {
	     unsigned char      *id_beg = p - 1;
	     int                 id_len;

	     --exp_p;
	     while (p != limit && is_idchar[*p])
		p++;
	     id_len = p - id_beg;

	     if (is_idstart[c])
	       {
		  struct arglist     *arg;

		  for (arg = arglist; arg; arg = arg->next)
		    {
		       reflist            *tpat;

		       if (arg->name[0] == c
			   && arg->length == id_len
			   && strncmp((const char *)arg->name,
				      (const char *)id_beg, id_len) == 0)
			 {
			    if (expected_delimiter
				&& CPP_OPTIONS(pfile)->warn_stringify)
			      {
                                 cpp_warning(pfile,
                                             "macro arg `%.*s' would be stringified with -traditional.",
                                             id_len, arg->name);
			      }
			    /* If ANSI, don't actually substitute inside a string.  */
			    if (expected_delimiter)
			       break;
			    /* make a pat node for this arg and append it to the end of
			     * the pat list */
			    tpat = (reflist *) xmalloc(sizeof(reflist));

			    tpat->next = NULL;
			    tpat->raw_before = concat == id_beg;
			    tpat->raw_after = 0;
			    tpat->rest_args = arg->rest_args;
			    tpat->stringify = (stringify == id_beg);

			    if (!endpat)
			       defn->pattern = tpat;
			    else
			       endpat->next = tpat;
			    endpat = tpat;

			    tpat->argno = arg->argno;
			    tpat->nchars = exp_p - lastp;
			    {
			       unsigned 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)
	       {
		  unsigned char      *lim1 = p;

		  p = id_beg;
		  while (p != lim1)
		     *exp_p++ = *p++;
		  if (stringify == id_beg)
		     cpp_error(pfile,
			       "`#' operator should be followed by a macro argument name");
	       }
	  }
     }

   if (expected_delimiter == 0)
     {
	/* If ANSI, put in a "@ " marker to prevent token pasting.
	 * But not if "inside a string" (which in ANSI mode
	 * happens only for -D option).  */
	*exp_p++ = '@';
	*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();

   return defn;
}

/*
 * 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)

/* Create a DEFINITION node from a #define directive.  Arguments are 
 * as for do_define. */
static void
create_definition(MACRODEF * mdef, unsigned char *buf, unsigned char *limit,
		  cpp_reader * pfile, int predefinition)
{
   unsigned char      *bp;	/* temp ptr into input buffer */
   unsigned char      *symname;	/* remember where symbol name starts */
   int                 sym_length;	/* and how long it is */
   int                 rest_args = 0;
   long                line, col;
   const char         *file =
      CPP_BUFFER(pfile) ? CPP_BUFFER(pfile)->nominal_fname : "";
   DEFINITION         *defn;
   int                 arglengths = 0;	/* Accumulate lengths of arg names
					 * plus number of args.  */

   cpp_buf_line_and_col(CPP_BUFFER(pfile), &line, &col);

   bp = buf;

   while (is_hor_space[*bp])
      bp++;

   symname = bp;		/* remember where it starts */

   sym_length = check_macro_name(pfile, 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 = (char *)bp;
	     temp->next = arg_ptrs;
	     temp->argno = argno++;
	     temp->rest_args = 0;
	     arg_ptrs = temp;

	     if (rest_args)
		cpp_pedwarn(pfile, "another parameter follows `%s'",
			    rest_extension);

	     if (!is_idstart[*bp])
		cpp_pedwarn(pfile, "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 ((unsigned)(limit - bp) > REST_EXTENSION_LENGTH &&
		      strncmp(rest_extension, (const char *)bp,
			      REST_EXTENSION_LENGTH) == 0)
		    {
		       rest_args = 1;
		       temp->rest_args = 1;
		       break;
		    }
	       }
	     temp->length = (char *)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 != ')'))
	       {
		  cpp_error(pfile,
			    "badly punctuated parameter list in `#define'");
		  goto nope;
	       }
	     if (*bp == ',')
	       {
		  bp++;
		  SKIP_WHITE_SPACE(bp);
	       }
	     if (bp >= limit)
	       {
		  cpp_error(pfile, "unterminated parameter list in `#define'");
		  goto nope;
	       }
	     {
		struct arglist     *otemp;

		for (otemp = temp->next; otemp; otemp = otemp->next)
		   if (temp->length == otemp->length &&
		       strncmp((const char *)temp->name,
			       (const char *)otemp->name, temp->length) == 0)
		     {
			char               *name;

			name = (char *)alloca(temp->length + 1);
			strncpy(name, (const char *)temp->name, temp->length);
			name[temp->length] = '\0';
			cpp_error(pfile,
				  "duplicate argument name `%s' in `#define'",
				  name);
			goto nope;
		     }
	     }
	  }

	++bp;			/* skip paren */
	SKIP_WHITE_SPACE(bp);
	/* now everything from bp before limit is the definition. */
	defn = collect_expansion(pfile, 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 = (unsigned char *)xmalloc(arglengths + 1);
	{
	   struct arglist     *temp;
	   int                 i = 0;

	   for (temp = arg_ptrs; temp; temp = temp->next)
	     {
		memcpy(&defn->args.argnames[i], temp->name, temp->length);
		i += temp->length;
		if (temp->next)
		  {
		     defn->args.argnames[i++] = ',';
		     defn->args.argnames[i++] = ' ';
		  }
	     }
	   defn->args.argnames[i] = 0;
	}
     }
   else
     {
	/* Simple expansion or empty definition.  */

	if (bp < limit)
	  {
	     if (is_hor_space[*bp])
	       {
		  bp++;
		  SKIP_WHITE_SPACE(bp);
	       }
	     else
	       {
		  switch (*bp)
		    {
		    case '!':
		    case '"':
		    case '#':
		    case '%':
		    case '&':
		    case '\'':
		    case ')':
		    case '*':
		    case '+':
		    case ',':
		    case '-':
		    case '.':
		    case '/':
		    case ':':
		    case ';':
		    case '<':
		    case '=':
		    case '>':
		    case '?':
		    case '[':
		    case '\\':
		    case ']':
		    case '^':
		    case '{':
		    case '|':
		    case '}':
		    case '~':
		       cpp_warning(pfile,
				   "missing white space after `#define %.*s'",
				   sym_length, symname);
		       break;

		    default:
		       cpp_pedwarn(pfile,
				   "missing white space after `#define %.*s'",
				   sym_length, symname);
		       break;
		    }
	       }
	  }
	/* now everything from bp before limit is the definition. */
	defn = collect_expansion(pfile, bp, limit, -1, NULL);
	defn->args.argnames = (unsigned char *)"";
     }

   defn->line = line;
   defn->file = file;

   /* OP is null if this is a predefinition */
   defn->predefined = predefinition;
   mdef->defn = defn;
   mdef->symnam = (char *)symname;
   mdef->symlen = sym_length;

   return;

 nope:
   mdef->defn = 0;
}

/* 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(cpp_reader * pfile, unsigned char *symname, const char *usage)
{
   unsigned char      *p;
   int                 sym_length;

   for (p = symname; is_idchar[*p]; p++)
      ;
   sym_length = p - symname;
   if (sym_length == 0)
     {
	cpp_error(pfile, "invalid %s name", usage);
     }
   else if (!is_idstart[*symname])
     {
	unsigned char      *msg;	/* what pain... */

	msg = (unsigned char *)alloca(sym_length + 1);
	memcpy(msg, symname, sym_length);
	msg[sym_length] = 0;
	cpp_error(pfile, "invalid %s name `%s'", usage, msg);
     }
   else
     {
	if (!strncmp((const char *)symname, "defined", 7) && sym_length == 7)
	   cpp_error(pfile, "invalid %s name `defined'", usage);
     }
   return sym_length;
}

/*
 * return zero if two DEFINITIONs are isomorphic
 */
static int
compare_defs(DEFINITION * d1, DEFINITION * d2)
{
   reflist            *a1, *a2;
   unsigned char      *p1 = d1->expansion;
   unsigned 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((const char *)p1, (const char *)p2, a1->nchars))
	     || !comp_def_part(first, p1, a1->nchars, p2, a2->nchars, 0))
	    || a1->argno != a2->argno || a1->stringify != a2->stringify
	    || a1->raw_before != a2->raw_before
	    || a1->raw_after != a2->raw_after)
	   return 1;
	first = 0;
	p1 += a1->nchars;
	p2 += a2->nchars;
     }
   if (a1 != a2)
      return 1;
   if (comp_def_part(first, p1, d1->length - (p1 - d1->expansion),
		     p2, d2->length - (p2 - d2->expansion), 1))
      return 1;
   return 0;
}

/* Return 1 if two parts of two macro definitions are effectively different.
 * One of the parts starts at BEG1 and has LEN1 chars;
 * the other has LEN2 chars at BEG2.
 * Any sequence of whitespace matches any other sequence of whitespace.
 * FIRST means these parts are the first of a macro definition;
 * so ignore leading whitespace entirely.
 * LAST means these parts are the last of a macro definition;
 * so ignore trailing whitespace entirely.  */

static int
comp_def_part(int first, unsigned char *beg1, int len1,
	      unsigned char *beg2, int len2, int last)
{
   unsigned char      *end1 = beg1 + len1;
   unsigned 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);
}

/* 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,
 * or NULL for a "predefined" macro.  */

static int
do_define(cpp_reader * pfile, struct directive *keyword,
	  unsigned char *buf, unsigned char *limit)
{
   int                 hashcode;
   MACRODEF            mdef;
   HASHNODE           *hp;

   create_definition(&mdef, buf, limit, pfile, !keyword);
   if (!mdef.defn)
      return 1;

   hashcode = hashf(mdef.symnam, mdef.symlen, HASHSIZE);

   if ((hp = cpp_lookup(mdef.symnam, mdef.symlen, hashcode)))
     {
	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 = !CPP_OPTIONS(pfile)->done_initializing;
	/* Print the warning if it's not ok.  */
	if (!ok)
	  {
	     char               *msg;	/* what pain... */

	     /* If we are passing through #define and #undef directives, do
	      * that for this re-definition now.  */
	     if (CPP_OPTIONS(pfile)->debug_output && keyword)
		pass_thru_directive((char *)buf, (char *)limit, pfile, keyword);

	     msg = (char *)alloca(mdef.symlen + 22);
	     *msg = '`';
	     memcpy(msg + 1, mdef.symnam, mdef.symlen);
	     strcpy((msg + mdef.symlen + 1), "' redefined");
	     cpp_pedwarn(pfile, msg);
	     if (hp->type == T_MACRO)
		cpp_pedwarn_with_file_and_line(pfile, hp->value.defn->file,
					       hp->value.defn->line,
					       "this is the location of the previous definition",
					       NULL, NULL, NULL);
	  }
	/* 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 (CPP_OPTIONS(pfile)->debug_output && keyword)
	   pass_thru_directive((char *)buf, (char *)limit, pfile, keyword);
	install(mdef.symnam, mdef.symlen, T_MACRO, 0,
		(char *)mdef.defn, hashcode);
     }

   return 0;
}

/* 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. */

/* raw and expanded are relative to ARG_BASE */
#define ARG_BASE ((pfile)->token_buffer)

struct argdata {
   /* Strings relative to pfile->token_buffer */
   long                raw, expanded, stringified;
   int                 raw_length, expand_length;
   int                 stringified_length;
   char                newlines;
   char                use_count;
};

cpp_buffer         *
cpp_push_buffer(cpp_reader * pfile, unsigned char *buffer, long length)
{
#ifdef STATIC_BUFFERS
   cpp_buffer         *buf = CPP_BUFFER(pfile);

   if (buf == pfile->buffer_stack)
      cpp_fatal("macro or `#include' recursion too deep");
   buf--;
   memset((char *)buf, 0, sizeof(cpp_buffer));
   CPP_BUFFER(pfile) = buf;
#else
   cpp_buffer         *buf = (cpp_buffer *) xmalloc(sizeof(cpp_buffer));

   memset((char *)buf, 0, sizeof(cpp_buffer));
   CPP_PREV_BUFFER(buf) = CPP_BUFFER(pfile);
   CPP_BUFFER(pfile) = buf;
#endif
   buf->if_stack = pfile->if_stack;
   buf->cleanup = null_cleanup;
   buf->underflow = null_underflow;
   buf->buf = buf->cur = buffer;
   buf->alimit = buf->rlimit = buffer + length;

   return buf;
}

static cpp_buffer  *
cpp_pop_buffer(cpp_reader * pfile)
{
   cpp_buffer         *buf = CPP_BUFFER(pfile);

#ifdef STATIC_BUFFERS
   (*buf->cleanup) (buf, pfile);
   return ++CPP_BUFFER(pfile);
#else
   cpp_buffer         *next_buf = CPP_PREV_BUFFER(buf);

   (*buf->cleanup) (buf, pfile);
   CPP_BUFFER(pfile) = next_buf;
   free(buf);
   return next_buf;
#endif
}

/* Scan until CPP_BUFFER (PFILE) is exhausted into PFILE->token_buffer.
 * Pop the buffer when done. */

static void
cpp_scan_buffer(cpp_reader * pfile)
{
   cpp_buffer         *buffer = CPP_BUFFER(pfile);

   for (;;)
     {
	enum cpp_token      token = cpp_get_token(pfile);

	if (token == CPP_EOF)	/* Should not happen ... */
	   break;
	if (token == CPP_POP && CPP_BUFFER(pfile) == buffer)
	  {
	     cpp_pop_buffer(pfile);
	     break;
	  }
     }
}

/*
 * Rescan a string (which may have escape marks) into pfile's buffer.
 * Place the result in pfile->token_buffer.
 *
 * The input is copied before it is scanned, so it is safe to pass
 * it something from the token_buffer that will get overwritten
 * (because it follows CPP_WRITTEN).  This is used by do_include.
 */

static void
cpp_expand_to_buffer(cpp_reader * pfile, unsigned char *buf, int length)
{
   cpp_buffer         *ip;
   unsigned char      *limit = buf + length;
   unsigned char      *buf1;

   if (length < 0)
      abort();

   /* Set up the input on the input stack.  */

   buf1 = (unsigned char *)alloca(length + 1);
   {
      unsigned char      *p1 = buf;
      unsigned char      *p2 = buf1;
      int                 in_string = 0;
      
#if 0 /* old behavior */ 
      while (p1 != limit) *p2++ = *p1++;
#else /* new one - handle \ escapes if not in string */
      while (p1 != limit)
        {
           if (!in_string)
             {
                if (*p1 == '"') in_string = 1;
                if (*p1 == '\\')
                  {
                     p1++;
                     if (p1 != limit) *p2++ = *p1++;
                  }
                else
                   *p2++ = *p1++;
             }
           else
             {
                if ((*p1 == '"') && (p1 > buf) && (p1[-1] != '\\'))
                   in_string = 0;
                *p2++ = *p1++;
             }
        }
#endif
      *p2 = 0;
      length = p2 - buf1;
   }

   ip = cpp_push_buffer(pfile, buf1, length);
   ip->has_escapes = 1;

   /* Scan the input, create the output.  */
   cpp_scan_buffer(pfile);

   CPP_NUL_TERMINATE(pfile);
}

static void
adjust_position(unsigned char *buf, unsigned char *limit, long *linep,
		long *colp)
{
   while (buf < limit)
     {
	unsigned char       ch = *buf++;

	if (ch == '\n')
	   (*linep)++, (*colp) = 1;
	else
	   (*colp)++;
     }
}

/* Move line_base forward, updating lineno and colno. */

static void
update_position(cpp_buffer * pbuf)
{
   unsigned char      *old_pos = pbuf->buf + pbuf->line_base;
   unsigned char      *new_pos = pbuf->cur;
   struct parse_marker *mark;

   for (mark = pbuf->marks; mark; mark = mark->next)
     {
	if (pbuf->buf + mark->position < new_pos)
	   new_pos = pbuf->buf + mark->position;
     }
   pbuf->line_base += new_pos - old_pos;
   adjust_position(old_pos, new_pos, &pbuf->lineno, &pbuf->colno);
}

void
cpp_buf_line_and_col(cpp_buffer * pbuf, long *linep, long *colp)
{
   long                dummy;

   if (!colp)
      colp = &dummy;
   if (pbuf)
     {
	*linep = pbuf->lineno;
	*colp = pbuf->colno;
	adjust_position(pbuf->buf + pbuf->line_base, pbuf->cur, linep, colp);
     }
   else
     {
	*linep = 0;
	*colp = 0;
     }
}

/* Return the cpp_buffer that corresponds to a file (not a macro). */

cpp_buffer         *
cpp_file_buffer(cpp_reader * pfile)
{
   cpp_buffer         *ip = CPP_BUFFER(pfile);

   for (; ip != CPP_NULL_BUFFER(pfile); ip = CPP_PREV_BUFFER(ip))
      if (ip->fname)
	 return ip;
   return NULL;
}

static long
count_newlines(unsigned char *buf, unsigned char *limit)
{
   long                count = 0;

   while (buf < limit)
     {
	unsigned char       ch = *buf++;

	if (ch == '\n')
	   count++;
     }
   return count;
}

/*
 * 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(cpp_reader * pfile, int conditional,
		    enum file_change_code file_change)
{
   long                line, col;
   cpp_buffer         *ip = CPP_BUFFER(pfile);

   if (CPP_OPTIONS(pfile)->no_line_commands
       || !ip->fname || CPP_OPTIONS(pfile)->no_output)
     {
	return;
     }
   update_position(ip);
   line = CPP_BUFFER(pfile)->lineno;
   col = CPP_BUFFER(pfile)->colno;
   adjust_position(CPP_LINE_BASE(ip), ip->cur, &line, &col);

   if (conditional)
     {
	if (line == pfile->lineno)
	   return;

	/* If the inherited line number is a little too small,
	 * output some newlines instead of a #line command.  */
	if (line > pfile->lineno && line < pfile->lineno + 8)
	  {
	     CPP_RESERVE(pfile, 20);
	     while (line > pfile->lineno)
	       {
		  CPP_PUTC_Q(pfile, '\n');
		  pfile->lineno++;
	       }
	     return;
	  }
     }

   CPP_RESERVE(pfile, 4 * strlen(ip->nominal_fname) + 50);
   {
#ifdef OUTPUT_LINE_COMMANDS
      static char         sharp_line[] = "#line ";

#else
      static char         sharp_line[] = "# ";

#endif
      CPP_PUTS_Q(pfile, sharp_line, sizeof(sharp_line) - 1);
   }

   sprintf((char *)CPP_PWRITTEN(pfile), "%d ", (int)line);
   CPP_ADJUST_WRITTEN(pfile, strlen((char *)CPP_PWRITTEN(pfile)));

   quote_string(pfile, ip->nominal_fname);
   if (file_change != same_file)
     {
	CPP_PUTC_Q(pfile, ' ');
	CPP_PUTC_Q(pfile, file_change == enter_file ? '1' : '2');
     }
   /* Tell cc1 if following text comes from a system header file.  */
   if (ip->system_header_p)
     {
	CPP_PUTC_Q(pfile, ' ');
	CPP_PUTC_Q(pfile, '3');
     }
#ifndef NO_IMPLICIT_EXTERN_C
   /* Tell cc1plus if following text should be treated as C.  */
   if (ip->system_header_p == 2 && CPP_OPTIONS(pfile)->cplusplus)
     {
	CPP_PUTC_Q(pfile, ' ');
	CPP_PUTC_Q(pfile, '4');
     }
#endif
   CPP_PUTC_Q(pfile, '\n');
   pfile->lineno = line;
}

/*
 * Parse a macro argument and append the info on PFILE's token_buffer.
 * REST_ARGS means to absorb the rest of the args.
 * Return nonzero to indicate a syntax error.
 */

static enum cpp_token
macarg(cpp_reader * pfile, int rest_args)
{
   int                 paren = 0;
   enum cpp_token      token;
   char                save_put_out_comments =
      CPP_OPTIONS(pfile)->put_out_comments;

   CPP_OPTIONS(pfile)->put_out_comments = 0;

   token = CPP_OTHER;
   /* Try to parse as much of the argument as exists at this
    * input stack level.  */
   pfile->no_macro_expand++;
   for (;;)
     {
	token = cpp_get_token(pfile);
	switch (token)
	  {
	  case CPP_EOF:
	     goto done;
	  case CPP_POP:
	     /* If we've hit end of file, it's an error (reported by caller).
	      * Ditto if it's the end of cpp_expand_to_buffer text.
	      * If we've hit end of macro, just continue.  */
	     if (!CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile)))
		goto done;
	     break;
	  case CPP_LPAREN:
	     paren++;
	     break;
	  case CPP_RPAREN:
	     if (--paren < 0)
		goto found;
	     break;
	  case CPP_COMMA:
	     /* if we've returned to lowest level and
	      * we aren't absorbing all args */
	     if (paren == 0 && rest_args == 0)
		goto found;
	     break;
	   found:
	     /* Remove ',' or ')' from argument buffer. */
	     CPP_ADJUST_WRITTEN(pfile, -1);
	     goto done;
	  default:;
	  }
     }

 done:
   CPP_OPTIONS(pfile)->put_out_comments = save_put_out_comments;
   pfile->no_macro_expand--;

   return token;
}

/* 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(unsigned char *start, int length)
{
   unsigned char      *ibp;
   unsigned char      *obp;
   unsigned char      *limit;
   int                 c;

   ibp = start;
   limit = start + length;
   obp = start;

   while (ibp < limit)
     {
	*obp++ = c = *ibp++;
	switch (c)
	  {

	  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;
}

static struct tm   *
timestamp(cpp_reader * pfile)
{
   if (!pfile->timebuf)
     {
	time_t              t = time((time_t *) 0);

	pfile->timebuf = localtime(&t);
     }
   return pfile->timebuf;
}

static const 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(HASHNODE * hp, cpp_reader * pfile)
{
   const char         *buf;
   char               *bufx;
   int                 len;
   int                 true_indepth;
   cpp_buffer         *ip = NULL;
   struct tm          *timebuf;

   int                 paren = 0;	/* For special `defined' keyword */

   for (ip = CPP_BUFFER(pfile);; ip = CPP_PREV_BUFFER(ip))
     {
	if (!ip)
	  {
	     cpp_error(pfile, "cccp error: not in any file?!");
	     return;		/* the show must go on */
	  }
	if (ip->fname)
	   break;
     }

   switch (hp->type)
     {
     case T_FILE:
     case T_BASE_FILE:
	{
	   const char         *string;

	   if (hp->type == T_BASE_FILE)
	     {
		while (CPP_PREV_BUFFER(ip))
		   ip = CPP_PREV_BUFFER(ip);
	     }
	   string = ip->nominal_fname;

	   if (!string)
	      string = "";
	   CPP_RESERVE(pfile, 3 + 4 * strlen(string));
	   quote_string(pfile, string);
	   return;
	}

     case T_INCLUDE_LEVEL:
	true_indepth = 0;
	for (ip = CPP_BUFFER(pfile); ip; ip = CPP_PREV_BUFFER(ip))
	   if (ip->fname)
	      true_indepth++;

	bufx = (char *)alloca(8);	/* Eight bytes ought to be more than enough */
	sprintf(bufx, "%d", true_indepth - 1);
	buf = bufx;
	break;

     case T_VERSION:
	bufx = (char *)alloca(3 + strlen(version_string));
	sprintf(bufx, "\"%s\"", version_string);
	buf = bufx;
	break;

#ifndef NO_BUILTIN_SIZE_TYPE
     case T_SIZE_TYPE:
	buf = SIZE_TYPE;
	break;
#endif

#ifndef NO_BUILTIN_PTRDIFF_TYPE
     case T_PTRDIFF_TYPE:
	buf = PTRDIFF_TYPE;
	break;
#endif

     case T_WCHAR_TYPE:
	buf = CPP_WCHAR_TYPE(pfile);
	break;

     case T_USER_LABEL_PREFIX_TYPE:
	buf = USER_LABEL_PREFIX;
	break;

     case T_REGISTER_PREFIX_TYPE:
	buf = REGISTER_PREFIX;
	break;

     case T_CONST:
	bufx = (char *)alloca(4 * sizeof(int));
	sprintf(bufx, "%d", hp->value.ival);
	buf = bufx;
	break;

     case T_SPECLINE:
	{
	   long                line = ip->lineno;
	   long                col = ip->colno;

	   adjust_position(CPP_LINE_BASE(ip), ip->cur, &line, &col);

	   bufx = (char *)alloca(10);
	   sprintf(bufx, "%d", (int)line);
	   buf = bufx;
	}
	break;

     case T_DATE:
     case T_TIME:
	bufx = (char *)alloca(20);
	timebuf = timestamp(pfile);
	if (hp->type == T_DATE)
	   sprintf(bufx, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon],
		   timebuf->tm_mday, timebuf->tm_year + 1900);
	else
	   sprintf(bufx, "\"%02d:%02d:%02d\"", timebuf->tm_hour,
		   timebuf->tm_min, timebuf->tm_sec);
	buf = bufx;
	break;

     case T_SPEC_DEFINED:
	buf = " 0 ";		/* Assume symbol is not defined */
	ip = CPP_BUFFER(pfile);
	SKIP_WHITE_SPACE(ip->cur);
	if (*ip->cur == '(')
	  {
	     paren++;
	     ip->cur++;		/* Skip over the paren */
	     SKIP_WHITE_SPACE(ip->cur);
	  }
	if (!is_idstart[*ip->cur])
	   goto oops;
	if ((hp = cpp_lookup((const char *)ip->cur, -1, -1)))
	  {
	     buf = " 1 ";
	  }
	while (is_idchar[*ip->cur])
	   ++ip->cur;
	SKIP_WHITE_SPACE(ip->cur);
	if (paren)
	  {
	     if (*ip->cur != ')')
		goto oops;
	     ++ip->cur;
	  }
	break;

      oops:

	cpp_error(pfile, "`defined' without an identifier");
	break;

     default:
	cpp_error(pfile, "cccp error: invalid special hash type");	/* time for gdb */
	abort();
     }
   len = strlen(buf);
   CPP_RESERVE(pfile, len + 1);
   CPP_PUTS_Q(pfile, buf, len);
   CPP_NUL_TERMINATE_Q(pfile);

   return;
}

/* Initialize the built-in macros.  */

static void
initialize_builtins(cpp_reader * pfile)
{
   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);
   install("__STDC__", -1, T_CONST, STDC_VALUE, 0, -1);
   if (CPP_OPTIONS(pfile)->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 (CPP_OPTIONS(pfile)->debug_output)
     {
	char                directive[2048];
	struct directive   *dp = &directive_table[0];
	struct tm          *timebuf = timestamp(pfile);
	cpp_buffer         *pbuffer = CPP_BUFFER(pfile);

	while (CPP_PREV_BUFFER(pbuffer))
	   pbuffer = CPP_PREV_BUFFER(pbuffer);
	sprintf(directive, " __BASE_FILE__ \"%s\"\n", pbuffer->nominal_fname);
	output_line_command(pfile, 0, same_file);
	pass_thru_directive(directive, &directive[strlen(directive)], pfile,
			    dp);

	sprintf(directive, " __VERSION__ \"%s\"\n", version_string);
	output_line_command(pfile, 0, same_file);
	pass_thru_directive(directive, &directive[strlen(directive)], pfile,
			    dp);

#ifndef NO_BUILTIN_SIZE_TYPE
	sprintf(directive, " __SIZE_TYPE__ %s\n", SIZE_TYPE);
	output_line_command(pfile, 0, same_file);
	pass_thru_directive(directive, &directive[strlen(directive)], pfile,
			    dp);
#endif

#ifndef NO_BUILTIN_PTRDIFF_TYPE
	sprintf(directive, " __PTRDIFF_TYPE__ %s\n", PTRDIFF_TYPE);
	output_line_command(pfile, 0, same_file);
	pass_thru_directive(directive, &directive[strlen(directive)], pfile,
			    dp);
#endif

	sprintf(directive, " __WCHAR_TYPE__ %s\n", CPP_WCHAR_TYPE(pfile));
	output_line_command(pfile, 0, same_file);
	pass_thru_directive(directive, &directive[strlen(directive)], pfile,
			    dp);

	sprintf(directive, " __DATE__ \"%s %2d %4d\"\n",
		monthnames[timebuf->tm_mon],
		timebuf->tm_mday, timebuf->tm_year + 1900);
	output_line_command(pfile, 0, same_file);
	pass_thru_directive(directive, &directive[strlen(directive)], pfile,
			    dp);

	sprintf(directive, " __TIME__ \"%02d:%02d:%02d\"\n",
		timebuf->tm_hour, timebuf->tm_min, timebuf->tm_sec);
	output_line_command(pfile, 0, same_file);
	pass_thru_directive(directive, &directive[strlen(directive)], pfile,
			    dp);

        sprintf(directive, " __STDC__ 1");
        output_line_command(pfile, 0, same_file);
        pass_thru_directive(directive, &directive[strlen(directive)],
                            pfile, dp);
     }
}

/* Return 1 iff a token ending in C1 followed directly by a token C2
 * could cause mis-tokenization. */

static int
unsafe_chars(int c1, int c2)
{
//   printf("unsafe %c %c ...", c1, c2);
   switch (c1)
     {
     case '+':
     case '-':
     case '.':
//        printf(" no0\n");
        return 0;
     case '0':
     case '1':
     case '2':
     case '3':
     case '4':
     case '5':
     case '6':
     case '7':
     case '8':
     case '9':
     case 'e':
     case 'E':
	if (c2 == '-' || c2 == '+')
          {
//             printf(" yes2\n");
             return 1;		/* could extend a pre-processing number */
          }
	goto letter;
     case 'L':
	if (c2 == '\'' || c2 == '\"')
          {
//             printf(" yes3\n");
             return 1;		/* Could turn into L"xxx" or L'xxx'. */
          }
	goto letter;
      letter:
     case '_':
     case 'a':
     case 'b':
     case 'c':
     case 'd':
     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 'F':
     case 'G':
     case 'H':
     case 'I':
     case 'J':
     case 'K':
     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':
	/* We're in the middle of either a name or a pre-processing number. */
	if (is_idchar[c2] || c2 == '.')
          {
//             printf(" yes4 %i %i\n", is_idchar[c2], c2 == '.');
             return 1;
          }
        else
          {
//             printf(" no5\n");
             return 0;
          }
     case '<':
     case '>':
     case '!':
     case '%':
     case '#':
     case ':':
     case '^':
     case '&':
     case '|':
     case '*':
     case '/':
     case '=':
	if (c2 == c1 || c2 == '=')
          {
//             printf(" yes6\n");
             return 1;
          }
        else
          {
//             printf(" no7\n");
             return 0;
          }
     }
//   printf(" no8\n");
   return 0;
}

/* 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(cpp_reader * pfile, HASHNODE * hp)
{
   int                 nargs;
   DEFINITION         *defn = hp->value.defn;
   unsigned char      *xbuf;
   long                start_line, start_column;
   int                 xbuf_len;
   struct argdata     *args;
   long                old_written = CPP_WRITTEN(pfile);

   int                 rest_args, rest_zero;
   int                 i;

   pfile->output_escapes++;
   cpp_buf_line_and_col(cpp_file_buffer(pfile), &start_line, &start_column);

   rest_zero = 0;
   args = NULL;
   nargs = defn->nargs;

   if (nargs >= 0)
     {
	enum cpp_token      token;

	token = CPP_OTHER;

	args = (struct argdata *)alloca((nargs + 1) * sizeof(struct argdata));

	for (i = 0; i < nargs; i++)
	  {
	     args[i].raw = args[i].expanded = 0;
	     args[i].raw_length = 0;
	     args[i].expand_length = args[i].stringified_length = -1;
	     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;
	rest_args = 0;
	FORWARD(1);		/* Discard the open-parenthesis before the first arg.  */
	do
	  {
	     if (rest_args)
		continue;
	     if (i < nargs || (nargs == 0 && i == 0))
	       {
                  unsigned char *bp;
                  
		  /* if we are working on last arg which absorbs rest of args... */
		  if (i == nargs - 1 && defn->rest_args)
		     rest_args = 1;
		  args[i].raw = CPP_WRITTEN(pfile);
		  token = macarg(pfile, rest_args);
		  args[i].raw_length = CPP_WRITTEN(pfile) - args[i].raw;
		  args[i].newlines = 0;	/* FIXME */
                  bp = ARG_BASE + args[i].raw;
                  while (is_space[(unsigned char)(*bp)]) { bp++; }
                  args[i].raw_length -= bp - (ARG_BASE + args[i].raw);
                  args[i].raw = bp - ARG_BASE;
                  if (args[i].raw_length > 0)
                    {
                       bp = ARG_BASE + args[i].raw + args[i].raw_length - 1;
                       while (is_space[(unsigned char)(*bp)])
                         {
                            bp--; 
                            args[i].raw_length--;
                            if (args[i].raw_length < 1) break;
                         }
                    }
	       }
	     else
		token = macarg(pfile, 0);
	     if (token == CPP_EOF || token == CPP_POP)
	       {
		  cpp_error_with_line(pfile, start_line, start_column,
				      "unterminated macro call");
		  return;
	       }
	     i++;
	  }
	while (token == CPP_COMMA);

	/* If we got one arg but it was just whitespace, call that 0 args.  */
	if (i == 1)
	  {
	     unsigned char      *bp = ARG_BASE + args[0].raw;
	     unsigned 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.  */
	if (nargs == 0 && i > 0)
	  {
	     cpp_error(pfile, "arguments given to macro `%s'", hp->name);
	  }
	else if (i < nargs)
	  {
	     if (i == nargs - 1 && defn->rest_args)
		rest_zero = 1;
	     else if (i == 0)
		cpp_error(pfile, "macro `%s' used without args", hp->name);
	     else if (i == 1)
		cpp_error(pfile, "macro `%s' used with just one arg", hp->name);
	     else
		cpp_error(pfile, "macro `%s' used with only %d args",
			  hp->name, i);
	  }
	else if (i > nargs)
	  {
	     cpp_error(pfile,
		       "macro `%s' used with too many (%d) args", hp->name, i);
	  }
     }
   /* 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
     {
	unsigned char      *exp = defn->expansion;
	int                 offset;	/* offset in expansion,
					 * copied a piece at a time */
	int                 totlen;	/* total amount of exp buffer filled so far */
	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; ap = ap->next)
	  {
	     if (ap->stringify)
	       {
		  struct argdata     *arg = &args[ap->argno];

		  /* Stringify it it hasn't already been */
		  if (arg->stringified_length < 0)
		    {
		       int                 arglen = arg->raw_length;
		       int                 escaped = 0;
		       int                 in_string = 0;
		       int                 c;

		       /* Initially need_space is -1.  Otherwise, 1 means the
		        * previous character was a space, but we suppressed it;
		        * 0 means the previous character was a non-space. */
		       int                 need_space = -1;

		       i = 0;
		       arg->stringified = CPP_WRITTEN(pfile);
                       CPP_PUTC(pfile, '\"');	/* insert beginning quote */
		       for (; i < arglen; i++)
			 {
			    c = (ARG_BASE + arg->raw)[i];

			    if (!in_string)
			      {
				 /* Internal sequences of whitespace are replaced by
				  * one space except within an string or char token. */
				 if (is_space[c])
				   {
				      if (CPP_WRITTEN(pfile) > arg->stringified
					  && (CPP_PWRITTEN(pfile))[-1] == '@')
					{
					   /* "@ " escape markers are removed */
					   CPP_ADJUST_WRITTEN(pfile, -1);
					   continue;
					}
				      if (need_space == 0)
					 need_space = 1;
				      continue;
				   }
				 else if (need_space > 0)
				    CPP_PUTC(pfile, ' ');
				 need_space = 0;
			      }
			    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 == '\\'))
			       CPP_PUTC(pfile, '\\');
			    if (isprint(c))
			       CPP_PUTC(pfile, c);
			    else
			      {
				 CPP_RESERVE(pfile, 4);
				 sprintf((char *)CPP_PWRITTEN(pfile), "\\%03o",
					 (unsigned int)c);
				 CPP_ADJUST_WRITTEN(pfile, 4);
			      }
			 }
                       CPP_PUTC(pfile, '\"');	/* insert ending quote */
		       arg->stringified_length
			  = CPP_WRITTEN(pfile) - arg->stringified;
		    }
		  xbuf_len += args[ap->argno].stringified_length;
	       }
	     else if (ap->raw_before || ap->raw_after)
               {
                  /* 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].expand_length < 0)
		    {
		       args[ap->argno].expanded = CPP_WRITTEN(pfile);
		       cpp_expand_to_buffer(pfile,
					    ARG_BASE + args[ap->argno].raw,
					    args[ap->argno].raw_length);

		       args[ap->argno].expand_length
			  = CPP_WRITTEN(pfile) - args[ap->argno].expanded;
		    }
		  /* 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 = (unsigned 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;
	     last_ap = ap, ap = ap->next)
	  {
	     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 && 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)
	       {
		  memcpy(xbuf + totlen, ARG_BASE + arg->stringified,
			 arg->stringified_length);
		  totlen += arg->stringified_length;
	       }
	     else if (ap->raw_before || ap->raw_after)
	       {
		  unsigned char      *p1 = ARG_BASE + arg->raw;
		  unsigned 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++;
		    }
		  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] == '-')
			      {
				 unsigned 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;
			 }
		    }
		  memcpy(xbuf + totlen, p1, l1 - p1);
		  totlen += l1 - p1;
	       }
	     else
	       {
		  unsigned char      *expanded = ARG_BASE + arg->expanded;

		  if (!ap->raw_before && totlen > 0 && arg->expand_length
		      && unsafe_chars(xbuf[totlen - 1], expanded[0]))
		    {
		       xbuf[totlen++] = '@';
		       xbuf[totlen++] = ' ';
		    }
		  memcpy(xbuf + totlen, expanded, arg->expand_length);
		  totlen += arg->expand_length;

		  if (!ap->raw_after && totlen > 0 && offset < defn->length
		      && unsafe_chars(xbuf[totlen - 1], exp[offset]))
		    {
		       xbuf[totlen++] = '@';
		       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(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 && last_ap->rest_args
		   && last_ap->raw_after))
		xbuf[totlen++] = exp[i];
	  }

	xbuf[totlen] = 0;
	xbuf_len = totlen;
     }

   pfile->output_escapes--;

   /* Now put the expansion on the input stack
    * so our caller will commence reading from it.  */
   push_macro_expansion(pfile, xbuf, xbuf_len, hp);
   CPP_BUFFER(pfile)->has_escapes = 1;

   /* Pop the space we've used in the token_buffer for argument expansion. */
   CPP_SET_WRITTEN(pfile, old_written);

   /* Recursive macro use sometimes works traditionally.
    * #define foo(x,y) bar (x (y,0), y)
    * foo (foo, baz)  */

   hp->type = T_DISABLED;
}

static void
push_macro_expansion(cpp_reader * pfile, unsigned char *xbuf, int xbuf_len,
		     HASHNODE * hp)
{
   cpp_buffer         *mbuf = cpp_push_buffer(pfile, xbuf, xbuf_len);

   mbuf->cleanup = macro_cleanup;
   mbuf->data = hp;

   /* The first chars of the expansion should be a "@ " added by
    * collect_expansion.  This is to prevent accidental token-pasting
    * between the text preceding the macro invocation, and the macro
    * expansion text.
    * 
    * We would like to avoid adding unneeded spaces (for the sake of
    * tools that use cpp, such as imake).  In some common cases we can
    * tell that it is safe to omit the space.
    * 
    * The character before the macro invocation cannot have been an
    * idchar (or else it would have been pasted with the idchars of
    * the macro name).  Therefore, if the first non-space character
    * of the expansion is an idchar, we do not need the extra space
    * to prevent token pasting.
    * 
    * Also, we don't need the extra space if the first char is '(',
    * or some other (less common) characters.  */

   if (xbuf[0] == '@' && xbuf[1] == ' '
       && (is_idchar[xbuf[2]] || xbuf[2] == '(' || xbuf[2] == '\''
	   || xbuf[2] == '\"'))
      mbuf->cur += 2;
}

/* Like cpp_get_token, except that it does not read past end-of-line.
 * Also, horizontal space is skipped, and macros are popped.  */

static enum cpp_token
get_directive_token(cpp_reader * pfile)
{
   for (;;)
     {
	long                old_written = CPP_WRITTEN(pfile);
	enum cpp_token      token;

	cpp_skip_hspace(pfile);
	if (PEEKC() == '\n')
	   return CPP_VSPACE;
	token = cpp_get_token(pfile);
	switch (token)
	  {
	  case CPP_POP:
	     if (!CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile)))
		return token;
	     /* ... else fall though ... */
	  case CPP_HSPACE:
	  case CPP_COMMENT:
	     CPP_SET_WRITTEN(pfile, old_written);
	     break;
	  default:
	     return token;
	  }
     }
}

/* Handle #include and #import.
 * This function expects to see "fname" or <fname> on the input.
 * 
 * The input is normally in part of the output_buffer following
 * CPP_WRITTEN, and will get overwritten by output_line_command.
 * I.e. in input file specification has been popped by handle_directive.
 * This is safe. */

static int
do_include(cpp_reader * pfile, struct directive *keyword,
	   unsigned char *unused1 __UNUSED__, unsigned char *unused2 __UNUSED__)
{
   int                 importing = (keyword->type == T_IMPORT);
   int                 skip_dirs = (keyword->type == T_INCLUDE_NEXT);
   char               *fname;	/* Dynamically allocated fname buffer */
   unsigned char      *fbeg, *fend;	/* Beginning and end of fname */
   enum cpp_token      token;

   /* Chain of dirs to search */
   file_name_list     *search_start = CPP_OPTIONS(pfile)->include;
   file_name_list      dsp[1];	/* First in chain, if #include "..." */
   file_name_list     *searchptr = 0;
   long                old_written = CPP_WRITTEN(pfile);
   int                 flen;
   int                 f;	/* file number */
   int                 angle_brackets = 0;	/* 0 for "...", 1 for <...> */

   f = -1;			/* JF we iz paranoid! */

   if (importing && CPP_OPTIONS(pfile)->warn_import
       && !CPP_OPTIONS(pfile)->inhibit_warnings
       && !CPP_BUFFER(pfile)->system_header_p && !pfile->import_warning)
     {
	pfile->import_warning = 1;
	cpp_warning(pfile, "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");
     }
   pfile->parsing_include_directive++;
   token = get_directive_token(pfile);
   pfile->parsing_include_directive--;

   if (token == CPP_STRING)
     {
	/* FIXME - check no trailing garbage */
	fbeg = pfile->token_buffer + old_written + 1;
	fend = CPP_PWRITTEN(pfile) - 1;
	if (fbeg[-1] == '<')
	  {
	     angle_brackets = 1;
	     /* If -I-, start with the first -I dir after the -I-.  */
	     if (CPP_OPTIONS(pfile)->first_bracket_include)
		search_start = CPP_OPTIONS(pfile)->first_bracket_include;
	  }
	/* If -I- was specified, don't search current dir, only spec'd ones. */
	else if (!CPP_OPTIONS(pfile)->ignore_srcdir)
	  {
	     cpp_buffer         *fp;

	     /* We have "filename".  Figure out directory this source
	      * file is coming from and put it on the front of the list. */

	     for (fp = CPP_BUFFER(pfile); fp; fp = CPP_PREV_BUFFER(fp))
	       {
		  int                 n;
		  const char         *ep, *nam;

		  if ((nam = fp->nominal_fname))
		    {
		       /* 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 = strrchr(nam, '/');
#else /* VMS */
		       ep = strrchr(nam, ']');
		       if (!ep)
			  ep = strrchr(nam, '>');
		       if (!ep)
			  ep = strrchr(nam, ':');
		       if (ep)
			  ep++;
#endif /* VMS */
		       if (ep)
			 {
			    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 > pfile->max_include_len)
			       pfile->max_include_len = n + INCLUDE_LEN_FUDGE;
			 }
		       else
			 {
			    dsp[0].fname = 0;	/* Current directory */
			 }
		       dsp[0].got_name_map = 0;
		       break;
		    }
	       }
	  }
     }
   else
     {
	cpp_error(pfile,
		  "`#%s' expects \"FILENAME\" or <FILENAME>", keyword->name);
	CPP_SET_WRITTEN(pfile, old_written);
	skip_rest_of_line(pfile);
	return 0;
     }

   *fend = 0;

   token = get_directive_token(pfile);
   if (token != CPP_VSPACE)
     {
	cpp_error(pfile, "junk at end of `#include'");
	while (token != CPP_VSPACE && token != CPP_EOF && token != CPP_POP)
	   token = get_directive_token(pfile);
     }
   /* For #include_next, skip in the search path
    * past the dir in which the containing file was found.  */
   if (skip_dirs)
     {
	cpp_buffer         *fp;

	for (fp = CPP_BUFFER(pfile); fp; fp = CPP_PREV_BUFFER(fp))
	   if (fp->fname)
	     {
		/* 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 == SELF_DIR_DUMMY)
		   search_start = CPP_OPTIONS(pfile)->include;
		else if (fp->dir)
		   search_start = fp->dir->next;
		break;
	     }
     }
   CPP_SET_WRITTEN(pfile, old_written);

   flen = fend - fbeg;

   if (flen == 0)
     {
	cpp_error(pfile, "empty file name in `#%s'", keyword->name);
	return 0;
     }
   /* Allocate this permanently, because it gets stored in the definitions
    * of macros.  */
   fname = (char *)xmalloc(pfile->max_include_len + flen + 4);
   /* + 2 above for slash and terminating null.  */
   /* + 2 added for '.h' on VMS (to support '#include filename') */

   /* If specified file name is absolute, just open it.  */

#ifndef __EMX__
   if (*fbeg == '/')
#else
   if (_fnisabs(fbeg))
#endif
     {
	strncpy(fname, (const char *)fbeg, flen);
	fname[flen] = 0;
	if (redundant_include_p(pfile, fname))
	   return 0;
	if (importing)
	   f = lookup_import(pfile, fname, NULL);
	else
	   f = open_include_file(pfile, fname, NULL);
	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, (const char *)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;
		  /* if it's '#include filename', add the missing .h */
		  if (!strchr(fname, '.'))
		    {
		       strcat(fname, ".h");
		    }
	       }
#endif /* VMS */
	     /* ??? There are currently 3 separate mechanisms for avoiding processing
	      * of redundant include files: #import, #pragma once, and
	      * redundant_include_p.  It would be nice if they were unified.  */
	     if (redundant_include_p(pfile, fname))
		return 0;
	     if (importing)
		f = lookup_import(pfile, fname, searchptr);
	     else
		f = open_include_file(pfile, fname, searchptr);
	     if (f == -2)
		return 0;	/* Already included this file */
#ifdef EACCES
	     else if (f == -1 && errno == EACCES)
		cpp_warning(pfile, "Header file %s exists, but is not readable",
			    fname);
#endif
	     if (f >= 0)
		break;
	  }
     }

   if (f < 0)
     {
	/* A file that was not found.  */
	strncpy(fname, (const char *)fbeg, flen);
	fname[flen] = 0;
	/* If generating dependencies and -MG was specified, we assume missing
	 * files are leaf files, living in the same directory as the source file
	 * or other similar place; these missing files may be generated from
	 * other files and may not exist yet (eg: y.tab.h).  */

	if (CPP_OPTIONS(pfile)->print_deps_missing_files
	    && CPP_PRINT_DEPS(pfile)
	    > (angle_brackets || (pfile->system_include_depth > 0)))
	  {
	     /* If it was requested as a system header file,
	      * then assume it belongs in the first place to look for such.  */
	     if (angle_brackets)
	       {
		  for (searchptr = search_start; searchptr;
		       searchptr = searchptr->next)
		    {
		       if (searchptr->fname)
			 {
			    char               *p;

			    if (searchptr->fname[0] == 0)
			       continue;
			    p = (char *)alloca(strlen(searchptr->fname)
					       + strlen(fname) + 2);
			    strcpy(p, searchptr->fname);
			    strcat(p, "/");
			    strcat(p, fname);
			    deps_output(pfile, p, ' ');
			    break;
			 }
		    }
	       }
	     else
	       {
		  /* Otherwise, omit the directory, as if the file existed
		   * in the directory with the source.  */
		  deps_output(pfile, fname, ' ');
	       }
	  }
	/* 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. */
	else if (CPP_PRINT_DEPS(pfile)
		 && (CPP_PRINT_DEPS(pfile)
		     <= (angle_brackets || (pfile->system_include_depth > 0))))
	   cpp_warning(pfile, "No include path in which to find %s", fname);
	else if (search_start)
	   cpp_error_from_errno(pfile, fname);
	else
	   cpp_error(pfile, "No include path in which to find %s", fname);
     }
   else
     {
	/* Check to see if this include file is a once-only include file.
	 * If so, give up.  */

	file_name_list     *ptr;

	for (ptr = pfile->dont_repeat_files; ptr; ptr = ptr->next)
	  {
	     if (!strcmp(ptr->fname, fname))
	       {
		  close(f);
		  return 0;	/* This file was once'd. */
	       }
	  }

	for (ptr = pfile->all_include_files; ptr; ptr = ptr->next)
	  {
	     if (!strcmp(ptr->fname, fname))
		break;		/* This file was included before. */
	  }

	if (!ptr)
	  {
	     /* This is the first time for this file.  */
	     /* Add it to list of files included.  */

	     ptr = (file_name_list *) xmalloc(sizeof(file_name_list));

	     ptr->control_macro = 0;
	     ptr->c_system_include_path = 0;
	     ptr->next = pfile->all_include_files;
	     pfile->all_include_files = ptr;
	     ptr->fname = savestring(fname);
	     ptr->got_name_map = 0;

	     /* For -M, add this file to the dependencies.  */
	     if (CPP_PRINT_DEPS(pfile)
		 > (angle_brackets || (pfile->system_include_depth > 0)))
		deps_output(pfile, fname, ' ');
	  }
	/* Handle -H option.  */
	if (CPP_OPTIONS(pfile)->print_include_names)
	  {
	     cpp_buffer         *buf = CPP_BUFFER(pfile);

	     while ((buf = CPP_PREV_BUFFER(buf)))
		putc('.', stderr);
	     fprintf(stderr, "%s\n", fname);
	  }
	if (angle_brackets)
	   pfile->system_include_depth++;

	/* Actually process the file.  */

	/* Record file on "seen" list for #import. */
	add_import(pfile, f, fname);

	/* Actually process the file */
	cpp_push_buffer(pfile, NULL, 0);
	if (finclude(pfile, f, fname, is_system_include(pfile, fname),
		     searchptr != dsp ? searchptr : SELF_DIR_DUMMY))
	  {
	     output_line_command(pfile, 0, enter_file);
	     pfile->only_seen_white = 2;
	  }
	if (angle_brackets)
	   pfile->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(cpp_reader * pfile, char *name)
{
   file_name_list     *l = pfile->all_include_files;

   for (; l; l = l->next)
      if (!strcmp(name, l->fname)
	  && l->control_macro
	  && cpp_lookup((const char *)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.
 * 
 * The value is 2 if the file is a C-language system header file
 * for which C++ should (on most systems) assume `extern "C"'.  */

static int
is_system_include(cpp_reader * pfile, char *filename)
{
   file_name_list     *searchptr;

   for (searchptr = CPP_OPTIONS(pfile)->first_system_include; searchptr;
	searchptr = searchptr->next)
      if (searchptr->fname)
	{
	   char               *sys_dir = searchptr->fname;
	   unsigned            length = strlen(sys_dir);

	   if (!strncmp(sys_dir, filename, length) && filename[length] == '/')
	     {
		if (searchptr->c_system_include_path)
		   return 2;
		else
		   return 1;
	     }
	}
   return 0;
}

/*
 * 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(cpp_reader * pfile, const char *name, int len, int hash)
{
   ASSERTION_HASHNODE *hp;
   int                 i, bucket;

   i = sizeof(ASSERTION_HASHNODE) + len + 1;
   hp = (ASSERTION_HASHNODE *) xmalloc(i);
   bucket = hash;
   hp->bucket_hdr = &pfile->assertion_hashtab[bucket];
   hp->next = pfile->assertion_hashtab[bucket];
   pfile->assertion_hashtab[bucket] = hp;
   hp->prev = NULL;
   if (hp->next)
      hp->next->prev = hp;
   hp->length = len;
   hp->value = 0;
   hp->name = ((char *)hp) + sizeof(ASSERTION_HASHNODE);
   memcpy(hp->name, name, len);
   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(cpp_reader * pfile, const char *name, int len, int hash)
{
   ASSERTION_HASHNODE *bucket;

   bucket = pfile->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(ASSERTION_HASHNODE * hp)
{
   struct tokenlist_list *tail;

   if (hp->prev)
      hp->prev->next = hp->next;
   if (hp->next)
      hp->next->prev = hp->prev;

   for (tail = hp->value; tail;)
     {
	struct tokenlist_list *next = tail->next;

	free_token_list(tail->tokens);
	free(tail);
	tail = next;
     }

   /* 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);
}

/* Convert a character string literal into a nul-terminated string.
 * The input string is [IN ... LIMIT).
 * The result is placed in RESULT.  RESULT can be the same as IN.
 * The value returned in the end of the string written to RESULT,
 * or NULL on error.  */

static char        *
convert_string(cpp_reader * pfile, char *result, char *in, char *limit,
	       int handle_escapes)
{
   unsigned char       c;

   c = *in++;
   if (c != '\"')
      return NULL;
   while (in < limit)
     {
	c = *in++;

	switch (c)
	  {
	  case '\0':
	     return NULL;
	  case '\"':
	     limit = in;
	     break;
	  case '\\':
	     if (handle_escapes)
	       {
		  char               *bpc = in;
		  int                 i =
		     (unsigned char)cpp_parse_escape(pfile, &bpc);

		  in = bpc;
		  if (i >= 0)
		     *result++ = (unsigned char)c;
		  break;
	       }
	     /* else fall through */
	  default:
	     *result++ = c;
	  }
     }
   *result = 0;
   return result;
}

/*
 * interpret #line command.  Remembers previously seen fnames
 * in its very own hash table.
 */
#define FNAME_HASHSIZE 37

static int
do_line(cpp_reader * pfile, struct directive *keyword __UNUSED__,
	unsigned char *unused1 __UNUSED__, unsigned char *unused2 __UNUSED__)
{
   cpp_buffer         *ip = CPP_BUFFER(pfile);
   int                 new_lineno;
   long                old_written = CPP_WRITTEN(pfile);
   enum file_change_code file_change = same_file;
   enum cpp_token      token;

   token = get_directive_token(pfile);

   if (token != CPP_NUMBER || !isdigit(pfile->token_buffer[old_written]))
     {
	cpp_error(pfile, "invalid format `#line' command");
	goto bad_line_directive;
     }
   /* 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((char *)(pfile->token_buffer + old_written)) - 1;
   CPP_SET_WRITTEN(pfile, old_written);

   /* NEW_LINENO is one less than the actual line number here.  */
   if (CPP_PEDANTIC(pfile) && new_lineno < 0)
      cpp_pedwarn(pfile, "line number out of range in `#line' command");

   token = get_directive_token(pfile);

   if (token == CPP_STRING)
     {
	char               *fname = (char *)pfile->token_buffer + old_written;
	char               *end_name;
	static HASHNODE    *fname_table[FNAME_HASHSIZE];
	HASHNODE           *hp, **hash_bucket;
	unsigned char      *p;
	long                num_start;
	int                 fname_length;

	/* Turn the file name, which is a character string literal,
	 * into a null-terminated string.  Do this in place.  */
	end_name =
	   convert_string(pfile, fname, fname, (char *)CPP_PWRITTEN(pfile), 1);
	if (!end_name)
	  {
	     cpp_error(pfile, "invalid format `#line' command");
	     goto bad_line_directive;
	  }
	fname_length = end_name - fname;

	num_start = CPP_WRITTEN(pfile);
	token = get_directive_token(pfile);
	if (token != CPP_VSPACE && token != CPP_EOF && token != CPP_POP)
	  {
	     p = pfile->token_buffer + num_start;
	     if (CPP_PEDANTIC(pfile))
		cpp_pedwarn(pfile, "garbage at end of `#line' command");

	     if (token != CPP_NUMBER || *p < '0' || *p > '4' || p[1] != '\0')
	       {
		  cpp_error(pfile, "invalid format `#line' command");
		  goto bad_line_directive;
	       }
	     if (*p == '1')
		file_change = enter_file;
	     else if (*p == 2)
		file_change = leave_file;
	     else if (*p == 3)
		ip->system_header_p = 1;
	     else		/* if (*p == 4) */
		ip->system_header_p = 2;

	     CPP_SET_WRITTEN(pfile, num_start);
	     token = get_directive_token(pfile);
	     p = pfile->token_buffer + num_start;
	     if (token == CPP_NUMBER && p[1] == '\0'
		 && (*p == '3' || *p == '4'))
	       {
		  ip->system_header_p = *p == 3 ? 1 : 2;
		  token = get_directive_token(pfile);
	       }
	     if (token != CPP_VSPACE)
	       {
		  cpp_error(pfile, "invalid format `#line' command");
		  goto bad_line_directive;
	       }
	  }
	hash_bucket = &fname_table[hashf(fname, fname_length, FNAME_HASHSIZE)];
	for (hp = *hash_bucket; hp; 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)
	  {
	     /* 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);
	     memcpy(hp->value.cpval, fname, fname_length);
	  }
     }
   else if (token != CPP_VSPACE && token != CPP_EOF)
     {
	cpp_error(pfile, "invalid format `#line' command");
	goto bad_line_directive;
     }
   ip->lineno = new_lineno;
 bad_line_directive:
   skip_rest_of_line(pfile);
   CPP_SET_WRITTEN(pfile, old_written);
   output_line_command(pfile, 0, file_change);
   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(cpp_reader * pfile, struct directive *keyword, unsigned char *buf,
	 unsigned char *limit)
{
   int                 sym_length;
   HASHNODE           *hp;
   unsigned char      *orig_buf = buf;

   SKIP_WHITE_SPACE(buf);
   sym_length = check_macro_name(pfile, buf, "macro");

   while ((hp = cpp_lookup((const char *)buf, sym_length, -1)))
     {
	/* If we are generating additional info for debugging (with -g) we
	 * need to pass through all effective #undef commands.  */
	if (CPP_OPTIONS(pfile)->debug_output && keyword)
	   pass_thru_directive((char *)orig_buf, (char *)limit, pfile, keyword);
	if (hp->type != T_MACRO)
	   cpp_warning(pfile, "undefining `%s'", hp->name);
	delete_macro(hp);
     }

   if (CPP_PEDANTIC(pfile))
     {
	buf += sym_length;
	SKIP_WHITE_SPACE(buf);
	if (buf != limit)
	   cpp_pedwarn(pfile, "garbage after `#undef' directive");
     }
   return 0;
}

/*
 * Report an error detected by the program we are processing.
 * Use the text of the line in the error message.
 * (We use error because it prints the filename & line#.)
 */

static int
do_error(cpp_reader * pfile, struct directive *keyword __UNUSED__,
	 unsigned char *buf, unsigned char *limit)
{
   int                 length = limit - buf;
   unsigned char      *copy = (unsigned char *)xmalloc(length + 1);

   memcpy(copy, buf, length);
   copy[length] = 0;
   SKIP_WHITE_SPACE(copy);
   cpp_error(pfile, "#error %s", copy);
   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(cpp_reader * pfile, struct directive *keyword __UNUSED__,
	   unsigned char *buf, unsigned char *limit)
{
   int                 length = limit - buf;
   unsigned char      *copy = (unsigned char *)xmalloc(length + 1);

   memcpy(copy, buf, length);
   copy[length] = 0;
   SKIP_WHITE_SPACE(copy);
   cpp_warning(pfile, "#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(cpp_reader * pfile)
{
   cpp_buffer         *ip = NULL;
   file_name_list     *new_;

   for (ip = CPP_BUFFER(pfile);; ip = CPP_PREV_BUFFER(ip))
     {
	if (!ip)
	   return 0;
	if (ip->fname)
	   break;
     }

   new_ = (file_name_list *) xmalloc(sizeof(file_name_list));

   new_->next = pfile->dont_repeat_files;
   pfile->dont_repeat_files = new_;
   new_->fname = savestring(ip->fname);
   new_->control_macro = 0;
   new_->got_name_map = 0;
   new_->c_system_include_path = 0;

   return 0;
}

/* #ident has already been copied to the output file, so just ignore it.  */

static int
do_ident(cpp_reader * pfile, struct directive *keyword __UNUSED__,
	 unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__)
{
/*  long old_written = CPP_WRITTEN (pfile); */

   /* Allow #ident in system headers, since that's not user's fault.  */
   if (CPP_PEDANTIC(pfile) && !CPP_BUFFER(pfile)->system_header_p)
      cpp_pedwarn(pfile, "ANSI C does not allow `#ident'");

   /* Leave rest of line to be read by later calls to cpp_get_token. */

   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(cpp_reader * pfile, struct directive *keyword __UNUSED__,
	  unsigned char *buf, unsigned char *limit __UNUSED__)
{
   while (*buf == ' ' || *buf == '\t')
      buf++;

   if (!strncmp((const char *)buf, "once", 4))
     {
	/* Allow #pragma once in system headers, since that's not the user's
	 * fault.  */
	if (!CPP_BUFFER(pfile)->system_header_p)
	   cpp_warning(pfile, "`#pragma once' is obsolete");
	do_once(pfile);
     }
   if (!strncmp((const char *)buf, "implementation", 14))
     {
	/* Be quiet about `#pragma implementation' for a file only if it hasn't
	 * been included yet.  */
	file_name_list     *ptr;
	char               *p = (char *)buf + 14, *fname, *inc_fname;
	int                 fname_len;

	SKIP_WHITE_SPACE(p);
	if (*p == '\n' || *p != '\"')
	   return 0;

	fname = p + 1;
	p = strchr(fname, '\"');
	fname_len = (p) ? (int)(p - fname) : (int)strlen(fname);

	for (ptr = pfile->all_include_files; ptr; ptr = ptr->next)
	  {
	     inc_fname = strrchr(ptr->fname, '/');
	     inc_fname = inc_fname ? inc_fname + 1 : (char *)ptr->fname;
	     if (inc_fname && !strncmp(inc_fname, fname, fname_len))
		cpp_warning(pfile,
			    "`#pragma implementation' for `%s' appears after file is included",
			    fname);
	  }
     }
   return 0;
}

/* Just ignore #sccs, on systems where we define it at all.  */

/*
 * 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(cpp_reader * pfile, struct directive *keyword __UNUSED__,
      unsigned char *buf, unsigned char *limit)
{
   HOST_WIDE_INT       value = eval_if_expression(pfile, buf, limit - buf);

   conditional_skip(pfile, value == 0, T_IF, NULL);
   return 0;
}

/*
 * handle a #elif directive by not changing  if_stack  either.
 * see the comment above do_else.
 */

static int
do_elif(cpp_reader * pfile, struct directive *keyword __UNUSED__,
	unsigned char *buf, unsigned char *limit)
{
   if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack)
     {
	cpp_error(pfile, "`#elif' not within a conditional");
	return 0;
     }
   else
     {
	if (pfile->if_stack->type != T_IF && pfile->if_stack->type != T_ELIF)
	  {
	     cpp_error(pfile, "`#elif' after `#else'");
	     if (pfile->if_stack->fname
		 && CPP_BUFFER(pfile)->fname
		 && strcmp(pfile->if_stack->fname,
			   CPP_BUFFER(pfile)->nominal_fname) != 0)
		fprintf(stderr, ", file %s", pfile->if_stack->fname);
	     fprintf(stderr, ")\n");
	  }
	pfile->if_stack->type = T_ELIF;
     }

   if (pfile->if_stack->if_succeeded)
      skip_if_group(pfile, 0);
   else
     {
	HOST_WIDE_INT       value = eval_if_expression(pfile, buf, limit - buf);

	if (value == 0)
	   skip_if_group(pfile, 0);
	else
	  {
	     ++pfile->if_stack->if_succeeded;	/* continue processing input */
	     output_line_command(pfile, 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              HOST_WIDE_INT
eval_if_expression(cpp_reader * pfile, unsigned char *buf __UNUSED__,
		   int length __UNUSED__)
{
   HASHNODE           *save_defined;
   HOST_WIDE_INT       value;
   long                old_written = CPP_WRITTEN(pfile);

   save_defined = install("defined", -1, T_SPEC_DEFINED, 0, 0, -1);
   pfile->pcp_inside_if = 1;

   value = cpp_parse_expr(pfile);
   pfile->pcp_inside_if = 0;
   delete_macro(save_defined);	/* clean up special symbol */

   CPP_SET_WRITTEN(pfile, old_written);	/* Pop */

   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(cpp_reader * pfile, struct directive *keyword,
	  unsigned char *unused1 __UNUSED__, unsigned char *unused2 __UNUSED__)
{
   int                 skip;
   cpp_buffer         *ip = CPP_BUFFER(pfile);
   char               *ident;
   int                 ident_length;
   enum cpp_token      token;
   int                 start_of_file = 0;
   unsigned char      *control_macro = 0;
   int                 old_written = CPP_WRITTEN(pfile);
   int                 c;

   /* Detect a #ifndef at start of file (not counting comments).  */
   if (ip->fname != 0 && keyword->type == T_IFNDEF)
      start_of_file = pfile->only_seen_white == 2;

   pfile->no_macro_expand++;
   token = get_directive_token(pfile);
   pfile->no_macro_expand--;

   ident = (char *)pfile->token_buffer + old_written;
   ident_length = CPP_WRITTEN(pfile) - old_written;
   CPP_SET_WRITTEN(pfile, old_written);	/* Pop */

   if (token == CPP_VSPACE || token == CPP_POP || token == CPP_EOF)
     {
	skip = (keyword->type == T_IFDEF);
        cpp_pedwarn(pfile, "`#%s' with no argument", keyword->name);
     }
   else if (token == CPP_NAME)
     {
	HASHNODE           *hp = cpp_lookup(ident, ident_length, -1);

	skip = (!hp) ^ (keyword->type == T_IFNDEF);
	if (start_of_file && !skip)
	  {
	     control_macro = (unsigned char *)xmalloc(ident_length + 1);
	     memcpy(control_macro, ident, ident_length + 1);
	  }
     }
   else
     {
	skip = (keyword->type == T_IFDEF);
        cpp_error(pfile, "`#%s' with invalid argument", keyword->name);
     }

   cpp_skip_hspace(pfile);
   c = PEEKC();
   if (c != EOF && c != '\n')
      cpp_pedwarn(pfile, "garbage at end of `#%s' argument",
                  keyword->name);
   skip_rest_of_line(pfile);

   conditional_skip(pfile, 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(cpp_reader * pfile, int skip, enum node_type type,
		 unsigned char *control_macro)
{
   IF_STACK_FRAME     *temp;

   temp = (IF_STACK_FRAME *) xcalloc(1, sizeof(IF_STACK_FRAME));
   temp->fname = CPP_BUFFER(pfile)->nominal_fname;
   temp->next = pfile->if_stack;
   temp->control_macro = control_macro;
   pfile->if_stack = temp;

   pfile->if_stack->type = type;

   if (skip != 0)
     {
	skip_if_group(pfile, 0);
	return;
     }
   else
     {
	++pfile->if_stack->if_succeeded;
	output_line_command(pfile, 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(cpp_reader * pfile, int any)
{
   int                 c;
   struct directive   *kt;
   IF_STACK_FRAME     *save_if_stack = pfile->if_stack;	/* don't pop past here */

   int                 ident_length;
   char               *ident;
   struct parse_marker line_start_mark;

   parse_set_mark(&line_start_mark, pfile);

   if (CPP_OPTIONS(pfile)->output_conditionals)
     {
	static char         failed[] = "#failed\n";

	CPP_PUTS(pfile, failed, sizeof(failed) - 1);
	pfile->lineno++;
	output_line_command(pfile, 1, same_file);
     }
 beg_of_line:
   if (CPP_OPTIONS(pfile)->output_conditionals)
     {
	cpp_buffer         *pbuf = CPP_BUFFER(pfile);
	unsigned char      *start_line = pbuf->buf + line_start_mark.position;

	CPP_PUTS(pfile, start_line, pbuf->cur - start_line);
     }
   parse_move_mark(&line_start_mark, pfile);
   cpp_skip_hspace(pfile);
   c = GETC();
   if (c == '#')
     {
	int                 old_written = CPP_WRITTEN(pfile);

	cpp_skip_hspace(pfile);

	parse_name(pfile, GETC());
	ident_length = CPP_WRITTEN(pfile) - old_written;
	ident = (char *)pfile->token_buffer + old_written;
	pfile->limit = (unsigned char *)ident;

	for (kt = directive_table; kt->length >= 0; kt++)
	  {
	     IF_STACK_FRAME     *temp;

	     if (ident_length == kt->length
		 && strncmp(ident, kt->name, kt->length) == 0)
	       {
		  /* If we are asked to return on next directive, do so now.  */
		  if (any)
		     goto done;

		  switch (kt->type)
		    {
		    case T_IF:
		    case T_IFDEF:
		    case T_IFNDEF:
		       temp
			  =
			  (IF_STACK_FRAME *) xcalloc(1, sizeof(IF_STACK_FRAME));
		       temp->next = pfile->if_stack;
		       pfile->if_stack = temp;
		       temp->fname = CPP_BUFFER(pfile)->nominal_fname;
		       temp->type = kt->type;
		       break;
		    case T_ELSE:
		    case T_ENDIF:
		       if (CPP_PEDANTIC(pfile)
			   && pfile->if_stack != save_if_stack)
			  validate_else(pfile,
					kt->type ==
					T_ELSE ? "#else" : "#endif");
		    case T_ELIF:
		       if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack)
			 {
			    cpp_error(pfile,
				      "`#%s' not within a conditional",
				      kt->name);
			    break;
			 }
		       else if (pfile->if_stack == save_if_stack)
			  goto done;	/* found what we came for */

		       if (kt->type != T_ENDIF)
			 {
			    if (pfile->if_stack->type == T_ELSE)
			       cpp_error(pfile,
					 "`#else' or `#elif' after `#else'");
			    pfile->if_stack->type = kt->type;
			    break;
			 }
		       temp = pfile->if_stack;
		       pfile->if_stack = temp->next;
		       free(temp);
		       break;
		    default:;
		    }
		  break;
	       }
	     /* Don't let erroneous code go by.  */
	     if (kt->length < 0 && !CPP_OPTIONS(pfile)->lang_asm
		 && CPP_PEDANTIC(pfile))
		cpp_pedwarn(pfile, "invalid preprocessor directive name");
	  }
	c = GETC();
     }
   /* We're in the middle of a line.  Skip the rest of it. */
   for (;;)
     {
	switch (c)
	  {
	     long                old;

	  case EOF:
	     goto done;
	  case '/':		/* possible comment */
	     c = skip_comment(pfile, NULL);
	     if (c == EOF)
		goto done;
	     break;
	  case '\"':
	  case '\'':
	     FORWARD(-1);
	     old = CPP_WRITTEN(pfile);
	     cpp_get_token(pfile);
	     CPP_SET_WRITTEN(pfile, old);
	     break;
	  case '\\':
	     /* Char after backslash loses its special meaning.  */
	     if (PEEKC() == '\n')
		FORWARD(1);
	     break;
	  case '\n':
	     goto beg_of_line;
	     break;
	  }
	c = GETC();
     }
 done:
   if (CPP_OPTIONS(pfile)->output_conditionals)
     {
	static char         end_failed[] = "#endfailed\n";

	CPP_PUTS(pfile, end_failed, sizeof(end_failed) - 1);
	pfile->lineno++;
     }
   pfile->only_seen_white = 1;
   parse_goto_mark(&line_start_mark, pfile);
   parse_clear_mark(&line_start_mark);
}

/*
 * 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(cpp_reader * pfile, struct directive *keyword __UNUSED__,
	unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__)
{
   cpp_buffer         *ip = CPP_BUFFER(pfile);

   if (CPP_PEDANTIC(pfile))
      validate_else(pfile, "#else");
   skip_rest_of_line(pfile);

   if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack)
     {
	cpp_error(pfile, "`#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.  */
	pfile->if_stack->control_macro = 0;

	if (pfile->if_stack->type != T_IF && pfile->if_stack->type != T_ELIF)
	  {
	     cpp_error(pfile, "`#else' after `#else'");
	     fprintf(stderr, " (matches line %d", pfile->if_stack->lineno);
	     if (strcmp(pfile->if_stack->fname, ip->nominal_fname) != 0)
		fprintf(stderr, ", file %s", pfile->if_stack->fname);
	     fprintf(stderr, ")\n");
	  }
	pfile->if_stack->type = T_ELSE;
     }

   if (pfile->if_stack->if_succeeded)
      skip_if_group(pfile, 0);
   else
     {
	++pfile->if_stack->if_succeeded;	/* continue processing input */
	output_line_command(pfile, 1, same_file);
     }
   return 0;
}

/*
 * unstack after #endif command
 */

static int
do_endif(cpp_reader * pfile, struct directive *keyword __UNUSED__,
	 unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__)
{
   if (CPP_PEDANTIC(pfile))
      validate_else(pfile, "#endif");
   skip_rest_of_line(pfile);

   if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack)
     {
	cpp_error(pfile, "unbalanced `#endif'");
     }
   else
     {
	IF_STACK_FRAME     *temp = pfile->if_stack;

	pfile->if_stack = temp->next;
	if (temp->control_macro)
	  {
	     /* This #endif matched a #ifndef at the start of the file.
	      * See if it is at the end of the file.  */
	     struct parse_marker start_mark;
	     int                 c;

	     parse_set_mark(&start_mark, pfile);

	     for (;;)
	       {
		  cpp_skip_hspace(pfile);
		  c = GETC();
		  if (c != '\n')
		     break;
	       }
	     parse_goto_mark(&start_mark, pfile);
	     parse_clear_mark(&start_mark);

	     if (c == EOF)
	       {
		  /* 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.
		   * 
		   * Do not do this for the top-level file in a -include or any
		   * file in a -imacros.  */
		  {
		     file_name_list     *ifile = pfile->all_include_files;

		     for (; ifile; ifile = ifile->next)
		       {
			  if (!strcmp(ifile->fname, CPP_BUFFER(pfile)->fname))
			    {
			       ifile->control_macro = temp->control_macro;
			       break;
			    }
		       }
		  }
	       }
	  }
	free(temp);
	output_line_command(pfile, 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(cpp_reader * pfile, const char *directive)
{
   int                 c;

   cpp_skip_hspace(pfile);
   c = PEEKC();
   if (c != EOF && c != '\n')
      cpp_pedwarn(pfile,
		  "text following `%s' violates ANSI standard", directive);
}

/* Get the next token, and add it to the text in pfile->token_buffer.
 * Return the kind of token we got. */

enum cpp_token
cpp_get_token(cpp_reader * pfile)
{
   int                 c, c2, c3;
   long                old_written = 0;
   long                start_line, start_column;
   enum cpp_token      token;
   struct cpp_options *opts = CPP_OPTIONS(pfile);

   CPP_BUFFER(pfile)->prev = CPP_BUFFER(pfile)->cur;
 get_next:
   c = GETC();
   if (c == EOF)
     {
      handle_eof:
	if (CPP_BUFFER(pfile)->seen_eof)
	  {
	     if (cpp_pop_buffer(pfile) != CPP_NULL_BUFFER(pfile))
		goto get_next;
	     else
		return CPP_EOF;
	  }
	else
	  {
	     cpp_buffer         *next_buf = CPP_PREV_BUFFER(CPP_BUFFER(pfile));

	     CPP_BUFFER(pfile)->seen_eof = 1;
	     if (CPP_BUFFER(pfile)->nominal_fname && next_buf)
	       {
		  /* We're about to return from an #include file.
		   * Emit #line information now (as part of the CPP_POP) result.
		   * But the #line refers to the file we will pop to. */
		  cpp_buffer         *cur_buffer = CPP_BUFFER(pfile);

		  CPP_BUFFER(pfile) = next_buf;
		  pfile->input_stack_listing_current = 0;
		  output_line_command(pfile, 0, leave_file);
		  CPP_BUFFER(pfile) = cur_buffer;
	       }
	     return CPP_POP;
	  }
     }
   else
     {
	switch (c)
	  {
	     long                newlines;
	     struct parse_marker start_mark;

	  case '/':
	     if (PEEKC() == '=')
		goto op2;
	     if (opts->put_out_comments)
		parse_set_mark(&start_mark, pfile);
	     newlines = 0;
	     cpp_buf_line_and_col(cpp_file_buffer(pfile),
				  &start_line, &start_column);
	     c = skip_comment(pfile, &newlines);
	     if (opts->put_out_comments && (c == '/' || c == EOF))
		parse_clear_mark(&start_mark);
	     if (c == '/')
		goto randomchar;
	     if (c == EOF)
	       {
		  cpp_error_with_line(pfile, start_line, start_column,
				      "unterminated comment");
		  goto handle_eof;
	       }
	     c = '/';		/* Initial letter of comment. */
	   return_comment:
	     /* Comments are equivalent to spaces.
	      * For -traditional, a comment is equivalent to nothing.  */
	     if (opts->put_out_comments)
	       {
		  cpp_buffer         *pbuf = CPP_BUFFER(pfile);
		  unsigned char      *start = pbuf->buf + start_mark.position;
		  int                 len = pbuf->cur - start;

		  CPP_RESERVE(pfile, 1 + len);
		  CPP_PUTC_Q(pfile, c);
		  CPP_PUTS_Q(pfile, start, len);
		  pfile->lineno += newlines;
		  parse_clear_mark(&start_mark);
		  return CPP_COMMENT;
	       }
	     else if (newlines > 0)
	       {
		 output_line_command(pfile, 0, same_file);
		 CPP_RESERVE(pfile, 1);
		 CPP_PUTC_Q(pfile, ' ');
		 return CPP_VSPACE;
	       }
	     else
	       {
		  CPP_RESERVE(pfile, 1);
		  CPP_PUTC_Q(pfile, ' ');
		  return CPP_HSPACE;
	       }

	  case '#':
	     if (!pfile->only_seen_white)
		goto randomchar;
	     if (handle_directive(pfile))
		return CPP_DIRECTIVE;
	     pfile->only_seen_white = 0;
	     return CPP_OTHER;

	  case '\"':
	  case '\'':
	     /* A single quoted string is treated like a double -- some
	      * programs (e.g., troff) are perverse this way */
	     cpp_buf_line_and_col(cpp_file_buffer(pfile),
				  &start_line, &start_column);
	     old_written = CPP_WRITTEN(pfile);
	   string:
	     CPP_PUTC(pfile, c);
	     while (1)
	       {
		  int                 cc = GETC();

		  if (cc == EOF)
		    {
		       if (CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile)))
			 {
			    /* 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.  */
			    cpp_buffer         *next_buf
			       = CPP_PREV_BUFFER(CPP_BUFFER(pfile));

			    (*CPP_BUFFER(pfile)->cleanup)
			       (CPP_BUFFER(pfile), pfile);
			    CPP_BUFFER(pfile) = next_buf;
			    continue;
			 }
                       cpp_error_with_line(pfile, start_line, start_column,
                                           "unterminated string or character constant");
                       if (pfile->multiline_string_line != start_line
                           && pfile->multiline_string_line != 0)
                          cpp_error_with_line(pfile,
                                              pfile->multiline_string_line,
                                              -1,
                                              "possible real start of unterminated constant");
                       pfile->multiline_string_line = 0;
		       break;
		    }
		  CPP_PUTC(pfile, cc);
		  switch (cc)
		    {
		    case '\n':
		       /* Traditionally, end of line ends a string constant with
		        * no error.  So exit the loop and record the new line.  */
		       if (c == '\'')
			 {
			    cpp_error_with_line(pfile, start_line, start_column,
						"unterminated character constant");
			    goto while2end;
			 }
		       if (CPP_PEDANTIC(pfile)
			   && pfile->multiline_string_line == 0)
			 {
			    cpp_pedwarn_with_line(pfile, start_line,
						  start_column,
						  "string constant runs past end of line");
			 }
		       if (pfile->multiline_string_line == 0)
			  pfile->multiline_string_line = start_line;
		       break;

		    case '\\':
		       cc = GETC();
		       if (cc == '\n')
			 {
			    /* Backslash newline is replaced by nothing at all. */
			    CPP_ADJUST_WRITTEN(pfile, -1);
			    pfile->lineno++;
			 }
		       else
			 {
			    /* ANSI stupidly requires that in \\ the second \
			     * is *not* prevented from combining with a newline.  */
			    NEWLINE_FIX1(cc);
			    if (cc != EOF)
			       CPP_PUTC(pfile, cc);
			 }
		       break;

		    case '\"':
		    case '\'':
		       if (cc == c)
			  goto while2end;
		       break;
		    }
	       }
	   while2end:
	     pfile->lineno += count_newlines(pfile->token_buffer + old_written,
					     CPP_PWRITTEN(pfile));
	     pfile->only_seen_white = 0;
	     return c == '\'' ? CPP_CHAR : CPP_STRING;

	  case '$':
	     if (!opts->dollars_in_ident)
		goto randomchar;
	     goto letter;

	  case ':':
	     if (opts->cplusplus && PEEKC() == ':')
		goto op2;
	     goto randomchar;

	  case '&':
	  case '+':
	  case '|':
	     NEWLINE_FIX;
	     c2 = PEEKC();
	     if (c2 == c || c2 == '=')
		goto op2;
	     goto randomchar;

	  case '*':
	  case '!':
	  case '%':
	  case '=':
	  case '^':
	     NEWLINE_FIX;
	     if (PEEKC() == '=')
		goto op2;
	     goto randomchar;

	  case '-':
	     NEWLINE_FIX;
	     c2 = PEEKC();
	     if (c2 == '-' && opts->chill)
	       {
		  /* Chill style comment */
		  if (opts->put_out_comments)
		     parse_set_mark(&start_mark, pfile);
		  FORWARD(1);	/* Skip second '-'. */
		  for (;;)
		    {
		       c = GETC();
		       if (c == EOF)
			  break;
		       if (c == '\n')
			 {
			    /* Don't consider final '\n' to be part of comment. */
			    FORWARD(-1);
			    break;
			 }
		    }
		  c = '-';
		  goto return_comment;
	       }
	     if (c2 == '-' || c2 == '=' || c2 == '>')
		goto op2;
	     goto randomchar;

	  case '<':
	     if (pfile->parsing_include_directive)
	       {
		  for (;;)
		    {
		       CPP_PUTC(pfile, c);
		       if (c == '>')
			  break;
		       c = GETC();
		       NEWLINE_FIX1(c);
		       if (c == '\n' || c == EOF)
			 {
			    cpp_error(pfile,
				      "missing '>' in `#include <FILENAME>'");
			    break;
			 }
		    }
		  return CPP_STRING;
	       }
	     /* else fall through */
	  case '>':
	     NEWLINE_FIX;
	     c2 = PEEKC();
	     if (c2 == '=')
		goto op2;
	     if (c2 != c)
		goto randomchar;
	     FORWARD(1);
	     CPP_RESERVE(pfile, 4);
	     CPP_PUTC(pfile, c);
	     CPP_PUTC(pfile, c2);
	     NEWLINE_FIX;
	     c3 = PEEKC();
	     if (c3 == '=')
		CPP_PUTC_Q(pfile, GETC());
	     CPP_NUL_TERMINATE_Q(pfile);
	     pfile->only_seen_white = 0;
	     return CPP_OTHER;

	  case '@':
	     if (CPP_BUFFER(pfile)->has_escapes)
	       {
		  c = GETC();
		  if (c == '-')
		    {
#if 1 // fix macro expansions starting with - losing the -
                       CPP_PUTS(pfile, "-", 1);
		       return CPP_OTHER;
#else                       
		       if (pfile->output_escapes)
			  CPP_PUTS(pfile, "@-", 2);
		       parse_name(pfile, GETC());
		       return CPP_NAME;
#endif                       
		    }
		  else if (is_space[c])
		    {
		       CPP_RESERVE(pfile, 1);
		       if (pfile->output_escapes)
			  CPP_PUTC_Q(pfile, '@');
		       return CPP_HSPACE;
		    }
	       }
	     if (pfile->output_escapes)
	       {
		  CPP_PUTS(pfile, "@@", 2);
		  return CPP_OTHER;
	       }
	     goto randomchar;

	  case '.':
	     NEWLINE_FIX;
	     c2 = PEEKC();
	     if (isdigit(c2))
	       {
		  CPP_RESERVE(pfile, 2);
		  CPP_PUTC_Q(pfile, '.');
		  c = GETC();
		  goto number;
	       }
	     /* FIXME - misses the case "..\\\n." */
	     if (c2 == '.' && PEEKN(1) == '.')
	       {
		  CPP_RESERVE(pfile, 4);
		  CPP_PUTC_Q(pfile, '.');
		  CPP_PUTC_Q(pfile, '.');
		  CPP_PUTC_Q(pfile, '.');
		  FORWARD(2);
		  CPP_NUL_TERMINATE_Q(pfile);
		  pfile->only_seen_white = 0;
		  return CPP_3DOTS;
	       }
	     goto randomchar;

	   op2:
	     token = CPP_OTHER;
	     pfile->only_seen_white = 0;
	   op2any:
	     CPP_RESERVE(pfile, 3);
	     CPP_PUTC_Q(pfile, c);
	     CPP_PUTC_Q(pfile, GETC());
	     CPP_NUL_TERMINATE_Q(pfile);
	     return token;

	  case 'L':
	     NEWLINE_FIX;
	     c2 = PEEKC();
	     if ((c2 == '\'' || c2 == '\"'))
	       {
		  CPP_PUTC(pfile, c);
		  c = GETC();
		  goto string;
	       }
	     goto letter;

	  case '0':
	  case '1':
	  case '2':
	  case '3':
	  case '4':
	  case '5':
	  case '6':
	  case '7':
	  case '8':
	  case '9':
	   number:
	     c2 = '.';
	     for (;;)
	       {
		  CPP_RESERVE(pfile, 2);
		  CPP_PUTC_Q(pfile, c);
		  NEWLINE_FIX;
		  c = PEEKC();
		  if (c == EOF)
		     break;
		  if (!is_idchar[c] && c != '.'
		      && ((c2 != 'e' && c2 != 'E') || (c != '+' && c != '-')))
		     break;
		  FORWARD(1);
		  c2 = c;
	       }
	     CPP_NUL_TERMINATE_Q(pfile);
	     pfile->only_seen_white = 0;
	     return CPP_NUMBER;
	  case 'b':
	  case 'c':
	  case 'd':
	  case 'h':
	  case 'o':
	  case 'B':
	  case 'C':
	  case 'D':
	  case 'H':
	  case 'O':
	     if (opts->chill && PEEKC() == '\'')
	       {
		  pfile->only_seen_white = 0;
		  CPP_RESERVE(pfile, 2);
		  CPP_PUTC_Q(pfile, c);
		  CPP_PUTC_Q(pfile, '\'');
		  FORWARD(1);
		  for (;;)
		    {
		       c = GETC();
		       if (c == EOF)
			  goto chill_number_eof;
		       if (!is_idchar[c])
			 {
			    if (c == '\\' && PEEKC() == '\n')
			      {
				 FORWARD(2);
				 continue;
			      }
			    break;
			 }
		       CPP_PUTC(pfile, c);
		    }
		  if (c == '\'')
		    {
		       CPP_RESERVE(pfile, 2);
		       CPP_PUTC_Q(pfile, c);
		       CPP_NUL_TERMINATE_Q(pfile);
		       return CPP_STRING;
		    }
		  else
		    {
		       FORWARD(-1);
		     chill_number_eof:
		       CPP_NUL_TERMINATE(pfile);
		       return CPP_NUMBER;
		    }
	       }
	     else
		goto letter;
	  case '_':
	  case 'a':
	  case 'e':
	  case 'f':
	  case 'g':
	  case 'i':
	  case 'j':
	  case 'k':
	  case 'l':
	  case 'm':
	  case 'n':
	  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 'E':
	  case 'F':
	  case 'G':
	  case 'I':
	  case 'J':
	  case 'K':
	  case 'M':
	  case 'N':
	  case 'P':
	  case 'Q':
	  case 'R':
	  case 'S':
	  case 'T':
	  case 'U':
	  case 'V':
	  case 'W':
	  case 'X':
	  case 'Y':
	  case 'Z':
	   letter:
	     {
		HASHNODE           *hp;
		char               *ident;
		int                 before_name_written = CPP_WRITTEN(pfile);
		int                 ident_len;

		parse_name(pfile, c);
		pfile->only_seen_white = 0;
		if (pfile->no_macro_expand)
		   return CPP_NAME;
		ident = (char *)pfile->token_buffer + before_name_written;
		ident_len = CPP_PWRITTEN(pfile) - (unsigned char *)ident;
		hp = cpp_lookup(ident, ident_len, -1);
		if (!hp)
		   return CPP_NAME;
		if (hp->type == T_DISABLED)
		  {
		     if (pfile->output_escapes)
		       {	/* Return "@-IDENT", followed by '\0'. */
			  int                 i;

			  CPP_RESERVE(pfile, 3);
			  ident =
			     (char *)pfile->token_buffer + before_name_written;
			  CPP_ADJUST_WRITTEN(pfile, 2);
			  for (i = ident_len; i >= 0; i--)
			     ident[i + 2] = ident[i];
			  ident[0] = '@';
			  ident[1] = '-';
		       }
		     return CPP_NAME;
		  }
		/* If macro wants an arglist, verify that a '(' follows.
		 * first skip all whitespace, copying it to the output
		 * after the macro name.  Then, if there is no '(',
		 * decide this is not a macro call and leave things that way.  */
		if (hp->type == T_MACRO && hp->value.defn->nargs >= 0)
		  {
		     struct parse_marker macro_mark;
		     int                 is_macro_call;

		     while (CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile)))
		       {
			  cpp_buffer         *next_buf;

			  cpp_skip_hspace(pfile);
			  if (PEEKC() != EOF)
			     break;
			  next_buf = CPP_PREV_BUFFER(CPP_BUFFER(pfile));
			  (*CPP_BUFFER(pfile)->cleanup) (CPP_BUFFER(pfile),
							 pfile);
			  CPP_BUFFER(pfile) = next_buf;
		       }
		     parse_set_mark(&macro_mark, pfile);
		     for (;;)
		       {
			  cpp_skip_hspace(pfile);
			  c = PEEKC();
			  is_macro_call = c == '(';
			  if (c != '\n')
			     break;
			  FORWARD(1);
		       }
		     if (!is_macro_call)
			parse_goto_mark(&macro_mark, pfile);
		     parse_clear_mark(&macro_mark);
		     if (!is_macro_call)
			return CPP_NAME;
		  }
		/* This is now known to be a macro call. */

		/* it might not actually be a macro.  */
		if (hp->type != T_MACRO)
		  {
		     int                 xbuf_len;
		     unsigned char      *xbuf;

		     CPP_SET_WRITTEN(pfile, before_name_written);
		     special_symbol(hp, pfile);
		     xbuf_len = CPP_WRITTEN(pfile) - before_name_written;
		     xbuf = (unsigned char *)xmalloc(xbuf_len + 1);
		     CPP_SET_WRITTEN(pfile, before_name_written);
		     memcpy(xbuf, CPP_PWRITTEN(pfile), xbuf_len + 1);
		     push_macro_expansion(pfile, xbuf, xbuf_len, hp);
		  }
		else
		  {
		     /* Expand the macro, reading arguments as needed,
		      * and push the expansion on the input stack.  */
		     macroexpand(pfile, hp);
		     CPP_SET_WRITTEN(pfile, before_name_written);
		  }

		/* An extra "@ " is added to the end of a macro expansion
		 * to prevent accidental token pasting.  We prefer to avoid
		 * unneeded extra spaces (for the sake of cpp-using tools like
		 * imake).  Here we remove the space if it is safe to do so. */
		if (pfile->buffer->rlimit - pfile->buffer->cur >= 3
		    && pfile->buffer->rlimit[-2] == '@'
		    && pfile->buffer->rlimit[-1] == ' ')
		  {
		     int                 c1 = pfile->buffer->rlimit[-3];

		     c2 = CPP_BUF_PEEK(CPP_PREV_BUFFER(CPP_BUFFER(pfile)));

		     if (c2 == EOF || !unsafe_chars(c1, c2))
			pfile->buffer->rlimit -= 2;
		  }
	     }
	     goto get_next;

	  case ' ':
	  case '\t':
	  case '\v':
	  case '\r':
	     for (;;)
	       {
		  CPP_PUTC(pfile, c);
		  c = PEEKC();
		  if (c == EOF || !is_hor_space[c])
		     break;
		  FORWARD(1);
	       }
	     return CPP_HSPACE;

	  case '\\':
	     c2 = PEEKC();
	     if (c2 != '\n')
		goto randomchar;
	     token = CPP_HSPACE;
	     goto op2any;

	  case '\n':
	     CPP_PUTC(pfile, c);
	     if (pfile->only_seen_white == 0)
		pfile->only_seen_white = 1;
	     pfile->lineno++;
	     output_line_command(pfile, 1, same_file);
	     return CPP_VSPACE;

	  case '(':
	     token = CPP_LPAREN;
	     goto char1;
	  case ')':
	     token = CPP_RPAREN;
	     goto char1;
	  case '{':
	     token = CPP_LBRACE;
	     goto char1;
	  case '}':
	     token = CPP_RBRACE;
	     goto char1;
	  case ',':
	     token = CPP_COMMA;
	     goto char1;
	  case ';':
	     token = CPP_SEMICOLON;
	     goto char1;

	   randomchar:
	  default:
	     token = CPP_OTHER;
	   char1:
	     pfile->only_seen_white = 0;
	     CPP_PUTC(pfile, c);
	     return token;
	  }
     }
}

#if 0				/* Unused */
/* Like cpp_get_token, but skip spaces and comments. */
enum cpp_token
cpp_get_non_space_token(cpp_reader * pfile)
{
   int                 old_written = CPP_WRITTEN(pfile);

   for (;;)
     {
	enum cpp_token      token = cpp_get_token(pfile);

	if (token != CPP_COMMENT && token != CPP_POP
	    && token != CPP_HSPACE && token != CPP_VSPACE)
	   return token;
	CPP_SET_WRITTEN(pfile, old_written);
     }
}
#endif

/* Parse an identifier starting with C. */

int
parse_name(cpp_reader * pfile, int c)
{
   for (;;)
     {
	if (!is_idchar[c])
	  {
	     if (c == '\\' && PEEKC() == '\n')
	       {
		  FORWARD(2);
		  continue;
	       }
	     FORWARD(-1);
	     break;
	  }
	CPP_RESERVE(pfile, 2);	/* One more for final NUL. */
	CPP_PUTC_Q(pfile, c);
	c = GETC();
	if (c == EOF)
	   break;
     }
   CPP_NUL_TERMINATE_Q(pfile);
   return 1;
}

/* Maintain and search list of included files, for #import.  */

/* Hash a file name for import_hash_table.  */

static int
import_hash(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(cpp_reader * pfile, char *filename, file_name_list * searchptr)
{
   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 = pfile->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_include_file(pfile, filename, searchptr);
   if (fd < 0)
      return fd;
   fstat(fd, &sb);
   for (h = 0; h < IMPORT_HASH_SIZE; h++)
     {
	i = pfile->import_hash_table[h];
	while (i)
	  {
	     /* Compare the inode and the device.
	      * Supposedly on some systems the inode is not a scalar.  */
	     if (!memcmp
		 ((char *)&i->inode, (char *)&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(cpp_reader * pfile, 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);
   memcpy((char *)&i->inode, (char *)&sb.st_ino, sizeof(sb.st_ino));
   i->dev = sb.st_dev;
   i->next = pfile->import_hash_table[hashval];
   pfile->import_hash_table[hashval] = i;
}

/* The file_name_map structure holds a mapping of file names for a
 * particular directory.  This mapping is read from the file named
 * FILE_NAME_MAP_FILE in that directory.  Such a file can be used to
 * map filenames on a file system with severe filename restrictions,
 * such as DOS.  The format of the file name map file is just a series
 * of lines with two tokens on each line.  The first token is the name
 * to map, and the second token is the actual name to use.  */

struct file_name_map {
   struct file_name_map *map_next;
   char               *map_from;
   char               *map_to;
};

#if USE_FILE_NAME_MAPS

#define FILE_NAME_MAP_FILE "header.gcc"

/* Read a space delimited string of unlimited length from a stdio
 * file.  */

static char        *
read_filename_string(int ch, FILE * f)
{
   char               *alloc, *set;
   int                 len;

   len = 20;
   set = alloc = (char *)xmalloc(len + 1);
   if (!is_space[ch])
     {
	*set++ = ch;
	while ((ch = getc(f)) != EOF && !is_space[ch])
	  {
	     if (set - alloc == len)
	       {
		  len *= 2;
		  alloc = (char *)xrealloc(alloc, len + 1);
		  set = alloc + len / 2;
	       }
	     *set++ = ch;
	  }
     }
   *set = '\0';
   ungetc(ch, f);
   return alloc;
}

/* This structure holds a linked list of file name maps, one per directory. */
struct file_name_map_list {
   struct file_name_map_list *map_list_next;
   char               *map_list_name;
   struct file_name_map *map_list_map;
};

/* Read the file name map file for DIRNAME.  */

static struct file_name_map *
read_name_map(cpp_reader * pfile, const char *dirname)
{
   struct file_name_map_list *map_list_ptr;
   char               *name;
   FILE               *f;

   for (map_list_ptr = CPP_OPTIONS(pfile)->map_list; map_list_ptr;
	map_list_ptr = map_list_ptr->map_list_next)
      if (!strcmp(map_list_ptr->map_list_name, dirname))
	 return map_list_ptr->map_list_map;

   map_list_ptr =
      ((struct file_name_map_list *)xmalloc(sizeof(struct file_name_map_list)));

   map_list_ptr->map_list_name = savestring(dirname);
   map_list_ptr->map_list_map = NULL;

   name = (char *)alloca(strlen(dirname) + strlen(FILE_NAME_MAP_FILE) + 2);
   strcpy(name, dirname);
   if (*dirname)
      strcat(name, "/");
   strcat(name, FILE_NAME_MAP_FILE);
#ifndef __EMX__
   f = fopen(name, "r");
#else
   f = fopen(name, "rt");
#endif
   if (!f)
      map_list_ptr->map_list_map = NULL;
   else
     {
	int                 ch;
	int                 dirlen = strlen(dirname);

	while ((ch = getc(f)) != EOF)
	  {
	     char               *from, *to;
	     struct file_name_map *ptr;

	     if (is_space[ch])
		continue;
	     from = read_filename_string(ch, f);
	     while ((ch = getc(f)) != EOF && is_hor_space[ch]);
	     to = read_filename_string(ch, f);

	     ptr =
		((struct file_name_map *)xmalloc(sizeof(struct file_name_map)));

	     ptr->map_from = from;

	     /* Make the real filename absolute.  */
	     if (*to == '/')
		ptr->map_to = to;
	     else
	       {
		  ptr->map_to = (char *)xmalloc(dirlen + strlen(to) + 2);
		  strcpy(ptr->map_to, dirname);
		  ptr->map_to[dirlen] = '/';
		  strcpy(ptr->map_to + dirlen + 1, to);
		  free(to);
	       }

	     ptr->map_next = map_list_ptr->map_list_map;
	     map_list_ptr->map_list_map = ptr;

	     while ((ch = getc(f)) != '\n')
		if (ch == EOF)
		   break;
	  }
	fclose(f);
     }

   map_list_ptr->map_list_next = CPP_OPTIONS(pfile)->map_list;
   CPP_OPTIONS(pfile)->map_list = map_list_ptr;

   return map_list_ptr->map_list_map;
}

/* Try to open include file FILENAME.  SEARCHPTR is the directory
 * being tried from the include file search path.  This function maps
 * filenames on file systems based on information read by
 * read_name_map.  */

static int
open_include_file(cpp_reader * pfile, char *filename,
		  file_name_list * searchptr)
{
   struct file_name_map *map;
   const char         *from;
   const char         *p, *dir;

   if (searchptr && !searchptr->got_name_map)
     {
	searchptr->name_map = read_name_map(pfile,
					    searchptr->fname
					    ? searchptr->fname : ".");
	searchptr->got_name_map = 1;
     }
   /* First check the mapping for the directory we are using.  */
   if (searchptr && searchptr->name_map)
     {
	from = filename;
	if (searchptr->fname)
	   from += strlen(searchptr->fname) + 1;
	for (map = searchptr->name_map; map; map = map->map_next)
	  {
	     if (!strcmp(map->map_from, from))
	       {
		  /* Found a match.  */
		  return open(map->map_to, O_RDONLY, 0666);
	       }
	  }
     }
   /* Try to find a mapping file for the particular directory we are
    * looking in.  Thus #include <sys/types.h> will look up sys/types.h
    * in /usr/include/header.gcc and look up types.h in
    * /usr/include/sys/header.gcc.  */
   p = strrchr(filename, '/');
   if (!p)
      p = filename;
   if (searchptr
       && searchptr->fname
       && strlen(searchptr->fname) == (unsigned)(p - filename)
       && !strncmp(searchptr->fname, filename, p - filename))
     {
	/* FILENAME is in SEARCHPTR, which we've already checked.  */
	return open(filename, O_RDONLY, 0666);
     }
   if (p == filename)
     {
	dir = ".";
	from = filename;
     }
   else
     {
	char               *s;

	s = (char *)alloca(p - filename + 1);
	memcpy(s, filename, p - filename);
	s[p - filename] = '\0';
	from = p + 1;
	dir = s;
     }
   for (map = read_name_map(pfile, dir); map; map = map->map_next)
      if (!strcmp(map->map_from, from))
	 return open(map->map_to, O_RDONLY, 0666);

   return open(filename, O_RDONLY, 0666);
}

#else

static int
open_include_file(cpp_reader * pfile __UNUSED__, char *filename,
		  file_name_list * searchptr __UNUSED__)
{
   return open(filename, O_RDONLY, 0666);
}

#endif /* USE_FILE_NAME_MAPS */

static int
dos2unix(cpp_buffer *fp, int length)
{
   unsigned char *tbuf;
   int nlen = 0, i;
   
   tbuf = xmalloc(length + 4);
   if (!tbuf) return length;
   for (i = 0; i < length; i++)
     {
        if ((fp->buf[i] == '\r') &&
            (fp->buf[i + 1] == '\n'))
          {
             // skip \r in \r\n
             continue;
          }
        tbuf[nlen] = fp->buf[i];
        nlen++;
     }
   tbuf[nlen] = 0;
   
   free(fp->buf);
   fp->buf = tbuf;
   return nlen;
}

/* 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 or via the current directory.
 * Return 1 on success, 0 on failure.
 * 
 * The caller is responsible for the cpp_push_buffer.  */

static int
finclude(cpp_reader * pfile, int f, const char *fname, int system_header_p,
	 file_name_list * dirptr)
{
   int                 st_mode;
   long                st_size;
   long                i;
   int                 length;
   cpp_buffer         *fp;	/* For input stack frame */

   if (file_size_and_mode(f, &st_mode, &st_size) < 0)
     {
	cpp_perror_with_name(pfile, fname);
	close(f);
	cpp_pop_buffer(pfile);
	return 0;
     }
   fp = CPP_BUFFER(pfile);
   fp->nominal_fname = fp->fname = fname;
   fp->dir = dirptr;
   fp->system_header_p = system_header_p;
   fp->lineno = 1;
   fp->colno = 1;
   fp->cleanup = file_cleanup;

   if (S_ISREG(st_mode))
     {
	fp->buf = (unsigned char *)xmalloc(st_size + 2);
	/* Read the file contents, knowing that st_size is an upper bound
	 * on the number of bytes we can read.  */
	length = safe_read(f, (char *)fp->buf, st_size);
        length = dos2unix(fp, length);
        
	fp->alimit = fp->buf + st_size + 2;
	fp->cur = fp->buf;
	fp->rlimit = fp->buf + length;
	if (length < 0)
	   goto nope;
     }
   else if (S_ISDIR(st_mode))
     {
	cpp_error(pfile, "directory `%s' specified in #include", fname);
	close(f);
	return 0;
     }
   else
     {
	/* Cannot count its file size before reading.
	 * First read the entire file into heap and
	 * copy them into buffer on stack. */

	int                 bsize = 2000;

	st_size = 0;
	fp->buf = (unsigned char *)xmalloc(bsize + 2);

	for (;;)
	  {
	     i = safe_read(f, (char *)(fp->buf + st_size), bsize - st_size);
	     if (i < 0)
		goto nope;	/* error! */
	     st_size += i;
	     if (st_size != bsize)
		break;		/* End of file */
	     bsize *= 2;
	     fp->buf = (unsigned char *)xrealloc(fp->buf, bsize + 2);
	  }
	length = st_size;
        length = dos2unix(fp, length);
   }

   if ((length > 0 && fp->buf[length - 1] != '\n')
       /* Backslash-newline at end is not good enough.  */
       || (length > 1 && fp->buf[length - 2] == '\\'))
     {
	fp->buf[length++] = '\n';
     }
   fp->buf[length] = '\0';
     
   fp->rlimit = fp->buf + length;

   /* 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.  */

   pfile->input_stack_listing_current = 0;

   return 1;

 nope:

   cpp_perror_with_name(pfile, fname);
   close(f);
   free(fp->buf);
   return 1;
}

int
push_parse_file(cpp_reader * pfile, const char *fname)
{
   struct cpp_options *opts = CPP_OPTIONS(pfile);
   struct cpp_pending *pend;
   char               *p;
   int                 f;
   cpp_buffer         *fp;

   /* 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.  */
   struct default_include *include_defaults = include_defaults_array;

   /* 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 && !opts->no_standard_includes)
      path_include(pfile, p);

   /* Now that dollars_in_ident is known, initialize is_idchar.  */
   initialize_char_syntax(opts);

   /* Do partial setup of input buffer for the sake of generating
    * early #line directives (when -g is in effect).  */
   fp = cpp_push_buffer(pfile, NULL, 0);
   if (!opts->in_fname)
      opts->in_fname = "";
   fp->nominal_fname = fp->fname = opts->in_fname;
   fp->lineno = 0;

   /* Install __LINE__, etc.  Must follow initialize_char_syntax
    * and option processing.  */
   initialize_builtins(pfile);

   /* Do standard #defines and assertions
    * that identify system and machine type.  */

   if (!opts->inhibit_predefs)
     {
	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 (opts->debug_output)
		     output_line_command(pfile, 0, same_file);
		  cpp_define(pfile, (unsigned char *)q);
		  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(pfile, "-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.  */
   /* First reverse the list. */
   opts->pending = nreverse_pending(opts->pending);

   for (pend = opts->pending; pend; pend = pend->next)
     {
	if (pend->cmd && pend->cmd[0] == '-')
	  {
	     switch (pend->cmd[1])
	       {
	       case 'U':
		  if (opts->debug_output)
		     output_line_command(pfile, 0, same_file);
		  do_undef(pfile, NULL, (unsigned char *)pend->arg,
			   (unsigned char *)pend->arg + strlen(pend->arg));
		  break;
	       case 'D':
		  if (opts->debug_output)
		     output_line_command(pfile, 0, same_file);
		  cpp_define(pfile, (unsigned char *)pend->arg);
		  break;
	       case 'A':
		  make_assertion(pfile, "-A", pend->arg);
		  break;
	       }
	  }
     }

   opts->done_initializing = 1;

   {				/* read the appropriate environment variable and if it exists
				 * replace include_defaults with the listed path. */
      char               *epath = 0;

      switch ((opts->objc << 1) + opts->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) || *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 = opts->cplusplus;
		     include_defaults[num_dirs].cxx_aware = 1;
		     num_dirs++;
		     if (*endp == '\0')
			break;
		     endp = startp = endp + 1;
		  }
		else
		   endp++;
	     }
	   /* Put the usual defaults back in at the end.  */
	   memcpy((char *)&include_defaults[num_dirs],
		  (char *)include_defaults_array,
		  sizeof(include_defaults_array));
	}
   }

   append_include_chain(pfile, opts->before_system, opts->last_before_system);
   opts->first_system_include = opts->before_system;

   /* Unless -fnostdinc,
    * tack on the standard include file dirs to the specified list */
   if (!opts->no_standard_includes)
     {
	struct default_include *di = include_defaults;
	char               *specd_prefix = opts->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 && default_len != 0)
	   for (di = include_defaults; di->fname; di++)
	     {
		/* Some standard dirs are only for C++.  */
		if (!di->cplusplus
		    || (opts->cplusplus
			&& !opts->no_standard_cplusplus_includes))
		  {
		     /* Does this dir start with the prefix?  */
		     if (!strncmp(di->fname, default_prefix, default_len))
		       {
			  /* Yes; change prefix and add to search list.  */
			  file_name_list     *new_
			     =
			     (file_name_list *) xmalloc(sizeof(file_name_list));
			  int                 this_len =
			     strlen(specd_prefix) + strlen(di->fname) -
			     default_len;
			  char               *str =
			     (char *)xmalloc(this_len + 1);

			  strcpy(str, specd_prefix);
			  strcat(str, di->fname + default_len);
			  new_->fname = str;
			  new_->control_macro = 0;
			  new_->c_system_include_path = !di->cxx_aware;
			  new_->got_name_map = 0;
			  append_include_chain(pfile, new_, new_);
			  if (!opts->first_system_include)
			     opts->first_system_include = new_;
		       }
		  }
	     }
	/* Search ordinary names for GNU include directories.  */
	for (di = include_defaults; di->fname; di++)
	  {
	     /* Some standard dirs are only for C++.  */
	     if (!di->cplusplus
		 || (opts->cplusplus && !opts->no_standard_cplusplus_includes))
	       {
		  file_name_list     *new_
		     = (file_name_list *) xmalloc(sizeof(file_name_list));

		  new_->control_macro = 0;
		  new_->c_system_include_path = !di->cxx_aware;
		  new_->fname = (char *)di->fname;
		  new_->got_name_map = 0;
		  append_include_chain(pfile, new_, new_);
		  if (!opts->first_system_include)
		     opts->first_system_include = new_;
	       }
	  }
     }
   /* Tack the after_include chain at the end of the include chain.  */
   append_include_chain(pfile, opts->after_include, opts->last_after_include);
   if (!opts->first_system_include)
      opts->first_system_include = opts->after_include;

   /* With -v, print the list of dirs to search.  */
   if (opts->verbose)
     {
	file_name_list     *fl;

	fprintf(stderr, "#include \"...\" search starts here:\n");
	for (fl = opts->include; fl; fl = fl->next)
	  {
	     if (fl == opts->first_bracket_include)
		fprintf(stderr, "#include <...> search starts here:\n");
	     fprintf(stderr, " %s\n", fl->fname);
	  }
	fprintf(stderr, "End of search list.\n");
     }
   /* Scan the -imacros files before the main input.
    * Much like #including them, but with no_output set
    * so that only their macro definitions matter.  */

   opts->no_output++;
   pfile->no_record_file++;
   for (pend = opts->pending; pend; pend = pend->next)
     {
	if (pend->cmd && strcmp(pend->cmd, "-imacros") == 0)
	  {
	     int                 fd = open(pend->arg, O_RDONLY, 0666);

	     if (fd < 0)
	       {
		  cpp_perror_with_name(pfile, pend->arg);
		  return FATAL_EXIT_CODE;
	       }
	     cpp_push_buffer(pfile, NULL, 0);
	     finclude(pfile, fd, pend->arg, 0, NULL);
	     cpp_scan_buffer(pfile);
	  }
     }
   opts->no_output--;
   pfile->no_record_file--;

   /* Copy the entire contents of the main input file into
    * the stacked input buffer previously allocated for it.  */
   if (!fname || *fname == 0)
     {
	fname = "";
	f = 0;
     }
   else if ((f = open(fname, O_RDONLY, 0666)) < 0)
      cpp_pfatal_with_name(pfile, fname);

   /* -MG doesn't select the form of output and must be specified with one of
    * -M or -MM.  -MG doesn't make sense with -MD or -MMD since they don't
    * inhibit compilation.  */
   if (opts->print_deps_missing_files
       && (opts->print_deps == 0 || !opts->no_output))
      cpp_fatal("-MG must be specified with one of -M or -MM");

   /* Either of two environment variables can specify output of deps.
    * Its value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET",
    * where OUTPUT_FILE is the file to write deps info to
    * and DEPS_TARGET is the target to mention in the deps.  */

   if (opts->print_deps == 0
       && (getenv("SUNPRO_DEPENDENCIES") != 0
	   || getenv("DEPENDENCIES_OUTPUT") != 0))
     {
	char               *spec = getenv("DEPENDENCIES_OUTPUT");
	char               *s;
	char               *output_file;

	if (!spec)
	  {
	     spec = getenv("SUNPRO_DEPENDENCIES");
	     opts->print_deps = 2;
	  }
	else
	   opts->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)
	  {
	     opts->deps_target = s + 1;
	     output_file = (char *)xmalloc(s - spec + 1);
	     memcpy(output_file, spec, s - spec);
	     output_file[s - spec] = 0;
	  }
	else
	  {
	     opts->deps_target = 0;
	     output_file = spec;
	  }

	opts->deps_file = output_file;
	opts->print_deps_append = 1;
     }
   /* For -M, print the expected object file name
    * as the target of this Make-rule.  */
   if (opts->print_deps)
     {
	pfile->deps_allocated_size = 200;
	pfile->deps_buffer = (char *)xmalloc(pfile->deps_allocated_size);
	pfile->deps_buffer[0] = 0;
	pfile->deps_size = 0;
	pfile->deps_column = 0;

	if (opts->deps_target)
	   deps_output(pfile, opts->deps_target, ':');
	else if (*opts->in_fname == 0)
	   deps_output(pfile, "-", ':');
	else
	  {
	     char               *q;
	     int                 len;

	     /* Discard all directory prefixes from filename.  */
	     if ((q = (char *)strrchr(opts->in_fname, '/'))
#ifdef DIR_SEPARATOR
		 && (q = strrchr(opts->in_fname, DIR_SEPARATOR))
#endif
		)
		++q;
	     else
		q = (char *)opts->in_fname;

	     /* Copy remainder to mungable area.  */
	     p = (char *)alloca(strlen(q) + 8);
	     strcpy(p, q);

	     /* Output P, but remove known suffixes.  */
	     len = strlen(p);
	     q = p + len;
	     if (len >= 2 && p[len - 2] == '.' && strchr("cCsSm", p[len - 1]))
		q = p + (len - 2);
	     else if (len >= 3
		      && p[len - 3] == '.'
		      && p[len - 2] == 'c' && p[len - 1] == 'c')
		q = p + (len - 3);
	     else if (len >= 4
		      && p[len - 4] == '.'
		      && p[len - 3] == 'c'
		      && p[len - 2] == 'x' && p[len - 1] == 'x')
		q = p + (len - 4);
	     else if (len >= 4
		      && p[len - 4] == '.'
		      && p[len - 3] == 'c'
		      && p[len - 2] == 'p' && p[len - 1] == 'p')
		q = p + (len - 4);

	     /* Supply our own suffix.  */
	     strcpy(q, ".o");

	     deps_output(pfile, p, ':');
	     deps_output(pfile, opts->in_fname, ' ');
	  }
     }

   /* Scan the -include files before the main input.
    * We push these in reverse order, so that the first one is handled first.  */

   pfile->no_record_file++;
   opts->pending = nreverse_pending(opts->pending);
   for (pend = opts->pending; pend; pend = pend->next)
     {
	if (pend->cmd && strcmp(pend->cmd, "-include") == 0)
	  {
	     int                 fd = open(pend->arg, O_RDONLY, 0666);

	     if (fd < 0)
	       {
		  cpp_perror_with_name(pfile, pend->arg);
		  return FATAL_EXIT_CODE;
	       }
	     cpp_push_buffer(pfile, NULL, 0);
	     finclude(pfile, fd, pend->arg, 0, NULL);
	  }
     }
   pfile->no_record_file--;

   /* Free the pending list. */
   for (pend = opts->pending; pend;)
     {
	struct cpp_pending *next = pend->next;

	free(pend);
	pend = next;
     }
   opts->pending = NULL;

   if (finclude(pfile, f, fname, 0, NULL))
      output_line_command(pfile, 0, same_file);
   return SUCCESS_EXIT_CODE;
}

void
init_parse_file(cpp_reader * pfile)
{
   memset((char *)pfile, 0, sizeof(cpp_reader));
   pfile->get_token = cpp_get_token;

   pfile->token_buffer_size = 200;
   pfile->token_buffer = (unsigned char *)xmalloc(pfile->token_buffer_size);
   CPP_SET_WRITTEN(pfile, 0);

   pfile->system_include_depth = 0;
   pfile->dont_repeat_files = 0;
   pfile->all_include_files = 0;
   pfile->max_include_len = 0;
   pfile->timebuf = NULL;
   pfile->only_seen_white = 1;
   pfile->buffer = CPP_NULL_BUFFER(pfile);
}

static struct cpp_pending *
nreverse_pending(struct cpp_pending *list)
{
   struct cpp_pending *prev = 0, *next, *pend;

   for (pend = list; pend; pend = next)
     {
	next = pend->next;
	pend->next = prev;
	prev = pend;
     }
   return prev;
}

static void
push_pending(cpp_reader * pfile, const char *cmd, const char *arg)
{
   struct cpp_pending *pend
      = (struct cpp_pending *)xmalloc(sizeof(struct cpp_pending));

   pend->cmd = cmd;
   pend->arg = arg;
   pend->next = CPP_OPTIONS(pfile)->pending;
   CPP_OPTIONS(pfile)->pending = pend;
}

/* Handle command-line options in (argc, argv).
 * Can be called multiple times, to handle multiple sets of options.
 * Returns if an unrecognized option is seen.
 * Returns number of handled arguments.  */

int
cpp_handle_options(cpp_reader * pfile, int argc, char **argv)
{
   int                 i;
   struct cpp_options *opts = CPP_OPTIONS(pfile);

   for (i = 0; i < argc; i++)
     {
	if (argv[i][0] != '-')
	  {
	     if (opts->out_fname)
		cpp_fatal("Usage: %s [switches] input output", argv[0]);
	     else if (opts->in_fname)
		opts->out_fname = argv[i];
	     else
		opts->in_fname = argv[i];
	  }
	else
	  {
	     switch (argv[i][1])
	       {

	       case 'i':
		  if (!strcmp(argv[i], "-include")
		      || !strcmp(argv[i], "-imacros"))
		    {
		       if (i + 1 == argc)
			  cpp_fatal("Filename missing after `%s' option",
				    argv[i]);
		       else
			  push_pending(pfile, argv[i], argv[i + 1]), i++;
		    }
		  if (!strcmp(argv[i], "-iprefix"))
		    {
		       if (i + 1 == argc)
			  cpp_fatal("Filename missing after `-iprefix' option");
		       else
			  opts->include_prefix = argv[++i];
		    }
		  if (!strcmp(argv[i], "-ifoutput"))
		    {
		       opts->output_conditionals = 1;
		    }
		  if (!strcmp(argv[i], "-isystem"))
		    {
		       file_name_list     *dirtmp;

		       if (i + 1 == argc)
			  cpp_fatal("Filename missing after `-isystem' option");

		       dirtmp =
			  (file_name_list *) xmalloc(sizeof(file_name_list));

		       dirtmp->next = 0;
		       dirtmp->control_macro = 0;
		       dirtmp->c_system_include_path = 1;
		       dirtmp->fname = (char *)xmalloc(strlen(argv[i + 1]) + 1);
		       strcpy(dirtmp->fname, argv[++i]);
		       dirtmp->got_name_map = 0;

		       if (!opts->before_system)
			  opts->before_system = dirtmp;
		       else
			  opts->last_before_system->next = dirtmp;
		       opts->last_before_system = dirtmp;	/* Tail follows the last one */
		    }
		  /* Add directory to end of path for includes,
		   * with the default prefix at the front of its name.  */
		  if (!strcmp(argv[i], "-iwithprefix"))
		    {
		       file_name_list     *dirtmp;
		       char               *prefix;

		       if (opts->include_prefix)
			  prefix = opts->include_prefix;
		       else
			 {
			    prefix = savestring(GCC_INCLUDE_DIR);
			    /* Remove the `include' from /usr/local/lib/gcc.../include.  */
			    if (!strcmp
				(prefix + strlen(prefix) - 8, "/include"))
			       prefix[strlen(prefix) - 7] = 0;
			 }

		       dirtmp =
			  (file_name_list *) xmalloc(sizeof(file_name_list));

		       dirtmp->next = 0;	/* New one goes on the end */
		       dirtmp->control_macro = 0;
		       dirtmp->c_system_include_path = 0;
		       if (i + 1 == argc)
			  cpp_fatal
			     ("Directory name missing after `-iwithprefix' option");

		       dirtmp->fname = (char *)xmalloc(strlen(argv[i + 1])
						       + strlen(prefix) + 1);
		       strcpy(dirtmp->fname, prefix);
		       strcat(dirtmp->fname, argv[++i]);
		       dirtmp->got_name_map = 0;

		       if (!opts->after_include)
			  opts->after_include = dirtmp;
		       else
			  opts->last_after_include->next = dirtmp;
		       opts->last_after_include = dirtmp;	/* Tail follows the last one */
		    }
		  /* Add directory to main path for includes,
		   * with the default prefix at the front of its name.  */
		  if (!strcmp(argv[i], "-iwithprefixbefore"))
		    {
		       file_name_list     *dirtmp;
		       char               *prefix;

		       if (opts->include_prefix)
			  prefix = opts->include_prefix;
		       else
			 {
			    prefix = savestring(GCC_INCLUDE_DIR);
			    /* Remove the `include' from /usr/local/lib/gcc.../include.  */
			    if (!strcmp
				(prefix + strlen(prefix) - 8, "/include"))
			       prefix[strlen(prefix) - 7] = 0;
			 }

		       dirtmp =
			  (file_name_list *) xmalloc(sizeof(file_name_list));

		       dirtmp->next = 0;	/* New one goes on the end */
		       dirtmp->control_macro = 0;
		       dirtmp->c_system_include_path = 0;
		       if (i + 1 == argc)
			  cpp_fatal
			     ("Directory name missing after `-iwithprefixbefore' option");

		       dirtmp->fname = (char *)xmalloc(strlen(argv[i + 1])
						       + strlen(prefix) + 1);
		       strcpy(dirtmp->fname, prefix);
		       strcat(dirtmp->fname, argv[++i]);
		       dirtmp->got_name_map = 0;

		       append_include_chain(pfile, dirtmp, dirtmp);
		    }
		  /* Add directory to end of path for includes.  */
		  if (!strcmp(argv[i], "-idirafter"))
		    {
		       file_name_list     *dirtmp;

		       dirtmp =
			  (file_name_list *) xmalloc(sizeof(file_name_list));

		       dirtmp->next = 0;	/* New one goes on the end */
		       dirtmp->control_macro = 0;
		       dirtmp->c_system_include_path = 0;
		       if (i + 1 == argc)
			  cpp_fatal
			     ("Directory name missing after `-idirafter' option");
		       else
			  dirtmp->fname = argv[++i];
		       dirtmp->got_name_map = 0;

		       if (!opts->after_include)
			  opts->after_include = dirtmp;
		       else
			  opts->last_after_include->next = dirtmp;
		       opts->last_after_include = dirtmp;	/* Tail follows the last one */
		    }
		  break;

	       case 'o':
		  if (opts->out_fname)
		     cpp_fatal("Output filename specified twice");
		  if (i + 1 == argc)
		     cpp_fatal("Filename missing after -o option");
		  opts->out_fname = argv[++i];
		  if (!strcmp(opts->out_fname, "-"))
		     opts->out_fname = "";
		  break;

	       case 'p':
		  if (!strcmp(argv[i], "-pedantic"))
		     CPP_PEDANTIC(pfile) = 1;
		  else if (!strcmp(argv[i], "-pedantic-errors"))
		    {
		       CPP_PEDANTIC(pfile) = 1;
		       opts->pedantic_errors = 1;
		    }
		  break;

	       case 't':
		  if (!strcmp(argv[i], "-trigraphs"))
		    {
		       if (!opts->chill)
			  opts->no_trigraphs = 0;
		    }
		  break;

	       case 'l':
		  if (!strcmp(argv[i], "-lang-c"))
		     opts->cplusplus = 0, opts->cplusplus_comments =
			0, opts->objc = 0;
		  if (!strcmp(argv[i], "-lang-c++"))
		     opts->cplusplus = 1, opts->cplusplus_comments =
			1, opts->objc = 0;
		  if (!strcmp(argv[i], "-lang-c-c++-comments"))
		     opts->cplusplus = 0, opts->cplusplus_comments =
			1, opts->objc = 0;
		  if (!strcmp(argv[i], "-lang-objc"))
		     opts->objc = 1, opts->cplusplus =
			0, opts->cplusplus_comments = 1;
		  if (!strcmp(argv[i], "-lang-objc++"))
		     opts->objc = 1, opts->cplusplus =
			1, opts->cplusplus_comments = 1;
		  if (!strcmp(argv[i], "-lang-asm"))
		     opts->lang_asm = 1;
		  if (!strcmp(argv[i], "-lint"))
		     opts->for_lint = 1;
		  if (!strcmp(argv[i], "-lang-chill"))
		     opts->objc = 0, opts->cplusplus = 0, opts->chill = 1,
			opts->traditional = 1, opts->no_trigraphs = 1;
		  break;

	       case '+':
		  opts->cplusplus = 1, opts->cplusplus_comments = 1;
		  break;

	       case 'w':
		  opts->inhibit_warnings = 1;
		  break;

	       case 'W':
		  if (!strcmp(argv[i], "-Wtrigraphs"))
		     opts->warn_trigraphs = 1;
		  else if (!strcmp(argv[i], "-Wno-trigraphs"))
		     opts->warn_trigraphs = 0;
		  else if (!strcmp(argv[i], "-Wcomment"))
		     opts->warn_comments = 1;
		  else if (!strcmp(argv[i], "-Wno-comment"))
		     opts->warn_comments = 0;
		  else if (!strcmp(argv[i], "-Wcomments"))
		     opts->warn_comments = 1;
		  else if (!strcmp(argv[i], "-Wno-comments"))
		     opts->warn_comments = 0;
		  else if (!strcmp(argv[i], "-Wtraditional"))
		     opts->warn_stringify = 1;
		  else if (!strcmp(argv[i], "-Wno-traditional"))
		     opts->warn_stringify = 0;
		  else if (!strcmp(argv[i], "-Wimport"))
		     opts->warn_import = 1;
		  else if (!strcmp(argv[i], "-Wno-import"))
		     opts->warn_import = 0;
		  else if (!strcmp(argv[i], "-Werror"))
		     opts->warnings_are_errors = 1;
		  else if (!strcmp(argv[i], "-Wno-error"))
		     opts->warnings_are_errors = 0;
		  else if (!strcmp(argv[i], "-Wall"))
		    {
		       opts->warn_trigraphs = 1;
		       opts->warn_comments = 1;
		    }
		  break;

	       case 'M':
		  /* The style of the choices here is a bit mixed.
		   * The chosen scheme is a hybrid of keeping all options in one string
		   * and specifying each option in a separate argument:
		   * -M|-MM|-MD file|-MMD file [-MG].  An alternative is:
		   * -M|-MM|-MD file|-MMD file|-MG|-MMG; or more concisely:
		   * -M[M][G][D file].  This is awkward to handle in specs, and is not
		   * as extensible.  */
		  /* ??? -MG must be specified in addition to one of -M or -MM.
		   * This can be relaxed in the future without breaking anything.
		   * The converse isn't true.  */

		  /* -MG isn't valid with -MD or -MMD.  This is checked for later.  */
		  if (!strcmp(argv[i], "-MG"))
		    {
		       opts->print_deps_missing_files = 1;
		       break;
		    }
		  if (!strcmp(argv[i], "-M"))
		     opts->print_deps = 2;
		  else if (!strcmp(argv[i], "-MM"))
		     opts->print_deps = 1;
		  else if (!strcmp(argv[i], "-MD"))
		     opts->print_deps = 2;
		  else if (!strcmp(argv[i], "-MMD"))
		     opts->print_deps = 1;
		  /* For -MD and -MMD options, write deps on file named by next arg.  */
		  if (!strcmp(argv[i], "-MD") || !strcmp(argv[i], "-MMD"))
		    {
		       if (i + 1 == argc)
			  cpp_fatal("Filename missing after %s option",
				    argv[i]);
		       opts->deps_file = argv[++i];
		    }
		  else
		    {
		       /* For -M and -MM, write deps on standard output
		        * and suppress the usual output.  */
		       opts->no_output = 1;
		    }
		  break;

	       case 'd':
		  {
		     char               *p = argv[i] + 2;
		     char                c;

		     while ((c = *p++) != 0)
		       {
			  /* Arg to -d specifies what parts of macros to dump */
			  switch (c)
			    {
			    case 'M':
			       opts->dump_macros = dump_only;
			       opts->no_output = 1;
			       break;
			    case 'N':
			       opts->dump_macros = dump_names;
			       break;
			    case 'D':
			       opts->dump_macros = dump_definitions;
			       break;
			    }
		       }
		  }
		  break;

	       case 'g':
		  if (argv[i][2] == '3')
		     opts->debug_output = 1;
		  break;

	       case 'v':
		  fprintf(stderr, "GNU CPP version %s", version_string);
#ifdef TARGET_VERSION
		  TARGET_VERSION;
#endif
		  fprintf(stderr, "\n");
		  opts->verbose = 1;
		  break;

	       case 'H':
		  opts->print_include_names = 1;
		  break;

	       case 'D':
		  if (argv[i][2] != 0)
		     push_pending(pfile, "-D", argv[i] + 2);
		  else if (i + 1 == argc)
		     cpp_fatal("Macro name missing after -D option");
		  else
		     i++, push_pending(pfile, "-D", argv[i]);
		  break;

	       case 'A':
		  {
		     char               *p = NULL;

		     if (argv[i][2] != 0)
			p = argv[i] + 2;
		     else if (i + 1 == argc)
			cpp_fatal("Assertion missing after -A option");
		     else
			p = argv[++i];

		     if (!strcmp(p, "-"))
		       {
			  struct cpp_pending **ptr;

			  /* -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.  */

			  opts->inhibit_predefs = 1;
			  for (ptr = &opts->pending; *ptr;)
			    {
			       struct cpp_pending *pend = *ptr;

			       if (pend->cmd && pend->cmd[0] == '-'
				   && (pend->cmd[1] == 'D'
				       || pend->cmd[1] == 'A'))
				 {
				    *ptr = pend->next;
				    free(pend);
				 }
			       else
				  ptr = &pend->next;
			    }
		       }
		     else
		       {
			  push_pending(pfile, "-A", p);
		       }
		  }
		  break;

	       case 'U':	/* JF #undef something */
		  if (argv[i][2] != 0)
		     push_pending(pfile, "-U", argv[i] + 2);
		  else if (i + 1 == argc)
		     cpp_fatal("Macro name missing after -U option");
		  else
		     push_pending(pfile, "-U", argv[i + 1]), i++;
		  break;

	       case 'C':
		  opts->put_out_comments = 1;
		  break;

	       case 'E':	/* -E comes from cc -E; ignore it.  */
		  break;

	       case 'P':
		  opts->no_line_commands = 1;
		  break;

	       case '$':	/* Don't include $ in identifiers.  */
		  opts->dollars_in_ident = 0;
		  break;

	       case 'I':	/* Add directory to path for includes.  */
		  {
		     file_name_list     *dirtmp;

		     if (!CPP_OPTIONS(pfile)->ignore_srcdir
			 && !strcmp(argv[i] + 2, "-"))
		       {
			  CPP_OPTIONS(pfile)->ignore_srcdir = 1;
			  /* Don't use any preceding -I directories for #include <...>.  */
			  CPP_OPTIONS(pfile)->first_bracket_include = 0;
		       }
		     else
		       {
			  dirtmp =
			     (file_name_list *) xmalloc(sizeof(file_name_list));

			  dirtmp->next = 0;	/* New one goes on the end */
			  dirtmp->control_macro = 0;
			  dirtmp->c_system_include_path = 0;
			  if (argv[i][2] != 0)
			     dirtmp->fname = argv[i] + 2;
			  else if (i + 1 == argc)
			     cpp_fatal
				("Directory name missing after -I option");
			  else
			     dirtmp->fname = argv[++i];
			  dirtmp->got_name_map = 0;
			  append_include_chain(pfile, 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.  */
		     opts->no_standard_includes = 1;
		  else if (!strcmp(argv[i], "-nostdinc++"))
		     /* -nostdinc++ causes no default C++-specific include directories. */
		     opts->no_standard_cplusplus_includes = 1;
		  break;

	       case 'u':
		  /* Sun compiler passes undocumented switch "-undef".
		   * Let's assume it means to inhibit the predefined symbols.  */
		  opts->inhibit_predefs = 1;
		  break;

	       case '\0':	/* JF handle '-' as file name meaning stdin or stdout */
		  if (!opts->in_fname)
		    {
		       opts->in_fname = "";
		       break;
		    }
		  else if (!opts->out_fname)
		    {
		       opts->out_fname = "";
		       break;
		    }		/* else fall through into error */
	       default:
		  return i;
	       }
	  }
     }
   return i;
}

void
cpp_finish(cpp_reader * pfile)
{
   struct cpp_options *opts = CPP_OPTIONS(pfile);

   if (opts->print_deps)
     {
	/* Stream on which to print the dependency information.  */
	FILE               *deps_stream;

	/* Don't actually write the deps file if compilation has failed.  */
	if (pfile->errors == 0)
	  {
	     const char         *deps_mode =
		opts->print_deps_append ? "a" : "w";

	     if (!opts->deps_file)
		deps_stream = stdout;
	     else if (!(deps_stream = fopen(opts->deps_file, deps_mode)))
		cpp_pfatal_with_name(pfile, opts->deps_file);
	     fputs(pfile->deps_buffer, deps_stream);
	     putc('\n', deps_stream);
	     if (opts->deps_file)
	       {
		  if (ferror(deps_stream) || fclose(deps_stream) != 0)
		     cpp_fatal("I/O error on output");
	       }
	  }
     }
}

static int
do_assert(cpp_reader * pfile, struct directive *keyword __UNUSED__,
	  unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__)
{
   long                symstart;	/* remember where symbol name starts */
   int                 c;
   int                 sym_length;	/* and how long it is */
   struct arglist     *tokens = NULL;

   if (CPP_PEDANTIC(pfile) && CPP_OPTIONS(pfile)->done_initializing
       && !CPP_BUFFER(pfile)->system_header_p)
      cpp_pedwarn(pfile, "ANSI C does not allow `#assert'");

   cpp_skip_hspace(pfile);
   symstart = CPP_WRITTEN(pfile);	/* remember where it starts */
   parse_name(pfile, GETC());
   sym_length = check_macro_name(pfile, pfile->token_buffer + symstart,
				 "assertion");

   cpp_skip_hspace(pfile);
   if (PEEKC() != '(')
     {
	cpp_error(pfile, "missing token-sequence in `#assert'");
	goto error;
     }
   {
      int                 error_flag = 0;

      tokens = read_token_list(pfile, &error_flag);
      if (error_flag)
	 goto error;
      if (!tokens)
	{
	   cpp_error(pfile, "empty token-sequence in `#assert'");
	   goto error;
	}
      cpp_skip_hspace(pfile);
      c = PEEKC();
      if (c != EOF && c != '\n')
	 cpp_pedwarn(pfile, "junk at end of `#assert'");
      skip_rest_of_line(pfile);
   }

   /* 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;
      const char         *symname = (char *)pfile->token_buffer + symstart;
      int                 hashcode =
	 hashf(symname, sym_length, ASSERTION_HASHSIZE);
      struct tokenlist_list *value =
	 (struct tokenlist_list *)xmalloc(sizeof(struct tokenlist_list));

      hp = assertion_lookup(pfile, symname, sym_length, hashcode);
      if (!hp)
	{
	   if (sym_length == 7 && !strncmp(symname, "defined", sym_length))
	      cpp_error(pfile, "`defined' redefined as assertion");
	   hp = assertion_install(pfile, 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;
   }
   CPP_SET_WRITTEN(pfile, symstart);	/* Pop */
   return 0;
 error:
   CPP_SET_WRITTEN(pfile, symstart);	/* Pop */
   skip_rest_of_line(pfile);
   return 1;
}

static int
do_unassert(cpp_reader * pfile, struct directive *keyword __UNUSED__,
	    unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__)
{
   long                symstart;	/* remember where symbol name starts */
   int                 sym_length;	/* and how long it is */
   int                 c;

   struct arglist     *tokens = NULL;
   int                 tokens_specified = 0;

   if (CPP_PEDANTIC(pfile) && CPP_OPTIONS(pfile)->done_initializing
       && !CPP_BUFFER(pfile)->system_header_p)
      cpp_pedwarn(pfile, "ANSI C does not allow `#unassert'");

   cpp_skip_hspace(pfile);

   symstart = CPP_WRITTEN(pfile);	/* remember where it starts */
   parse_name(pfile, GETC());
   sym_length = check_macro_name(pfile, pfile->token_buffer + symstart,
				 "assertion");

   cpp_skip_hspace(pfile);
   if (PEEKC() == '(')
     {
	int                 error_flag = 0;

	tokens = read_token_list(pfile, &error_flag);
	if (error_flag)
	   goto error;
	if (!tokens)
	  {
	     cpp_error(pfile, "empty token list in `#unassert'");
	     goto error;
	  }
	tokens_specified = 1;
     }
   cpp_skip_hspace(pfile);
   c = PEEKC();
   if (c != EOF && c != '\n')
      cpp_error(pfile, "junk at end of `#unassert'");
   skip_rest_of_line(pfile);

   {
      ASSERTION_HASHNODE *hp;
      const char         *symname = (char *)pfile->token_buffer + symstart;
      int                 hashcode =
	 hashf(symname, sym_length, ASSERTION_HASHSIZE);
      struct tokenlist_list *tail, *prev;

      hp = assertion_lookup(pfile, symname, sym_length, hashcode);
      if (!hp)
	 return 1;

      /* If no token list was specified, then eliminate this assertion
       * entirely.  */
      if (!tokens_specified)
	 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;
	     }
	}
   }

   CPP_SET_WRITTEN(pfile, symstart);	/* Pop */
   return 0;
 error:
   CPP_SET_WRITTEN(pfile, symstart);	/* Pop */
   skip_rest_of_line(pfile);
   return 1;
}

/* 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.  */

static int
check_assertion(cpp_reader * pfile, const char *name, int sym_length,
		int tokens_specified, struct arglist *tokens)
{
   ASSERTION_HASHNODE *hp;
   int                 hashcode = hashf(name, sym_length, ASSERTION_HASHSIZE);

   if (CPP_PEDANTIC(pfile) && !CPP_BUFFER(pfile)->system_header_p)
      cpp_pedwarn(pfile, "ANSI C does not allow testing assertions");

   hp = assertion_lookup(pfile, name, sym_length, hashcode);
   if (!hp)
      /* 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(struct arglist *l1, struct arglist *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;
}

struct arglist     *
reverse_token_list(struct arglist *tokens)
{
   struct arglist     *prev = 0, *cur, *next;

   for (cur = tokens; cur; cur = next)
     {
	next = cur->next;
	cur->next = prev;
	prev = cur;
     }
   return prev;
}

/* 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.) */

static struct arglist *
read_token_list(cpp_reader * pfile, int *error_flag)
{
   struct arglist     *token_ptrs = 0;
   int                 depth = 1;
   int                 length;

   *error_flag = 0;
   FORWARD(1);			/* Skip '(' */

   /* Loop over the assertion value tokens.  */
   while (depth > 0)
     {
	struct arglist     *temp;
	long                name_written = CPP_WRITTEN(pfile);
	int                 c;

	cpp_skip_hspace(pfile);

	c = GETC();

	/* Find the end of the token.  */
	if (c == '(')
	  {
	     CPP_PUTC(pfile, c);
	     depth++;
	  }
	else if (c == ')')
	  {
	     depth--;
	     if (depth == 0)
		break;
	     CPP_PUTC(pfile, c);
	  }
	else if (c == '"' || c == '\'')
	  {
	     FORWARD(-1);
	     cpp_get_token(pfile);
	  }
	else if (c == '\n')
	   break;
	else
	  {
	     while (c != EOF && !is_space[c] && c != '(' && c != ')'
		    && c != '"' && c != '\'')
	       {
		  CPP_PUTC(pfile, c);
		  c = GETC();
	       }
	     if (c != EOF)
		FORWARD(-1);
	  }

	length = CPP_WRITTEN(pfile) - name_written;
	temp = (struct arglist *)xmalloc(sizeof(struct arglist) + length + 1);

	temp->name = (char *)(temp + 1);
	memcpy(temp->name, (char *)(pfile->token_buffer + name_written),
	       length);
	temp->name[length] = 0;
	temp->next = token_ptrs;
	token_ptrs = temp;
	temp->length = length;

	CPP_ADJUST_WRITTEN(pfile, -length);	/* pop */

	if (c == EOF || c == '\n')
	  {			/* FIXME */
	     cpp_error(pfile,
		       "unterminated token sequence following  `#' operator");
	     return 0;
	  }
     }

   /* We accumulated the names in reverse order.
    * Now reverse them to get the proper order.  */
   return reverse_token_list(token_ptrs);
}

static void
free_token_list(struct arglist *tokens)
{
   while (tokens)
     {
	struct arglist     *next = tokens->next;

	free(tokens->name);
	free(tokens);
	tokens = next;
     }
}

/* 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(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;
}

/* Read LEN bytes at PTR from descriptor DESC, for file FILENAME,
 * retrying if necessary.  Return a negative value if an error occurs,
 * otherwise return the actual number of bytes read,
 * which must be LEN unless end-of-file was reached.  */

static int
safe_read(int desc, char *ptr, int len)
{
   int                 left = len;

   while (left > 0)
     {
	int                 nchars = read(desc, ptr, left);

	if (nchars < 0)
	  {
#ifdef EINTR
	     if (errno == EINTR)
		continue;
#endif
	     return nchars;
	  }
	if (nchars == 0)
	   break;
	ptr += nchars;
	left -= nchars;
     }
   return len - left;
}

static char        *
savestring(const char *input)
{
   unsigned            size = strlen(input);
   char               *output = (char *)xmalloc(size + 1);

   strcpy(output, input);
   return output;
}

/* Initialize PMARK to remember the current position of PFILE. */
void
parse_set_mark(struct parse_marker *pmark, cpp_reader * pfile)
{
   cpp_buffer         *pbuf = CPP_BUFFER(pfile);

   pmark->next = pbuf->marks;
   pbuf->marks = pmark;
   pmark->buf = pbuf;
   pmark->position = pbuf->cur - pbuf->buf;
}

/* Cleanup PMARK - we no longer need it. */
static void
parse_clear_mark(struct parse_marker *pmark)
{
   struct parse_marker **pp = &pmark->buf->marks;

   for (;; pp = &(*pp)->next)
     {
	if (!*pp)
	   cpp_fatal("internal error", "in parse_set_mark");
	if (*pp == pmark)
	   break;
     }
   *pp = pmark->next;
}

/* Backup the current position of PFILE to that saved in PMARK. */

static void
parse_goto_mark(struct parse_marker *pmark, cpp_reader * pfile)
{
   cpp_buffer         *pbuf = CPP_BUFFER(pfile);

   if (pbuf != pmark->buf)
      cpp_fatal("internal error %s", "parse_goto_mark");
   pbuf->cur = pbuf->buf + pmark->position;
}

/* Reset PMARK to point to the current position of PFILE.  (Same
 * as parse_clear_mark (PMARK), parse_set_mark (PMARK, PFILE) but faster. */

static void
parse_move_mark(struct parse_marker *pmark, cpp_reader * pfile)
{
   cpp_buffer         *pbuf = CPP_BUFFER(pfile);

   if (pbuf != pmark->buf)
      cpp_fatal("internal error %s", "parse_move_mark");
   pmark->position = pbuf->cur - pbuf->buf;
}

int
cpp_read_check_assertion(cpp_reader * pfile)
{
   int                 name_start = CPP_WRITTEN(pfile);
   int                 name_length, name_written;
   int                 result;

   FORWARD(1);			/* Skip '#' */
   cpp_skip_hspace(pfile);
   parse_name(pfile, GETC());
   name_written = CPP_WRITTEN(pfile);
   name_length = name_written - name_start;
   cpp_skip_hspace(pfile);
   if (CPP_BUF_PEEK(CPP_BUFFER(pfile)) == '(')
     {
	int                 error_flag;
	struct arglist     *token_ptrs = read_token_list(pfile, &error_flag);

	result = check_assertion(pfile,
				 (char *)pfile->token_buffer + name_start,
				 name_length, 1, token_ptrs);
     }
   else
      result = check_assertion(pfile,
			       (char *)pfile->token_buffer + name_start,
			       name_length, 0, NULL);
   CPP_ADJUST_WRITTEN(pfile, -name_length);	/* pop */
   return result;
}

static void
cpp_print_file_and_line(cpp_reader * pfile)
{
   cpp_buffer         *ip = cpp_file_buffer(pfile);

   if (ip)
     {
	long                line, col;

	cpp_buf_line_and_col(ip, &line, &col);
	cpp_file_line_for_message(pfile, ip->nominal_fname,
				  line, pfile->show_column ? col : -1);
     }
}

static void
cpp_error_v(cpp_reader * pfile, const char *msg, va_list args)
{
   cpp_print_containing_files(pfile);
   cpp_print_file_and_line(pfile);
   cpp_message_v(pfile, 1, msg, args);
}

void
cpp_error(cpp_reader * pfile, const char *msg, ...)
{
   va_list             args;

   va_start(args, msg);

   cpp_error_v(pfile, msg, args);

   va_end(args);
}

/* Print error message but don't count it.  */

static void
cpp_warning_v(cpp_reader * pfile, const char *msg, va_list args)
{
   if (CPP_OPTIONS(pfile)->inhibit_warnings)
      return;

   if (CPP_OPTIONS(pfile)->warnings_are_errors)
      pfile->errors++;

   cpp_print_containing_files(pfile);
   cpp_print_file_and_line(pfile);
   cpp_message_v(pfile, 0, msg, args);
}

void
cpp_warning(cpp_reader * pfile, const char *msg, ...)
{
   va_list             args;

   va_start(args, msg);

   cpp_warning_v(pfile, msg, args);

   va_end(args);
}

/* Print an error message and maybe count it.  */

void
cpp_pedwarn(cpp_reader * pfile, const char *msg, ...)
{
   va_list             args;

   va_start(args, msg);

   if (CPP_OPTIONS(pfile)->pedantic_errors)
      cpp_error_v(pfile, msg, args);
   else
      cpp_warning_v(pfile, msg, args);

   va_end(args);
}

static void
cpp_error_with_line(cpp_reader * pfile, int line, int column, const char *msg)
{
   cpp_buffer         *ip = cpp_file_buffer(pfile);

   cpp_print_containing_files(pfile);

   if (ip)
      cpp_file_line_for_message(pfile, ip->nominal_fname, line, column);

   cpp_message(pfile, 1, msg, NULL, NULL, NULL);
}

static void
cpp_warning_with_line(cpp_reader * pfile, int line, int column, const char *msg)
{
   cpp_buffer         *ip;

   if (CPP_OPTIONS(pfile)->inhibit_warnings)
      return;

   if (CPP_OPTIONS(pfile)->warnings_are_errors)
      pfile->errors++;

   cpp_print_containing_files(pfile);

   ip = cpp_file_buffer(pfile);

   if (ip)
      cpp_file_line_for_message(pfile, ip->nominal_fname, line, column);

   cpp_message(pfile, 0, msg, NULL, NULL, NULL);
}

static void
cpp_pedwarn_with_line(cpp_reader * pfile, int line, int column, const char *msg)
{
   if (CPP_OPTIONS(pfile)->pedantic_errors)
      cpp_error_with_line(pfile, column, line, msg);
   else
      cpp_warning_with_line(pfile, line, column, msg);
}

/* Report a warning (or an error if pedantic_errors)
 * giving specified file name and line number, not current.  */

void
cpp_pedwarn_with_file_and_line(cpp_reader * pfile,
			       const char *file, int line,
			       const char *msg, const char *arg1,
			       const char *arg2, const char *arg3)
{
   if (!CPP_OPTIONS(pfile)->pedantic_errors
       && CPP_OPTIONS(pfile)->inhibit_warnings)
      return;
   if (file)
      cpp_file_line_for_message(pfile, file, line, -1);
   cpp_message(pfile, CPP_OPTIONS(pfile)->pedantic_errors,
	       msg, arg1, arg2, arg3);
}

/* This defines "errno" properly for VMS, and gives us EACCES. */
#include <errno.h>
#ifndef errno
extern int          errno;

#endif

#ifndef HAVE_STRERROR
extern int          sys_nerr;

#if defined(bsd4_4)
extern const char  *const sys_errlist[];

#else
extern char        *sys_errlist[];

#endif
#endif /* HAVE_STRERROR */

/*
 * my_strerror - return the descriptive text associated with an `errno' code.
 */

static const char  *
my_strerror(int errnum)
{
   const char         *result;

#ifndef HAVE_STRERROR
   result = ((errnum < sys_nerr) ? sys_errlist[errnum] : 0);
#else
   result = strerror(errnum);
#endif

   if (!result)
      result = "undocumented I/O error";

   return result;
}

/* Error including a message from `errno'.  */

static void
cpp_error_from_errno(cpp_reader * pfile, const char *name)
{
   cpp_buffer         *ip = cpp_file_buffer(pfile);

   cpp_print_containing_files(pfile);

   if (ip)
      cpp_file_line_for_message(pfile, ip->nominal_fname, ip->lineno, -1);

   cpp_message(pfile, 1, "%s: %s", name, my_strerror(errno), NULL);
}

void
cpp_perror_with_name(cpp_reader * pfile, const char *name)
{
   cpp_message(pfile, 1, "%s: %s: %s", progname, name, my_strerror(errno));
}

/* TODO:
 * No pre-compiled header file support.
 *
 * Possibly different enum token codes for each C/C++ token.
 *
 * Should clean up remaining directives to that do_XXX functions
 *   only take two arguments and all have command_reads_line.
 *
 * Find and cleanup remaining uses of static variables,
 *
 * Support for trigraphs.
 *
 * Support -dM flag (dump_all_macros).
 *
 * Support for_lint flag.
 */
