/*
** Splint - annotation-assisted static program checker
** Copyright (C) 1994-2003 University of Virginia,
**         Massachusetts Institute of Technology
**
** 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 of the License, 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.
** 
** The GNU General Public License is available from http://www.gnu.org/ or
** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
** MA 02111-1307, USA.
**
** For information on splint: info@splint.org
** To report a bug: splint-bug@splint.org
** For more information: http://www.splint.org
*/
/*
** cpplib.c
*/
/*
   Copyright (C) 1986, 87, 89, 92-6, 1997 Free Software Foundation, Inc.
   Contributed by Per Bothner, 1994-95.
   Based on CCCP program by Paul Rubin, June 1986
   Adapted to ANSI C, Richard Stallman, Jan 1987

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

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

# include <time.h>
# include <errno.h>

# include "splintMacros.nf"
# include "basic.h"

# include "cppconf.h"
# include "cpplib.h"
# include "cpperror.h"
# include "cpphash.h"
# include "cppexp.h"
# include "osd.h"

/*
** This is really kludgey code...
*/

/*@+boolint@*/
/*@+charint@*/

/* Warnings for using sprintf - suppress them all for now... */
/*@-bufferoverflowhigh@*/
/*@-bounds@*/

/*@constant int IMPORT_FOUND@*/
# define IMPORT_FOUND -2

/*@constant int SKIP_INCLUDE@*/
# define SKIP_INCLUDE IMPORT_FOUND

/*@constant unused int IMPORT_NOT_FOUND@*/
# define IMPORT_NOT_FOUND -1

#ifndef STDC_VALUE
/*@constant unused int STDC_VALUE@*/
#define STDC_VALUE 1
#endif

/*@notfunction@*/
#define constlen(f) (sizeof(f) - 1)

static void parse_name (cppReader *, int);

static int cpp_openIncludeFile (char *p_filename)
     /*@modifies fileSystem @*/ ;

static void cpp_setLocation (cppReader *p_pfile)
     /*@modifies g_currentloc@*/ ;

static enum cpp_token cpp_handleComment (cppReader *p_pfile,
					 struct parse_marker *p_smark)
   /*@modifies p_pfile, p_smark@*/;

static bool cpp_shouldCheckMacroAux (const char *p_p);
static bool cpp_shouldCheckMacro (cppReader *p_pfile, const char *p_p);

static size_t identifier_length (const /*@observer@*/ char *p_symname);
static bool identifier_seems_valid (const /*@observer@*/ char *p_symname);
static bool macro_name_is_valid (const /*@observer@*/ char *p_symname, size_t p_symlen);
static /*@only@*/ cstring invalid_macro_message (const /*@observer@*/ char *p_symname, size_t symlen);

static bool cpp_skipIncludeFile (cstring p_fname) /*@*/ ;

/* Symbols to predefine.  */
  
static /*@observer@*/ const char *predefs = CPP_PREDEFINES;

/* 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
/*@constant observer char *SIZE_TYPE@*/
#define SIZE_TYPE "long unsigned int"
#endif

/* The string value for __PTRDIFF_TYPE__.  */

#ifndef PTRDIFF_TYPE
/*@constant observer char *PTRDIFF_TYPE@*/
#define PTRDIFF_TYPE "long int"
#endif

/* The string value for __WCHAR_TYPE__.  */

#ifndef WCHAR_TYPE
/*@constant observer char *WCHAR_TYPE@*/
#define WCHAR_TYPE "int"
#endif
  
/* The string value for __USER_LABEL_PREFIX__ */

#ifndef USER_LABEL_PREFIX
/*@constant observer char *USER_LABEL_PREFIX@*/
#define USER_LABEL_PREFIX ""
#endif

/* The string value for __REGISTER_PREFIX__ */

#ifndef REGISTER_PREFIX
/*@constant observer char *REGISTER_PREFIX@*/
#define REGISTER_PREFIX ""
#endif

/* table to tell if char can be part of a C identifier.  */
static bool is_idchar[256];
/* table to tell if char can be first char of a c identifier.  */
static bool is_idstart[256];
/* table to tell if c is horizontal space.  */
static bool is_hor_space[256];
/* table to tell if c is horizontal or vertical space.  */
static bool is_space[256];
  
static /*@exposed@*/ /*@null@*/ cppBuffer *
cppReader_getBuffer (/*@special@*/ cppReader *p_pfile)
     /*@uses p_pfile->buffer@*/
     /*@modifies nothing@*/ ;

/*@notfunction@*/
# define SKIP_CLASS(p,class) do { /*@access cstring@*/ while (class[(int) *(p)]) { (p)++; } } /*@noaccess cstring@*/ while (0)

/*@notfunction@*/
# define SKIP_WHITE_SPACE(p)        SKIP_CLASS(p, is_hor_space)

#ifdef DEADCODE
/*@notfunction@*/
# define SKIP_ALL_WHITE_SPACE(p)    SKIP_CLASS(p, is_space)
#endif

/*@notfunction@*/
# define SKIP_IDENTIFIER(p)         SKIP_CLASS(p, is_idchar)

static int cpp_peekN (cppReader *p_pfile, int p_n) /*@*/ ;

static void cppReader_growBuffer (cppReader *, size_t);

/* Make sure PFILE->token_buffer has space for at least N more characters. */

/*@function static void cpplib_reserve (sef cppReader *, sef size_t); @*/
#define cpplib_reserve(PFILE, N) \
  do { \
    if ((cpplib_getWritten (PFILE) + (N) > (PFILE)->token_buffer_size)) \
      cppReader_growBuffer (PFILE, (N)); \
  } while (0) 

/*@function static int cppBuffer_get (sef cppBuffer *p_b) modifies *p_b ; @*/
# define cppBuffer_get(BUFFER) \
  ((BUFFER)->cur < (BUFFER)->rlimit ? *(BUFFER)->cur++ : EOF)

/*@function static int cppBuffer_reachedEOF (sef cppBuffer *p_b) modifies nothing; @*/
# define cppBuffer_reachedEOF(b) \
  ((b)->cur < (b)->rlimit ? FALSE : TRUE)

/* Append string STR (of length N) to PFILE's output buffer. Assume there is enough space. */

/*@function static void cppReader_putStrN (sef cppReader *p_file, const @unique@ char *p_str,
                         sef size_t p_n) modifies *p_file; @*/
#define cppReader_putStrN(PFILE, STR, N) \
  do { memcpy ((PFILE)->limit, STR, (N)); (PFILE)->limit += (N); } while (0)
  
/* Append string STR (of length N) to PFILE's output buffer.  Make space. */

/*@function static void cppReader_puts (sef cppReader *p_file, char *p_str, sef size_t p_n)
                     modifies *p_file; @*/
# define cppReader_puts(PFILE, STR, N) \
  do { cpplib_reserve(PFILE, N); cppReader_putStrN (PFILE, STR,N); } while (0)

/* Append character CH to PFILE's output buffer.  Assume sufficient space. */

/*@function static void cppReader_putCharQ (cppReader *p_file, char p_ch)
                    modifies *p_file; @*/
# define cppReader_putCharQ(PFILE, CH) (*(PFILE)->limit++ = (CH))

/* Append character CH to PFILE's output buffer.  Make space if need be. */

/*@function static void cppReader_putChar (sef cppReader *p_file, char p_ch)
                    modifies *p_file; @*/
#define cppReader_putChar(PFILE, CH) \
    do { cpplib_reserve (PFILE, (size_t) 1); cppReader_putCharQ (PFILE, CH); } while (0)

/* Make sure PFILE->limit is followed by '\0'. */

/*@function static void cppReader_nullTerminateQ (cppReader *p_file)
      modifies *p_file; @*/
#define cppReader_nullTerminateQ(PFILE) (*(PFILE)->limit = 0)

/*@function static void cppReader_nullTerminate (sef cppReader *p_file)
                           modifies *p_file; @*/
# define cppReader_nullTerminate(PFILE) \
  do { cpplib_reserve (PFILE, (size_t) 1); *(PFILE)->limit = 0; } while (0)

/*@function static void cppReader_adjustWritten (cppReader *p_file, size_t)
                           modifies *p_file; @*/
#define cppReader_adjustWritten(PFILE,DELTA) ((PFILE)->limit += (DELTA))

/*@function static bool cppReader_isC89 (cppReader *) modifies nothing; @*/
#define cppReader_isC89(PFILE) (CPPOPTIONS(PFILE)->c89)

static void cppBuffer_forward (cppBuffer *p_buf, int p_n) /*@modifies *p_buf@*/ ;

/*@function static void cppReader_forward (cppReader *p_pfile, int) modifies *p_pfile; @*/
# define cppReader_forward(pfile, N) \
  (cppBuffer_forward (cppReader_getBufferSafe (pfile), (N)))

/*@function static int cppReader_getC (cppReader *p_pfile) modifies *p_pfile; @*/
# define cppReader_getC(pfile)   (cppBuffer_get (cppReader_getBufferSafe (pfile)))

/*@function static int cppReader_reachedEOF (sef cppReader *p_pfile) modifies *p_pfile; @*/
# define cppReader_reachedEOF(pfile)   (cppBuffer_reachedEOF (cppReader_getBufferSafe (pfile)))

/*@function static int cppReader_peekC (cppReader *) modifies nothing;@*/
# define cppReader_peekC(pfile)  (cpplib_bufPeek (cppReader_getBufferSafe (pfile)))

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

/*@notfunction@*/
# define NEWLINE_FIX \
   do { \
     while (cppReader_peekC (pfile) == '\\' && cpp_peekN (pfile, 1) == '\n') \
       { \
         cppReader_forward (pfile, 2); \
       } \
   } while(FALSE)

/* Same, but assume we've already read the potential '\\' into C.  */

/*@notfunction@*/
# define NEWLINE_FIX1(C) \
    do { \
     while ((C) == '\\' && cppReader_peekC (pfile) == '\n') \
      { \
        cppReader_forward (pfile, 1); \
        (C) = cppReader_getC (pfile); \
      } \
    } while(FALSE)

static void parseSetMark (/*@out@*/ struct parse_marker *,
			  cppReader *);
static void parseClearMark (struct parse_marker *);
static void parseGotoMark (struct parse_marker *, cppReader *);
static void parseMoveMark (struct parse_marker *, cppReader *);

/* If we have a huge buffer, may need to cache more recent counts */
static /*@exposed@*/ char *cppLineBase (/*@sef@*/ cppBuffer *);

static /*@exposed@*/ /*@null@*/ cppBuffer *
   cppReader_pushBuffer (cppReader *p_pfile,
			 /*@owned@*/ /*@null@*/ char *, size_t)
     /*@modifies p_pfile@*/ ;

static void cppReader_appendIncludeChain (cppReader *p_pfile,
                                          /*@keep@*/ struct file_name_list *p_first,
                                          /*@dependent@*/ struct file_name_list *p_last,
                                          bool p_system);

static void cppReader_macroCleanup (cppBuffer *p_pbuf, cppReader *p_pfile);
static enum cpp_token cppReader_nullUnderflow (/*@unused@*/ cppReader *p_pfile);

static void cppReader_nullCleanup (/*@unused@*/ cppBuffer *p_pbuf,
				   /*@unused@*/ cppReader *p_pfile);

static void cppReader_fileCleanup (cppBuffer *p_pbuf,
				   /*@unused@*/ cppReader *p_pfile);

static int cppReader_handleDirective (cppReader *p_pfile);

static void cppReader_scanBuffer (cppReader *p_pfile);

/*
** cppBuffer_isMacro is true if the buffer contains macro expansion.
** (Note that it is false while we're expanding marco *arguments*.)
*/

static bool cppBuffer_isMacro (/*@null@*/ cppBuffer *) /*@*/ ;

static void initialize_builtins (cppReader *p_pfile)
     /*@modifies p_pfile@*/ ;

static void initialize_char_syntax (struct cppOptions *p_opts) ;

static int /*@alt void@*/ finclude (cppReader *p_pfile, int p_f,
				    cstring p_fname,
				    bool p_system_header_p,
				    /*@dependent@*/ /*@null@*/ struct file_name_list *p_dirptr);

static void validate_else (cppReader *p_pfile, cstring p_directive);
  
static void conditional_skip (cppReader *p_pfile, int p_skip,
			      enum node_type p_type,
			      /*@dependent@*/ /*@null@*/ char *p_control_macro);

static HOST_WIDE_INT eval_if_expression (cppReader *p_pfile);

static void skip_if_group (cppReader *p_pfile, bool p_any);

static bool comp_def_part (bool p_first, char *p_beg1, int p_len1,
			   char *p_beg2, int p_len2, bool p_last);

static bool redundant_include_p (cppReader *p_pfile, /*@null@*/ cstring p_name);
static bool is_system_include (cppReader *p_pfile, cstring p_filename);

static /*@observer@*/ /*@null@*/ struct file_name_map *
read_name_map (cppReader *p_pfile, cstring p_dirname);

static cstring read_filename_string (int p_ch, /*:open:*/ FILE *p_f);

static int open_include_file (cppReader *p_pfile,
			      /*@owned@*/ cstring p_fname,
			      /*@null@*/ struct file_name_list *p_searchptr);

static void push_macro_expansion (cppReader *,
				  /*@owned@*/ char *, size_t,
				  /*@dependent@*/ hashNode);

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

struct directive;
typedef void (*handleDirective) (cppReader *, struct directive *, const char *, const char *);

/* `struct directive' defines one #-directive, including how to handle it.  */

struct directive {
  int length;			/* Length of name */
  /*@null@*/ handleDirective func; /* Function to handle directive */
  const /*@observer@*/ char *name; /* Name of directive */
  enum node_type type;		/* Code which describes which directive.  */
  bool command_reads_line;      /* True if rest of line is read by func.  */
  bool traditional_comments;	/* True: keep comments if -traditional.  */
  bool pass_thru;		/* Copy preprocessed directive to output file.*/
};

static void do_define (cppReader *, /*@null@*/ struct directive *, 
		       const /*@exposed@*/ char *, const char *);
static void do_defineAux (cppReader *, /*@null@*/ struct directive *,
			  const /*@exposed@*/ char *, const char *, bool);
     
static void do_line (cppReader *, /*@null@*/ struct directive *, const char *, const char *);
static void do_include (cppReader *, struct directive *, const char *, const char *);
static void do_undef (cppReader *, struct directive *, const char *, const char *);
static void do_error (cppReader *, struct directive *, const char *, const char *);
static void do_pragma (cppReader *, struct directive *, const char *, const char *);
static void do_ident (cppReader *, struct directive *, const char *, const char *);
static void do_if (cppReader *, struct directive *, const char *, const char *);
static void do_xifdef (cppReader *, struct directive *, const char *, const char *);
static void do_else (cppReader *, struct directive *, const char *, const char *);
static void do_elif (cppReader *, struct directive *, const char *, const char *);
static void do_endif (cppReader *, struct directive *, const char *, const char *);
static void do_warning (cppReader *, struct directive *, const char *, const char *);

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

/*@constant observer struct file_name_list *SELF_DIR_DUMMY@*/
#define SELF_DIR_DUMMY ((struct 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.  */

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

/*@access cstring@*/

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

static cstring searchPath_unparse (struct file_name_list *search_start) 
{
  cstring res = cstring_newEmpty ();
  struct file_name_list *searchptr;

  for (searchptr = search_start; searchptr != NULL;
       searchptr = searchptr->next)
    {
      if (!cstring_isEmpty (searchptr->fname))
        {
          res = cstring_concatFree1 (res, searchptr->fname);
          if (searchptr->next != NULL) {
            res = cstring_appendChar (res, PATH_SEPARATOR);
          }
        }
    }

  return res;
}

/*@+charint@*/
static void
initialize_char_syntax (struct cppOptions *opts)
{
  char 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 that
   * refer to them.
   */

  for (i = 'a'; i <= 'z'; i++) {
    is_idchar[i - 'a' + 'A'] = TRUE;
    is_idchar[(int) i] = TRUE;
    is_idstart[i - 'a' + 'A'] = TRUE;
    is_idstart[(int) i] = TRUE;
  }

  for (i = '0'; i <= '9'; i++)
    {
      is_idchar[(int) i] = TRUE;
    }

  is_idchar['_'] = TRUE;
  is_idstart['_'] = TRUE;
  is_idchar['$'] = opts->dollars_in_ident;
  is_idstart['$'] = opts->dollars_in_ident;

  /* horizontal space table */
  is_hor_space[' '] = TRUE;
  is_hor_space['\t'] = TRUE;
  is_hor_space['\v'] = TRUE;
  is_hor_space['\f'] = TRUE;
  is_hor_space['\r'] = TRUE;

  is_space[' '] = TRUE;
  is_space['\t'] = TRUE;
  is_space['\v'] = TRUE;
  is_space['\f'] = TRUE;
  is_space['\n'] = TRUE;
  is_space['\r'] = TRUE;
}

/* Place into P_PFILE a quoted string representing the string SRC.
   Caller must reserve enough space in pfile->token_buffer.  */

static void
quote_string (cppReader *pfile, const char *src)
{
  cppReader_putCharQ (pfile, '\"');
  for (;;)
    {
      char c = *src++;
  
      switch (c)
	{
	default:
	  if (isprint (c))
	    cppReader_putCharQ (pfile, c);
	  else
	    {
	      sprintf (cpplib_getPWritten (pfile), "\\%03o",
		       (unsigned int) c);
	      cppReader_adjustWritten (pfile, (size_t) 4);
	    }
	  /*@switchbreak@*/ break;

	case '\"':
	case '\\':
	  cppReader_putCharQ (pfile, '\\');
	  cppReader_putCharQ (pfile, c);
	  /*@switchbreak@*/ break;

	case '\0':
	  cppReader_putCharQ (pfile, '\"');
	  cppReader_nullTerminateQ (pfile);
	  return;
	}
    }
}

/* Re-allocates PFILE->token_buffer so it will hold at least N more chars.  */

static void
cppReader_growBuffer (cppReader *pfile, size_t n)
{
  size_t old_written = cpplib_getWritten (pfile);
  pfile->token_buffer_size = n + 2 * pfile->token_buffer_size;
  pfile->token_buffer = (char *)
    drealloc (pfile->token_buffer, pfile->token_buffer_size);
  cppReader_setWritten (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.
 */

static void
cppReader_defineReal (cppReader *pfile, const char *str)
{
  char *buf = NULL;
  const char *p;
  size_t idlen;

  DPRINTF (("Cpp reader define: %s", cstring_fromChars (str)));

  idlen = identifier_length (str);
  if (idlen == 0)
    {
      cppReader_error (pfile,
                       message ("Malformed option `-D%s'",
                                cstring_fromChars (str)));
      return;
    }
  p = str + idlen;

  if (*p == '(') {
    p++;
    while (*p != ')' && *p != '\0') {
      p++;
    }

    if (*p == ')') {
      p++;
    } else {
      cppReader_error 
	(pfile,
	 message ("Malformed option: -D%s (no closing parenthesis)", 
		  cstring_fromChars (str)));
    }
  }

  DPRINTF (("Here 2"));

  if (*p == '\0')
    {
      buf = (char *) dmalloc (size_fromInt (p - str + 4));
      strcpy ((char *) buf, str);
      strcat ((char *) buf, " 1");
    }
  else if (*p != '=')
    {
      DPRINTF (("ERROR 2"));
      cppReader_error (pfile,
		       message ("Malformed option: -D%s (expected '=', found '%c')",
				cstring_fromChars (str),
				*p));
      return;
    }
  else
    {
      char *q;
      /* Copy the entire option so we can modify it.  */
      DPRINTF (("Copying..."));
      buf = (char *) dmalloc (2 * strlen (str) + 1);
      strncpy (buf, str, size_fromInt (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 != '\0')
	{
	  if (*p == '\\' && p[1] == '\n')
	    p += 2;
	  else
	    *q++ = *p++;
	}

      DPRINTF (("Here we are..."));
      *q = '\0';
    }

  llassert (buf != NULL);
  DPRINTF (("Do define: %s / %ld", buf, size_toLong (strlen (buf))));
  (void) cpp_shouldCheckMacroAux (buf);
  do_define (pfile, NULL, buf, buf + strlen (buf));
  sfree (buf);
}

static void
cppReader_define (cppReader *pfile, const char *str)
{
  cppBuffer *tbuf = CPPBUFFER (pfile);

  CPPBUFFER (pfile) = NULL;
  cppReader_defineReal (pfile, str);
  CPPBUFFER (pfile) = tbuf;
}
  
/* Append a chain of `struct file_name_list's
   to the end of the main include chain.
   FIRST is the beginning of the chain to append, and LAST is the end.  */

static void
cppReader_appendIncludeChain (cppReader *pfile,
		      struct file_name_list *first,
		      struct file_name_list *last,
                      bool system)
{
  struct cppOptions *opts = CPPOPTIONS (pfile);

  if (first == NULL || last == NULL)
    {
      return;
    }

  if (opts->include == NULL)
    {
      opts->include = first;
    }
  else
    {
      llassert (opts->last_include->next == NULL);
      opts->last_include->next = first;
    }

  if (opts->first_bracket_include == NULL)
    {
      opts->first_bracket_include = first;
    }

  if (system)
    {
      /*@-usereleased@*/ /* Spurious warnings for opts->first_system_include */
      if (opts->first_system_include == NULL)
        {
          opts->first_system_include = first;
        }
      /*@=usereleased@*/
    }

  llassert (last->next == NULL);
  opts->last_include = last;
}
  
static void
cppReader_appendDirIncludeChain (cppReader *pfile, char* fname, bool system)
{
  struct file_name_list *dirtmp;

  DPRINTF (("Adding include dir: %s", fname));

  dirtmp = (struct file_name_list *) dmalloc (sizeof (*dirtmp));
  llassert (dirtmp != NULL);

  /* create a chain of only one element */
  dirtmp->fname = mstring_copy (fname);
  dirtmp->next = NULL;
  dirtmp->control_macro = NULL;
  dirtmp->name_map = NULL;
  dirtmp->got_name_map = FALSE;

  cppReader_appendIncludeChain (pfile, dirtmp, dirtmp, system);
}

static void
pathList_to_fileNameList (const char *pathList,
    struct file_name_list **pfirst, struct file_name_list **plast)
{
  struct file_name_list *first = NULL, *last = NULL;

  llassert (pfirst != NULL);
  llassert (plast != NULL);

  if (pathList != NULL && *pathList != '\0')
    {
      const char *p, *q;
      
#ifdef __CYGWIN32__
      char *win32temp;

      /* if we have a posix path list, convert to win32 path list */
      win32temp = (char *) dmalloc /*@i4@*/
        (cygwin32_posix_to_win32_path_list_buf_size (pathList));
      cygwin32_posix_to_win32_path_list (pathList, win32temp);
      p = win32temp;
#else
      p = pathList;
#endif

      do
        {
          q = p;

          /* Find the end of this name.  */
          while (*q != '\0' && *q != PATH_SEPARATOR)
            {
              q++;
            }

          {
            char *name;
            struct file_name_list *tmp;

            if (q == p)
            {
              /* An empty name in the path stands for the current directory.  */
              name = (char *) dmalloc ((size_t) 2);
              name[0] = '.';
              name[1] = '\0';
            }
            else
            {
              /* Otherwise use the directory that is named.  */
              size_t len = size_fromInt (q-p);
              name = (char *) dmalloc (len + 1);
              memcpy (name, p, len);
              name[len] = '\0';
            }

            tmp = (struct file_name_list *) dmalloc (sizeof (*tmp));
            llassert (tmp != NULL);

            tmp->next = NULL;		/* New one goes on the end */
            tmp->fname = cstring_fromChars (name);
            tmp->control_macro = NULL;
            tmp->name_map = NULL;
            tmp->got_name_map = FALSE;

            if (last == NULL) /* initialize first & last */
            {
              llassert (first == NULL);
              last = first = tmp;
            }
            else
            {
              llassert (first != NULL);

              last->next = tmp;
              last = tmp;
            }
          }

          p = q+1; /* Advance past this name.  */
        }
      while (*q != '\0');

#ifdef __CYGWIN32__
      sfree (win32temp);
#endif
    }

  *pfirst = first;
  *plast = last;
}

static void
cppReader_appendPathListIncludeChain (cppReader *pfile,
    const char *pathList, bool system)
{
  struct file_name_list *first, *last;
  pathList_to_fileNameList (pathList, &first, &last);
  cppReader_appendIncludeChain (pfile, first, last, system);
}

static void
cppOptions_init (/*@out@*/ cppOptions *opts)
{
  memset ((char *) opts, 0, sizeof *opts);
  assertSet (opts);

  opts->in_fname = NULL;
  opts->out_fname = NULL;

  /* Initialize is_idchar to allow $.  */
  opts->dollars_in_ident = TRUE;

  opts->no_line_commands = FALSE;
  opts->no_trigraphs = TRUE;
  opts->put_out_comments = TRUE;
  opts->dump_macros = DUMP_DEFINITIONS; /* DUMP_NONE; */
  opts->no_output = FALSE;

  opts->cplusplus_comments = TRUE;
  opts->verbose = FALSE;
  opts->lang_asm = FALSE;
  opts->pedantic_errors = FALSE;
  opts->warn_comments = FALSE;
  opts->warnings_are_errors = FALSE;

  /* Added 2003-07-10: */
  opts->traditional = FALSE;
  opts->c89 = TRUE;
}

enum cpp_token
cppReader_nullUnderflow (/*@unused@*/ cppReader *pfile)
{
  return CPP_EOF;
}

void
cppReader_nullCleanup (/*@unused@*/ cppBuffer *pbuf,
		       /*@unused@*/ cppReader *pfile)
{
  ;
}

void
cppReader_macroCleanup (cppBuffer *pbuf, /*@unused@*/ cppReader *pfile)
{
  hashNode macro = pbuf->hnode;

  if (macro->type == T_DISABLED)
    {
      macro->type = T_MACRO;
    }

  if (macro->type != T_MACRO || pbuf->buf != macro->value.defn->expansion)
    {
      sfree (pbuf->buf);
      pbuf->buf = NULL;
    }
}

void
cppReader_fileCleanup (cppBuffer *pbuf, /*@unused@*/ cppReader *pfile)
{
  if (pbuf->buf != NULL)
    {
      sfree (pbuf->buf);
      pbuf->buf = NULL;
    }
}

/* 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 (cppReader *pfile, /*@null@*/ long *linep)
{
  llassert (pfile->buffer != NULL);
  llassert (pfile->buffer->cur != NULL);

  while (cppReader_peekC (pfile) == '\\' && cpp_peekN (pfile, 1) == '\n')
    {
      if (linep != NULL)
	{
	  (*linep)++;
	}

      cppReader_forward (pfile, 2);
    }

  if (cppReader_peekC (pfile) == '*')
    {
      int c = 0;

      cppReader_forward (pfile, 1);

      for (;;)
	{
	  int prev_c = c;
	  c = cppReader_getC (pfile);

	  if (c == EOF)
	    {
	      return EOF;
	    }

	  while (c == (int) '\\' && cppReader_peekC (pfile) == (int) '\n')
	    {
	      if (linep != NULL )
		{
		  (*linep)++;
		}

	      cppReader_forward (pfile, 1), c = cppReader_getC (pfile);
	    }

	  if (prev_c == (int) '*' && c == (int) '/')
	    {
	      return (int) ' ';
	    }

	  if (c == (int) '\n' && (linep != NULL))
	    {
	      (*linep)++;
	    }
	}
    }
  else if (cppReader_peekC (pfile) == '/' 
	   && CPPOPTIONS (pfile)->cplusplus_comments)
    {
      (void) cppoptgenerror 
	(FLG_SLASHSLASHCOMMENT,
	 message ("C++ style // comment" 
		 ),
	 pfile);
      
      cppReader_forward (pfile, 1);

      for (;;)
	{
	  int c = cppReader_getC (pfile);

	  if (c == EOF)
	    {
	      /* Allow hash comment to be terminated by EOF.  */
	      return (int) ' '; 
	    }

	  while (c == (int) '\\' && cppReader_peekC (pfile) == '\n')
	    {
	      cppReader_forward (pfile, 1);
	      c = cppReader_getC (pfile);

	      if (linep != NULL)
		{
		  (*linep)++;
		}
	    }

	  if (c == (int) '\n')
	    {
	      /* Don't consider final '\n' to be part of comment.  */
	      cppReader_forward (pfile, -1);
	      return (int) ' ';
	    }
	}
    }
  else
    {
      return (int) '/';
    }
}

/* Skip whitespace \-newline and comments.  Does not macro-expand.  */
int /*@alt void@*/
cppSkipHspace (cppReader *pfile)
{
  int nspaces = 0;

  while (TRUE)
    {
      int c;

      llassert (pfile->buffer != NULL);

      c = cppReader_peekC (pfile);

      if (c == EOF)
	{
	  return 0; /* FIXME */
	}

      if (is_hor_space[c])
	{
	  if ((c == '\f' || c == '\v') && cppReader_isPedantic (pfile))
	    cppReader_pedwarn (pfile,
			 message ("%s in preprocessing directive",
				  c == '\f'
				  ? cstring_makeLiteralTemp ("formfeed")
				  : cstring_makeLiteralTemp ("vertical tab")));

	  nspaces++;
	  cppReader_forward (pfile, 1);
	}
      else if (c == '/')
	{
	  cppReader_forward (pfile, 1);
	  c = skip_comment (pfile, NULL);

	  if (c == '/')
	    {
	      cppReader_forward (pfile, -1);
	    }

	  if (c == EOF || c == '/')
	    {
	      return nspaces;
	    }
	}
      else if (c == '\\' && cpp_peekN (pfile, 1) == '\n')
	{
	  cppReader_forward (pfile, 2);
	}
      else if (c == '@' && CPPBUFFER (pfile)->has_escapes
	       && is_hor_space [cpp_peekN (pfile, 1)])
	{
	  cppReader_forward (pfile, 2);
	}
      else
	{
	  return nspaces;
	}
    }
}

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

static void
copy_rest_of_line (cppReader *pfile)
{
  struct cppOptions *opts = CPPOPTIONS (pfile);

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

      llassert (pfile->buffer != NULL);

      c = cppReader_getC (pfile);

      switch (c)
	{
	case EOF:
	  goto end_directive;
	case '\\':
	  /*
	  ** Patch from Brian St. Pierre for handling MS-DOS files.
	  */

	  DPRINTF (("Reading directive: %d", (int) c));

	  if (cppReader_peekC (pfile) == '\n'
	      || cppReader_peekC (pfile) == '\r')
	    {
	      DPRINTF (("Reading directive..."));
	      if (cppReader_peekC (pfile) == '\r')
		{
		  DPRINTF (("Reading directive..."));
		  cppReader_forward (pfile, 1);
		}
             
	      DPRINTF (("Reading directive..."));
	      cppReader_forward (pfile, 1);
	      continue;
	    }

	  DPRINTF (("Falling..."));
	/*@fallthrough@*/ case '\'': case '\"':
	  goto scan_directive_token;

	case '/':
	  nextc = cppReader_peekC (pfile);

	  /*
	  ** was (opts->cplusplus_comments && nextc == '*')
	  ** yoikes!
	  */

	  if (nextc == '*'
	      || (opts->cplusplus_comments && nextc == '/'))
	    {
	      goto scan_directive_token;
	    }
	  /*@switchbreak@*/ break;
	case '\f':
	case '\v':
	  if (cppReader_isPedantic (pfile))
	    cppReader_pedwarn (pfile,
			 message ("%s in preprocessing directive",
				  c == '\f'
				  ? cstring_makeLiteralTemp ("formfeed")
				  : cstring_makeLiteralTemp ("vertical tab")));
	  /*@switchbreak@*/ break;

	case '\n':
	  cppReader_forward (pfile, -1);
	  goto end_directive;
	scan_directive_token:
	  cppReader_forward (pfile, -1);
	  (void) cpplib_getToken (pfile);
	  continue;
	}
      cppReader_putChar (pfile, (char) c);
    }
end_directive: ;
  cppReader_nullTerminate (pfile);
}

void
cppReader_skipRestOfLine (cppReader *pfile)
{
  size_t old = cpplib_getWritten (pfile);
  copy_rest_of_line (pfile);
  cppReader_setWritten (pfile, old);
}

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

static int
cppReader_handleDirective (cppReader *pfile)
{
  int c;
  struct directive *kt;
  int ident_length;
  size_t after_ident = 0;
  char *ident = NULL;
  char *line_end = NULL;
  size_t old_written = cpplib_getWritten (pfile);
  int nspaces = cppSkipHspace (pfile);

  c = cppReader_peekC (pfile);

  if (c >= '0' && c <= '9')
    {
      /* Handle # followed by a line number.  */
      if (cppReader_isPedantic (pfile))
	{
	  cppReader_pedwarnLit
	    (pfile,
	     cstring_makeLiteralTemp ("`#' followed by integer"));
	}

      do_line (pfile, NULL, NULL, NULL);
      return 1;
    }


  /* Now find the directive name.  */

  cppReader_putChar (pfile, '#');

  parse_name (pfile, cppReader_getC (pfile));

  llassert (pfile->token_buffer != NULL);
  ident = pfile->token_buffer + old_written + 1;

  ident_length = cpplib_getPWritten (pfile) - ident;

  if (ident_length == 0 && cppReader_peekC (pfile) == '\n')
    {
      /* A line of just `#' becomes blank.  */
      return 1; 
    }

  for (kt = directive_table; ; kt++) 
    {
      if (kt->length <= 0)
	{
	  return 0; /* goto not_a_directive; */
	}

      if (kt->length == ident_length &&
              strncmp (kt->name, ident, size_fromInt (kt->length)) == 0)
	{
	  break;
	}
    }

  if (kt->command_reads_line)
    {
      after_ident = 0;
    }
  else
    {
      /* True means do not delete comments within the directive.
	 #define needs this when -traditional.  */
      bool save_put_out_comments = CPPOPTIONS (pfile)->put_out_comments;
      CPPOPTIONS (pfile)->put_out_comments =
          /*cppReader_isTraditional (pfile) && kt->traditional_comments */ TRUE; 
      after_ident = cpplib_getWritten (pfile);
      copy_rest_of_line (pfile);
      CPPOPTIONS (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 = cpplib_getPWritten (pfile);


  if (!kt->pass_thru && kt->type != T_DEFINE)
    {
      cppReader_setWritten (pfile, old_written);
    }

  llassert (pfile->token_buffer != NULL);

  /* was kt->pass_thru || */

  if (kt->type == T_DEFINE
      && cpp_shouldCheckMacro (pfile, pfile->token_buffer + old_written))
    {
      char *p = pfile->token_buffer + old_written;

      /*
      ** Still need to record value for preprocessing, so 
      ** #ifdef's, etc. using the value behave correctly.
      */
      
      do_defineAux (pfile, kt, 
		    pfile->token_buffer + after_ident,
		    line_end,
		    FALSE);

      /*
      ** Transform "[#{hor_space}*]define" to "[ ]@QLMR(x)", where x
      ** is the minimum between 9 and nspaces.
      */
      
      if (*p == '#')
	{
	  *p = ' ';
	}

      SKIP_WHITE_SPACE (p);

      llassert (constlen (LLMRCODE) + 1 == size_fromInt (kt->length));
      strcpy (p, LLMRCODE);
      p += constlen (LLMRCODE);

      /*
      ** This is way-bogus.  We use the last char to record the number of
      ** spaces.  Its too hard to get them back into the input stream.
      */

      if (nspaces > 9) nspaces = 9;

      *p++ = '0' + nspaces;

      return 0; /* not_a_directive */
    }
  else if (kt->pass_thru)
    {
      /* Just leave the entire #define in the output stack.  */
      return 0; /* not_a_directive */
    }
  else if (kt->type == T_DEFINE
	   && CPPOPTIONS (pfile)->dump_macros == DUMP_NAMES)
    {
      char *p = pfile->token_buffer + old_written + 1 + kt->length; /* Skip "#define". */
      SKIP_WHITE_SPACE (p);
      SKIP_IDENTIFIER (p);
      pfile->limit = p;
      cppReader_putChar (pfile, '\n');
    }
  else if (kt->type == T_DEFINE)
    {
      cppReader_setWritten (pfile, old_written);
    }
  else
    {
      ;
    }

  llassert (kt->func != NULL);
  (kt->func) (pfile, kt, pfile->token_buffer + after_ident, line_end);
  return 1;
}

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

  cpplib_reserve (pfile,
		     size_fromInt (2 + keyword_length + (limit - buf)));
  cppReader_putCharQ (pfile, '#');
  /*@-observertrans@*/
  cppReader_putStrN (pfile, keyword->name, size_fromInt (keyword_length));
  /*:=observertrans@*/

  if (limit != buf && buf[0] != ' ')
    {
      /* Was a bug, since reserve only used 1 + ... */
      cppReader_putCharQ (pfile, ' ');
    }

  cppReader_putStrN (pfile, buf, size_fromInt (limit - buf));
}

/* The arglist structure is built by create_definition to tell
   collect_expansion 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_expansion 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 {
  /*@null@*/ struct arglist *next;
  const /*@dependent@*/ char *name;
  size_t length;
  int argno;
  bool rest_args;
};


/* Read a replacement list for a macro with parameters.
   Build the DEFINITION structure.
   Reads characters of text starting at BUF until LIMIT.
   ARGLIST specifies the formal parameters to look for
   in the text of the definition; NARGS is the number of args
   in that list, or -1 for a macro name that wants no argument list.
   MACRONAME is the macro name itself (so we can avoid recursive expansion)
   and NAMELEN is its length in characters.

   Note that comments, backslash-newlines, and leading white space
   have already been deleted from the argument.  */

static DEFINITION *
collect_expansion (cppReader *pfile, const char *buf, const char *limit,
		   int nargs, /*@null@*/ struct arglist *arglist)
{
  DEFINITION *defn;
  const char *p, *lastp;
  char *exp_p;
  struct reflist *endpat = NULL;
  /* Pointer to first nonspace after last ## seen.  */
  const char *concat = NULL;
  /* Pointer to first nonspace after last single-# seen.  */
  const char *stringify = NULL;
  size_t maxsize;
  char expected_delimiter = '\0';

  llassert (buf <= limit);

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

  /* Find the beginning of the trailing whitespace.  */
  while (buf < limit && is_space[(int) 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 (*defn) + (limit - buf) + 5);

  /* Occurrences of '@' get doubled, so allocate extra space for them.  */
  p = buf;
  while (p < limit)
    {
      if (*p++ == '@')
	{
	  maxsize++;
	}
    }

  defn = (DEFINITION *) dmalloc (maxsize);
  defn->expand = TRUE;
  defn->file = NULL;
  defn->pattern = NULL;
  defn->nargs = nargs;
  defn->predefined = NULL;

  exp_p = defn->expansion = (char *) defn + sizeof (*defn);
  *defn->expansion = '\0'; /* convince splint it is initialized */

  defn->line = 0;
  defn->rest_args = FALSE;
  defn->args.argnames = NULL;

  lastp = exp_p;

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

  p = buf;
  if (limit - p >= 2 && p[0] == '#' && p[1] == '#') {
    cppReader_errorLit (pfile,
			cstring_makeLiteralTemp ("`##' at start of macro definition"));
    p += 2;
  }

  /* Process the main body of the definition.  */
  while (p < limit) {
    char c = *p++;

    *exp_p++ = c;

    if (!cppReader_isTraditional (pfile)) {
      switch (c) {
      case '\'':
      case '\"':
	if (expected_delimiter != '\0')
	  {
	    if (c == expected_delimiter)
	      expected_delimiter = '\0';
	  }
	else
	  {
	    expected_delimiter = c;
	  }
	/*@switchbreak@*/ break;

      case '\\':
	if (p < limit && (expected_delimiter != '\0'))
	  {
	    /* In a string, backslash goes through
	       and makes next char ordinary.  */
	    *exp_p++ = *p++;
	  }
	/*@switchbreak@*/ break;

      case '@':
	/* An '@' in a string or character constant stands for itself,
	   and does not need to be escaped.  */
	if (expected_delimiter == '\0')
	  {
	    *exp_p++ = c;
	  }

	/*@switchbreak@*/ break;

      case '#':
	/* # is ordinary inside a string.  */
	if (expected_delimiter != '\0')
	  {
	    /*@switchbreak@*/ break;
	  }

	if (p < limit && *p == '#') {
	  /* ##: concatenate preceding and following tokens.  */
	  /* Take out the first #, discard preceding whitespace.  */
	  exp_p--;

	  /*@-usedef@*/
	  while (exp_p > lastp && is_hor_space[(int) exp_p[-1]])
	    {
	      --exp_p;
	    }
	  /*@=usedef@*/

	  /* Skip the second #.  */
	  p++;
	  /* Discard following whitespace.  */
	  SKIP_WHITE_SPACE (p);
	  concat = p;
	  if (p == limit)
	    {
	      cppReader_errorLit (pfile,
			    cstring_makeLiteralTemp ("`##' 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 || !identifier_seems_valid (p))
	    cppReader_errorLit (pfile,
			  cstring_makeLiteralTemp ("`#' operator is not followed by a macro argument name"));
	  else
	    stringify = p;
	} else {
	  ; /* BADBRANCH; */
	}

	/*@switchbreak@*/ break;
      }
    } else {
      /* In -traditional mode, recognize arguments inside strings and
	 and character constants, and ignore special properties of #.
	 Arguments inside strings are considered "stringified", but no
	 extra quote marks are supplied.  */
      switch (c) {
      case '\'':
      case '\"':
	if (expected_delimiter != '\0') {
	  if (c == expected_delimiter)
	    expected_delimiter = '\0';
	} else
	  expected_delimiter = c;
	/*@switchbreak@*/ break;

      case '\\':
	/* Backslash quotes delimiters and itself, but not macro args.  */
	if (expected_delimiter != '\0' && p < limit
	    && (*p == expected_delimiter || *p == '\\')) {
	  *exp_p++ = *p++;
	  continue;
	}
	/*@switchbreak@*/ break;

      case '/':
	if (expected_delimiter != '\0') /* No comments inside strings.  */
	  /*@switchbreak@*/ break;
	if (*p == '*') {
	  /* If we find a comment that wasn't removed by cppReader_handleDirective,
	     this must be -traditional.  So replace the comment with
	     nothing at all.  */
	  exp_p--;
	  p += 1;
	  while (p < limit && !(p[-2] == '*' && p[-1] == '/'))
	    {
	      p++;
	    }
	}
	/*@switchbreak@*/ break;
      }
    }

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

      --exp_p;
      while (p != limit && is_idchar[(int) *p])
	{
	  p++;
	}

      id_len = size_fromInt (p - id_beg);

      if (id_len == 0 || identifier_seems_valid (id_beg)) {
	struct arglist *arg;

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

	  if (arg->name[0] == c
	      && arg->length == id_len
	      && strncmp (arg->name, id_beg, id_len) == 0) {
	    if (expected_delimiter && CPPOPTIONS (pfile)->warn_stringify) {
	      if (cppReader_isTraditional (pfile)) {
		cppReader_warning (pfile,
				   message ("macro argument `%x' is stringified.",
					    cstring_prefix (cstring_fromChars (arg->name), id_len)));
	      } else {
		cppReader_warning (pfile,
				   message ("macro arg `%x' would be stringified with -traditional.",
					    cstring_prefix (cstring_fromChars (arg->name), id_len)));
	      }
	    }
	    /* If ANSI, don't actually substitute inside a string.  */
	    if (!cppReader_isTraditional (pfile) && expected_delimiter)
	      /*@innerbreak@*/ break;
	    /* make a pat node for this arg and append it to the end of
	       the pat list */
	    tpat = (struct reflist *) dmalloc (sizeof (*tpat));
	    tpat->next = NULL;
	    tpat->raw_before = (concat == id_beg);
	    tpat->raw_after = FALSE;
	    tpat->rest_args = arg->rest_args;
	    tpat->stringify = (cppReader_isTraditional (pfile)
			       ? expected_delimiter != '\0'
			       : stringify == id_beg);

	    if (endpat == NULL)
	      {
		defn->pattern = tpat;
	      }
	    else
	      {
		endpat->next = tpat;
		/*@-branchstate@*/
	      } /*@=branchstate@*/ /* evs 2000 was =branchstate */

	    endpat = tpat;

	    tpat->argno = arg->argno;
	    tpat->nchars = exp_p - lastp;

            {
              const char *p1 = p;

              SKIP_WHITE_SPACE (p1);

              if (p1 + 2 <= limit && p1[0] == '#' && p1[1] == '#')
                {
                  tpat->raw_after = TRUE;
                }
            }

	    lastp = exp_p;	/* place to start copying from next time */
	    skipped_arg = TRUE;

	    /*@innerbreak@*/ break;
	  }
	}
      }

      /* If this was not a macro arg, copy it into the expansion.  */
      if (!skipped_arg) {
	const char *lim1 = p;
	p = id_beg;

	while (p != lim1)
	  {
	    *exp_p++ = *p++;
	  }

	if (stringify == id_beg)
	  cppReader_errorLit (pfile,
			      cstring_makeLiteralTemp ("`#' operator should be followed by a macro argument name"));
      }
    }
  }

  if (!cppReader_isTraditional (pfile) && 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 = size_fromInt (exp_p - defn->expansion);

  /* Crash now if we overrun the allocated size.  */
  if (defn->length + 1 > maxsize)
    {
      llfatalbug (cstring_makeLiteral ("Maximum definition size exceeded."));
    }

  return defn; /* Spurious warning here */
}

# if 0 /* FIXME */
/*
** evans 2001-12-31
** Gasp...cut-and-pasted from above to deal with pfile (should replace throughout with this...)
*/

static DEFINITION *
collect_expansionLoc (fileloc loc, const char *buf, const char *limit,
		      int nargs, /*@null@*/ struct arglist *arglist)
{
  DEFINITION *defn;
  const char *p, *lastp;
  char *exp_p;
  struct reflist *endpat = NULL;
  /* Pointer to first nonspace after last ## seen.  */
  const char *concat = NULL;
  /* Pointer to first nonspace after last single-# seen.  */
  const char *stringify = NULL;
  size_t maxsize;
  char expected_delimiter = '\0';

  llassert (buf <= limit);

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

  /* Find the beginning of the trailing whitespace.  */
  while (buf < limit && is_space[(int) 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 (*defn) + (limit - buf) + 5);

  /* Occurrences of '@' get doubled, so allocate extra space for them.  */
  p = buf;
  while (p < limit)
    {
      if (*p++ == '@')
	{
	  maxsize++;
	}
    }

  defn = (DEFINITION *) dmalloc (maxsize);
  defn->expand = TRUE;
  defn->file = NULL;
  defn->pattern = NULL;
  defn->nargs = nargs;
  defn->predefined = NULL;
  exp_p = defn->expansion = (char *) defn + sizeof (*defn);

  defn->line = 0;
  defn->rest_args = FALSE;
  defn->args.argnames = NULL;

  lastp = exp_p;

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

  p = buf;
  if (limit - p >= 2 && p[0] == '#' && p[1] == '#') {
    voptgenerror (FLG_PREPROC,
		  cstring_makeLiteral ("Paste marker ## at start of macro definition"),
		  loc);
    p += 2;
  }

  /* Process the main body of the definition.  */
  while (p < limit) {
    char c = *p++;

    *exp_p++ = c;

    if (/* !cppReader_isTraditional (pfile) */ TRUE) { 
      switch (c) {
      case '\'':
      case '\"':
	if (expected_delimiter != '\0')
	  {
	    if (c == expected_delimiter)
	      expected_delimiter = '\0';
	  }
	else
	  {
	    expected_delimiter = c;
	  }
	/*@switchbreak@*/ break;

      case '\\':
	if (p < limit && (expected_delimiter != '\0'))
	  {
	    /* In a string, backslash goes through
	       and makes next char ordinary.  */
	    *exp_p++ = *p++;
	  }
	/*@switchbreak@*/ break;

      case '@':
	/* An '@' in a string or character constant stands for itself,
	   and does not need to be escaped.  */
	if (expected_delimiter == '\0')
	  {
	    *exp_p++ = c;
	  }

	/*@switchbreak@*/ break;

      case '#':
	/* # is ordinary inside a string.  */
	if (expected_delimiter != '\0')
	  {
	    /*@switchbreak@*/ break;
	  }

	if (p < limit && *p == '#') {
	  /* ##: concatenate preceding and following tokens.  */
	  /* Take out the first #, discard preceding whitespace.  */
	  exp_p--;

	  /*@-usedef@*/
	  while (exp_p > lastp && is_hor_space[(int) exp_p[-1]])
	    {
	      --exp_p;
	    }
	  /*@=usedef@*/

	  /* Skip the second #.  */
	  p++;
	  /* Discard following whitespace.  */
	  SKIP_WHITE_SPACE (p);
	  concat = p;
	  if (p == limit)
	    {
		voptgenerror (FLG_PREPROC,
			      cstring_makeLiteral ("`##' at end of macro definition"),
			      loc);
	    }
	} 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 || !identifier_seems_valid (p))
	    {
		voptgenerror 
		  (FLG_PREPROC,
		   cstring_makeLiteral ("`#' operator is not followed by a macro argument name"),
		   loc);
	    }
	  else
	    stringify = p;
	} else {
	  ; /* BADBRANCH; */
	}

	/*@switchbreak@*/ break;
      }
    } else {
      /* In -traditional mode, recognize arguments inside strings and
	 and character constants, and ignore special properties of #.
	 Arguments inside strings are considered "stringified", but no
	 extra quote marks are supplied.  */
      switch (c) {
      case '\'':
      case '\"':
	if (expected_delimiter != '\0') {
	  if (c == expected_delimiter)
	    expected_delimiter = '\0';
	} else
	  expected_delimiter = c;
	/*@switchbreak@*/ break;

      case '\\':
	/* Backslash quotes delimiters and itself, but not macro args.  */
	if (expected_delimiter != '\0' && p < limit
	    && (*p == expected_delimiter || *p == '\\')) {
	  *exp_p++ = *p++;
	  continue;
	}
	/*@switchbreak@*/ break;
  
      case '/':
	if (expected_delimiter != '\0') /* No comments inside strings.  */
	  /*@switchbreak@*/ break;
	if (*p == '*') {
	  /* If we find a comment that wasn't removed by cppReader_handleDirective,
	     this must be -traditional.  So replace the comment with
	     nothing at all.  */
	  exp_p--;
	  p += 1;
	  while (p < limit && !(p[-2] == '*' && p[-1] == '/'))
	    {
	      p++;
	    }
	}
	/*@switchbreak@*/ break;
      }
    }

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

      --exp_p;
      while (p != limit && is_idchar[(int) *p])
	{
	  p++;
	}

      id_len = size_fromInt (p - id_beg);

      if (id_len == 0 || identifier_seems_valid (id_beg)) {
	struct arglist *arg;

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

	  if (arg->name[0] == c
	      && arg->length == id_len
	      && strncmp (arg->name, id_beg, id_len) == 0) {
	    if (expected_delimiter /* && CPPOPTIONS (pfile)->warn_stringify */) { 
	      if (/* cppReader_isTraditional (pfile) */ FALSE) {
		voptgenerror (FLG_PREPROC,
			      message ("macro argument `%x' is stringified.",
				       cstring_prefix (cstring_fromChars (arg->name), id_len)),
			      loc);

	      } else {
		voptgenerror (FLG_PREPROC,
			      message ("Macro arg `%x' would be stringified with -traditional.",
				       cstring_prefix (cstring_fromChars (arg->name), id_len)),
			      loc);
		
	      }
	    }
	    /* If ANSI, don't actually substitute inside a string.  */
	    if (/* !cppReader_isTraditional (pfile) && */ expected_delimiter)
	      /*@innerbreak@*/ break;
	    /* make a pat node for this arg and append it to the end of
	       the pat list */
	    tpat = (struct reflist *) dmalloc (sizeof (*tpat));
	    tpat->next = NULL;
	    tpat->raw_before = (concat == id_beg);
	    tpat->raw_after = FALSE;
	    tpat->rest_args = arg->rest_args;
	    tpat->stringify = (FALSE /* cppReader_isTraditional (pfile) */
			       ? expected_delimiter != '\0'
			       : stringify == id_beg);

	    if (endpat == NULL)
	      {
		defn->pattern = tpat;
	      }
	    else
	      {
		endpat->next = tpat;
		/*@-branchstate@*/
	      } /*@=branchstate@*/ /* evs 2000 was =branchstate */

	    endpat = tpat;

	    tpat->argno = arg->argno;
	    tpat->nchars = exp_p - lastp;

            {
              const char *p1 = p;

              SKIP_WHITE_SPACE (p1);

              if (p1 + 2 <= limit && p1[0] == '#' && p1[1] == '#')
                {
                  tpat->raw_after = TRUE;
                }
            }

	    lastp = exp_p;	/* place to start copying from next time */
	    skipped_arg = TRUE;

	    /*@innerbreak@*/ break;
	  }
	}
      }

      /* If this was not a macro arg, copy it into the expansion.  */
      if (!skipped_arg) {
	const char *lim1 = p;
	p = id_beg;

	while (p != lim1)
	  {
	    *exp_p++ = *p++;
	  }

	if (stringify == id_beg)
	  {
	    voptgenerror
	      (FLG_PREPROC,
	       cstring_makeLiteral ("# operator should be followed by a macro argument name"),
	       loc);
	  }
      }
    }
  }

  if (/*!cppReader_isTraditional (pfile) && */ 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 = size_fromInt (exp_p - defn->expansion);

  /* Crash now if we overrun the allocated size.  */
  if (defn->length + 1 > maxsize)
    {
      llfatalbug (cstring_makeLiteral ("Maximum definition size exceeded."));
    }
  
  /*@-compdef@*/ /* defn->expansion defined? */
  return defn; 
  /*@=compdef@*/
}
# endif

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

/*@-readonlytrans@*/
static const char rest_extension[] = "...";
/*:=readonlytrans@*/

/*@notfunction@*/
#define REST_EXTENSION_LENGTH	(constlen (rest_extension))

/*@-readonlytrans@*/
static const char rest_name[] = "__VA_ARGS__";
/*:=readonlytrans@*/

/*@notfunction@*/
#define REST_NAME_LENGTH	(constlen (rest_name))

/* Create a DEFINITION node from a #define directive.  Arguments are
   as for do_define.  */
  
static /*@null@*/ macroDef
create_definition (/*@exposed@*/ const char *buf, const char *limit,
		   cppReader *pfile, bool predefinition,
		   bool expand)
{
  const char *bp;		/* temp ptr into input buffer */
  const char *symname;		/* remember where symbol name starts */
  size_t sym_length;		/* and how long it is */
  int line;
  cstring file = (CPPBUFFER (pfile) != NULL)
    ? CPPBUFFER (pfile)->nominal_fname : cstring_makeLiteralTemp ("");
  DEFINITION *defn;
  macroDef mdef;
  struct arglist *arg_ptrs = NULL;

  cppBuffer_getLineAndColumn (CPPBUFFER (pfile), &line, NULL);

  bp = buf;

  SKIP_WHITE_SPACE (bp);

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

  sym_length = identifier_length (symname);
  if (!macro_name_is_valid (symname, sym_length))
    {
      cppReader_error (pfile, invalid_macro_message (symname, sym_length));
      goto nope;
    }

  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 == '(') {
    size_t arglengths = 0;	/* Accumulate lengths of arg names
                                   plus number of args.  */
    int argno = 0;
    bool rest_args = FALSE;

    bp++;			/* skip '(' */
    SKIP_WHITE_SPACE (bp);

    /* Loop over macro argument names.  */
    while (*bp != ')')
      {
	struct arglist *temp = (struct arglist *) dmalloc (sizeof (*temp));
	temp->name = bp;
	temp->next = arg_ptrs;
	temp->argno = argno++;

	arg_ptrs = temp;

	if (rest_args)
	  {
	    cppReader_pedwarn (pfile,
			 message ("another parameter follows `%s'",
				  cstring_fromChars (rest_extension)));
	  }

        temp->length = identifier_length (bp);
        bp += temp->length;

        if (temp->length == REST_NAME_LENGTH &&
            strncmp(temp->name, rest_name, REST_NAME_LENGTH) == 0)
          {
            cppReader_warning (pfile, 
                message ("ISO C99 forbids the use of %s as a macro parameter name",
                  cstring_fromChars (rest_name)));
          }

        /* do we have a "special" rest-args extension here? */
        if (limit - bp > size_toInt (REST_EXTENSION_LENGTH)
            && strncmp (rest_extension, bp, REST_EXTENSION_LENGTH) == 0)
          {
            rest_args = TRUE;
            temp->rest_args = TRUE;
            if (temp->length == 0) /* ellipsis without identifier */
              {
                temp->name = rest_name;
                temp->length = REST_NAME_LENGTH;
              }
            else
              {
                if (!context_getFlag (FLG_GNUEXTENSIONS))
                  {
                    genppllerrorhint (FLG_PREPROC,
                        cstring_makeLiteral ("Macros with named variadic parameters list are not supported by ISO C99"),
                        cstring_makeLiteral ("Use +gnuextensions to allow macros with named variadic parameters list"
                          "(and other GNU language extensions) without this warning"));
                  }
              }
            bp += REST_EXTENSION_LENGTH;
          }
        else
          {
            temp->rest_args = FALSE;
          }

        if (temp->length == 0) /* not (valid name or ellipsis) */
	  {
	    cppReader_errorLit (pfile,
			    cstring_makeLiteralTemp ("Invalid character in macro parameter name"));
            goto nope;
	  }

	SKIP_WHITE_SPACE (bp);

	if (*bp != ',' && *bp != ')') {
	  cppReader_errorLit (pfile,
			cstring_makeLiteralTemp ("Parameter list for #define is not parseable"));
	  goto nope;
	}

	if (*bp == ',') {
	  bp++;
	  SKIP_WHITE_SPACE (bp);
          if (*bp == ')') {
            cppReader_errorLit (pfile,
                          cstring_makeLiteralTemp ("Missing parameter name in #define"));
            goto nope;
          }
	}
	{
	  struct arglist *otemp;

	  for (otemp = temp->next; otemp != NULL; otemp = otemp->next)
	    {
	      if (temp->length == otemp->length &&
		  strncmp (temp->name, otemp->name, temp->length) == 0) {
		cstring name = cstring_copyLength (temp->name, temp->length);
		cppReader_error (pfile,
			   message ("duplicate argument name `%x' in `#define'", name));
		goto nope;
	      }
	    }
	}
	arglengths += temp->length + 2;
      }
  
    ++bp;			/* skip paren */
    SKIP_WHITE_SPACE (bp);

    if (bp > limit)
      {
        cppReader_errorLit (pfile,
                      cstring_makeLiteralTemp ("Unterminated parameter list in #define"));
        goto nope;
      }

    /* 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 = (char *) dmalloc (arglengths + 1);
  
    {
      struct arglist *temp;
      size_t i = 0;
      for (temp = arg_ptrs; temp != NULL; temp = temp->next) 
	{
	  memcpy (&defn->args.argnames[i], temp->name, temp->length);
	  i += temp->length;
	  if (temp->next != 0)
	    {
	      defn->args.argnames[i++] = ',';
	      defn->args.argnames[i++] = ' ';
	    }
	}
      
      defn->args.argnames[i] = '\0';
    }
  } else { /* Simple expansion or empty definition.  */
    if (bp < limit)
      {
	if (is_hor_space[(int) *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 '~':
	    cppReader_warning (pfile,
			 message ("Missing white space after #define %x",
				  cstring_prefix (cstring_fromChars (symname),
						  sym_length)));
	    break;

	  default:
	    cppReader_pedwarn (pfile,
			 message ("Missing white space after #define %x",
				  cstring_prefix (cstring_fromChars (symname),
						  sym_length)));
	    break;
	  }
	}
      }

    if (bp > limit)
      {
        cppReader_errorLit (pfile,
            cstring_makeLiteralTemp ("Failed to process #define"));
        goto nope;
      }

    /* now everything from bp before limit is the definition.  */
    defn = collect_expansion (pfile, bp, limit, -1, NULL);
    defn->args.argnames = mstring_createEmpty ();
  }

  defn->expand = expand;
  DPRINTF (("Expand: %d", expand));

  defn->line = line;

  /* not: llassert (cstring_isUndefined (defn->file)); */
  defn->file = file;

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

  goto leave;

nope:
  mdef.defn = NULL;
  mdef.symnam = NULL;

leave:
  while (arg_ptrs != NULL)
    {
      struct arglist *temp = arg_ptrs->next;
      sfree (arg_ptrs);
      arg_ptrs = temp;
    }
  return mdef;
}

# if 0
/*@null@*/ macroDef
cpplib_createDefinition (cstring def,
			 fileloc loc,
			 bool predefinition,
			 bool expand)
{
  const char *buf = cstring_toCharsSafe (def);
  const char *limit = buf + cstring_length (def);
  const char *bp;		/* temp ptr into input buffer */
  const char *symname;		/* remember where symbol name starts */
  size_t sym_length;		/* and how long it is */
  int line = fileloc_lineno (loc);
  cstring file = fileloc_filename (loc);
  DEFINITION *defn;
  macroDef mdef;
  struct arglist *arg_ptrs = NULL;

  DPRINTF (("Creating definition: %s", buf));

  bp = buf;

  SKIP_WHITE_SPACE (bp);

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

  sym_length = identifier_length (symname);
  if (!macro_name_is_valid (symname, sym_length))
    {
      voptgenerror (FLG_PREPROC,
          invalid_macro_message (symname, sym_length), loc);
      goto nope;
    }

  DPRINTF (("length: %d", sym_length));

  bp += sym_length;

  DPRINTF (("Here: %s", bp));

  /* 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 == '(') {
    size_t arglengths = 0;	/* Accumulate lengths of arg names
                                   plus number of args.  */
    int argno = 0;
    bool rest_args = FALSE;
  
    bp++;			/* skip '(' */
    SKIP_WHITE_SPACE (bp);

    /* Loop over macro argument names.  */
    while (*bp != ')')
      {
	struct arglist *temp = (struct arglist *) dmalloc (sizeof (*temp));
	temp->name = bp;
	temp->next = arg_ptrs;
	temp->argno = argno++;

	arg_ptrs = temp;

	if (rest_args)
	  {
	    voptgenerror (FLG_PREPROC,
			  message ("Another parameter follows %s",
				   cstring_fromChars (rest_extension)),
			  loc);
	  }

        temp->length = identifier_length (bp);
        bp += temp->length;

        if (temp->length == REST_NAME_LENGTH &&
            strncmp(temp->name, rest_name, REST_NAME_LENGTH) == 0)
          {
	    voptgenerror (FLG_PREPROC,
                message ("ISO C99 forbids the use of %s as a macro parameter name",
                  cstring_fromChars (rest_name)),
                loc);
          }

        /* do we have a "special" rest-args extension here? */
        if (limit - bp > size_toInt (REST_EXTENSION_LENGTH)
            && strncmp (rest_extension, bp, REST_EXTENSION_LENGTH) == 0)
          {
            rest_args = TRUE;
            temp->rest_args = TRUE;
            if (temp->length == 0) /* ellipsis without identifier */
              {
                temp->name = rest_name;
                temp->length = REST_NAME_LENGTH;
              }
            else
              {
                if (!context_flagOn (FLG_GNUEXTENSIONS, loc))
                  {
                    llgenhinterror (FLG_PREPROC,
                        cstring_makeLiteral ("Macros with named variadic parameters list are not supported by ISO C99"),
                        cstring_makeLiteral ("Use +gnuextensions to allow macros with named variadic parameters list "
                          "(and other GNU language extensions) without this warning"),
                        loc);
                  }
              }
            bp += REST_EXTENSION_LENGTH;
          }
        else
          {
            temp->rest_args = FALSE;
          }

        if (temp->length == 0) /* not (valid name or ellipsis) */
	  {
	    voptgenerror (FLG_PREPROC,
			  message ("Invalid character in macro parameter name: %c", *bp),
			  loc);
            goto nope;
	  }

	SKIP_WHITE_SPACE (bp);
	
	if (*bp != ',' && *bp != ')') {
	  voptgenerror (FLG_PREPROC,
			cstring_makeLiteral ("Parameter list for #define is not parseable"),
			loc);
	  goto nope;
	}

	if (*bp == ',') {
	  bp++;
	  SKIP_WHITE_SPACE (bp);
          if (*bp == ')') {
            voptgenerror (FLG_PREPROC,
                          cstring_makeLiteral ("Missing parameter name in #define"),
                          loc);
            goto nope;
          }
	}
	{
	  struct arglist *otemp;

	  for (otemp = temp->next; otemp != NULL; otemp = otemp->next)
	    {
	      if (temp->length == otemp->length &&
		  strncmp (temp->name, otemp->name, temp->length) == 0) {
		cstring name = cstring_copyLength (temp->name, temp->length);

		voptgenerror (FLG_PREPROC,
			      message ("Duplicate argument name in #define: %s", name),
			      loc);
		goto nope;
	      }
	    }
	}
	arglengths += temp->length + 2;
      }
    
    ++bp;			/* skip paren */
    SKIP_WHITE_SPACE (bp);

    if (bp > limit)
      {
        voptgenerror (FLG_PREPROC,
                      cstring_makeLiteral ("Unterminated parameter list in #define"),
                      loc);
        goto nope;
      }

    /* now everything from bp before limit is the definition.  */
    defn = collect_expansionLoc (loc, 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 = (char *) dmalloc (arglengths + 1);

    {
      struct arglist *temp;
      size_t i = 0;
      for (temp = arg_ptrs; temp != NULL; temp = temp->next)
      {
	memcpy (&defn->args.argnames[i], temp->name, temp->length);
	i += temp->length;
	if (temp->next != 0)
          {
	    defn->args.argnames[i++] = ',';
	    defn->args.argnames[i++] = ' ';
	  }
      }

      defn->args.argnames[i] = '\0';
    }
  } else { /* Simple expansion or empty definition.  */
    if (bp < limit)
      {
	if (is_hor_space[(int) *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 '~':
	    voptgenerror (FLG_PREPROC,
			  message ("Missing white space after #define %x",
				   cstring_prefix (cstring_fromChars (symname),
						   sym_length)),
			  loc);
	    break;

	  default:
	    voptgenerror (FLG_PREPROC,
			  message ("Missing white space after #define %x",
				   cstring_prefix (cstring_fromChars (symname),
						   sym_length)),
			  loc);
	    break;
	  }
	}
      }

    if (bp > limit)
      {
        voptgenerror (FLG_PREPROC,
                      cstring_makeLiteral ("Failed to process #define"),
                      loc);
        goto nope;
      }

    /* now everything from bp before limit is the definition.  */
    defn = collect_expansionLoc (loc, bp, limit, -1, NULL);
    defn->args.argnames = mstring_createEmpty ();
  }

  defn->expand = expand;
  DPRINTF (("Expand: %d", expand));

  defn->line = line;

  /* not: llassert (cstring_isUndefined (defn->file)); */
  defn->file = file;

  /* OP is null if this is a predefinition */
  defn->predefined = predefinition;

  mdef.defn = defn;
  mdef.symnam = symname;
  mdef.symlen = sym_length;

  goto leave;

nope:
  mdef.defn = NULL;
  mdef.symnam = NULL;

leave:
  while (arg_ptrs != NULL)
    {
      struct arglist *temp = arg_ptrs->next;
      sfree (arg_ptrs);
      arg_ptrs = temp;
    }
  return mdef;
}
# endif

/* Yield the length of a purported macro name SYMNAME. */

static size_t
identifier_length (const char *symname)
{
  const char *p = symname;
  if (!is_idstart[(int) *symname])
    return 0;
  ++p;
  SKIP_IDENTIFIER (p);
  return size_fromInt (p - symname);
}

/* Check a purported identifier name SYMNAME. */

static bool
identifier_seems_valid (const char *symname)
{
  if (!is_idstart[(int) *symname])
    return FALSE;
  if (symname[0] == 'L' && (symname[1] == '\'' || symname[1] == '\"'))
    return FALSE;

  return TRUE;
}

/* Check a purported macro name SYMNAME. */

static bool
macro_name_is_valid (const char *symname, size_t symlen)
{
  if (symlen == 0)
    return FALSE;
  if (!identifier_seems_valid (symname))
    return FALSE;
  if (symlen == 7 && strncmp (symname, "defined", 7) == 0)
    return FALSE;
  if (symlen == REST_NAME_LENGTH && strncmp (symname, rest_name, REST_NAME_LENGTH) == 0)
    return FALSE;

  return TRUE;
}

static /*@only@*/ cstring
invalid_macro_message (const char *symname, size_t symlen)
{
  return (symname == 0) ?
          cstring_makeLiteral ("invalid macro name") :
          message ("invalid macro name `%q'", cstring_copyLength (symname, symlen));
}

/* Return zero if two DEFINITIONs are isomorphic.  */

static bool
compare_defs (DEFINITION *d1, DEFINITION *d2)
{
  struct reflist *a1, *a2;
  char *p1 = d1->expansion;
  char *p2 = d2->expansion;
  bool first = TRUE;

  if (d1->nargs != d2->nargs)
    {
      return TRUE;
    }

  llassert (d1->args.argnames != NULL);
  llassert (d2->args.argnames != NULL);

  if (strcmp ((char *)d1->args.argnames, (char *)d2->args.argnames) != 0)
    {
      return TRUE;
    }

  for (a1 = d1->pattern, a2 = d2->pattern;
       (a1 != NULL) && (a2 != NULL);
       a1 = a1->next, a2 = a2->next) {
    if (!((a1->nchars == a2->nchars
	   && (strncmp (p1, p2, size_fromInt (a1->nchars)) == 0))
	  || ! 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 TRUE;
    first = 0;
    p1 += a1->nchars;
    p2 += a2->nchars;
  }
  if (a1 != a2)
    return TRUE;

  if (comp_def_part (first, p1, size_toInt (d1->length - (p1 - d1->expansion)),
		     p2, size_toInt (d2->length - (p2 - d2->expansion)), 1))
    return TRUE;

  return FALSE;
}

/*
** Return TRUE 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 bool
comp_def_part (bool first, char *beg1, int len1, char *beg2, int len2, bool last)
{
  char *end1 = beg1 + len1;
  char *end2 = beg2 + len2;

  if (first) {
    while (beg1 != end1 && is_space[(int) *beg1]) { beg1++; }
    while (beg2 != end2 && is_space[(int) *beg2]) { beg2++; }
  }
  if (last) {
    while (beg1 != end1 && is_space[(int) end1[-1]]) { end1--; }
    while (beg2 != end2 && is_space[(int) end2[-1]]) { end2--; }
  }
  while (beg1 != end1 && beg2 != end2) {
    if (is_space[(int) *beg1] && is_space[(int) *beg2]) {
      while (beg1 != end1 && is_space[(int) *beg1]) { beg1++; }
      while (beg2 != end2 && is_space[(int) *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 void
do_defineAux (cppReader *pfile, struct directive *keyword,
	      const /*@exposed@*/ char *buf, const char *limit, bool expand)
{
  int hashcode;
  macroDef mdef;
  hashNode hp;

  mdef = create_definition (buf, limit, pfile, keyword == NULL, expand);

  if (mdef.defn == NULL)
    return;

  hashcode = cpphash_hashCode (mdef.symnam, mdef.symlen, CPP_HASHSIZE);

  if ((hp = cpphash_lookup (mdef.symnam, mdef.symlen, hashcode)) != NULL)
    {
      bool ok;

      /* Redefining a precompiled key is ok.  */
      if (hp->type == T_PCSTRING)
	ok = TRUE;
      /* 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 = !pfile->done_initializing;
      else
	ok = FALSE; /* Redefining anything else is bad. */

      /* Print the warning if it's not ok.  */
      if (!ok)
	{
	  /*
	  ** If we are passing through #define and #undef directives, do
	  ** that for this re-definition now.
	  */

	  if (CPPOPTIONS (pfile)->debug_output && (keyword != NULL))
	    {
	      /* llassert (keyword != NULL); */
	      pass_thru_directive (buf, limit, pfile, keyword);
	    }

	  cpp_setLocation (pfile);

	  if (hp->type == T_MACRO)
	    {
	      if (hp->value.defn->expand)
		{
		  genppllerrorhint
		    (FLG_MACROREDEF,
		     message ("Macro %q already defined",
			      cstring_copyLength (mdef.symnam, mdef.symlen)),
		     message ("%q: Previous definition of %q",
			      fileloc_unparseRaw (hp->value.defn->file,
						 (int) hp->value.defn->line),
			      cstring_copyLength (mdef.symnam, mdef.symlen)));
		}
              /* else error will be reported checking macros */
	    }
	  else
	    {
	      genppllerror (FLG_MACROREDEF,
			    message ("Macro %q already defined",
				     cstring_copyLength (mdef.symnam,
							 mdef.symlen)));
	    }
	}

      /* 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 (CPPOPTIONS (pfile)->debug_output && (keyword != NULL))
	{
	  pass_thru_directive (buf, limit, pfile, keyword);
	}

      DPRINTF (("Define macro: %s / %d", 
		mdef.symnam, mdef.defn->expand));
      
      cpphash_installMacro (mdef.symnam, mdef.symlen, mdef.defn, hashcode);
      /*@-branchstate@*/
    } /*@=branchstate@*/
}

static void
do_define (cppReader *pfile, struct directive *keyword,
	   const /*@exposed@*/ char *buf, const char *limit)
{
  DPRINTF (("Regular do define"));
  do_defineAux (pfile, keyword, buf, limit, TRUE);
}

/*
** 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 */
/*@notfunction@*/
#define ARG_BASE ((pfile)->token_buffer)

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

/* 
** Allocate a new cppBuffer for PFILE, and push it on the input buffer stack.
**   If BUFFER != NULL, then use the LENGTH characters in BUFFER
**   as the new input buffer.
**   Return the new buffer, or NULL on failure.  
*/

/*@null@*/ /*@exposed@*/ cppBuffer *
cppReader_pushBuffer (cppReader *pfile, char *buffer, size_t length)
{
  cppBuffer *buf = cppReader_getBufferSafe (pfile);

  if (buf == pfile->buffer_stack)
    {
      cppReader_fatalError
	(pfile,
	 message ("%s: macro or `#include' recursion too deep",
		  (buf->fname != NULL)
		  ? buf->fname
		  : cstring_makeLiteral ("<no name>")));
      sfreeEventually (buffer);
      return NULL;
    }

  llassert (buf != NULL);

  buf--;
  memset ((char *) buf, 0, sizeof (*buf));
  DPRINTF (("Pushing buffer: %s", cstring_copyLength (buffer, length)));
  CPPBUFFER (pfile) = buf;

  buf->if_stack = pfile->if_stack;
  buf->cleanup = cppReader_nullCleanup;
  buf->underflow = cppReader_nullUnderflow;
  buf->buf = buffer;
  buf->cur = buf->buf;

  if (buffer != NULL)
    {
      buf->alimit = buf->rlimit = buffer + length;
    }
  else
    {
      buf->alimit = buf->rlimit = NULL;
    }

  return buf;
}

cppBuffer *
cppReader_popBuffer (cppReader *pfile)
{
  cppBuffer *buf = CPPBUFFER (pfile);

  llassert (buf != NULL);

  (void) (*buf->cleanup) (buf, pfile);
  return ++CPPBUFFER (pfile);
}

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

void
cppReader_scanBuffer (cppReader *pfile)
{
  cppBuffer *buffer = CPPBUFFER (pfile);
  for (;;)
    {
      enum cpp_token token;
      
      token = cpplib_getToken (pfile);

      if (token == CPP_EOF) /* Should not happen ...  */
	{
	  break;
	}

      if (token == CPP_POP && CPPBUFFER (pfile) == buffer)
	{
	  (void) cppReader_popBuffer (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 cpplib_getWritten).  This is used by do_include.
 */

static void
cpp_expand_to_buffer (cppReader *pfile, const char *buf, size_t length)
{
  cppBuffer *ip;
  cstring newbuf;

  newbuf = cstring_copyLength (buf, length);
  DPRINTF (("Expand to buffer: %s", newbuf));

  /* Set up the input on the input stack.  */
  ip = cppReader_pushBuffer (pfile, cstring_toCharsSafe (newbuf), length);

  if (ip == NULL)
    return;

  ip->has_escapes = TRUE;

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

  cppReader_nullTerminate (pfile);
}

static void
adjust_position (char *buf, char *limit, int *linep, int *colp)
{
  while (buf < limit)
    {
      char ch = *buf++;
      if (ch == '\n')
	(*linep)++, (*colp) = 1;
      else
	(*colp)++;
    }
}

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

static void
update_position (cppBuffer *pbuf)
{
  char *old_pos;
  char *new_pos = pbuf->cur;
  struct parse_marker *mark;

  llassert (pbuf->buf != NULL);
  old_pos = pbuf->buf + pbuf->line_base;

  for (mark = pbuf->marks;  mark != NULL; mark = mark->next)
    {
      if (pbuf->buf + mark->position < new_pos)
	new_pos = pbuf->buf + mark->position;
    }
  pbuf->line_base += new_pos - old_pos;

  llassert (old_pos != NULL);
  llassert (new_pos != NULL);

  adjust_position (old_pos, new_pos, &pbuf->lineno, &pbuf->colno);
}

void
cppBuffer_getLineAndColumn (/*@null@*/ cppBuffer *pbuf, /*@out@*/ int *linep,
			    /*@null@*/ /*@out@*/ int *colp)
{
  int dummy;

  if (colp == NULL)
    {
      colp = &dummy;
      /*@-branchstate@*/
    } /*@=branchstate@*/

  if (pbuf != NULL)
    {
      *linep = pbuf->lineno;
      *colp = pbuf->colno;

      llassert (pbuf->buf != NULL);
      llassert (pbuf->cur != NULL);

      adjust_position (pbuf->buf + pbuf->line_base, pbuf->cur, linep, colp);
    }
  else
    {
      *linep = 0;
      *colp = 0;
    }
}

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

/*@exposed@*/ /*@null@*/ cppBuffer *cppReader_fileBuffer (cppReader *pfile)
{
  cppBuffer *ip = cppReader_getBuffer (pfile);

  for ( ;
	ip != NULL && ip != cppReader_nullBuffer (pfile); 
	ip = cppBuffer_prevBuffer (ip))
    {
      if (ip->fname != NULL)
	{
	  return ip;
	}
    }
  
  return NULL;
}

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

  while (buf < limit)
    {
      char ch = *buf++;
      if (ch == '\n')
	count++;
    }
  return count;
}

/*
 * write out a #line command, for instance, after an #include file.
 * If CONDITIONAL is true, 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 (cppReader *pfile, bool conditional,
		     enum file_change_code file_change)
{
  int line, col;
  cppBuffer *ip = CPPBUFFER (pfile);
  cppBuffer *buf;

  llassert (ip != NULL);

  if (ip->fname == NULL)
    return;

  update_position (ip);

  if (CPPOPTIONS (pfile)->no_line_commands
      || CPPOPTIONS (pfile)->no_output)
    return;

  buf = CPPBUFFER (pfile);

  llassert (buf != NULL);

  line = buf->lineno;
  col = buf->colno;

  llassert (ip->cur != NULL);

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

  if (CPPOPTIONS (pfile)->no_line_commands)
    return;

  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)
      {
	cpplib_reserve (pfile, 20);
	while (line > pfile->lineno)
	  {
	    cppReader_putCharQ (pfile, '\n');
	    pfile->lineno++;
	  }

	return;
      }
  }

  cpplib_reserve (pfile, 4 * cstring_length (ip->nominal_fname) + 50);

  {
#ifdef OUTPUT_LINE_COMMANDS
    static const char sharp_line[] = "#line ";
#else
    static const char sharp_line[] = "# ";
#endif
    cppReader_putStrN (pfile, sharp_line, constlen(sharp_line));
  }

  sprintf (cpplib_getPWritten (pfile), "%d ", line);
  cppReader_adjustWritten (pfile, strlen (cpplib_getPWritten (pfile)));

  quote_string (pfile, cstring_toCharsSafe (ip->nominal_fname));

  if (file_change != same_file) {
    cppReader_putCharQ (pfile, ' ');
    cppReader_putCharQ (pfile, file_change == enter_file ? '1' : '2');
  }
  /* Tell cc1 if following text comes from a system header file.  */
  if (ip->system_header_p) {
    cppReader_putCharQ (pfile, ' ');
    cppReader_putCharQ (pfile, '3');
  }
  cppReader_putCharQ (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 (cppReader *pfile, bool rest_args)
{
  int paren = 0;
  enum cpp_token token;
  char save_put_out_comments = CPPOPTIONS (pfile)->put_out_comments;
  bool oldexpand = pfile->no_macro_expand;
  CPPOPTIONS (pfile)->put_out_comments = 1;

  /* Try to parse as much of the argument as exists at this
     input stack level.  */

  pfile->no_macro_expand = TRUE;

  for (;;)
    {
      token = cpplib_getToken (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 (!cppBuffer_isMacro (CPPBUFFER (pfile)))
	    goto done;
	  /*@switchbreak@*/ break;
	case CPP_LPAREN:
	  paren++;
	  /*@switchbreak@*/ break;
	case CPP_RPAREN:
	  if (--paren < 0)
	    goto found;
	  /*@switchbreak@*/ break;
	case CPP_COMMA:
	  /* if we've returned to lowest level and
	     we aren't absorbing all args */
	  if (paren == 0 && !rest_args)
	    goto found;
	  /*@switchbreak@*/ break;
	found:
	  /* Remove ',' or ')' from argument buffer.  */
	  cppReader_adjustWritten (pfile, -1);
	  goto done;
	default:
	  ;
	}
    }

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

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

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

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

    case '\'':
    case '\"':
      /* Notice and skip strings, so that we don't delete newlines in them.  */
      {
	char quotec = c;
	while (ibp < limit) {
	  *obp++ = c = *ibp++;
	  if (c == quotec)
	    /*@innerbreak@*/ break;
	  if (c == '\n' && quotec == '\'')
	    /*@innerbreak@*/ break;
	}
      }
    /*@switchbreak@*/ break;
    }
  }

  return obp - start;
}

static /*@observer@*/ struct tm *
timestamp (/*@returned@*/ cppReader *pfile)
{
  if (pfile->timebuf == NULL)
    {
      time_t t = time ((time_t *) 0);
      pfile->timebuf = localtime (&t);
    }

  llassert (pfile->timebuf != NULL);

  return pfile->timebuf;
}

static /*@observer@*/ 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, cppReader *pfile)
{
  cstring buf = cstring_undefined;
  size_t len;
  int true_indepth;
  cppBuffer *ip;

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

  for (ip = cppReader_getBuffer (pfile); ip != NULL; ip = cppBuffer_prevBuffer (ip))
    {
      if (ip == cppReader_nullBuffer (pfile))
	{
	  cppReader_errorLit (pfile,
			cstring_makeLiteralTemp ("cccp error: not in any file?!"));
	  return;			/* the show must go on */
	}

      if (ip != NULL && ip->fname != NULL)
	{
	  break;
	}
    }

  switch (hp->type)
    {
    case T_FILE:
    case T_BASE_FILE:
      {
	const char *string;
	if (hp->type == T_BASE_FILE)
	  {
	    while (cppBuffer_prevBuffer (ip) != cppReader_nullBuffer (pfile))
	      {
		ip = cppBuffer_prevBuffer (ip);
	      }
	  }

	llassert (ip != NULL);
	string = cstring_toCharsSafe (ip->nominal_fname);

	if (string == NULL)
	  {
	    string = "";
	  }

	cpplib_reserve (pfile, 3 + 4 * strlen (string));
	quote_string (pfile, string);
	return;
      }

    case T_INCLUDE_LEVEL:
      true_indepth = 0;
      ip = cppReader_getBuffer (pfile);

      for (;  ip != cppReader_nullBuffer (pfile) && ip != NULL;
	   ip = cppBuffer_prevBuffer (ip))
	{
	  if (ip != NULL && ip->fname != NULL)
	    {
	      true_indepth++;
	    }
	}

      buf = message ("%d", true_indepth - 1);
      break;

    case T_VERSION:
      buf = cstring_makeLiteral ("\"--- cpp version---\"");
      break;

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

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

    case T_WCHAR_TYPE:
      buf = cstring_makeLiteral (WCHAR_TYPE);
      break;

    case T_USER_LABEL_PREFIX_TYPE:
      buf = cstring_makeLiteral (USER_LABEL_PREFIX);
      break;

    case T_REGISTER_PREFIX_TYPE:
      buf = cstring_makeLiteral (REGISTER_PREFIX);
      break;

    case T_CONST:
      buf = message ("%d", hp->value.ival);
      break;

    case T_SPECLINE:
      {
	if (ip != NULL)
	  {
	    int line = ip->lineno;
	    int col = ip->colno;

	    llassert (ip->cur != NULL);
	    adjust_position (cppLineBase (ip), ip->cur, &line, &col);

	    buf = message ("%d", (int) line);
	  }
	else
	  {
	    BADBRANCH;
	  }
      }
    break;

    case T_DATE:
    case T_TIME:
      {
        struct tm *timebuf;
	char *sbuf = (char *) dmalloc (20);
	timebuf = timestamp (pfile);
	if (hp->type == T_DATE)
	  {
	    sprintf (sbuf, "\"%3s %2d %4d\"", monthnames[timebuf->tm_mon],
		     timebuf->tm_mday, timebuf->tm_year + 1900);
	  }
	else
	  {
	    sprintf (sbuf, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min,
		     timebuf->tm_sec);
	  }

	buf = cstring_fromCharsNew (sbuf);
	sfree (sbuf);
	break;
      }

    case T_SPEC_DEFINED:
      buf = cstring_makeLiteral (" 0 ");     /* Assume symbol is not defined */
      ip = cppReader_getBuffer (pfile);
      llassert (ip != NULL);
      llassert (ip->cur != NULL);
      SKIP_WHITE_SPACE (ip->cur);

      if (*ip->cur == '(')
	{
	  paren++;
	  ip->cur++;			/* Skip over the paren */
	  SKIP_WHITE_SPACE (ip->cur);
	}

      {
        size_t idlen = identifier_length (ip->cur);
        if (idlen == 0 || !identifier_seems_valid (ip->cur))
          goto oops;

        if ((hp = cpphash_lookup (ip->cur, idlen, -1)) != 0)
          {
            cstring_free (buf);
            buf = cstring_makeLiteral (" 1 ");
          }
        ip->cur += idlen;
      }
      SKIP_WHITE_SPACE (ip->cur);

      if (paren != 0)
	{
	  if (*ip->cur != ')')
	    goto oops;
	  ++ip->cur;
	}
      break;

    oops:

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

    default:
      cpp_setLocation (pfile);
      llfatalerror (message ("Pre-processing error: invalid special hash type"));
    }

  len = cstring_length (buf);

  cpplib_reserve (pfile, len + 1);
  cppReader_putStrN (pfile, cstring_toCharsSafe (buf), len);
  cppReader_nullTerminateQ (pfile);

  cstring_free (buf);
  return;
}

/* Write out a #define command for the special named MACRO_NAME
   to PFILE's token_buffer.  */

static void
dump_special_to_buffer (cppReader *pfile, const char *macro_name)
{
  static const char define_directive[] = "#define ";
  size_t macro_name_length = strlen (macro_name);
  output_line_command (pfile, 0, same_file);
  cpplib_reserve (pfile, constlen(define_directive) + macro_name_length + 1);
  cppReader_putStrN (pfile, define_directive, constlen(define_directive));
  cppReader_putStrN (pfile, macro_name, macro_name_length);
  cppReader_putCharQ (pfile, ' ');
  cpp_expand_to_buffer (pfile, macro_name, macro_name_length);
  cppReader_putChar (pfile, '\n');
}

/* Initialize the built-in macros.  */

static void
cpplib_installBuiltinAux (const /*@observer@*/ char *name, size_t len,
                          ctype ctyp, enum node_type type,
                          int ivalue, /*@null@*/ /*@only@*/ char *value,
                          int hash)
{
  cstring sname = cstring_fromCharsNew (name);

  llassert (usymtab_inGlobalScope ());

  /*
  ** Be careful here: this is done before the ctype table has
  ** been initialized.
  */

  if (!usymtab_exists (sname))
    {
      uentry ue = uentry_makeConstant (sname, ctyp, fileloc_createBuiltin ());

      if (ctype_equal (ctyp, ctype_string))
	{
	  qualList ql = qualList_new ();
	  ql = qualList_add (ql, qual_createObserver ());
	  uentry_reflectQualifiers (ue, ql);
	  qualList_free (ql);
	}
      
      usymtab_addGlobalEntry (ue);
    }
  else
    {
      ;
    }

  (void) cpphash_install (name, len, type, ivalue, value, hash);
  cstring_free (sname);
}

static void
cpplib_installBuiltinTypeAux (const /*@observer@*/ char *name, size_t len,
                              ctype ctyp, enum node_type type,
                              int ivalue, /*@only@*/ /*@null@*/ char *value,
                              int hash)
{
  cstring sname = cstring_fromChars (name);

  llassert (usymtab_inGlobalScope ());

  if (!usymtab_existsTypeEither (sname))
    {
      uentry ue = uentry_makeDatatype (sname, ctyp,
				       NO, qual_createConcrete (),
				       fileloc_createBuiltin ());
      llassert (!usymtab_existsEither (sname));
      usymtab_addGlobalEntry (ue);
    }

  (void) cpphash_install (name, len, type, ivalue, value, hash);
}

#define cpplib_installBuiltin(name, ctyp, type, ivalue, value, hash) \
    cpplib_installBuiltinAux(name, constlen(name), ctyp, type, ivalue, value, hash)

#define cpplib_installBuiltinType(name, ctyp, type, ivalue, value, hash) \
    cpplib_installBuiltinTypeAux (name, constlen(name), ctyp, type, ivalue, value, hash)

static void
initialize_builtins (cppReader *pfile)
{
  cpplib_installBuiltin ("__LINE__", ctype_int, T_SPECLINE, 0, NULL, -1);
  cpplib_installBuiltin ("__DATE__", ctype_string, T_DATE, 0, NULL, -1);
  cpplib_installBuiltin ("__FILE__", ctype_string, T_FILE, 0, NULL, -1);
  cpplib_installBuiltin ("__BASE_FILE__", ctype_string, T_BASE_FILE, 0, NULL, -1);
  cpplib_installBuiltin ("__INCLUDE_LEVEL__", ctype_int, T_INCLUDE_LEVEL, 0, NULL, -1);
  cpplib_installBuiltin ("__VERSION__", ctype_string, T_VERSION, 0, NULL, -1);
#ifndef NO_BUILTIN_SIZE_TYPE
  cpplib_installBuiltinType ("__SIZE_TYPE__", ctype_anyintegral, T_SIZE_TYPE, 0, NULL, -1);
#endif
#ifndef NO_BUILTIN_PTRDIFF_TYPE
  cpplib_installBuiltinType ("__PTRDIFF_TYPE__", ctype_anyintegral, T_PTRDIFF_TYPE, 0, NULL, -1);
#endif
  cpplib_installBuiltinType ("__WCHAR_TYPE__", ctype_anyintegral, T_WCHAR_TYPE, 0, NULL, -1);
  cpplib_installBuiltin ("__USER_LABEL_PREFIX__", ctype_string, T_USER_LABEL_PREFIX_TYPE, 0, NULL, -1);
  cpplib_installBuiltin ("__REGISTER_PREFIX__", ctype_string, T_REGISTER_PREFIX_TYPE, 0, NULL, -1);
  cpplib_installBuiltin ("__TIME__", ctype_string, T_TIME, 0, NULL, -1);

  if (!cppReader_isTraditional (pfile))
    {
      cpplib_installBuiltin ("__STDC__", ctype_int, T_CONST, STDC_VALUE, NULL, -1);
    }

  /*
  ** This is supplied using a -D by the compiler driver
  ** so that it is present only when truly compiling with GNU C.
  */

  /*  cpplib_install ("__GNUC__", -1, T_CONST, 2, 0, -1);  */

  cpplib_installBuiltin ("S_SPLINT_S", ctype_int, T_CONST, 2, NULL, -1);
  cpplib_installBuiltin ("__LCLINT__", ctype_int, T_CONST, 2, NULL, -1);

  if (CPPOPTIONS (pfile)->debug_output)
    {
      dump_special_to_buffer (pfile, "__BASE_FILE__");
      dump_special_to_buffer (pfile, "__VERSION__");
#ifndef NO_BUILTIN_SIZE_TYPE
      dump_special_to_buffer (pfile, "__SIZE_TYPE__");
#endif
#ifndef NO_BUILTIN_PTRDIFF_TYPE
      dump_special_to_buffer (pfile, "__PTRDIFF_TYPE__");
#endif
      dump_special_to_buffer (pfile, "__WCHAR_TYPE__");
      dump_special_to_buffer (pfile, "__DATE__");
      dump_special_to_buffer (pfile, "__TIME__");
      if (!cppReader_isTraditional (pfile))
	dump_special_to_buffer (pfile, "__STDC__");
    }
}


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

static bool
unsafe_chars (char c1, char c2)
{
  switch (c1)
    {
    case '+': case '-':
      if (c2 == c1 || c2 == '=')
	return 1;
      goto letter;
    case '.':
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
    case 'e': case 'E': case 'p': case 'P':
      if (c2 == '-' || c2 == '+')
	return 1; /* could extend a pre-processing number */
      goto letter;
    case 'L':
      if (c2 == '\'' || c2 == '\"')
	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 '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 '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.  */
      return (is_idchar[(int) c2] || c2 == '.');
    case '<': case '>': case '!': case '%': case '#': case ':':
    case '^': case '&': case '|': case '*': case '/': case '=':
      return (c2 == c1 || c2 == '=');
    }
  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
cpplib_macroExpand (cppReader *pfile, /*@dependent@*/ hashNode hp)
{
  int nargs;
  DEFINITION *defn = hp->value.defn;
  char *xbuf;
  char *oxbuf = NULL;
  int start_line;
  int start_column;
  int end_line;
  int end_column;
  size_t xbuf_len;
  size_t old_written = cpplib_getWritten (pfile);
  bool rest_zero = FALSE;
  int i;
  struct argdata *args = NULL;

  pfile->output_escapes++;
  cppBuffer_getLineAndColumn (cppReader_fileBuffer (pfile), &start_line, &start_column);
  DPRINTF (("Expand macro: %d:%d", start_line, start_column));

  nargs = defn->nargs;

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

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

      for (i = 0; i < nargs; i++)
	{
	  args[i].expanded = 0;
	  args[i].raw = 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 = FALSE;

      cppReader_forward (pfile, 1); /* Discard the open-parenthesis before the first arg.  */
      do
	{
	  if (rest_args)
	    {
	      continue;
	    }

	  if (i < nargs || (nargs == 0 && i == 0))
	    {
	      /* if we are working on last arg which absorbs rest of args... */
	      if (i == nargs - 1 && defn->rest_args)
		{
		  rest_args = TRUE;
		}

	      args[i].raw = size_toLong (cpplib_getWritten (pfile));
	      token = macarg (pfile, rest_args);
	      args[i].raw_length = size_toInt (cpplib_getWritten (pfile) - args[i].raw);
	      args[i].newlines = FALSE; /* FIXME */
	    }
	  else
	    {
	      token = macarg (pfile, FALSE);
	    }

	  if (token == CPP_EOF || token == CPP_POP)
	    {
	      cppReader_errorWithLine (pfile, start_line, start_column,
				   cstring_fromCharsNew ("unterminated macro call"));
	      sfree (args);
	      return;
	    }
	  i++;
	} while (token == CPP_COMMA);

      /* If we got one arg but it was just whitespace, call that 0 args.  */
      if (i == 1)
	{
	  char *bp;
	  char *lim;

	  assertSet (args);

	  bp = ARG_BASE + args[0].raw;
	  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[(int) *bp])
		{
		  bp++;
		}
	    }

	  if (bp == lim)
	    i = 0;
	}

      /* Don't output an error message if we have already output one for
	 a parse error above.  */
      rest_zero = FALSE;

      if (nargs == 0 && i > 0)
	{
	  cppReader_error (pfile,
		     message ("arguments given to macro `%s'", hp->name));
	}
      else if (i < nargs)
	{
	  /* traditional C allows foo() if foo wants one argument.  */
	  if (nargs == 1 && i == 0 && cppReader_isTraditional (pfile))
	    {
	      ;
	    }
	  /* the rest args token is allowed to absorb 0 tokens */
	  else if (i == nargs - 1 && defn->rest_args)
	    rest_zero = TRUE;
	  else if (i == 0)
	    cppReader_error (pfile,
		       message ("macro `%s' used without args", hp->name));
	  else if (i == 1)
	    cppReader_error (pfile,
		       message ("macro `%s' used with just one arg", hp->name));
	  else
	    {
	      cppReader_error (pfile,
			 message ("macro `%s' used with only %d args",
				  hp->name, i));
	    }
	}
      else if (i > nargs)
	{
	  cppReader_error (pfile,
		     message ("macro `%s' used with too many (%d) args", hp->name, i));
	}
      else
	{
	  ;
	}
    }

  /*
  ** If the agrument list was multiple lines, need to insert new lines to keep line
  ** numbers accurate.
  */

  cppBuffer_getLineAndColumn (cppReader_fileBuffer (pfile), &end_line, &end_column);
  DPRINTF (("Expand macro: %d:%d", end_line, end_column));
  
  /* 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
    {
      char *exp = defn->expansion;
      int offset;	/* offset in expansion,
				   copied a piece at a time */
      size_t totlen;	/* total amount of exp buffer filled so far */

      struct reflist *ap, *last_ap;
      
      assertSet (args); /* args is defined since the nargs > 0 path was taken */

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

      llassert (args != NULL);

      for (ap = defn->pattern; ap != NULL; ap = ap->next)
	{
	  if (ap->stringify)
	    {
	      struct argdata *arg = &args[ap->argno];

	      /* Stringify it it hasn't already been */
	      assertSet (arg);

	      if (arg->stringified_length < 0)
		{
		  int arglen = arg->raw_length;
		  bool escaped = FALSE;
		  char in_string = '\0';

		  /* 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 = cpplib_getWritten (pfile);
		  if (!cppReader_isTraditional (pfile))
		    cppReader_putChar (pfile, '\"'); /* insert beginning quote */
		  for (; i < arglen; i++)
		    {
                      char c = (ARG_BASE + arg->raw)[i];

		      if (in_string == '\0')
			{
			  /* Internal sequences of whitespace are replaced by
			     one space except within an string or char token.*/
			  if (is_space[(int) c])
			    {
			      if (cpplib_getWritten (pfile) > arg->stringified
				  && (cpplib_getPWritten (pfile))[-1] == '@')
				{
				  /* "@ " escape markers are removed */
				  cppReader_adjustWritten (pfile, -1);
				  /*@innercontinue@*/ continue;
				}
			      if (need_space == 0)
				need_space = 1;
			      /*@innercontinue@*/ continue;
			    }
			  else if (need_space > 0)
			    cppReader_putChar (pfile, ' ');
			  else
			    {
			      ;
			    }

			  need_space = 0;
			}

		      if (escaped)
			escaped = 0;
		      else
			{
			  if (c == '\\')
			    escaped = 1;

			  if (in_string != '\0')
			    {
			      if (c == in_string)
				in_string = '\0';
			    }
			  else if (c == '\"' || c == '\'')
			    {
			      in_string = c;
			    }
			  else
			    {
			      ;
			    }
			}

		      /* Escape these chars */
		      if (c == '\"' || (in_string != '\0' && c == '\\'))
			cppReader_putChar (pfile, '\\');
		      if (isprint (c))
			cppReader_putChar (pfile, c);
		      else
			{
			  cpplib_reserve (pfile, 4);
			  sprintf (cpplib_getPWritten (pfile), "\\%03o",
				   (unsigned int) c);
			  cppReader_adjustWritten (pfile, 4);
			}
		    }
		  if (!cppReader_isTraditional (pfile))
		    cppReader_putChar (pfile, '\"'); /* insert ending quote */
		  arg->stringified_length
		    = size_toInt (cpplib_getWritten (pfile) - arg->stringified);
		}

	      xbuf_len += args[ap->argno].stringified_length;
	    }
	  else if (ap->raw_before || ap->raw_after || cppReader_isTraditional (pfile))
	    {
	      /* Add 4 for two newline-space markers to prevent token concatenation. */
	      assertSet (args); /* Splint shouldn't need this */
	      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.  */

	      assertSet (args); /* shouldn't need this */

	      if (args[ap->argno].expand_length < 0)
		{
		  args[ap->argno].expanded = cpplib_getWritten (pfile);
		  cpp_expand_to_buffer (pfile,
					ARG_BASE + args[ap->argno].raw,
					size_fromInt (args[ap->argno].raw_length));

		  args[ap->argno].expand_length
		    = size_toInt (cpplib_getWritten (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 = (char *) dmalloc (xbuf_len + 1);
      oxbuf = xbuf;

      /*
      ** 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 = 0;
      totlen = 0;

      for (last_ap = NULL, ap = defn->pattern; ap != NULL;
	   last_ap = ap, ap = ap->next)
	{
	  struct argdata *arg = &args[ap->argno];
	  size_t count_before = totlen;

	  /* Add chars to XBUF.  */
	  for (i = 0; i < ap->nchars; i++, offset++)
	    {
	      xbuf[totlen++] = exp[offset];
	    }

	  /* If followed by an empty rest arg with concatenation,
	     delete the last run of nonwhite chars.  */
	  if (rest_zero && totlen > count_before
	      && ((ap->rest_args && ap->raw_before)
		  || (last_ap != NULL && last_ap->rest_args
		      && last_ap->raw_after)))
	    {
	      /* Delete final whitespace.  */
	      while (totlen > count_before && is_space[(int) xbuf[totlen - 1]])
		{
		  totlen--;
		}

	      /* Delete the nonwhites before them.  */
	      while (totlen > count_before && ! is_space[(int) xbuf[totlen - 1]])
		{
		  totlen--;
		}
	    }

	  if (ap->stringify != 0)
	    {
	      assertSet(arg);
	      memcpy (xbuf + totlen,
		      ARG_BASE + arg->stringified,
		      size_fromInt (arg->stringified_length));
	      totlen += arg->stringified_length;
	    }
	  else if (ap->raw_before || ap->raw_after || cppReader_isTraditional (pfile))
	    {
	      char *p1;
	      char *l1;

	      assertSet (arg);

	      p1 = ARG_BASE + arg->raw;
	      l1 = p1 + arg->raw_length;

	      if (ap->raw_before)
		{
		  while (p1 != l1 && is_space[(int) *p1])
		    {
		      p1++;
		    }

		  while (p1 != l1 && is_idchar[(int) *p1])
		    {
		      xbuf[totlen++] = *p1++;
		    }

		  /* Delete any no-reexpansion marker that follows
		     an identifier at the beginning of the argument
		     if the argument is concatenated with what precedes it.  */
		  if (p1[0] == '@' && p1[1] == '-')
		    p1 += 2;
		}
	      if (ap->raw_after)
		{
		  /* Arg is concatenated after: delete trailing whitespace,
		     whitespace markers, and no-reexpansion markers.  */
		  while (p1 != l1)
		    {
		      if (is_space[(int) l1[-1]]) l1--;
		      else if (l1[-1] == '-')
			{
			  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) != 0)
			    {
			      l1 -= 2;
			    }
			  else
			    {
			      /*@innerbreak@*/ break;
			    }
			}
		      else
			{
			  /*@innerbreak@*/ break;
			}
		    }
		}

	      memcpy (xbuf + totlen, p1, size_fromInt (l1 - p1));
	      totlen += l1 - p1;
	    }
	  else
	    {
	      char *expanded;

	      assertSet (arg);
	      expanded = ARG_BASE + arg->expanded;

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

	      memcpy (xbuf + totlen, expanded,
		      size_fromInt (arg->expand_length));
	      totlen += arg->expand_length;

	      if (!ap->raw_after && totlen > 0
		  && offset < size_toInt (defn->length)
		  && !cppReader_isTraditional(pfile)
		  && 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)
          {
            cppReader_error (pfile,
                cstring_makeLiteral ("failure in macro expansion"));
            sfree (args);
            return;
          }
	}

      /* if there is anything left of the definition
	 after handling the arg list, copy that in too.  */

      for (i = offset; i < size_toInt (defn->length); i++)
	{
	  /* if we've reached the end of the macro */
	  if (exp[i] == ')')
	    rest_zero = FALSE;
	  if (! (rest_zero && last_ap != NULL && 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.  */
  DPRINTF (("Pushing expansion: %s", cstring_copyLength (xbuf, xbuf_len)));
  
  if (end_line != start_line)
    {
      /* xbuf must have enough newlines */
      int newlines = end_line - start_line;
      int foundnewlines = 0;
      char *xbufptr = xbuf;

      while ((xbufptr = strchr (xbufptr, '\n')) != NULL && foundnewlines <= newlines)
	{
	  foundnewlines++;
	  xbufptr++;

	  if (*xbufptr == '\0') 
	    {
	      break;
	    }
	}
	  
      if (foundnewlines < newlines)
	{
	  cstring newbuf = cstring_copyLength (xbuf, xbuf_len);
	  
	  while (foundnewlines < newlines)
	    {
	      newbuf = cstring_appendChar (newbuf, '\n');
	      foundnewlines++;
	    }

	  sfree (oxbuf);
	  xbuf = cstring_toCharsSafe (newbuf);
	  xbuf_len = cstring_length (newbuf);
	  /*@-branchstate@*/ 
	} /*@=branchstate@*/ 
    }
  
  DPRINTF (("Pushing expansion: %s", cstring_copyLength (xbuf, xbuf_len)));

  push_macro_expansion (pfile, xbuf, xbuf_len, hp);
  DPRINTF (("After pushing expansion: %s", cstring_copyLength (xbuf, xbuf_len)));
  cppReader_getBufferSafe (pfile)->has_escapes = 1;

  /* Pop the space we've used in the token_buffer for argument expansion.  */
  cppReader_setWritten (pfile, old_written);
  DPRINTF (("Done set written"));
  
  /* Recursive macro use sometimes works traditionally.
     #define foo(x,y) bar (x (y,0), y)
     foo (foo, baz)  */

  if (!cppReader_isTraditional (pfile))
    hp->type = T_DISABLED;

  sfree (args);
}

static void
push_macro_expansion (cppReader *pfile, char *xbuf, size_t xbuf_len,
		      /*@dependent@*/ hashNode hp)
{
  cppBuffer *mbuf = cppReader_pushBuffer (pfile, xbuf, xbuf_len);

  if (mbuf == NULL)
    {
      return;
    }

  mbuf->cleanup = cppReader_macroCleanup;

  llassert (mbuf->hnode == NULL);
  mbuf->hnode = 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[(int) xbuf[2]] || xbuf[2] == '(' || xbuf[2] == '\''
	  || xbuf[2] == '\"'))
  {
    llassert (mbuf->cur != NULL);
    DPRINTF (("Eating: %c", xbuf[2]));
    mbuf->cur += 2;
  }
  
}


/* Like cpplib_getToken, 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 (cppReader *pfile)
{
  for (;;)
    {
      size_t old_written = cpplib_getWritten (pfile);
      enum cpp_token token;
      cppSkipHspace (pfile);
      if (cppReader_peekC (pfile) == '\n')
	{
	  return CPP_VSPACE;
	}

      token = cpplib_getToken (pfile);

      switch (token)
	{
	case CPP_POP:
	  if (!cppBuffer_isMacro (cppReader_getBuffer (pfile)))
	    return token;
	  /*@fallthrough@*/
	case CPP_HSPACE:
	case CPP_COMMENT:
	  cppReader_setWritten (pfile, old_written);
	  /*@switchbreak@*/ 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
   cpplib_getWritten, and will get overwritten by output_line_command.
   I.e. in input file specification has been popped by cppReader_handleDirective.
   This is safe.  */

static void
do_include (cppReader *pfile, struct directive *keyword,
	    const /*@unused@*/ char *buf, const /*@unused@*/ char *limit)
{
  bool skip_dirs = (keyword->type == T_INCLUDE_NEXT);
  cstring fname;
  char *fbeg, *fend;		/* Beginning and end of fname */
  enum cpp_token token;

  /* Chain of dirs to search */
  struct file_name_list *search_start = CPPOPTIONS (pfile)->include;
  struct file_name_list dsp[1];	/* First in chain, if #include "..." */
  struct file_name_list *searchptr = NULL;
  size_t old_written = cpplib_getWritten (pfile);
  size_t flen;

  int f;			/* file number */
  int angle_brackets = 0;	/* 0 for "...", 1 for <...> */
  f= -1;			/* JF we iz paranoid! */

  pfile->parsing_include_directive = TRUE;
  token = get_directive_token (pfile);
  pfile->parsing_include_directive = FALSE;

  if (token == CPP_STRING)
    {
      /* FIXME - check no trailing garbage */
      fbeg = pfile->token_buffer + old_written + 1;
      fend = cpplib_getPWritten (pfile) - 1;
      if (fbeg[-1] == '<')
	{
	  angle_brackets = 1;
	  /* If -I-, start with the first -I dir after the -I-.  */
	  if (CPPOPTIONS (pfile)->first_bracket_include != NULL)
	    search_start = CPPOPTIONS (pfile)->first_bracket_include;
	}
      /* If -I- was specified, don't search current dir, only spec'd ones.  */
      else if (!CPPOPTIONS (pfile)->ignore_srcdir)
	{
	  cppBuffer *fp = CPPBUFFER (pfile);
	  /* We have "filename".  Figure out directory this source
	     file is coming from and put it on the front of the list.  */

	  for ( ; fp != cppReader_nullBuffer (pfile); fp = cppBuffer_prevBuffer (fp))
	    {
	      char *ep,*nam;

	      llassert (fp != NULL);

	      nam = NULL;

	      if (cstring_isDefined (fp->nominal_fname))
		{
		  nam = cstring_toCharsSafe (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;

		  ep = strrchr (nam, CONNECTCHAR);
		  if (ep != NULL)
		    {
                      int n;
		      char save;

		      n = ep - nam;
		      save = nam[n];
		      nam[n] = '\0';

		      /*@-onlytrans@*/ /* This looks like a memory leak... */ 
		      dsp[0].fname = cstring_fromCharsNew (nam); /* evs 2000-07-20: was fromChars */
		      /*@=onlytrans@*/
		      nam[n] = save;
		    }
		  else
		    {
		      dsp[0].fname = cstring_undefined; /* Current directory */
		    }

		  dsp[0].got_name_map = FALSE;
		  break;
		}
	    }
	}
      else
	{
	  ;
	}
    }
  else
    {
      cppReader_error (pfile,
		 message ("Preprocessor command #%s expects \"FILENAME\" or <FILENAME>",
			  cstring_fromChars (keyword->name)));

      cppReader_setWritten (pfile, old_written);
      cppReader_skipRestOfLine (pfile);
      return;
    }

  *fend = 0;

  token = get_directive_token (pfile);
  if (token != CPP_VSPACE)
    {
      cppReader_errorLit (pfile,
		    cstring_makeLiteralTemp ("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)
    {
      cppBuffer *fp = CPPBUFFER (pfile);

      for (; fp != cppReader_nullBuffer (pfile); fp = cppBuffer_prevBuffer (fp))
	{
	  llassert (fp != NULL);

	  if (fp->fname != NULL)
	    {
	      /* fp->dir is null if the containing file was specified with
		 an absolute file name.  In that case, don't skip anything.  */
	      if (fp->dir == SELF_DIR_DUMMY)
		{
		  search_start = CPPOPTIONS (pfile)->include;
		}
	      else if (fp->dir != NULL)
		{
		  search_start = fp->dir->next;
		}
	      else
		{
		  ;
		}

	      break;
	    }
	}
    }
  
  cppReader_setWritten (pfile, old_written);

  flen = size_fromInt (fend - fbeg);

  DPRINTF (("fbeg: %s", fbeg));

  if (flen == 0)
    {
      cppReader_error (pfile,
		 message ("Empty file name in #%s", cstring_fromChars (keyword->name)));
      return;
    }

  /*
  ** Allocate this permanently, because it gets stored in the definitions
  ** of macros.
  */

  fname = cstring_undefined;

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

  if (osd_pathIsAbsolute (fbeg))
    {
      fname = cstring_copyLength (fbeg, flen);
      
      if (redundant_include_p (pfile, fname))
	{
	  cstring_free (fname);
	  return;
	}
      
      f = open_include_file (pfile, fname, NULL);
      
      if (f == IMPORT_FOUND)
	{
	  return;		/* Already included this file */
	}
    } 
  else 
    {
      /* Search directory path, trying to open the file.
	 Copy each filename tried into FNAME.  */

      for (searchptr = search_start; searchptr != NULL;
	   searchptr = searchptr->next)
	{
#if 0
          /* The empty string in a search path is ignored.
             This makes it possible to turn off entirely
             a standard piece of the list.  */
          if (cstring_isEmpty (searchptr->fname))
            continue;
#endif
	  if (!cstring_isEmpty (searchptr->fname))
	    {
	      fname = cstring_copy (searchptr->fname);
	      fname = cstring_appendChar (fname, CONNECTCHAR);
              osd_pathMakeNative (fname);
	      DPRINTF (("Here: %s", fname));
	    }
	  else
	    {
	      ;
	    }
	  
	  fname = cstring_concatLength (fname, fbeg, flen);

	  DPRINTF (("fname: %s", fname));
	  
	  /* ??? 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))
	    {
	      cstring_free (fname);
	      return;
	    }

	  DPRINTF (("Trying: %s", fname));

	  f = open_include_file (pfile, fname, searchptr);
	  
	  if (f == IMPORT_FOUND)
	    {
	      return; /* Already included this file */
	    }
#ifdef EACCES
	  else if (f == IMPORT_NOT_FOUND && errno == EACCES)
	    {
	      cppReader_warning (pfile,
				 message ("Header file %s exists, but is not readable", fname));
	    }
#endif
	  
	  if (f >= 0)
	    {
	      break;
	    }
	}
    }
  
  if (f < 0)
    {
      /* A file that was not found.  */
      fname = cstring_copyLength (fbeg, flen);

      if (search_start != NULL)
	{
	  cppReader_error (pfile,
			   message ("Cannot find include file %s on search path: %x", 
				    fname,
				    searchPath_unparse (search_start)));
	}
      else
	{
	  cppReader_error (pfile,
			   message ("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.
    */

    struct file_name_list *ptr;

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

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

	ptr = (struct file_name_list *) dmalloc (sizeof (*ptr));
	ptr->control_macro = NULL;
	ptr->next = pfile->all_include_files;
	ptr->fname = fname;
        ptr->name_map = NULL;
	ptr->got_name_map = FALSE;

	DPRINTF (("Including file: %s", fname));
	pfile->all_include_files = ptr;
	assertSet (pfile->all_include_files);
      }

    if (angle_brackets != 0)
      {
	pfile->system_include_depth++;
      }

    /* Actually process the file */
    if (cppReader_pushBuffer (pfile, NULL, 0) == NULL)
      {
	cstring_free (fname);
	return;
      }

    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--;
      }
    /*@-branchstate@*/
  } /*@=branchstate@*/ 
}

/* Return true 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 bool
redundant_include_p (cppReader *pfile, cstring name)
{
  struct file_name_list *l = pfile->all_include_files;

  for (; l != NULL; l = l->next)
    {
      if (cstring_equal (name, l->fname)
	  && (l->control_macro != NULL)
	  && (cpphash_lookup (l->control_macro, identifier_length(l->control_macro), -1) != NULL))
	{
	  return TRUE;
	}
    }

  return FALSE;
}

/* Return true if the given FILENAME is an absolute pathname which
   designates a file within one of the known "system" include file
   directories.  We assume here that if the given FILENAME looks like
   it is the name of a file which resides either directly in a "system"
   include file directory, or within any subdirectory thereof, then the
   given file must be a "system" include file.  This function tells us
   if we should suppress pedantic errors/warnings for the given FILENAME. */

static bool
is_system_include (cppReader *pfile, cstring filename)
{
  struct file_name_list *searchptr;

  for (searchptr = CPPOPTIONS (pfile)->first_system_include;
       searchptr != NULL;
       searchptr = searchptr->next)
    {
      if (!cstring_isEmpty (searchptr->fname)) 
	{
	  cstring sys_dir = searchptr->fname;
	  size_t length = cstring_length (sys_dir);
	  
	  if (cstring_equalLen (sys_dir, filename, length)
              && osd_isConnectChar (cstring_getChar (filename, length)))
	    {
              return TRUE;
	    }
	}
    }
  
  return FALSE;
}

/* 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 /*@null@*/ char *
convert_string (cppReader *pfile, /*@returned@*/ char *result,
		char *in, char *limit, int handle_escapes)
{
  char c;
  c = *in++;

  if (c != '\"')
    {
      return NULL;
    }

  while (in < limit)
    {
      c = *in++;

      switch (c)
	{
	case '\0':
	  return NULL;
	case '\"':
	  limit = in;
	  /*@switchbreak@*/ break;
	case '\\':
	  if (handle_escapes)
	    {
	      char *bpc = (char *) in;
	      int i = (char) cppReader_parseEscape (pfile, &bpc);
	      in = (char *) bpc;
	      if (i >= 0)
		*result++ = (char) c;
	      /*@switchbreak@*/ break;
	    }

	  /*@fallthrough@*/
	default:
	  *result++ = c;
	}
    }

  *result = 0;
  return result;
}

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

/*@constant int FNAME_HASHSIZE@*/
#define FNAME_HASHSIZE 37

static void
do_line (cppReader *pfile, /*@unused@*/ struct directive *keyword,
    const /*@unused@*/ char *buf, const /*@unused@*/ char *limit)
{
  cppBuffer *ip = cppReader_getBuffer (pfile);
  int new_lineno;
  size_t old_written = cpplib_getWritten (pfile);
  enum file_change_code file_change = same_file;
  enum cpp_token token;

  llassert (ip != NULL);
  token = get_directive_token (pfile);

  if (token != CPP_NUMBER
      || !isdigit(pfile->token_buffer[old_written]))
    {
      cppReader_errorLit (pfile,
		    cstring_makeLiteralTemp ("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 (pfile->token_buffer + old_written) - 1;
  cppReader_setWritten (pfile, old_written);

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

  token = get_directive_token (pfile);

  if (token == CPP_STRING) {
    char *fname = pfile->token_buffer + old_written;
    char *end_name;
    static hashNode fname_table[FNAME_HASHSIZE];
    hashNode hp; 
    hashNode *hash_bucket;
    char *p;
    size_t num_start;
    size_t 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, cpplib_getPWritten (pfile), 1);
    if (end_name == NULL)
      {
	cppReader_errorLit (pfile,
		      cstring_makeLiteralTemp ("invalid format `#line' command"));
	goto bad_line_directive;
      }

    fname_length = size_fromInt (end_name - fname);
    num_start = cpplib_getWritten (pfile);

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

      if (token != CPP_NUMBER || *p < '0' || *p > '3' || p[1] != '\0')
	{
	  cppReader_errorLit (pfile,
			cstring_makeLiteralTemp ("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 = TRUE;

      cppReader_setWritten (pfile, num_start);
      token = get_directive_token (pfile);
      p = pfile->token_buffer + num_start;
      if (token == CPP_NUMBER && p[1] == '\0' && *p == '3') {
	ip->system_header_p = TRUE;
	token = get_directive_token (pfile);
      }
      if (token != CPP_VSPACE) {
	cppReader_errorLit (pfile,
		      cstring_makeLiteralTemp ("invalid format `#line' command"));

	goto bad_line_directive;
      }
    }

    hash_bucket =
      &fname_table[cpphash_hashCode (fname, fname_length, FNAME_HASHSIZE)];

    for (hp = *hash_bucket; hp != NULL; hp = hp->next)
      {
	if (hp->length == fname_length)
	  {
	    llassert (hp->value.cpval != NULL);
	    
	    if (strncmp (hp->value.cpval, fname, fname_length) == 0) 
	      {
		ip->nominal_fname = cstring_fromChars (hp->value.cpval);
		break;
	      }
	  }
      }
    
    if (hp == 0) {
      /* Didn't find it; cons up a new one.  */
      hp = (hashNode) dmalloc (sizeof (*hp));

      hp->prev = NULL;
      hp->bucket_hdr = NULL;
      hp->type = T_NONE;
      hp->name = cstring_undefined;
      hp->next = *hash_bucket;

      *hash_bucket = hp;

      hp->length = fname_length;
      hp->value.cpval = dmalloc (sizeof (*hp->value.cpval) * (fname_length + 1));
      memcpy (hp->value.cpval, fname, fname_length);
      hp->value.cpval[fname_length] = '\0';
      ip->nominal_fname = cstring_fromChars (hp->value.cpval);
    }
  }
  else if (token != CPP_VSPACE && token != CPP_EOF)
    {
      cppReader_errorLit (pfile,
		    cstring_makeLiteralTemp ("invalid format `#line' command"));
      goto bad_line_directive;
    }
  else
    {
      ;
    }

  ip->lineno = new_lineno;
bad_line_directive:
  cppReader_skipRestOfLine (pfile);
  cppReader_setWritten (pfile, old_written);
  output_line_command (pfile, 0, file_change);
}

/*
 * 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 void
do_undef (cppReader *pfile, struct directive *keyword, const char *buf, const char *limit)
{
  size_t sym_length;
  hashNode hp;
  const char *orig_buf = buf;

  SKIP_WHITE_SPACE (buf);

  sym_length = identifier_length (buf);
  if (!macro_name_is_valid (buf, sym_length))
    {
      cppReader_error (pfile, invalid_macro_message (buf, sym_length));
    }
  else
    {
      while ((hp = cpphash_lookup (buf, sym_length, -1)) != NULL)
        {
          /* If we are generating additional info for debugging (with -g) we
             need to pass through all effective #undef commands.  */
          if (CPPOPTIONS (pfile)->debug_output && (keyword != NULL))
            {
              pass_thru_directive (orig_buf, limit, pfile, keyword);
            }

          if (hp->type != T_MACRO)
            {
              cppReader_warning (pfile,
                           message ("Undefining preprocessor builtin: %s",
                                    hp->name));
            }

          cpphash_deleteMacro (hp);
        }
    }

  if (cppReader_isPedantic (pfile)) {
    buf += sym_length;
    SKIP_WHITE_SPACE (buf);
    if (buf != limit)
      {
	cppReader_pedwarnLit (pfile,
			cstring_makeLiteralTemp ("garbage after `#undef' directive"));
      }
  }
}


/*
 * 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 void
do_error (cppReader *pfile, /*@unused@*/ struct directive *keyword,
	  const char *buf, const char *limit)
{
  cstring msg;
  SKIP_WHITE_SPACE (buf);
  if (buf >= limit)
    msg = cstring_makeLiteral("#error");
  else
    msg = message ("#error %q",
        cstring_copyLength (buf, size_fromInt (limit - buf)));
  cppReader_error (pfile, msg);
}

/*
 * 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 void
do_warning (cppReader *pfile, /*@unused@*/ struct directive *keyword,
	    const char *buf, const char *limit)
{
  cstring msg;
  SKIP_WHITE_SPACE (buf);
  if (buf >= limit)
    msg = cstring_makeLiteral("#warning");
  else
    msg = message ("#warning %q",
        cstring_copyLength (buf, size_fromInt (limit - buf)));
  cppReader_warning (pfile, msg);
}


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

static void
do_ident (cppReader *pfile, /*@unused@*/ struct directive *keyword,
	  const /*@unused@*/ char *buf, const /*@unused@*/ char *limit)
{
  /* Allow #ident in system headers, since that's not user's fault.  */
  if (cppReader_isPedantic (pfile) 
      && !cppReader_getBufferSafe (pfile)->system_header_p)
    cppReader_pedwarnLit (pfile,
		    cstring_makeLiteralTemp ("ANSI C does not allow `#ident'"));

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

/* #pragma and its argument line have already been copied to the output file.
   Just check for some recognized pragmas that need validation here.  */

static void
do_pragma (cppReader *pfile, /*@unused@*/ struct directive *keyword,
	   const char *buf, const /*@unused@*/ char *limit)
{
  static const char implstr[] = "implementation";

  SKIP_WHITE_SPACE (buf);

  if (strncmp (buf, implstr, constlen(implstr)) == 0) {
    /* Be quiet about `#pragma implementation' for a file only if it hasn't
       been included yet.  */
    struct file_name_list *ptr;
    const char *p, *fname;
    size_t fname_len;

    p = buf + constlen(implstr);
    SKIP_WHITE_SPACE (p);
    if (*p == '\n' || *p != '\"')
      return;

    fname = p + 1;
    p = (char *) strchr (fname, '\"');
    fname_len = p != NULL ? size_fromInt (p - fname) : mstring_length (fname);

    for (ptr = pfile->all_include_files; ptr != NULL; ptr = ptr->next)
      {
        char *inc_fname = strrchr (cstring_toCharsSafe (ptr->fname), CONNECTCHAR);
	inc_fname = (inc_fname != NULL)
	  ? inc_fname + 1 : cstring_toCharsSafe (ptr->fname);

	if ((inc_fname != NULL)
	    && (strncmp (inc_fname, fname, fname_len) == 0))
	  {
	    cpp_setLocation (pfile);

	    ppllerror (message ("`#pragma %s' for `%s' appears after file is included",
                        cstring_fromChars (implstr), cstring_fromChars (fname)));
	  }
      }
  }
}

/*
 * handle #if command by
 *   1) inserting special `defined' keyword into the hash table
 *	that gets turned into 0 or 1 by special_symbol (thus,
 *	if the luser has a symbol called `defined' already, it won't
 *      work inside the #if command)
 *   2) rescan the input into a temporary output buffer
 *   3) pass the output buffer to the yacc parser and collect a value
 *   4) clean up the mess left from steps 1 and 2.
 *   5) call conditional_skip to skip til the next #endif (etc.),
 *      or not, depending on the value from step 3.
 */

static void
do_if (cppReader *pfile, /*@unused@*/ struct directive *keyword,
       const /*@unused@*/ char *buf, const /*@unused@*/ char *limit)
{
  HOST_WIDE_INT value;
  value = eval_if_expression (pfile);
  conditional_skip (pfile, value == 0, T_IF, NULL);
}

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

static void
do_elif (cppReader *pfile, /*@unused@*/ struct directive *keyword,
	 const /*@unused@*/ char *buf, const /*@unused@*/ char *limit)
{
  if (pfile->if_stack == cppReader_getBufferSafe (pfile)->if_stack)
    {
      cppReader_errorLit (pfile,
		    cstring_makeLiteralTemp ("Preprocessor command #elif is not within a conditional"));
      return;
    }
  else
    {
      llassert (pfile->if_stack != NULL);

      if (pfile->if_stack->type != T_IF && pfile->if_stack->type != T_ELIF)
	{
	  cppReader_errorLit (pfile,
			cstring_makeLiteralTemp ("`#elif' after `#else'"));

	  if (pfile->if_stack->fname != NULL
	      && cppReader_getBufferSafe (pfile)->fname != NULL
	      && !cstring_equal (pfile->if_stack->fname,
				 cppReader_getBufferSafe (pfile)->nominal_fname))
	    fprintf (stderr, ", file %s", cstring_toCharsSafe (pfile->if_stack->fname));
	  fprintf (stderr, ")\n");
	}
      pfile->if_stack->type = T_ELIF;
    }

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

/*
 * evaluate a #if expression in BUF, of length LENGTH,
 * then parse the result as a C expression and return the value as an int.
 */

static HOST_WIDE_INT
eval_if_expression (cppReader *pfile)
{
  hashNode save_defined;
  HOST_WIDE_INT value;
  size_t old_written = cpplib_getWritten (pfile);
  static const char defstr[] = "defined";

  DPRINTF (("Saving defined..."));
  save_defined = cpphash_install (defstr, constlen(defstr), T_SPEC_DEFINED, 0, 0, -1);
  pfile->pcp_inside_if = TRUE;

  value = cppReader_parseExpression (pfile);
  pfile->pcp_inside_if = FALSE;

  /* Clean up special symbol */
  DPRINTF (("Removing defined..."));
  cpphash_deleteMacro (save_defined);
  cppReader_setWritten (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 void
do_xifdef (cppReader *pfile, struct directive *keyword,
	   const /*@unused@*/ char *buf, const /*@unused@*/ char *limit)
{
  int skip;
  cppBuffer *ip = cppReader_getBufferSafe (pfile);
  char *ident;
  size_t ident_length;
  enum cpp_token token;
  int start_of_file = 0;
  char *control_macro = 0;
  size_t old_written = cpplib_getWritten (pfile);

  DPRINTF (("do xifdef: %d",
	    keyword->type == T_IFNDEF));

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

  pfile->no_macro_expand = TRUE;
  token = get_directive_token (pfile);
  pfile->no_macro_expand = FALSE;

  ident = pfile->token_buffer + old_written;
  DPRINTF (("Ident: %s", ident));

  ident_length = cpplib_getWritten (pfile) - old_written;
  cppReader_setWritten (pfile, old_written); /* Pop */

  if (token == CPP_VSPACE || token == CPP_POP || token == CPP_EOF)
    {
      skip = (keyword->type == T_IFDEF);
      if (! cppReader_isTraditional (pfile))
	{
	  cppReader_pedwarn (pfile,
			     message ("`#%s' with no argument", cstring_fromChars (keyword->name)));
	}
    }
  else if (token == CPP_NAME)
    {
      hashNode hp = cpphash_lookup (ident, ident_length, -1);

      skip = (keyword->type == T_IFDEF) ? (hp == NULL) : (hp != NULL);
      
      DPRINTF (("hp null: %d / %d / %d", hp == NULL, keyword->type == T_IFNDEF, skip));
      
      if (start_of_file && !skip)
	{
	  DPRINTF (("Not skipping!"));
	  control_macro = (char *) dmalloc (ident_length + 1);
	  memcpy (control_macro, ident, ident_length + 1);
	}
    }
  else
    {
      skip = (keyword->type == T_IFDEF);
      if (! cppReader_isTraditional (pfile))
	{
	  cppReader_error (pfile,
		     message ("`#%s' with invalid argument", cstring_fromChars (keyword->name)));
	}
    }

  if (!cppReader_isTraditional (pfile))
    {
      int c;
      cppSkipHspace (pfile);
      c = cppReader_peekC (pfile);
      if (c != EOF && c != '\n')
	{
	  cppReader_pedwarn (pfile,
			     message ("garbage at end of `#%s' argument", cstring_fromChars (keyword->name)));
	}
    }

  cppReader_skipRestOfLine (pfile);

  DPRINTF (("Conditional skip: %d", skip));
  conditional_skip (pfile, skip, T_IF, control_macro);
}

/* 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 (cppReader *pfile, int skip,
		  enum node_type type,
		  /*@dependent@*/ char *control_macro)
{
  cppIfStackFrame *temp = (cppIfStackFrame *) dmalloc (sizeof (*temp));

  temp->fname = cppReader_getBufferSafe (pfile)->nominal_fname;
  temp->next = pfile->if_stack;
  temp->control_macro = control_macro;
  temp->lineno = 0;
  temp->if_succeeded = 0;

  pfile->if_stack = temp;
  pfile->if_stack->type = type;

  if (skip != 0)
    {
      skip_if_group (pfile, FALSE);
      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 true, return at next directive of any sort.
 */

static void
skip_if_group (cppReader *pfile, bool any)
{
  int c;
  struct directive *kt;
  cppIfStackFrame *save_if_stack = pfile->if_stack; /* don't pop past here */
  int ident_length;
  char *ident;
  struct parse_marker line_start_mark;

  parseSetMark (&line_start_mark, pfile);

  if (CPPOPTIONS (pfile)->output_conditionals) {
    static const char failed[] = "#failed\n";
    cppReader_puts (pfile, failed, constlen(failed));
    pfile->lineno++;
    output_line_command (pfile, 1, same_file);
  }

beg_of_line:
  if (CPPOPTIONS (pfile)->output_conditionals)
    {
      cppBuffer *pbuf = cppReader_getBufferSafe (pfile);
      char *start_line;

      llassert (pbuf->buf != NULL);

      start_line = pbuf->buf + line_start_mark.position;
      cppReader_puts (pfile, start_line, size_fromInt (pbuf->cur - start_line));
    }

  parseMoveMark (&line_start_mark, pfile);

  if (!cppReader_isTraditional (pfile))
    {
      cppSkipHspace (pfile);
    }

  c  = cppReader_getC (pfile);
  if (c == '#')
    {
      size_t old_written = cpplib_getWritten (pfile);
      cppSkipHspace (pfile);

      parse_name (pfile, cppReader_getC (pfile));
      ident_length = size_toInt (cpplib_getWritten (pfile) - old_written);
      ident = pfile->token_buffer + old_written;
      pfile->limit = ident;

      for (kt = directive_table; kt->length >= 0; kt++)
	{
	  cppIfStackFrame *temp;
          if (ident_length == kt->length &&
              cstring_equalPrefix (cstring_fromChars (kt->name),
                cstring_fromChars (ident)))
	    {
	      /* 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 = (cppIfStackFrame *) dmalloc (sizeof (*temp));
		  temp->next = pfile->if_stack;
		  temp->fname = cppReader_getBufferSafe (pfile)->nominal_fname;
		  temp->type = kt->type;
		  temp->lineno = 0;
		  temp->if_succeeded = 0;
		  temp->control_macro = NULL;

		  pfile->if_stack = temp;
		  /*@switchbreak@*/ break;
		case T_ELSE:
		case T_ENDIF:
		  if (cppReader_isPedantic (pfile) && pfile->if_stack != save_if_stack)
		    validate_else (pfile,
				   cstring_makeLiteralTemp (kt->type == T_ELSE ? "#else" : "#endif"));
		  /*@fallthrough@*/
		case T_ELIF:
		  if (pfile->if_stack == cppReader_getBufferSafe (pfile)->if_stack)
		    {
		      cppReader_error (pfile,
				 message ("Preprocessor command #%s is not within a conditional",
                                   cstring_fromChars (kt->name)));
		      /*@switchbreak@*/ break;
		    }
		  else if (pfile->if_stack == save_if_stack)
		    {
		      goto done;		/* found what we came for */
		    }
		  else
		    {
		      ;
		    }

		  if (kt->type != T_ENDIF)
		    {
		      llassert (pfile->if_stack != NULL);

		      if (pfile->if_stack->type == T_ELSE)
			{
			  cppReader_errorLit (pfile,
					cstring_makeLiteralTemp ("`#else' or `#elif' after `#else'"));
			}

		      pfile->if_stack->type = kt->type;
		      /*@switchbreak@*/ break;
		    }

		  temp = pfile->if_stack;
		  llassert (temp != NULL);
		  pfile->if_stack = temp->next;
		  sfree (temp);
		  /*@switchbreak@*/ break;
		default: ;
		  /*@-branchstate@*/ 
		}
	      /*@=branchstate@*/
	      break;
	    }
	  
	  /* Don't let erroneous code go by.  */
	  
	  if (kt->length < 0 && !CPPOPTIONS (pfile)->lang_asm
	      && cppReader_isPedantic (pfile))
	    {
	      cppReader_pedwarnLit (pfile,
				    cstring_makeLiteralTemp ("Invalid preprocessor directive name"));
	    }
	}

      c = cppReader_getC (pfile);
    }
  /* We're in the middle of a line.  Skip the rest of it.  */
  for (;;) {
    size_t old;

    switch (c)
      {
      case EOF:
	goto done;
      case '/':			/* possible comment */
	c = skip_comment (pfile, NULL);
	if (c == EOF)
	  goto done;
	/*@switchbreak@*/ break;
      case '\"':
      case '\'':
	cppReader_forward (pfile, -1);
	old = cpplib_getWritten (pfile);
	(void) cpplib_getToken (pfile);
	cppReader_setWritten (pfile, old);
	/*@switchbreak@*/ break;
      case '\\':
	/* Char after backslash loses its special meaning.  */
	if (cppReader_peekC (pfile) == '\n')
	  {
	    cppReader_forward (pfile, 1);
	  }

	/*@switchbreak@*/ break;
      case '\n':
	goto beg_of_line;
      }
    c = cppReader_getC (pfile);
  }
done:
  if (CPPOPTIONS (pfile)->output_conditionals) {
    static const char end_failed[] = "#endfailed\n";
    cppReader_puts (pfile, end_failed, constlen(end_failed));
    pfile->lineno++;
  }
  pfile->only_seen_white = 1;

  parseGotoMark (&line_start_mark, pfile);
  parseClearMark (&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 void
do_else (cppReader *pfile, /*@unused@*/ struct directive *keyword,
	 const /*@unused@*/ char *buf, const /*@unused@*/ char *limit)
{
  if (cppReader_isPedantic (pfile))
    {
      validate_else (pfile, cstring_makeLiteralTemp ("#else"));
    }

  cppReader_skipRestOfLine (pfile);

  if (pfile->if_stack == cppReader_getBufferSafe (pfile)->if_stack) {
    cppReader_errorLit (pfile,
		  cstring_makeLiteralTemp ("Preprocessor command #else is not within a conditional"));
    return;
  } else {
    /* #ifndef can't have its special treatment for containing the whole file
       if it has a #else clause.  */

    llassert (pfile->if_stack != NULL);

    pfile->if_stack->control_macro = 0;

    if (pfile->if_stack->type != T_IF && pfile->if_stack->type != T_ELIF)
      {
	cpp_setLocation (pfile);
	genppllerrorhint (FLG_PREPROC,
			  message ("Pre-processor directive #else after #else"),
			  message ("%q: Location of match",
				   fileloc_unparseRaw (pfile->if_stack->fname,
						       pfile->if_stack->lineno)));
      }

    pfile->if_stack->type = T_ELSE;
  }

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

/*
 * unstack after #endif command
 */

static void
do_endif (cppReader *pfile, /*@unused@*/ struct directive *keyword,
	  const /*@unused@*/ char *buf, const /*@unused@*/ char *limit)
{
  if (cppReader_isPedantic (pfile))
    {
      validate_else (pfile, cstring_makeLiteralTemp ("#endif"));
    }

  cppReader_skipRestOfLine (pfile);

  if (pfile->if_stack == cppReader_getBufferSafe (pfile)->if_stack)
    {
      cppReader_errorLit (pfile, cstring_makeLiteralTemp ("Unbalanced #endif"));
    }
  else
    {
      cppIfStackFrame *temp = pfile->if_stack;

      llassert (temp != NULL);

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

	  parseSetMark (&start_mark, pfile);

	  for (;;)
	    {
	      cppSkipHspace (pfile);
	      c = cppReader_getC (pfile);

	      if (c != '\n')
		break;
	    }

	  parseGotoMark (&start_mark, pfile);
	  parseClearMark (&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.  */
	      struct file_name_list *ifile = pfile->all_include_files;

	      for ( ; ifile != NULL; ifile = ifile->next)
		{
		  if (cstring_equal (ifile->fname, cppReader_getBufferSafe (pfile)->fname))
		    {
		      ifile->control_macro = temp->control_macro;
		      break;
		    }
		}
	    }
	}

      sfree (temp);
      output_line_command (pfile, 1, same_file);
    }
}

/* 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 (cppReader *pfile, cstring directive)
{
  int c;
  cppSkipHspace (pfile);
  c = cppReader_peekC (pfile);
  if (c != EOF && c != '\n')
    {
      cppReader_pedwarn (pfile,
		   message ("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.
*/

static enum cpp_token
cpplib_getTokenAux (cppReader *pfile, bool forceExpand)
{
  int c, c2, c3;
  size_t old_written = 0;
  int start_line, start_column;
  enum cpp_token token;
  struct cppOptions *opts = CPPOPTIONS (pfile);
  cppReader_getBufferSafe (pfile)->prev = cppReader_getBufferSafe (pfile)->cur;

get_next:
  c = cppReader_getC (pfile);
  DPRINTF (("Get next token: %c", c));

  if (c == EOF)
    {
    handle_eof:
      if (cppReader_getBufferSafe (pfile)->seen_eof)
	{
	  cppBuffer *buf = cppReader_popBuffer (pfile);

	  if (buf != cppReader_nullBuffer (pfile))
	    {
	      goto get_next;
	    }
	  else
	    {
	      return CPP_EOF;
	    }
	}
      else
	{
	  cppBuffer *next_buf = cppBuffer_prevBuffer (cppReader_getBufferSafe (pfile));
	  cppReader_getBufferSafe (pfile)->seen_eof = TRUE;

	  if (cstring_isDefined (cppReader_getBufferSafe (pfile)->nominal_fname)
	      && next_buf != cppReader_nullBuffer (pfile))
	    {
	      /* 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.  */
	      cppBuffer *cur_buffer = CPPBUFFER (pfile);
	      CPPBUFFER (pfile) = next_buf;
	      pfile->input_stack_listing_current = FALSE;
	      output_line_command (pfile, 0, leave_file);
	      CPPBUFFER (pfile) = cur_buffer;
	    }
	  return CPP_POP;
	}
    }
  else
    {
      long newlines;
      struct parse_marker start_mark;

      switch (c)
	{
	case '/':
	  if (cppReader_peekC (pfile) == '=')
	    {
	      goto op2;
	    }

	  if (opts->put_out_comments)
	    {
	      parseSetMark (&start_mark, pfile);
	    }

	  newlines = 0;
	  cppBuffer_getLineAndColumn (cppReader_fileBuffer (pfile),
				   &start_line, &start_column);
	  c = skip_comment (pfile, &newlines);
	  DPRINTF (("c = %c", c));
	  if (opts->put_out_comments && (c == '/' || c == EOF))
	    {
	      assertSet (&start_mark);
	      parseClearMark (&start_mark);
	    }

	  if (c == '/')
	    goto randomchar;
	  if (c == EOF)
	    {
	      cppReader_errorWithLine (pfile, start_line, start_column,
				       cstring_makeLiteral ("Unterminated comment"));
	      goto handle_eof;
	    }
	  /* Comments are equivalent to spaces.
	     For -traditional, a comment is equivalent to nothing.  */

	  if (opts->put_out_comments)
	    {
	      enum cpp_token res;

	      assertSet (&start_mark);
	      res = cpp_handleComment (pfile, &start_mark);
	      pfile->lineno += newlines;
	      return res;
	    }
	  else if (cppReader_isTraditional (pfile))
	    {
	      return CPP_COMMENT;
	    }
	  else
	    {
	      cpplib_reserve(pfile, 1);
	      cppReader_putCharQ (pfile, ' ');
	      return CPP_HSPACE;
	    }

	case '#':
	  if (!pfile->only_seen_white)
	    {
	      goto randomchar;
	    }

	  if (cppReader_handleDirective (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 */
	  cppBuffer_getLineAndColumn (cppReader_fileBuffer (pfile),
				      &start_line, &start_column);
	  old_written = cpplib_getWritten (pfile);
	string:
	  DPRINTF (("Reading string: %c", c));
	  cppReader_putChar (pfile, (char) c);
	  while (TRUE)
	    {
	      /* evans-2003-06-07
	      ** Because of ISO8859-1 characters in string literals, we need a special test here.
	      */

	      if (cppReader_reachedEOF (pfile)) 
		{
		  
		  DPRINTF (("Matches EOF!"));
		  if (cppBuffer_isMacro (CPPBUFFER (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.  */
		      cppBuffer *next_buf
			= cppBuffer_prevBuffer (cppReader_getBufferSafe (pfile));
		      (*cppReader_getBufferSafe (pfile)->cleanup)
			(cppReader_getBufferSafe (pfile), pfile);
		      CPPBUFFER (pfile) = next_buf;
		      continue;
		    }
		  
		  if (!cppReader_isTraditional (pfile))
		    {
		      cpp_setLocation (pfile);

		      setLine (long_toInt (start_line));
		      setColumn (long_toInt (start_column));
		      
		      if (pfile->multiline_string_line != long_toInt (start_line)
			  && pfile->multiline_string_line != 0)
			{
			  genppllerrorhint
			    (FLG_PREPROC,
			     message ("Unterminated string or character constant"),
			     message ("%q: Possible real start of unterminated constant",
				      fileloc_unparseRaw 
				      (fileloc_filename (g_currentloc),
				       pfile->multiline_string_line)));
			  pfile->multiline_string_line = 0;
			}
		      else
			{
			  genppllerror
			    (FLG_PREPROC,
			     message ("Unterminated string or character constant"));
			}
		    }
		  /*@loopbreak@*/ break;		  
		} 
	      else
		{
		  int cc = cppReader_getC (pfile); 
		  DPRINTF (("cc: %c [%d]", cc, cc));
		  DPRINTF (("putting char: %c", (char) cc));
		  cppReader_putChar (pfile, (char) 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 (cppReader_isTraditional (pfile))
			goto while2end;
		      if (c == '\'')
			{
			  goto while2end;
			}
		      if (cppReader_isPedantic (pfile)
			  && pfile->multiline_string_line == 0)
			{
			  cppReader_pedwarnWithLine
			    (pfile, long_toInt (start_line),
			     long_toInt (start_column),
			     cstring_makeLiteral ("String constant runs past end of line"));
			}
		      if (pfile->multiline_string_line == 0)
			{
			  pfile->multiline_string_line = start_line;
			}
		      
		      /*@switchbreak@*/ break;
		      
		    case '\\':
		      cc = cppReader_getC (pfile);
		      if (cc == '\n')
			{
			  /* Backslash newline is replaced by nothing at all.  */
			  pfile->lineno++; /* 2003-11-03: AMiller suggested adding this, but
					      its not clear why it is needed. */
			  cppReader_adjustWritten (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)
			    cppReader_putChar (pfile, (char) cc);
			}
		      /*@switchbreak@*/ break;
		      
		    case '\"':
		    case '\'':
		      if (cc == c)
			goto while2end;
		      /*@switchbreak@*/ break;
		    }
		}
	    }
	while2end:
	  pfile->lineno += count_newlines (pfile->token_buffer + old_written,
					   cpplib_getPWritten (pfile));
	  pfile->only_seen_white = 0;
	  return c == '\'' ? CPP_CHAR : CPP_STRING;

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

	case ':':
	  goto randomchar;

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

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

	case '-':
	  NEWLINE_FIX;
	  c2 = cppReader_peekC (pfile);
	  if (c2 == '-' || c2 == '=' || c2 == '>')
	    goto op2;
	  goto randomchar;

	case '<':
	  if (pfile->parsing_include_directive)
	    {
	      for (;;)
		{
		  cppReader_putChar (pfile, (char) c);
		  if (c == '>')
		    /*@loopbreak@*/ break;
		  c = cppReader_getC (pfile);
		  NEWLINE_FIX1 (c);
		  if (c == '\n' || c == EOF)
		    {
		      cppReader_errorLit (pfile,
				    cstring_makeLiteralTemp ("Missing '>' in \"#include <FILENAME>\""));
		      /*@loopbreak@*/ break;
		    }
		}
	      return CPP_STRING;
	    }
	  /*@fallthrough@*/
	case '>':
	  NEWLINE_FIX;
	  c2 = cppReader_peekC (pfile);
	  if (c2 == '=')
	    goto op2;
	  if (c2 != c)
	    goto randomchar;
	  cppReader_forward (pfile, 1);
	  cpplib_reserve (pfile, 4);
	  cppReader_putChar (pfile, (char) c);
	  cppReader_putChar (pfile, (char) c2);
	  NEWLINE_FIX;
	  c3 = cppReader_peekC (pfile);
	  if (c3 == '=')
	    cppReader_putCharQ (pfile, (char) cppReader_getC (pfile));
	  cppReader_nullTerminateQ (pfile);
	  pfile->only_seen_white = 0;
	  return CPP_OTHER;

	case '@':
	  DPRINTF (("Macro @!"));
	  if (cppReader_getBufferSafe (pfile)->has_escapes)
	    {
	      c = cppReader_getC (pfile);
	      DPRINTF (("got c: %c", c));
	      if (c == '-')
		{
		  if (pfile->output_escapes)
		    cppReader_puts (pfile, "@-", 2);
		  parse_name (pfile, cppReader_getC (pfile));
		  return CPP_NAME;
		}
	      else if (is_space [c])
		{
		  cpplib_reserve (pfile, 2);
		  if (pfile->output_escapes)
		    cppReader_putCharQ (pfile, '@');
		  cppReader_putCharQ (pfile, (char) c);
		  return CPP_HSPACE;
		}
	      else
		{
		  ;
		}
	    }
	  if (pfile->output_escapes)
	    {
	      cppReader_puts (pfile, "@@", 2);
	      return CPP_OTHER;
	    }
	  goto randomchar;
	case '.':
	  NEWLINE_FIX;
	  c2 = cppReader_peekC (pfile);
	  if (isdigit(c2))
	    {
	      cpplib_reserve(pfile, 2);
	      cppReader_putCharQ (pfile, '.');
	      c = cppReader_getC (pfile);
	      goto number;
	    }

	  /* FIXME - misses the case "..\\\n." */
	  if (c2 == '.' && cpp_peekN (pfile, 1) == '.')
	    {
	      cpplib_reserve(pfile, 4);
	      cppReader_putCharQ (pfile, '.');
	      cppReader_putCharQ (pfile, '.');
	      cppReader_putCharQ (pfile, '.');
	      cppReader_forward (pfile, 2);
	      cppReader_nullTerminateQ (pfile);
	      pfile->only_seen_white = 0;
	      return CPP_3DOTS;
	    }
	  goto randomchar;
	op2:
	  token = CPP_OTHER;
	  pfile->only_seen_white = 0;
        op2any: /* jumped to for \ continuations */
	  cpplib_reserve(pfile, 3);
	  cppReader_putCharQ (pfile, (char) c);

	  /* evans 2003-08-24: This is a hack to fix line output for \
	     continuations.  Someday I really should get a decent pre-processor! 
	  */

	  if (c == '\\') {
	    (void) cppReader_getC (pfile); /* skip the newline to avoid extra lines */
	  } else {
	    cppReader_putCharQ (pfile, (char) cppReader_getC (pfile)); 
	  }

	  cppReader_nullTerminateQ (pfile);
	  return token;

	case 'L':
	  NEWLINE_FIX;
	  c2 = cppReader_peekC (pfile);
	  if ((c2 == '\'' || c2 == '\"') && !cppReader_isTraditional (pfile))
	    {
	      cppReader_putChar (pfile, (char) c);
	      c = cppReader_getC (pfile);
	      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 (;;)
	    {
	      cpplib_reserve (pfile, 2);
	      cppReader_putCharQ (pfile, (char) c);
	      NEWLINE_FIX;
	      c = cppReader_peekC (pfile);
	      if (c == EOF)
		/*@loopbreak@*/ break;
	      if (!is_idchar[c] && c != '.'
		  && ((c2 != 'e' && c2 != 'E'
		       && ((c2 != 'p' && c2 != 'P') || cppReader_isC89 (pfile)))
		      || (c != '+' && c != '-')))
		/*@loopbreak@*/ break;
	      cppReader_forward (pfile, 1);
	      c2= c;
	    }

	  cppReader_nullTerminateQ (pfile);
	  pfile->only_seen_white = 0;
	  return CPP_NUMBER;
	
	case '_':
	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
        case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
        case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
        case 's': case 't': case 'u': case 'v': case 'w': case 'x':
        case 'y': case 'z':
	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
        case 'G': case 'H': case 'I': case 'J': case 'K':
        case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
	case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
	case 'Y': case 'Z':
        letter:
          {
	    hashNode hp;
	    char *ident;
	    size_t before_name_written = cpplib_getWritten (pfile);
	    size_t ident_len;
	    parse_name (pfile, c);
	    pfile->only_seen_white = 0;

	    if (pfile->no_macro_expand)
	      {
		DPRINTF (("Not expanding: %s", pfile->token_buffer));
		return CPP_NAME;
	      }

	    ident = pfile->token_buffer + before_name_written;
	    DPRINTF (("Ident: %s", ident));

	    ident_len = size_fromInt ((cpplib_getPWritten (pfile)) - ident);

	    hp = cpphash_lookupExpand (ident, ident_len, -1, forceExpand);

	    if (hp == NULL)
	      {
		DPRINTF (("No expand: %s %d", ident, ident_len));
		return CPP_NAME;
	      }

	    if (hp->type == T_DISABLED)
	      {
		DPRINTF (("Disabled!"));

		if (pfile->output_escapes)
		  { /* Return "@-IDENT", followed by '\0'.  */
		    int i;
		    cpplib_reserve (pfile, 3);
		    ident = pfile->token_buffer + before_name_written;
		    cppReader_adjustWritten (pfile, 2);

		    for (i = size_toInt (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;

		DPRINTF (("Arglist macro!"));

		/*
		** evans 2002-07-03: Moved this here (from below).
		**   This bug caused necessary whitespace to be lost
		**   when parsing parameterized macros without parameters.
		*/

		parseSetMark (&macro_mark, pfile); 

		while (cppBuffer_isMacro (CPPBUFFER (pfile)))
		  {
		    cppBuffer *next_buf;
		    cppSkipHspace (pfile);
		    if (cppReader_peekC (pfile) != EOF)
		      {
			DPRINTF (("Peeking!"));
			/*@loopbreak@*/ break;
		      }

		  next_buf = cppBuffer_prevBuffer (cppReader_getBufferSafe (pfile));
		  (*cppReader_getBufferSafe (pfile)->cleanup) (cppReader_getBufferSafe (pfile), pfile);
		  CPPBUFFER (pfile) = next_buf;
		  }

		/* parseSetMark (&macro_mark, pfile); */

		for (;;)
		  {
		    cppSkipHspace (pfile);
		    c = cppReader_peekC (pfile);
		    DPRINTF (("c: %c", c));
		    is_macro_call = c == '(';
		    if (c != '\n')
		      /*@loopbreak@*/ break;
		    cppReader_forward (pfile, 1);
		  }

		if (!is_macro_call)
		  {
		    parseGotoMark (&macro_mark, pfile);
		  }

		parseClearMark (&macro_mark);

		if (!is_macro_call)
		  {
		    DPRINTF (("not 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)
	      {
		size_t xbuf_len;
		char *xbuf;

		cppReader_setWritten (pfile, before_name_written);
		special_symbol (hp, pfile);
		xbuf_len = cpplib_getWritten (pfile) - before_name_written;
		xbuf = (char *) dmalloc (xbuf_len + 1);
		cppReader_setWritten (pfile, before_name_written);
		memcpy (xbuf, cpplib_getPWritten (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. 
		*/

		cpplib_macroExpand (pfile, hp);
		cppReader_setWritten (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.  */

	    llassert (pfile->buffer->rlimit != NULL);

	    if (pfile->buffer->rlimit - pfile->buffer->cur >= 3
		&& pfile->buffer->rlimit[-2] == '@'
		&& pfile->buffer->rlimit[-1] == ' ')
	      {
		int c1 = pfile->buffer->rlimit[-3];
		int cl2 = cpplib_bufPeek (cppBuffer_prevBuffer (CPPBUFFER (pfile)));

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


	case ' ':  case '\t':  case '\v':  case '\r':
	  for (;;)
	    {
	      cppReader_putChar (pfile, (char) c);
	      c = cppReader_peekC (pfile);
	      if (c == EOF || !is_hor_space[c])
		/*@loopbreak@*/ break;
	      cppReader_forward (pfile, 1);
	    }
	  return CPP_HSPACE;

        case '\\':
	  c2 = cppReader_peekC (pfile);
	  /* allow other stuff here if a flag is set? */
	  DPRINTF (("Got continuation!"));
	  if (c2 != '\n')
	    goto randomchar;
	  token = CPP_HSPACE;
	  goto op2any;

	case '\n':
	  cppReader_putChar (pfile, (char) 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;
	  cppReader_putChar (pfile, (char) c);
	  return token;
	}
    }

  /*@notreached@*/
  BADBRANCH;
}

enum cpp_token
cpplib_getToken (cppReader *pfile)
{
  return cpplib_getTokenAux (pfile, FALSE);
}

enum cpp_token
cpplib_getTokenForceExpand (cppReader *pfile)
{
  return cpplib_getTokenAux (pfile, TRUE);
}

/* Parse an identifier starting with C.  */

void
parse_name (cppReader *pfile, int c)
{
  for (;;)
    {
      if (!is_idchar[c])
	{
	  if (c == '\\' && cppReader_peekC (pfile) == '\n')
	    {
	      cppReader_forward (pfile, 2);
	      continue;
	    }

	  cppReader_forward (pfile, -1);
	  break;
	}

      if (c == '$' && cppReader_isPedantic (pfile))
	{
	  cppReader_pedwarnLit (pfile,
			  cstring_makeLiteralTemp ("`$' in identifier"));
	}

      cpplib_reserve(pfile, 2); /* One more for final NUL.  */
      cppReader_putCharQ (pfile, (char) c);
      c = cppReader_getC (pfile);

      if (c == EOF)
	break;
    }

  cppReader_nullTerminateQ (pfile);
}

/* 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;
  cstring map_from;
  cstring map_to;
};

/*@constant observer char *FILE_NAME_MAP_FILE*/
#define FILE_NAME_MAP_FILE "header.gcc"

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

static cstring read_filename_string (int ch, /*:open:*/ FILE *f)
{
  char *alloc, *set;
  size_t len;

  len = 20;
  set = alloc = dmalloc (len + 1);

  if (!is_space[ch])
    {
      *set++ = (char) ch;
      while ((ch = getc (f)) != EOF && !is_space[ch])
	{
	  if (set - alloc == size_toInt (len))
	    {
	      len *= 2;
	      alloc = drealloc (alloc, len + 1);
	      set = alloc + len / 2;
	      /*@-branchstate@*/ }

	  *set++ = (char) ch;
	} /*@=branchstate@*/
    }
  *set = '\0';
  check (ungetc (ch, f) != EOF);

  return cstring_fromChars (alloc);
}

/* This structure holds a linked list of file name maps, one per directory.  */

struct file_name_map_list
{
  /*@only@*/ struct file_name_map_list *map_list_next;
  /*@only@*/ cstring map_list_name;
  /*@null@*/ struct file_name_map *map_list_map;
};

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

static struct file_name_map *
read_name_map (cppReader *pfile, cstring dirname)
{
  struct file_name_map_list *map_list_ptr;
  cstring name;
  FILE *f;

  for (map_list_ptr = CPPOPTIONS (pfile)->map_list;
       map_list_ptr != NULL;
       map_list_ptr = map_list_ptr->map_list_next)
    {
      if (cstring_equal (map_list_ptr->map_list_name, dirname))
	{
	  return map_list_ptr->map_list_map;
	}
    }

  map_list_ptr = (struct file_name_map_list *) dmalloc (sizeof (*map_list_ptr));
  map_list_ptr->map_list_name = cstring_copy (dirname);
  map_list_ptr->map_list_map = NULL;

  name = cstring_copy (dirname);

  if (cstring_length (dirname) > 0)
    {
      name = cstring_appendChar (name, CONNECTCHAR);
    }

  name = cstring_concatFree1 (name, cstring_makeLiteralTemp (FILE_NAME_MAP_FILE));

  f = fileTable_openReadFile (context_fileTable (), name);
  cstring_free (name);

  if (f == NULL)
    {
      map_list_ptr->map_list_map = NULL;
    }
  else
    {
      int ch;

      while ((ch = getc (f)) != EOF)
	{
	  cstring 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 *) dmalloc (sizeof (*ptr));
	  ptr->map_from = from;

	  /* Make the real filename absolute.  */
	  if (cstring_length (to) > 1 
	      && osd_isConnectChar (cstring_firstChar (to)))
	    {
	      ptr->map_to = to;
	    }
	  else
	    {
	      ptr->map_to = cstring_copy (dirname);
	      ptr->map_to = cstring_appendChar (ptr->map_to, CONNECTCHAR);
	      ptr->map_to = cstring_concatFree (ptr->map_to, 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)
		{
		  /*@innerbreak@*/ break;
		}
	    }
	}

      assertSet (map_list_ptr->map_list_map);
      check (fileTable_closeFile (context_fileTable (),f) == 0);
    }

  map_list_ptr->map_list_next = pfile->opts->map_list;
  pfile->opts->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 (cppReader *pfile,
		   cstring fname,
		   struct file_name_list *searchptr)
{
  char *filename = cstring_toCharsSafe (fname);
  struct file_name_map *map;
  char *from;
  char *p, *dir;

  cstring_markOwned (fname);

  cpp_setLocation (pfile);

  if (context_getFlag (FLG_NEVERINCLUDE))
    {
      if (fileLib_isHeader (fname))
	{
	  return SKIP_INCLUDE;
	}
    }

  if ((searchptr != NULL) && ! searchptr->got_name_map)
    {
      searchptr->name_map = read_name_map (pfile,
					   !cstring_isEmpty (searchptr->fname)
					   ? searchptr->fname :
					   cstring_makeLiteralTemp ("."));
      searchptr->got_name_map = TRUE;
    }

  /* First check the mapping for the directory we are using.  */

  if ((searchptr != NULL)
      && (searchptr->name_map != NULL))
    {
      from = filename;

      if (!cstring_isEmpty (searchptr->fname))
	{
	  from += cstring_length (searchptr->fname) + 1;
	}

      for (map = searchptr->name_map;
	   map != NULL;
	   map = map->map_next)
	{
	  if (cstring_equal (map->map_from, cstring_fromChars (from)))
	    {
              if (cpp_skipIncludeFile (map->map_to))
              {
                return SKIP_INCLUDE;
              }
              return cpp_openIncludeFile (cstring_toCharsSafe (map->map_to));
	    }
	}
    }

  /*
  ** 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, CONNECTCHAR);
  if (p == NULL)
    {
      p = filename;
    }

  if ((searchptr != NULL)
      && (cstring_isDefined (searchptr->fname))
      && (size_toInt (cstring_length (searchptr->fname)) == p - filename)
      && !strncmp (cstring_toCharsSafe (searchptr->fname),
		   filename,
		   size_fromInt (p - filename)))
    {
      /* filename is in SEARCHPTR, which we've already checked.  */
      if (cpp_skipIncludeFile (cstring_fromChars (filename)))
      {
        return SKIP_INCLUDE;
      }
      return cpp_openIncludeFile (filename);
    }

  if (p == filename)
    {
      dir = mstring_copy (".");
      from = filename;
    }
  else
    {
      dir = (char *) dmalloc (size_fromInt (p - filename + 1));
      memcpy (dir, filename, size_fromInt (p - filename));
      dir[p - filename] = '\0';
      from = p + 1;
    }

  for (map = read_name_map (pfile, cstring_fromChars (dir));
       map != NULL;
       map = map->map_next)
    {
      if (cstring_equal (map->map_from, cstring_fromChars (from)))
	{
	  sfree (dir);
          if (cpp_skipIncludeFile (map->map_to))
          {
            return SKIP_INCLUDE;
          }
          return cpp_openIncludeFile (cstring_toCharsSafe (map->map_to));
	}
    }

  sfree (dir);

  if (cpp_skipIncludeFile (cstring_fromChars (filename)))
  {
    return SKIP_INCLUDE;
  }
  return cpp_openIncludeFile (filename);
}

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

static int
finclude (cppReader *pfile, int f,
	  cstring fname,
	  bool system_header_p,
	  /*@dependent@*/ struct file_name_list *dirptr)
{
  filemode mode;
  size_t size;
  long i;
  int length = 0;
  cppBuffer *fp;			/* For input stack frame */

  if (osd_file_mode_and_size (f, &mode, &size) < 0)
    {
      cppReader_perrorWithName (pfile, fname);
      check (osd_close (f) == 0);
      (void) cppReader_popBuffer (pfile);
      /*@-mustfree@*/
      return 0;
      /*@=mustfree@*/
    }

  fp = cppReader_getBufferSafe (pfile);

  /*@-temptrans@*/ /* fname shouldn't really be temp */
  fp->nominal_fname = fp->fname = fname;
  /*@=temptrans@*/

  fp->dir = dirptr;
  fp->system_header_p = system_header_p;
  fp->lineno = 1;
  fp->colno = 1;
  fp->cleanup = cppReader_fileCleanup;

  if (mode == OSD_MODEREGULAR)
    {
      sfree (fp->buf);
      fp->buf = (char *) dmalloc (size + 2);
      fp->alimit = fp->buf + size + 2;
      fp->cur = fp->buf;

      /* Read the file contents, knowing that size is an upper bound
	 on the number of bytes we can read.  */
      length = osd_readSafe (f, fp->buf, size_toInt (size));
      fp->rlimit = fp->buf + length;
      if (length < 0) goto nope;
    }
  else if (mode == OSD_MODEDIRECTORY)
    {
      cppReader_error (pfile,
		       message ("Directory specified where file is expected: %s", fname));
      check (osd_close (f) == 0);
      return 0;
    }
  else
    {
      /*
      ** Cannot count its file size before reading.
      ** First read the entire file into heap and
      ** copy them into buffer on stack.
      */

      size_t bsize = 2000;

      size = 0;

      sfree (fp->buf);
      fp->buf = (char *) dmalloc (bsize + 2);

      for (;;) {
	i = osd_readSafe (f, fp->buf + size, size_toInt (bsize - size));

	if (i < 0)
	  goto nope;      /* error! */
	size += i;

	if (size != bsize)
	  {
	    break;	/* End of file */
	  }

	bsize *= 2;
	fp->buf = (char *) drealloc (fp->buf, bsize + 2);
      }

      fp->cur = fp->buf;
      length = size_toInt (size);
    }

  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.  */
  check (osd_close (f) == 0);

  /* Must do this before calling trigraph_pcp, so that the correct file name
     will be printed in warning messages.  */

  pfile->input_stack_listing_current = FALSE;
  return 1;

 nope:

  cppReader_perrorWithName (pfile, fname);
  check (osd_close (f) == 0);
  sfree (fp->buf);
  return 1;
}

static void
cpplib_init (/*@out@*/ cppReader *pfile)
{
  memset ((char *) pfile, 0, sizeof (*pfile));

  pfile->get_token = cpplib_getToken;
  pfile->token_buffer_size = 200;
  pfile->token_buffer = (char *) dmalloc (pfile->token_buffer_size);
  pfile->all_include_files = NULL;

  assertSet (pfile);

  cppReader_setWritten (pfile, 0);

  pfile->system_include_depth = 0;
  pfile->timebuf = NULL;
  pfile->only_seen_white = 1;

  pfile->buffer = cppReader_nullBuffer (pfile);

  pfile->opts = (struct cppOptions *) dmalloc (sizeof (struct cppOptions));
}

void
cppReader_finish (/*@unused@*/ cppReader *pfile)
{
  ;
}

/* Free resources used by PFILE.
   This is the cppReader 'finalizer' or 'destructor' (in C++ terminology).  */

void
cppCleanup (/*@special@*/ cppReader *pfile) 
     /*@uses pfile@*/
     /*@releases pfile@*/
{
  DPRINTF (("cppCleanup!"));

  while (CPPBUFFER (pfile) != cppReader_nullBuffer (pfile))
    {
      (void) cppReader_popBuffer (pfile);
    }

  if (pfile->token_buffer != NULL)
    {
      sfree (pfile->token_buffer);
      pfile->token_buffer = NULL;
    }

  while (pfile->if_stack != NULL)
    {
      cppIfStackFrame *temp = pfile->if_stack;
      pfile->if_stack = temp->next;
      sfree (temp);
    }

  while (pfile->all_include_files != NULL)
    {
      struct file_name_list *temp = pfile->all_include_files;
      pfile->all_include_files = temp->next;
      /*@-dependenttrans@*/
      cstring_free (temp->fname);
      /*@=dependenttrans@*/
      sfree (temp);
    }

  /* evans 2002-07-12 */
  while (pfile->opts->map_list != NULL)
    {
      struct file_name_map_list *temp = pfile->opts->map_list;
      pfile->opts->map_list = pfile->opts->map_list->map_list_next;
      cstring_free (temp->map_list_name);
      sfree (temp);
    }

  while (pfile->opts->include != NULL)
    {
      struct file_name_list *temp = pfile->opts->include;
      pfile->opts->include = pfile->opts->include->next;
      /* cstring_free (temp->fname); */
      sfree (temp);
    }

  sfree (pfile->opts);
  pfile->opts = NULL;
  cpphash_cleanup ();
}

/* Initialize PMARK to remember the current position of PFILE.  */

void
parseSetMark (struct parse_marker *pmark, cppReader *pfile)
{
  cppBuffer *pbuf = cppReader_getBufferSafe (pfile);

  pmark->next = pbuf->marks;
  /*@-temptrans@*/
  pbuf->marks = pmark;
  /*@=temptrans@*/

  pmark->buf = pbuf;
  pmark->position = pbuf->cur - pbuf->buf;
  DPRINTF (("set mark: %d / %s", pmark->position, pbuf->cur));
}

/* Cleanup PMARK - we no longer need it.  */

void parseClearMark (struct parse_marker *pmark)
{
  struct parse_marker **pp = &pmark->buf->marks;

  for (; ; pp = &(*pp)->next)
    {
      llassert (*pp != NULL);
      if (*pp == pmark) break;
    }

  *pp = pmark->next;
}

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

void
parseGotoMark (struct parse_marker *pmark, cppReader *pfile)
{
  cppBuffer *pbuf = cppReader_getBufferSafe (pfile);

  if (pbuf != pmark->buf)
    {
      cpp_setLocation (pfile);
      llfatalbug (cstring_makeLiteral ("Internal error parseGotoMark"));
    }

  llassert (pbuf->buf != NULL);
  pbuf->cur = pbuf->buf + pmark->position;
  DPRINTF (("goto mark: %d / %s", pmark->position, pbuf->cur));
}

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

static void
parseMoveMark (struct parse_marker *pmark, cppReader *pfile)
{
  cppBuffer *pbuf = cppReader_getBufferSafe (pfile);

  if (pbuf != pmark->buf)
    {
      cpp_setLocation (pfile);
      llfatalerror (cstring_makeLiteral ("Internal error parseMoveMark"));
    }

  pmark->position = pbuf->cur - pbuf->buf;
  DPRINTF (("move mark: %d", pmark->position));
}

static void
handleCmdFlag (cppReader *pfile, /* const */ char *arg)
{
  llassert (CPPOPTIONS (pfile) != NULL);
  llassert (arg[0] == 'D' || arg[0] == 'U' || arg[0] == 'I');

  if (arg[0] == 'D') {
    cppReader_define (pfile, arg+1);
  } else if (arg[0] == 'U') {
    size_t sym_length;
    hashNode hp;

    sym_length = identifier_length (arg+1);
    if (!macro_name_is_valid (arg+1, sym_length))
      {
        cppReader_error (pfile, invalid_macro_message (arg+1, sym_length));
      }
    else
      {
        while ((hp = cpphash_lookup (arg+1, sym_length, -1)) != NULL)
          {
            cpphash_deleteMacro (hp);
          }
      }
  } else /* if (arg[0] == 'I') */ {
    if (mstring_equal (arg+1, "-")) { /* Special handle for "-I-". */
      CPPOPTIONS (pfile)->ignore_srcdir = TRUE;
      CPPOPTIONS (pfile)->first_bracket_include = NULL;
    } else {
      cppReader_appendDirIncludeChain (pfile, arg+1, FALSE);
    }
  }
}

void cpplib_initializeReader (cppReader *pfile, cstringList *cmdArgs)
{
  struct cppOptions *opts;

  cpplib_init (pfile);

  opts = CPPOPTIONS (pfile);
  cppOptions_init (opts);

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

  /* CppReader_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) {
    char *p, *r;
    r = p = (char *) dmalloc (strlen (predefs) + 1);
    strcpy (p, predefs);

    while (*p)
      {
        char *q;

        SKIP_WHITE_SPACE (p);

	/* expect -D options.  */
	if (p[0] != '-' || p[1] != 'D')
          {
            llfatalbug (message ("Unexpected predefine option %s",
                        cstring_fromChars(p)));
          }

        q = &p[2];
        while (*p && !is_hor_space [(int) *p])
          {
            p++;
          }

        if (*p != 0)
          {
            *p++= 0;
          }

        if (opts->debug_output)
          {
            output_line_command (pfile, 0, same_file);
          }

        cppReader_define (pfile, q);

        SKIP_WHITE_SPACE (p);
      }

    sfree (r);
  }

  /* Process user -D, -U and -I options, if any. */
  DPRINTF (("Pass through: %s", cstringSList_unparse (*cmdArgs)));
  cstringList_elements (*cmdArgs, thisarg)
    {
      handleCmdFlag (pfile, cstring_toCharsSafe (thisarg));
    } 
  end_cstringList_elements;

  pfile->done_initializing = TRUE;

  cppReader_appendPathListIncludeChain(pfile,
# if defined(WIN32) || defined(OS2)
          getenv ("INCLUDE"),
# else
          getenv ("CPATH"),
# endif
          FALSE);

  /* Unless -nostdinc,
   tack on the standard include file dirs to the specified list */
  if (!opts->no_standard_includes)
    {
      cppReader_appendIncludeChain (pfile, opts->before_system,
                                    opts->last_before_system, TRUE);

      cppReader_appendPathListIncludeChain(pfile, getenv ("C_INCLUDE_PATH"), TRUE);

      cppReader_appendPathListIncludeChain(pfile, context_getString (FLG_SYSTEMDIRS), TRUE);

      /* Tack the after_include chain at the end of the include chain.  */
      cppReader_appendIncludeChain (pfile, opts->after_include,
                                    opts->last_after_include, TRUE);
    }

  /* With -v, print the list of dirs to search.  */
  if (opts->verbose)
    {
      struct file_name_list *p;
      fprintf (stderr, "#include \"...\" search starts here:\n");

      for (p = opts->include; p != NULL; p = p->next) {
        if (p == opts->first_bracket_include)
          fprintf (stderr, "#include <...> search starts here:\n");
        if (p == opts->first_system_include)
          fprintf (stderr, "system search starts here:\n");
        fprintf (stderr, " %s\n", cstring_toCharsSafe (p->fname));
      }
      fprintf (stderr, "End of search list.\n");
    }
}

int cppReader_startProcess (cppReader *pfile, cstring fname)
{
  cppBuffer *fp;
  int f;
  struct cppOptions *opts = CPPOPTIONS (pfile);

  fp = cppReader_pushBuffer (pfile, NULL, 0);

  if (fp == NULL)
    {
      return 0;
    }

  if (opts->in_fname == NULL)
    {
      opts->in_fname = cstring_makeLiteralTemp ("");
    }

  fp->fname = opts->in_fname;
  fp->nominal_fname = fp->fname;
  fp->lineno = 0;

  /* Copy the entire contents of the main input file into
     the stacked input buffer previously allocated for it.  */

  if (cstring_isEmpty (fname))
    {
      fname = cstring_makeLiteralTemp ("");
      f = 0;
    }
  else if ((f = osd_openForReading (cstring_toCharsSafe (fname))) < 0)
    {
      cppReader_error (pfile,
		       message ("Error opening %s for reading: %s",
				fname, lldecodeerror (errno)));

      return 0;
    }
  else
    {
      ;
    }

  if (finclude (pfile, f, fname, FALSE, NULL))
    {
      output_line_command (pfile, 0, same_file);
    }

  return 1;
}

static /*@exposed@*/ /*@null@*/ cppBuffer *cppReader_getBuffer (cppReader *pfile)
{
  return pfile->buffer;
}

/*@exposed@*/ cppBuffer *cppReader_getBufferSafe (cppReader *pfile)
{
  llassert (pfile->buffer != NULL);
  return pfile->buffer;
}

/*@exposed@*/ char *cppLineBase (cppBuffer *buf)
{
  llassert (buf->buf != NULL);
  return (buf->buf + buf->line_base);
}

int cpplib_bufPeek (cppBuffer *buf)
{
  if (buf->cur == NULL || buf->rlimit == NULL) {
    return EOF;
  }

  if (buf->cur < buf->rlimit) {
    return *(buf->cur);
  }

  return EOF;
}

bool cppBuffer_isMacro (cppBuffer *buf)
{
  if (buf != NULL)
    {
      return (buf->cleanup == cppReader_macroCleanup);
    }

  return FALSE;
}

static void cpp_setLocation (cppReader *pfile)
{
  fileloc res;

  if (pfile->buffer != NULL)
    {
      cppBuffer *buf;
      fileId fid;

      buf = cppReader_getBufferSafe (pfile);

      if (cstring_isDefined (buf->nominal_fname))
	{
	  DPRINTF (("Looking up: %s", buf->nominal_fname));
	  
	  if (fileTable_exists (context_fileTable (), buf->nominal_fname))
	    {
	      fid = fileTable_lookup (context_fileTable (), buf->nominal_fname);
	    }
	  else
	    {
	      DPRINTF (("Trying %s", buf->fname));

	      fid = fileTable_lookup (context_fileTable (), buf->fname);
	    }
	}
      else
	{
	  fid = fileTable_lookup (context_fileTable (), buf->fname);
	}
      
      if (fileId_isValid (fid))
	{
	  res = fileloc_create (fid, buf->lineno, 1);
	}
      else
	{
	  res = fileloc_createBuiltin ();
	}
    }
  else
    {
      res = fileloc_createBuiltin ();
    }

  fileloc_free (g_currentloc);
  g_currentloc = res;
}

static bool notparseable = FALSE;  /* preceeded by @notparseable@ */
static bool notfunction = FALSE;   /* preceeded by @notfunction@ */
static bool expectiter = FALSE;    /* preceeded by @iter@ */
static bool expectenditer = FALSE; /* second after @iter@ */
static bool expectconstant = FALSE;/* preceeded by @constant@ */

/*
** Returns true if the macro should be checked, false
** if it should be expanded normally.
*/

static bool cpp_shouldCheckMacro (cppReader *pfile, const char *p)
{
  cpp_setLocation (pfile);

  DPRINTF (("Should check macro? %s", p));

  if (expectiter || expectconstant || expectenditer)
    {
      if (expectiter)
	{
	  expectiter = FALSE;
	  expectenditer = TRUE;
	}
      else
	{
	  expectiter = FALSE;
	  expectconstant = FALSE;
	  expectenditer = FALSE;
	}

      if (notfunction || notparseable)
	{
	  notfunction = FALSE;
	  notparseable = FALSE;
	  return FALSE;
	}
      return TRUE;
    }

  llassert (*p == '#');
  p++;

  SKIP_WHITE_SPACE (p);
  llassert (strncmp (p, "define", 6) == 0);

  p += 6;
  SKIP_WHITE_SPACE (p);

  return cpp_shouldCheckMacroAux (p);
}

static bool cpp_shouldCheckMacroAux (const char *p)
{
  bool checkmacro;
  bool hasParams;
  bool nocontent;
  cstring sname;

  {
    char c;
    const char* q = p;

    while (((c = *q) != '\0')
           && !is_hor_space[(int)c]
           && c != '\\' && c != '('
           && !iscntrl (c))
      {
        q++;
      }

    sname = cstring_copyLength (p, (size_t) (q - p));

    hasParams = (*q == '(');

    SKIP_WHITE_SPACE (q);
    nocontent = (*q == '\0');
  }

  DPRINTF (("Check macro: %s", sname));

  checkmacro = FALSE;
  if (notparseable)
    {
      notparseable = FALSE;
    }
  else if (notfunction)
    {
      notfunction = FALSE;
    }
  else if (fileloc_isStandardLib (g_currentloc))
    {
      ;
    }
  else if (usymtab_existsReal (sname))
    {
      uentry ue = usymtab_lookup (sname);

      DPRINTF (("Lookup macro: %s", uentry_unparse (ue)));

      if (fileloc_isPreproc (uentry_whereLast (ue)))
        {
          goto macroDne;
        }
      else
        {
          if (uentry_isSpecified (ue))
            {
              checkmacro = context_getFlag (FLG_SPECMACROS);
            }
          else
            {
              if (hasParams)
                {
                  checkmacro = context_getFlag (FLG_LIBMACROS)
                    || context_getFlag (FLG_FCNMACROS);
                }
            }
        }
    }
  else
    {
      DPRINTF (("Macro doesn't exist"));

    macroDne:
      if (fileloc_isSystemFile (g_currentloc)
          && context_getFlag (FLG_SYSTEMDIREXPAND))
        {
          DPRINTF (("Don't check"));
        }
      else
        {
          uentry le;
          
          if (hasParams)
            {
              DPRINTF (("Has params..."));

              if (context_getFlag (FLG_FCNMACROS))
                {
                  if (usymtab_exists (sname))
                    {
                      /*
                      ** only get here is macro is redefined
                      ** error reported elsewhere
                      */

                      DPRINTF (("It exists!"));
                    }
                  else
                    {
                      /*
                      ** We make it a forward function, since it might be declared elsewhere.
                      ** After all headers have been processed, we should check the forward
                      ** functions.
                      */

                      fileloc loc = fileloc_makePreproc (g_currentloc);
                      /* the line is off-by-one, since the newline was already read */
                      decLine ();
                      le = uentry_makeForwardFunction (sname,
                                                       typeId_invalid, loc);
                      fileloc_free (loc);
                      incLine ();

                      /* Do not define here! */

                      (void) usymtab_addGlobalEntry (le);
                    }

                  checkmacro = TRUE;
                }
            }
          else
            {
              DPRINTF (("No params"));

              if (context_getFlag (FLG_CONSTMACROS))
                {
                  if (!usymtab_exists (sname))
                    {
                      fileloc loc = fileloc_makePreproc (g_currentloc);
                      DPRINTF (("Make constant: %s", sname));
                      le = uentry_makeMacroConstant (sname, ctype_unknown, loc);
                      /* Note: don't free loc! */
                      (void) usymtab_addGlobalEntry (le);
                    }

                  checkmacro = !nocontent;
                }
            }

          if (checkmacro && usymtab_existsType (sname))
            {
              DPRINTF (("Making false..."));
              decLine ();
              ppllerror (message ("Specified type implemented as macro: %s", sname));
              checkmacro = FALSE;
              incLine ();
            }
        }
    }

  if (!checkmacro)
    {
      fileloc tloc = fileloc_makePreproc (g_currentloc);

      if (usymtab_exists (sname))
	{
	  uentry ue = usymtab_lookupExpose (sname);
	  uentry_setDefined (ue, tloc);
	  uentry_setUsed (ue, fileloc_undefined);
	}
      else
	{
	  (void) usymtab_addGlobalEntry (uentry_makeExpandedMacro (sname, tloc));
	}

      fileloc_free (tloc);
    }

  cstring_free (sname);

  DPRINTF (("Returning: %s", bool_unparse (checkmacro)));
  return checkmacro;
}

static enum cpp_token
cpp_handleComment (cppReader *pfile, struct parse_marker *smark)
{
  cppBuffer *pbuf = cppReader_getBufferSafe (pfile);
  char *start;
  int len;
  fileloc loc;
  bool eliminateComment = FALSE;

  llassert (pbuf->buf != NULL);

  start = pbuf->buf + smark->position;

  llassert (pbuf->cur != NULL);
  len = pbuf->cur - start;

  if (start[0] == '*'
      && start[1] == context_getCommentMarkerChar ())
    {
      int i;
      char *scomment = start + 2;
      char savec = start[len];
      
      cpp_setLocation (pfile);
      loc = fileloc_copy (g_currentloc);

      start[0] = BEFORE_COMMENT_MARKER[0];
      start[1] = BEFORE_COMMENT_MARKER[1];

      llassert (start[len - 2] == '*');
      start[len - 2] = AFTER_COMMENT_MARKER[0];

      llassert (start[len - 1] == '/');
      start[len - 1] = AFTER_COMMENT_MARKER[1];

      cpplib_reserve(pfile, size_fromInt (1 + len));
      cppReader_putCharQ (pfile, ' ');

      cpp_setLocation (pfile);

      start[len] = '\0';

      if (mstring_containsString (scomment, "/*"))
	{
	  (void) cppoptgenerror 
	    (FLG_NESTCOMMENT,
	     message ("Comment starts inside syntactic comment: %s", 
		      cstring_fromChars (scomment)),
	     pfile);
	}

      start[len] = savec;

      if (mstring_equalPrefix (scomment, "ignore"))
	{
	  if (!context_getFlag (FLG_NOCOMMENTS))
	    {
	      context_enterSuppressRegion (loc);
	    }
	}
      else if (mstring_equalPrefix (scomment, "end"))
	{
	  if (!context_getFlag (FLG_NOCOMMENTS))
	    {
	      context_exitSuppressRegion (loc);
	    }
	}
      else if (mstring_equalPrefix (scomment, "notparseable"))
	{
	  notparseable = TRUE;
	  eliminateComment = TRUE;
	}
      else if (mstring_equalPrefix (scomment, "notfunction"))
	{
	  notfunction = TRUE;
	  eliminateComment = TRUE;
	}
      else if (mstring_equalPrefix (scomment, "iter"))
	{
	  expectiter = TRUE;
	}
      else if (mstring_equalPrefix (scomment, "function"))
	{
	  ;
	}
      else if (mstring_equalPrefix (scomment, "constant"))
	{
	  expectconstant = TRUE;
	}
      else
	{
	  char sChar = *scomment;

	  if (sChar == '='
	      || sChar == '-'
	      || sChar == '+')
	    {
	      char *rest = scomment + 1;
              static const char commchar[] = "commentchar";

	      if (mstring_equalPrefix (rest, commchar))
		{
		  eliminateComment = TRUE;

		  if (sChar == '=')
		    {
		      ppllerror (cstring_makeLiteral
				 ("Cannot restore commentchar"));
		    }
		  else if (!is_space[(int) scomment[constlen(commchar) + 1]])
                    {
                      ppllerror
                        (message
                         ("Syntactic commentchar comment is not followed by a "
                          "whitespace character: %c",
                          scomment[1 + constlen(commchar)]));
                    }
                  else if (scomment[constlen(commchar) + 1 + 1] == '\0')
                    {
                      ppllerror (cstring_makeLiteral
                                 ("Cannot set commentchar to NUL"));
                    }
                  else
                    {
                      context_setCommentMarkerChar (scomment[constlen(commchar) + 1 + 1]);
                    }
		}
	      else if (mstring_equalPrefix (rest, "nestcomment"))
		{
		  /* fix from Mike Miller <MikeM@xata.com> */
		  context_fileSetFlag (FLG_NESTCOMMENT,
				       ynm_fromCodeChar (sChar),
				       loc);
		}
	      else if (mstring_equalPrefix (rest, "namechecks"))
		{
		  context_fileSetFlag (FLG_NAMECHECKS,
				       ynm_fromCodeChar (sChar),
				       loc);
		}
	      else if (mstring_equalPrefix (rest, "macroredef"))
		{
		  context_fileSetFlag (FLG_MACROREDEF,
				       ynm_fromCodeChar (sChar),
				       loc);
		}
	      else if (mstring_equalPrefix (rest, "usevarargs"))
		{
		  context_fileSetFlag (FLG_USEVARARGS,
				       ynm_fromCodeChar (sChar),
				       loc);
		}
	      else if (mstring_equalPrefix (rest, "nextlinemacros"))
		{
		  context_fileSetFlag (FLG_MACRONEXTLINE,
				       ynm_fromCodeChar (sChar),
				       loc);
		}
	      else if (mstring_equalPrefix (rest, "allmacros")
		       || mstring_equalPrefix (rest, "fcnmacros")
		       || mstring_equalPrefix (rest, "constmacros"))
		{
		  flagcode fl;

		  if (mstring_equalPrefix (rest, "allmacros"))
		    {
		      fl = FLG_ALLMACROS;
		    }
		  else if (mstring_equalPrefix (rest, "fcnmacros"))
		    {
		      fl = FLG_FCNMACROS;
		    }
		  else
		    {
		      llassert (mstring_equalPrefix (rest, "constmacros"));
		      fl = FLG_CONSTMACROS;
		    }

		  context_fileSetFlag (fl, ynm_fromCodeChar (sChar), loc);
		  notfunction = FALSE;
		}
	      else
		{
		  ;
		}
	    }
	  else
	    {
	      ;
	    }
	}

      if (eliminateComment)
	{
	  goto removeComment;
	}

      /* Replaces comment char's in start with spaces */

      for (i = 2; i < len - 2; i++)
	{
	  if (start[i] == BEFORE_COMMENT_MARKER[0]
	      || start[i] == BEFORE_COMMENT_MARKER[1]
	      || start[i] == context_getCommentMarkerChar ())
	    {
	      start[i] = ' ';
	    }
	}

      cppReader_putStrN (pfile, start, size_fromInt (len));
      parseClearMark (smark);
      return CPP_COMMENT;
    }
  else
    {
    removeComment:
      {
	int i;

	/*
	** Output the comment as all spaces so line/column
	** in output file is still correct.
	*/

	char c = ' ';
	cstring lintcomment;

	if (!context_getFlag (FLG_LINTCOMMENTS))
	  {
	    lintcomment = cstring_undefined;
          }
        else if (mstring_equalPrefix (start, "*NOTREACHED*/"))
          {
            lintcomment = cstring_makeLiteralTemp ("l_notreach");
          }
        else if (mstring_equalPrefix (start, "*PRINTFLIKE*/"))
          {
            lintcomment = cstring_makeLiteralTemp ("l_printfli");
          }
        else if (mstring_equalPrefix (start, "*FALLTHROUGH*/"))
          {
            lintcomment = cstring_makeLiteralTemp ("l_fallthrou");
          }
        else if (mstring_equalPrefix (start, "*ARGSUSED*/"))
          {
            lintcomment = cstring_makeLiteralTemp ("l_argsus");
          }
        else if (mstring_equalPrefix (start, "*FALLTHRU*/"))
          {
            lintcomment = cstring_makeLiteralTemp ("l_fallth");
          }
        else
          {
            lintcomment = cstring_undefined;
          }

	if (cstring_isDefined (lintcomment))
	  {
	    c = BEFORE_COMMENT_MARKER[0];
	    start[0] = BEFORE_COMMENT_MARKER[1];

	    llassert (size_toLong (cstring_length (lintcomment)) == len - 3);

	    for (i = 1; i < len - 2; i++)
	      {
		start[i] = cstring_getChar (lintcomment, size_fromInt (i));
	      }
	    
	    start[len - 2] = AFTER_COMMENT_MARKER[0];
	    start[len - 1] = AFTER_COMMENT_MARKER[1];
	  }
	else
	  {
	    /* Replaces  char's in start with spaces */
	    for (i = 0; i < len; i++)
	      {
		if (start[i] == '/'
		    && i < len - 1
		    && start[i + 1] == '*') {
		  (void) cppoptgenerror 
		    (FLG_NESTCOMMENT,
		     message ("Comment starts inside comment"),
		     pfile);
		}
		
		if (start[i] != '\n')
		  {
		    start[i] = ' ';
		  }
	      }
	  }

	cpplib_reserve (pfile, size_fromInt (1 + len));
	cppReader_putCharQ (pfile, c);
	cppReader_putStrN (pfile, start, size_fromInt (len));
	parseClearMark (smark);
	return CPP_COMMENT;
      }
    }
}

static int cpp_openIncludeFile (char *filename)
{
  int res = osd_openForReading (filename);

  /* evans 2001-08-23: was (res) - open returns -1 on error! reported by Robin Watts */
  if (res >= 0)
  {
    if (!fileTable_exists (context_fileTable (),
          cstring_fromChars (filename)))
    {
      fileType ftype = context_inXHFile () ? FILE_XH : FILE_HEADER;
      (void) fileTable_addFile (context_fileTable (), ftype,
              cstring_fromChars (filename));
    }
    else
    {
      DPRINTF (("File already exists: %s", filename));
    }
  }

  return res;
}

static bool cpp_skipIncludeFile (cstring fname)
{
  if (context_isSystemDir (fname))
    {
      DPRINTF (("System dir: %s", fname));

      if (fileLib_isSkipHeader (fname))
	{
	  DPRINTF (("Skip include TRUE: %s", fname));
	  return TRUE;
	}
      
      if (context_getFlag (FLG_SKIPSYSHEADERS))
	{
	  /*
	  ** 2003-04-18: Patch from Randal Parsons
	  */

	  /*
	  ** Don't skip include file unless the file actually exists.  
	  ** It may be in a different directory.
	  */

          if (osd_fileIsReadable(fname))
            {
	      DPRINTF (("Skip include TRUE: %s", fname));
	      return TRUE;
            }
          else
            {
	      ; /* Keep looking... */
            }
	}
    }

  if (context_getFlag (FLG_SINGLEINCLUDE))
    {
      fname = osd_removePreDirs (fname);

      osd_pathMakePosix (fname);

      if (fileTable_exists (context_fileTable (), fname))
	{
	  DPRINTF (("Skip include TRUE: %s", fname));
	  return TRUE;
	}
    }

  DPRINTF (("Skip include FALSE: %s", fname));
  return FALSE;
}

static int cpp_peekN (cppReader *pfile, int n)
{
  cppBuffer *buf = cppReader_getBufferSafe (pfile);

  llassert (buf->cur != NULL);

  return (buf->rlimit - buf->cur >= (n)
	  ? buf->cur[n]
	  : EOF);
}

cppBuffer *cppBuffer_prevBuffer (cppBuffer *buf)
{
  return buf + 1;
}

void cppBuffer_forward (cppBuffer *buf, int n)
{
  llassert (buf->cur != NULL);
  buf->cur += n;
}

/*@=bufferoverflowhigh@*/
/*@=bounds@*/
