OSDN Git Service

contrib:
[pf3gnuchains/gcc-fork.git] / gcc / tradcpp.c
index 1117748..e5e0935 100644 (file)
@@ -1,5 +1,5 @@
 /* C Compatible Compiler Preprocessor (CCCP)
-Copyright (C) 1986, 1987, 1989, 2000 Free Software Foundation, Inc.
+Copyright (C) 1986, 1987, 1989, 2000, 2001 Free Software Foundation, Inc.
                     Written by Paul Rubin, June 1986
                    Adapted to ANSI C, Richard Stallman, Jan 1987
                    Dusted off, polished, and adapted for use as traditional
@@ -24,6 +24,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "version.h"
 #include "cppdefault.h"
 #include "tradcpp.h"
+#include "mkdeps.h"
 
 typedef unsigned char U_CHAR;
 
@@ -40,12 +41,33 @@ size_t max_include_len;
 
 int put_out_comments = 0;
 
+/* mkdeps.h opaque structure that encapsulates dependency information.  */
+struct deps *deps;
+
 /* Nonzero means print the names of included files rather than
    the preprocessed output.  1 means just the #include "...",
    2 means #include <...> as well.  */
 
 int print_deps = 0;
 
+/* Nonzero means print dummy targets for each header file.  */
+
+int print_deps_phony_targets = 0;
+
+/* If true, fopen (deps_file, "a") else fopen (deps_file, "w").  */
+
+int deps_append = 0;
+
+/* File name which deps are being written to.  This is 0 if deps are
+   being written to stdout.  */
+
+const char *deps_file = 0;
+
+/* Nonzero if missing .h files in -M output are assumed to be
+   generated files and not errors.  */
+
+int deps_missing_files = 0;
+       
 /* Nonzero means don't output line number information.  */
 
 int no_line_commands;
@@ -60,6 +82,9 @@ int dump_macros;
 
 int inhibit_warnings = 0;
 
+/* Non-0 means don't output the preprocessed program.  */
+int inhibit_output = 0;
+
 /* Nonzero means warn if slash-star appears in a comment.  */
 
 int warn_comments;
@@ -188,14 +213,22 @@ struct definition {
   const U_CHAR *argnames;
 };
 
+/* Chained list of answers to an assertion.  */
+struct answer
+{
+  struct answer *next;
+  const unsigned char *answer;
+  size_t len;
+};
+
 /* different kinds of things that can appear in the value field
    of a hash node.  Actually, this may be useless now. */
 union hashval {
   const char *cpval;
   DEFINITION *defn;
+  struct answer *answers;
 };
 
-
 /* The structure of a node in the hash table.  The hash table
    has entries for all tokens defined by #define commands (type T_MACRO),
    plus some special tokens like __LINE__ (these each have their own
@@ -215,6 +248,10 @@ enum node_type {
  T_UNDEF,      /* `#undef' */
  T_LINE,       /* `#line' */
  T_ENDIF,      /* `#endif' */
+ T_ERROR,      /* `#error' */
+ T_WARNING,    /* `#warning' */
+ T_ASSERT,     /* `#assert' */
+ T_UNASSERT,   /* `#unassert' */
  T_SPECLINE,   /* special symbol `__LINE__' */
  T_DATE,       /* `__DATE__' */
  T_FILE,       /* `__FILE__' */
@@ -242,6 +279,17 @@ struct hashnode {
 
 typedef struct hashnode HASHNODE;
 
+static HASHNODE *parse_assertion PARAMS ((const unsigned char *,
+                                         const unsigned char *,
+                                         struct answer **, int));
+static struct answer **find_answer PARAMS ((HASHNODE *,
+                                           const struct answer *));
+static int parse_answer PARAMS ((const unsigned char *, const unsigned char *,
+                                struct answer **, int));
+static unsigned char *canonicalize_text PARAMS ((const unsigned char *,
+                                                const unsigned char *,
+                                                const unsigned char **));
+
 /* Some definitions for the hash table.  The hash function MUST be
    computed as shown in hashf () below.  That is because the rescan
    loop computes the hash value `on the fly' for most tokens,
@@ -258,7 +306,7 @@ HASHNODE *hashtab[HASHSIZE];
 
 struct directive {
   int length;                  /* Length of name */
-  void (*func) PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+  void (*func) PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
                                /* Function to handle directive */
   const char *name;            /* Name of directive */
   enum node_type type;         /* Code which describes which directive. */
@@ -305,15 +353,21 @@ struct arglist {
 
 /* Function prototypes.  */
 
-static void do_define  PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-static void do_line    PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-static void do_include PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-static void do_undef   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-static void do_if      PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-static void do_xifdef  PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-static void do_else    PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-static void do_elif    PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-static void do_endif   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
+static void do_define  PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_error   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_warning PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_line    PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_include PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_undef   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_if      PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_ifdef   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_ifndef  PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_else    PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_elif    PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_endif   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_assert  PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_unassert        PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *));
+static void do_xifdef  PARAMS ((U_CHAR *, U_CHAR *, enum node_type));
 
 static struct hashnode *install PARAMS ((const U_CHAR *, int, enum node_type, int));
 static int hashf                PARAMS ((const U_CHAR *, int, int));
@@ -351,15 +405,18 @@ static void output_line_command PARAMS ((FILE_BUF *, FILE_BUF *,
 
 static int eval_if_expression  PARAMS ((const U_CHAR *, int));
 
-static void initialize_char_syntax     PARAMS ((void));
+static void output_deps                PARAMS ((void));
 static void initialize_builtins        PARAMS ((void));
-static void make_definition    PARAMS ((const U_CHAR *));
-static void make_undef         PARAMS ((U_CHAR *));
+static void run_directive      PARAMS ((const char *, size_t,
+                                        enum node_type));
+static void make_definition    PARAMS ((const char *));
+static void make_undef         PARAMS ((const char *));
+static void make_assertion     PARAMS ((const char *));
 
 static void grow_outbuf        PARAMS ((FILE_BUF *, int));
 static int handle_directive    PARAMS ((FILE_BUF *, FILE_BUF *));
 static void finclude           PARAMS ((int, const char *, FILE_BUF *));
-static void deps_output                PARAMS ((const char *, int));
+static void init_dependency_output PARAMS ((void));
 static void rescan             PARAMS ((FILE_BUF *, int));
 static void newline_fix                PARAMS ((U_CHAR *));
 static void name_newline_fix   PARAMS ((U_CHAR *));
@@ -384,27 +441,22 @@ struct directive directive_table[] = {
   {  6, do_define,  "define",  T_DEFINE  },
   {  7, do_include, "include", T_INCLUDE },
   {  5, do_endif,   "endif",   T_ENDIF   },
-  {  5, do_xifdef,  "ifdef",   T_IFDEF   },
+  {  5, do_ifdef,   "ifdef",   T_IFDEF   },
   {  2, do_if,      "if",      T_IF,     },
   {  4, do_else,    "else",    T_ELSE    },
-  {  6, do_xifdef,  "ifndef",  T_IFNDEF  },
+  {  6, do_ifndef,  "ifndef",  T_IFNDEF  },
   {  5, do_undef,   "undef",   T_UNDEF   },
   {  4, do_line,    "line",    T_LINE    },
   {  4, do_elif,    "elif",    T_ELIF    },
+  {  5, do_error,   "error",   T_ERROR   },
+  {  7, do_warning, "warning", T_WARNING },
+  {  6, do_assert,  "assert",  T_ASSERT  },
+  {  8, do_unassert,"unassert",T_UNASSERT},
   {  -1, 0, "", T_UNUSED},
 };
 
-/* table to tell if char can be part of a C identifier. */
-U_CHAR is_idchar[256];
-/* table to tell if char can be first char of a c identifier. */
-U_CHAR is_idstart[256];
-/* table to tell if c is horizontal space.  */
-U_CHAR is_hor_space[256];
-/* table to tell if c is horizontal or vertical space.  */
-U_CHAR is_space[256];
-
-#define SKIP_WHITE_SPACE(p) do { while (is_hor_space[*p]) p++; } while (0)
-#define SKIP_ALL_WHITE_SPACE(p) do { while (is_space[*p]) p++; } while (0)
+#define SKIP_WHITE_SPACE(p) do { while (is_nvspace(*p)) p++; } while (0)
+#define SKIP_ALL_WHITE_SPACE(p) do { while (is_space(*p)) p++; } while (0)
   
 int errors = 0;                        /* Error counter for exit code */
 
@@ -426,23 +478,20 @@ struct if_stack {
 typedef struct if_stack IF_STACK_FRAME;
 IF_STACK_FRAME *if_stack = NULL;
 
-/* Buffer of -M output.  */
-
-char *deps_buffer;
-
-/* Number of bytes allocated in above.  */
-int deps_allocated_size;
-
-/* Number of bytes used.  */
-int deps_size;
-
-/* Number of bytes since the last newline.  */
-int deps_column;
-
 /* Nonzero means -I- has been seen,
    so don't look for #include "foo" the source-file directory.  */
 int ignore_srcdir;
 
+/* Pending directives.  */
+enum pending_dir_t {PD_NONE = 0, PD_DEFINE, PD_UNDEF, PD_ASSERTION, PD_FILE};
+
+typedef struct pending_dir pending_dir;
+struct pending_dir
+{
+  const char *arg;
+  enum pending_dir_t type;
+};
+
 int
 main (argc, argv)
      int argc;
@@ -453,19 +502,9 @@ main (argc, argv)
   const char *in_fname, *out_fname;
   int f, i;
   FILE_BUF *fp;
-  const char **pend_files = (const char **) xmalloc (argc * sizeof (char *));
-  const char **pend_defs = (const char **) xmalloc (argc * sizeof (char *));
-  const char **pend_undefs = (const char **) xmalloc (argc * sizeof (char *));
+  pending_dir *pend = (pending_dir *) xcalloc (argc, sizeof (pending_dir));
   int no_standard_includes = 0;
 
-  /* Non-0 means don't output the preprocessed program.  */
-  int inhibit_output = 0;
-
-  /* Stream on which to print the dependency information.  */
-  FILE *deps_stream = 0;
-  /* Target-name to write with the dependency information.  */
-  char *deps_target = 0;
-
 #ifdef RLIMIT_STACK
   /* Get rid of any avoidable limit on stack size.  */
   {
@@ -484,18 +523,15 @@ main (argc, argv)
   in_fname = NULL;
   out_fname = NULL;
 
-  /* Initialize is_idchar to allow $.  */
-  initialize_char_syntax ();
-
   no_line_commands = 0;
   dump_macros = 0;
   no_output = 0;
 
   max_include_len = cpp_GCC_INCLUDE_DIR_len + 7;  /* ??? */
 
-  memset (pend_files, 0, argc * sizeof (char *));
-  memset (pend_defs, 0, argc * sizeof (char *));
-  memset (pend_undefs, 0, argc * sizeof (char *));
+  /* It's simplest to just create this struct whether or not it will
+     be needed.  */
+  deps = deps_init ();
 
   /* Process switches and find input file name.  */
 
@@ -508,11 +544,11 @@ main (argc, argv)
       else
        in_fname = argv[i];
     } else {
-      switch (argv[i][1]) {
-      case 'A':
+      int c = argv[i][1];
+
+      switch (c) {
       case 'E':
       case '$':
-      case 'g':
        break;  /* Ignore for compatibility with ISO/extended cpp.  */
 
       case 'l':
@@ -522,11 +558,11 @@ main (argc, argv)
        else if (!strcmp (argv[i], "-lang-c89"))
          fatal ("-traditional and -ansi are mutually exclusive");
        else if (!strcmp (argv[i], "-lang-objc"))
-         pend_defs[i] = "__OBJC__";
+         pend[i].type = PD_DEFINE, pend[i].arg = "__OBJC__";
        else if (!strcmp (argv[i], "-lang-asm"))
-         pend_defs[i] = "__ASSEMBLER__";
+         pend[i].type = PD_DEFINE, pend[i].arg = "__ASSEMBLER__";
        else if (!strcmp (argv[i], "-lang-fortran"))
-         pend_defs[i] = "_LANGUAGE_FORTRAN";
+         pend[i].type = PD_DEFINE, pend[i].arg = "_LANGUAGE_FORTRAN";
        /* All other possibilities ignored.  */
        break;
 
@@ -536,7 +572,7 @@ main (argc, argv)
            if (i + 1 == argc)
              fatal ("Filename missing after -i option");
            else
-             pend_files[i] = argv[i+1], i++;
+             pend[i].type = PD_FILE, pend[i].arg = argv[i + 1], i++;
          }
        else if (!strcmp (argv[i], "-iprefix"))
          i++; /* Ignore for compatibility */
@@ -580,11 +616,56 @@ main (argc, argv)
        break;
 
       case 'M':
-       if (!strcmp (argv[i], "-M"))
-         print_deps = 2;
-       else if (!strcmp (argv[i], "-MM"))
-         print_deps = 1;
-       inhibit_output = 1;
+       {
+         char *p = NULL;
+
+         /* -MD and -MMD for tradcpp are deprecated and undocumented
+            (use -M or -MM with -MF instead), and probably should be
+            removed with the next major GCC version.  For the moment
+            we allow these for the benefit of Automake 1.4, which
+            uses these when dependency tracking is enabled.  Automake
+            1.5 will fix this.  */
+         if (!strncmp (argv[i], "-MD", 3)) {
+           p = argv[i] + 3;
+           print_deps = 2;
+         } else if (!strncmp (argv[i], "-MMD", 4)) {
+           p = argv[i] + 4;
+           print_deps = 1;
+         } else if (!strcmp (argv[i], "-M")) {
+           print_deps = 2;
+         } else if (!strcmp (argv[i], "-MM")) {
+           print_deps = 1;
+         } else if (!strcmp (argv[i], "-MG")) {
+           deps_missing_files = 1;
+         } else if (!strcmp (argv[i], "-MF")) {
+           p = argv[i] + 3;
+         } else if (!strcmp (argv[i], "-MP")) {
+           print_deps_phony_targets = 1;
+         } else if (!strcmp (argv[i], "-MQ") || !strcmp (argv[i], "-MT")) {
+           /* Add a target.  -MQ quotes for Make.  */
+           const char *tgt = argv[i] + 3;
+           int quoted = argv[i][2] == 'Q';
+
+           if (*tgt == '\0' && i + 1 == argc)
+             fatal ("Target missing after %s option", argv[i]);
+           else
+             {
+               if (*tgt == '\0')
+                 tgt = argv[++i];
+             
+               deps_add_target (deps, tgt, quoted);
+             }
+         }
+
+         if (p) {
+           if (*p)
+             deps_file = p;
+           else if (i + 1 == argc)
+             fatal ("Filename missing after %s option", argv[i]);
+           else
+             deps_file = argv[++i];
+         }
+       }
        break;
 
       case 'd':
@@ -597,29 +678,28 @@ main (argc, argv)
        break;
 
       case 'D':
+      case 'U':
+      case 'A':
        {
          char *p;
 
          if (argv[i][2] != 0)
            p = argv[i] + 2;
          else if (i + 1 == argc)
-           fatal ("Macro name missing after -D option");
+           fatal ("Macro name missing after -%c option", c);
          else
            p = argv[++i];
 
-         pend_defs[i] = p;
+         if (c == 'D')
+           pend[i].type = PD_DEFINE;
+         else if (c == 'U')
+           pend[i].type = PD_UNDEF;
+         else
+           pend[i].type = PD_ASSERTION;
+         pend[i].arg = p;
        }
        break;
 
-      case 'U':                /* JF #undef something */
-       if (argv[i][2] != 0)
-         pend_undefs[i] = argv[i] + 2;
-       else if (i + 1 == argc)
-         fatal ("Macro name missing after -U option");
-       else
-         pend_undefs[i] = argv[i+1], i++;
-       break;
-
       case 'C':
        put_out_comments = 1;
        break;
@@ -689,22 +769,37 @@ main (argc, argv)
     }
   }
 
+  init_dependency_output ();
+
+  /* After checking the environment variables, check if -M or -MM has
+     not been specified, but other -M options have.  */
+  if (print_deps == 0
+      && (deps_missing_files || deps_file || print_deps_phony_targets))
+    fatal ("you must additionally specify either -M or -MM");
+
   if (user_label_prefix == 0)
     user_label_prefix = USER_LABEL_PREFIX;
 
-  /* Initialize is_idchar.  */
-  initialize_char_syntax ();
+  if (print_deps)
+    {
+      /* Set the default target (if there is none already), and
+        the dependency on the main file.  */
+      deps_add_default_target (deps, in_fname);
+
+      deps_add_dep (deps, in_fname);
+    }
 
-  /* Install __LINE__, etc.  Must follow initialize_char_syntax
-     and option processing.  */
+  /* Install __LINE__, etc.  Must follow option processing.  */
   initialize_builtins ();
 
   /* Do defines specified with -D and undefines specified with -U.  */
   for (i = 1; i < argc; i++)
-    if (pend_defs[i])
-      make_definition ((const U_CHAR *)pend_defs[i]);
-    else if (pend_undefs[i])
-      make_undef ((U_CHAR *)pend_undefs[i]);
+    if (pend[i].type == PD_DEFINE)
+      make_definition (pend[i].arg);
+    else if (pend[i].type == PD_UNDEF)
+      make_undef (pend[i].arg);
+    else if (pend[i].type == PD_ASSERTION)
+      make_assertion (pend[i].arg);
 
   /* Unless -fnostdinc,
      tack on the standard include file dirs to the specified list */
@@ -743,17 +838,24 @@ main (argc, argv)
      so that only their macro definitions matter.  */
 
   no_output++;
+  indepth++;
   for (i = 1; i < argc; i++)
-    if (pend_files[i]) {
-      int fd = open (pend_files[i], O_RDONLY, 0666);
-      if (fd < 0) {
-       perror_with_name (pend_files[i]);
-       return FATAL_EXIT_CODE;
+    if (pend[i].type == PD_FILE)
+      {
+       int fd = open (pend[i].arg, O_RDONLY, 0666);
+       if (fd < 0)
+         {
+           perror_with_name (pend[i].arg);
+           return FATAL_EXIT_CODE;
+         }
+       finclude (fd, pend[i].arg, &outbuf);
       }
-      finclude (fd, pend_files[i], &outbuf);
-    }
+  indepth--;
   no_output--;
 
+  /* Pending directives no longer needed.  */
+  free ((PTR) pend);
+
   /* Create an input stack level for the main input file
      and copy the entire contents of the file into it.  */
 
@@ -766,92 +868,6 @@ main (argc, argv)
   } else if ((f = open (in_fname, O_RDONLY, 0666)) < 0)
     goto sys_error;
 
-  /* Either of two environment variables can specify output of deps.
-     Its value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET",
-     where OUTPUT_FILE is the file to write deps info to
-     and DEPS_TARGET is the target to mention in the deps.  */
-
-  if (print_deps == 0
-      && (getenv ("SUNPRO_DEPENDENCIES") != 0
-         || getenv ("DEPENDENCIES_OUTPUT") != 0))
-    {
-      char *spec = getenv ("DEPENDENCIES_OUTPUT");
-      char *s;
-      char *output_file;
-
-      if (spec == 0)
-       {
-         spec = getenv ("SUNPRO_DEPENDENCIES");
-         print_deps = 2;
-       }
-      else
-       print_deps = 1;
-
-      /* Find the space before the DEPS_TARGET, if there is one.  */
-      s = strchr (spec, ' ');
-      if (s)
-       {
-         deps_target = s + 1;
-         output_file = (char *) xmalloc (s - spec + 1);
-         memcpy (output_file, spec, s - spec);
-         output_file[s - spec] = 0;
-       }
-      else
-       {
-         deps_target = 0;
-         output_file = spec;
-       }
-      
-      deps_stream = fopen (output_file, "a");
-      if (deps_stream == 0)
-       pfatal_with_name (output_file);
-    }
-  /* If the -M option was used, output the deps to standard output.  */
-  else if (print_deps)
-    deps_stream = stdout;
-
-  /* For -M, print the expected object file name
-     as the target of this Make-rule.  */
-  if (print_deps) {
-    deps_allocated_size = 200;
-    deps_buffer = (char *) xmalloc (deps_allocated_size);
-    deps_buffer[0] = 0;
-    deps_size = 0;
-    deps_column = 0;
-
-    if (deps_target) {
-      deps_output (deps_target, 0);
-      deps_output (":", 0);
-    } else if (*in_fname == 0)
-      deps_output ("-: ", 0);
-    else {
-      int len;
-      const char *p = in_fname;
-      const char *p1 = p;
-      /* Discard all directory prefixes from P.  */
-      while (*p1) {
-       if (*p1 == '/')
-         p = p1 + 1;
-       p1++;
-      }
-      /* Output P, but remove known suffixes.  */
-      len = strlen (p);
-      if (p[len - 2] == '.'
-         && (p[len - 1] == 'c' || p[len - 1] == 'C' || p[len - 1] == 'S'))
-       deps_output (p, len - 2);
-      else if (p[len - 3] == '.'
-              && p[len - 2] == 'c'
-              && p[len - 1] == 'c')
-       deps_output (p, len - 3);
-      else
-       deps_output (p, 0);
-      /* Supply our own suffix.  */
-      deps_output (".o : ", 0);
-      deps_output (in_fname, 0);
-      deps_output (" ", 0);
-    }
-  }
-
   if (file_size_and_mode (f, &st_mode, &st_size))
     goto sys_error;
   fp->fname = in_fname;
@@ -928,20 +944,16 @@ main (argc, argv)
 
   if (dump_macros)
     dump_all_macros ();
-  else if (! inhibit_output && deps_stream != stdout) {
+  else if (! inhibit_output)
     if (write (fileno (stdout), outbuf.buf, outbuf.bufp - outbuf.buf) < 0)
       fatal ("I/O error on output");
-  }
 
-  if (print_deps) {
-    fputs (deps_buffer, deps_stream);
-    putc ('\n', deps_stream);
-    if (deps_stream != stdout) {
-      fclose (deps_stream);
-      if (ferror (deps_stream))
-       fatal ("I/O error on output");
-    }
-  }
+  /* Don't write the deps file if preprocessing has failed.  */
+  if (print_deps && errors == 0)
+    output_deps ();
+
+  /* Destruct the deps object.  */
+  deps_free (deps);
 
   if (ferror (stdout))
     fatal ("I/O error on output");
@@ -954,6 +966,91 @@ main (argc, argv)
   pfatal_with_name (in_fname);
 }
 
+/* Set up dependency-file output.  */
+static void
+init_dependency_output ()
+{
+  char *spec, *s, *output_file;
+
+  /* Either of two environment variables can specify output of deps.
+     Its value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET",
+     where OUTPUT_FILE is the file to write deps info to
+     and DEPS_TARGET is the target to mention in the deps.  */
+
+  if (print_deps == 0)
+    {
+      spec = getenv ("DEPENDENCIES_OUTPUT");
+      if (spec)
+       print_deps = 1;
+      else
+       {
+         spec = getenv ("SUNPRO_DEPENDENCIES");
+         if (spec)
+           print_deps = 2;
+         else
+           return;
+       }
+
+      /* Find the space before the DEPS_TARGET, if there is one.  */
+      s = strchr (spec, ' ');
+      if (s)
+       {
+         /* Let the caller perform MAKE quoting.  */
+         deps_add_target (deps, s + 1, 0);
+         output_file = (char *) xmalloc (s - spec + 1);
+         memcpy (output_file, spec, s - spec);
+         output_file[s - spec] = 0;
+       }
+      else
+       output_file = spec;
+
+      /* Command line overrides environment variables.  */
+      if (deps_file == 0)
+       deps_file = output_file;
+      deps_append = 1;
+    }
+
+  /* If dependencies go to standard output, or -MG is used, we should
+     suppress output.  The user may be requesting other stuff to
+     stdout, with -dM, -v etc.  We let them shoot themselves in the
+     foot.  */
+  if (deps_file == 0 || deps_missing_files)
+    inhibit_output = 1;
+}
+
+/* Use mkdeps.c to output dependency information.  */
+static void
+output_deps ()
+{
+  /* Stream on which to print the dependency information.  */
+  FILE *deps_stream = 0;
+  const char *deps_mode = deps_append ? "a" : "w";
+
+  if (deps_file == 0)
+    deps_stream = stdout;
+  else
+    {
+      deps_stream = fopen (deps_file, deps_mode);
+      if (deps_stream == 0)
+       {
+         error_from_errno (deps_file);
+         return;
+       }
+    }
+
+  deps_write (deps, deps_stream, 72);
+
+  if (print_deps_phony_targets)
+    deps_phony_targets (deps, deps_stream);
+
+  /* Don't close stdout.  */
+  if (deps_file)
+    {
+      if (ferror (deps_stream) || fclose (deps_stream) != 0)
+       fatal ("I/O error on output");
+    }
+}
+
 /* Move all backslash-newline pairs out of embarrassing places.
    Exchange all such pairs following BP
    with any potentially-embarrasing characters that follow them.
@@ -1011,14 +1108,14 @@ name_newline_fix (bp)
 
   /* What follows the backslash-newlines is not embarrassing.  */
 
-  if (count == 0 || !is_idchar[*p])
+  if (count == 0 || !is_idchar (*p))
     return;
 
   /* Copy all potentially embarrassing characters
      that follow the backslash-newline pairs
      down to where the pairs originally started.  */
 
-  while (is_idchar[*p])
+  while (is_idchar (*p))
     *bp++ = *p++;
 
   /* Now write the same number of pairs after the embarrassing chars.  */
@@ -1422,7 +1519,7 @@ do { ip = &instack[indepth];              \
            /* If expanding a macro arg, keep the newline -.  */
            *obp++ = '-';
          }
-       } else if (is_space[*ibp]) {
+       } else if (is_space (*ibp)) {
          /* Newline Space does not prevent expansion of preceding token
             so expand the preceding token and then come back.  */
          if (ident_length > 0)
@@ -1473,7 +1570,7 @@ do { ip = &instack[indepth];              \
        ibp--;
        /* If we have an identifier that ends here, process it now, so
           we get the right error for recursion.  */
-       if (ident_length && ! is_idchar[*instack[indepth - 1].bufp]) {
+       if (ident_length && ! is_idchar (*instack[indepth - 1].bufp)) {
          redo_char = 1;
          goto randomchar;
        }
@@ -1601,7 +1698,7 @@ randomchar:
                      *obp++ = '/';
                    }
                  }
-                 else if (is_space[*ibp]) {
+                 else if (is_space (*ibp)) {
                    *obp++ = *ibp++;
                    if (ibp[-1] == '\n') {
                      if (ip->macro == 0) {
@@ -1796,7 +1893,7 @@ handle_directive (ip, op)
   bp = ip->bufp;
   /* Skip whitespace and \-newline.  */
   while (1) {
-    if (is_hor_space[*bp])
+    if (is_nvspace (*bp))
       bp++;
     else if (*bp == '/' && (newline_fix (bp + 1), bp[1]) == '*') {
       ip->bufp = bp;
@@ -1813,12 +1910,12 @@ handle_directive (ip, op)
 
   cp = bp;
   while (1) {
-    if (is_idchar[*cp])
+    if (is_idchar (*cp))
       cp++;
     else {
       if (*cp == '\\' && cp[1] == '\n')
        name_newline_fix (cp);
-      if (is_idchar[*cp])
+      if (is_idchar (*cp))
        cp++;
       else break;
     }
@@ -1955,11 +2052,11 @@ handle_directive (ip, op)
            if (*xp == '\n') {
              xp++;
              cp--;
-             if (cp != buf && is_space[cp[-1]]) {
-               while (cp != buf && is_space[cp[-1]]) cp--;
+             if (cp != buf && is_space (cp[-1])) {
+               while (cp != buf && is_space(cp[-1])) cp--;
                cp++;
                SKIP_WHITE_SPACE (xp);
-             } else if (is_space[*xp]) {
+             } else if (is_space (*xp)) {
                *cp++ = *xp++;
                SKIP_WHITE_SPACE (xp);
              }
@@ -2007,7 +2104,7 @@ handle_directive (ip, op)
         the temp buffer if it was necessary to make one.  cp
         points to the first char after the contents of the (possibly
         copied) command, in either case. */
-      (*kt->func) (buf, cp, op, kt);
+      (*kt->func) (buf, cp, op);
       check_expand (op, ip->length - (ip->bufp - ip->buf));
 
       return 1;
@@ -2131,11 +2228,15 @@ special_symbol (hp, op)
       SKIP_WHITE_SPACE (ip->bufp);
     }
 
-    if (!is_idstart[*ip->bufp])
+    if (!is_idstart (*ip->bufp))
       goto oops;
-    if (lookup (ip->bufp, -1, -1))
-      buf = " 1 ";
-    while (is_idchar[*ip->bufp])
+    {
+      HASHNODE *hp = lookup (ip->bufp, -1, -1);
+
+      if (hp && hp->type != T_UNUSED && hp->type != T_SPEC_DEFINED)
+       buf = " 1 ";
+    }
+    while (is_idchar (*ip->bufp))
       ++ip->bufp;
     SKIP_WHITE_SPACE (ip->bufp);
     if (paren) {
@@ -2168,10 +2269,9 @@ oops:
  * Expects to see "fname" or <fname> on the input.
  */
 static void
-do_include (buf, limit, op, keyword)
+do_include (buf, limit, op)
      U_CHAR *buf, *limit;
      FILE_BUF *op;
-     struct directive *keyword ATTRIBUTE_UNUSED;
 {
   char *fname;         /* Dynamically allocated fname buffer */
   U_CHAR *fbeg, *fend;         /* Beginning and end of fname */
@@ -2195,7 +2295,7 @@ get_filename:
   SKIP_WHITE_SPACE (fbeg);
   /* Discard trailing whitespace so we can easily see
      if we have parsed all the significant chars we were given.  */
-  while (limit != fbeg && is_hor_space[limit[-1]]) limit--;
+  while (limit != fbeg && is_nvspace (limit[-1])) limit--;
 
   switch (*fbeg++) {
   case '\"':
@@ -2274,7 +2374,7 @@ get_filename:
 
   /* If specified file name is absolute, just open it.  */
 
-  if (*fbeg == '/') {
+  if (IS_ABSOLUTE_PATHNAME (fbeg)) {
     strncpy (fname, (const char *)fbeg, flen);
     fname[flen] = 0;
     f = open (fname, O_RDONLY, 0666);
@@ -2299,19 +2399,37 @@ get_filename:
   if (f < 0) {
     strncpy (fname, (const char *)fbeg, flen);
     fname[flen] = 0;
-    error_from_errno (fname);
+    if (deps_missing_files
+       && print_deps > (system_header_p || (system_include_depth > 0))) {
 
-    /* For -M, add this file to the dependencies.  */
-    if (print_deps > (system_header_p || (system_include_depth > 0))) {
-      if (system_header_p)
-       warning ("nonexistent file <%.*s> omitted from dependency output",
-                fend - fbeg, fbeg);
+      /* If requested as a system header, assume it belongs in
+        the first system header directory. */
+      if (first_bracket_include)
+       stackp = first_bracket_include;
       else
-       {
-         deps_output ((const char *)fbeg, fend - fbeg);
-         deps_output (" ", 0);
-       }
-    }
+       stackp = include;
+
+      if (!system_header_p || IS_ABSOLUTE_PATHNAME (fbeg) || !stackp->fname)
+       deps_add_dep (deps, fname);
+      else {
+       char *p;
+       int len = strlen(stackp->fname);
+
+       p = (char *) alloca (len + flen + 2);
+       memcpy (p, stackp->fname, len);
+       p[len++] = '/';
+       memcpy (p + len, fbeg, flen);
+       len += flen;
+       p[len] = '\0';
+       deps_add_dep (deps, p);
+      }
+    } else if (print_deps
+              && print_deps <= (system_header_p
+                                || (system_include_depth > 0)))
+      warning ("No include path in which to find %.*s", flen, fbeg);
+    else
+      error_from_errno (fname);
+
   } else {
 
     /* Check to see if this include file is a once-only include file.
@@ -2341,10 +2459,8 @@ get_filename:
       ptr->fname = xstrdup (fname);
 
       /* For -M, add this file to the dependencies.  */
-      if (print_deps > (system_header_p || (system_include_depth > 0))) {
-       deps_output (fname, strlen (fname));
-       deps_output (" ", 0);
-      }
+      if (print_deps > (system_header_p || (system_include_depth > 0)))
+       deps_add_dep (deps, fname);
     }   
 
     if (system_header_p)
@@ -2443,6 +2559,8 @@ finclude (f, fname, op)
   output_line_command (fp, op, 0, enter_file);
   rescan (op, 0);
   indepth--;
+  instack[indepth].lineno++;
+  instack[indepth].bufp++;     /* Skip the new line.  */
   output_line_command (&instack[indepth], op, 0, leave_file);
   free (fp->buf);
   return;
@@ -2459,10 +2577,9 @@ LIMIT points to the first character past the end of the definition.
 KEYWORD is the keyword-table entry for #define.  */
 
 static void
-do_define (buf, limit, op, keyword)
+do_define (buf, limit, op)
      U_CHAR *buf, *limit;
      FILE_BUF *op ATTRIBUTE_UNUSED;
-     struct directive *keyword ATTRIBUTE_UNUSED;
 {
   U_CHAR *bp;                  /* temp ptr into input buffer */
   U_CHAR *symname;             /* remember where symbol name starts */
@@ -2475,25 +2592,32 @@ do_define (buf, limit, op, keyword)
 
   bp = buf;
 
-  while (is_hor_space[*bp])
+  while (is_nvspace (*bp))
     bp++;
 
   symname = bp;                        /* remember where it starts */
-  while (is_idchar[*bp] && bp < limit) {
+  while (is_idchar (*bp) && bp < limit) {
     bp++;
   }
   sym_length = bp - symname;
   if (sym_length == 0)
-    error ("invalid macro name");
-  else if (!is_idstart[*symname]) {
+    {
+      error ("invalid macro name");
+      return;
+    }
+  else if (!is_idstart (*symname)) {
     U_CHAR *msg;                       /* what pain... */
     msg = (U_CHAR *) alloca (sym_length + 1);
     memcpy (msg, symname, sym_length);
     msg[sym_length] = 0;
     error ("invalid macro name `%s'", msg);
+    return;
   } else {
     if (! strncmp ((const char *)symname, "defined", 7) && sym_length == 7)
-      error ("defining `defined' as a macro");
+      {
+       error ("\"defined\" cannot be used as a macro name");
+       return;
+      }
   }
 
   /* lossage will occur if identifiers or control keywords are broken
@@ -2517,11 +2641,11 @@ do_define (buf, limit, op, keyword)
       temp->argno = argno++;
       arg_ptrs = temp;
 
-      if (!is_idstart[*bp])
+      if (!is_idstart (*bp))
        warning ("parameter name starts with a digit in #define");
 
       /* Find the end of the arg name.  */
-      while (is_idchar[*bp]) {
+      while (is_idchar (*bp)) {
        bp++;
       }
       temp->length = bp - temp->name;
@@ -2542,7 +2666,7 @@ do_define (buf, limit, op, keyword)
     }
 
     ++bp;                      /* skip paren */
-    while (is_hor_space[*bp])  /* and leading whitespace */
+    while (is_nvspace (*bp))   /* and leading whitespace */
       ++bp;
     /* now everything from bp before limit is the definition. */
     defn = collect_expansion (bp, limit, argno, arg_ptrs);
@@ -2569,7 +2693,7 @@ do_define (buf, limit, op, keyword)
     }
   } else {
     /* simple expansion or empty definition; skip leading whitespace */
-    while (is_hor_space[*bp])
+    while (is_nvspace (*bp))
       ++bp;
     /* now everything from bp before limit is the definition. */
     defn = collect_expansion (bp, limit, -1, 0);
@@ -2650,17 +2774,17 @@ comp_def_part (first, beg1, len1, beg2, len2, last)
   register const U_CHAR *end1 = beg1 + len1;
   register const U_CHAR *end2 = beg2 + len2;
   if (first) {
-    while (beg1 != end1 && is_space[*beg1]) beg1++;
-    while (beg2 != end2 && is_space[*beg2]) beg2++;
+    while (beg1 != end1 && is_space (*beg1)) beg1++;
+    while (beg2 != end2 && is_space (*beg2)) beg2++;
   }
   if (last) {
-    while (beg1 != end1 && is_space[end1[-1]]) end1--;
-    while (beg2 != end2 && is_space[end2[-1]]) end2--;
+    while (beg1 != end1 && is_space (end1[-1])) end1--;
+    while (beg2 != end2 && is_space (end2[-1])) end2--;
   }
   while (beg1 != end1 && beg2 != end2) {
-    if (is_space[*beg1] && is_space[*beg2]) {
-      while (beg1 != end1 && is_space[*beg1]) beg1++;
-      while (beg2 != end2 && is_space[*beg2]) beg2++;
+    if (is_space (*beg1) && is_space (*beg2)) {
+      while (beg1 != end1 && is_space (*beg1)) beg1++;
+      while (beg2 != end2 && is_space (*beg2)) beg2++;
     } else if (*beg1 == *beg2) {
       beg1++; beg2++;
     } else break;
@@ -2717,8 +2841,8 @@ collect_expansion (buf, end, nargs, arglist)
   /* Find end of leading whitespace.  */
   limit = end;
   p = buf;
-  while (p < limit && is_space[limit[-1]]) limit--;
-  while (p < limit && is_space[*p]) p++;
+  while (p < limit && is_space (limit[-1])) limit--;
+  while (p < limit && is_space (*p)) p++;
 
   /* Allocate space for the text in the macro definition.
      Leading and trailing whitespace chars need 2 bytes each.
@@ -2737,7 +2861,7 @@ collect_expansion (buf, end, nargs, arglist)
   p = buf;
 
   /* Convert leading whitespace to Newline-markers.  */
-  while (p < limit && is_space[*p]) {
+  while (p < limit && is_space (*p)) {
     *exp_p++ = '\n';
     *exp_p++ = *p++;
   }
@@ -2787,15 +2911,15 @@ collect_expansion (buf, end, nargs, arglist)
       break;
     }
 
-    if (is_idchar[c] && nargs > 0) {
+    if (is_idchar (c) && nargs > 0) {
       U_CHAR *id_beg = p - 1;
       int id_len;
 
       --exp_p;
-      while (p != limit && is_idchar[*p]) p++;
+      while (p != limit && is_idchar (*p)) p++;
       id_len = p - id_beg;
 
-      if (is_idstart[c]) {
+      if (is_idstart (c)) {
        register struct arglist *arg;
 
        for (arg = arglist; arg != NULL; arg = arg->next) {
@@ -2848,7 +2972,7 @@ collect_expansion (buf, end, nargs, arglist)
 
   if (limit < end) {
     /* Convert trailing whitespace to Newline-markers.  */
-    while (limit < end && is_space[*limit]) {
+    while (limit < end && is_space (*limit)) {
       *exp_p++ = '\n';
       *exp_p++ = *limit++;
     }
@@ -2870,10 +2994,9 @@ collect_expansion (buf, end, nargs, arglist)
  */
 #define FNAME_HASHSIZE 37
 static void
-do_line (buf, limit, op, keyword)
+do_line (buf, limit, op)
      U_CHAR *buf, *limit;
      FILE_BUF *op;
-     struct directive *keyword ATTRIBUTE_UNUSED;
 {
   register U_CHAR *bp;
   FILE_BUF *ip = &instack[indepth];
@@ -2896,19 +3019,12 @@ do_line (buf, limit, op, keyword)
   /* The Newline at the end of this line remains to be processed.
      To put the next line at the specified line number,
      we must store a line number now that is one less.  */
-  new_lineno = atoi ((const char *)bp) - 1;
+  new_lineno = atoi ((const char *)bp);
 
   /* skip over the line number.  */
   while (ISDIGIT (*bp))
     bp++;
 
-#if 0 /* #line 10"foo.c" is supposed to be allowed.  */
-  if (*bp && !is_space[*bp]) {
-    error ("invalid format #line command");
-    return;
-  }
-#endif
-
   SKIP_WHITE_SPACE (bp);
 
   if (*bp == '\"') {
@@ -2974,6 +3090,7 @@ do_line (buf, limit, op, keyword)
 
   ip->lineno = new_lineno;
   output_line_command (ip, op, 0, file_change);
+  ip->bufp++;                  /* Skip the new line.  */
   check_expand (op, ip->length - (ip->bufp - ip->buf));
 }
 
@@ -2983,17 +3100,16 @@ do_line (buf, limit, op, keyword)
  * something that has no definitions, so it isn't one here either.
  */
 static void
-do_undef (buf, limit, op, keyword)
+do_undef (buf, limit, op)
      U_CHAR *buf;
      U_CHAR *limit ATTRIBUTE_UNUSED;
      FILE_BUF *op ATTRIBUTE_UNUSED;
-     struct directive *keyword ATTRIBUTE_UNUSED;
 {
   HASHNODE *hp;
 
   SKIP_WHITE_SPACE (buf);
 
-  if (! strncmp ((const char *)buf, "defined", 7) && ! is_idchar[buf[7]])
+  if (! strncmp ((const char *)buf, "defined", 7) && ! is_idchar (buf[7]))
     warning ("undefining `defined'");
 
   while ((hp = lookup (buf, -1, -1)) != NULL) {
@@ -3003,6 +3119,293 @@ do_undef (buf, limit, op, keyword)
   }
 }
 
+/* Read the tokens of the answer into the macro pool.  Only commit the
+   memory if we intend it as permanent storage, i.e. the #assert case.
+   Returns 0 on success.  */
+
+static int
+parse_answer (buf, limit, answerp, type)
+     const unsigned char *buf, *limit;
+     struct answer **answerp;
+     int type;
+{
+  const unsigned char *start;
+
+  /* Skip leading whitespace.  */
+  if (buf < limit && *buf == ' ')
+    buf++;
+
+  /* Parentheses are optional here.  */
+  if (buf == limit && type == T_UNASSERT)
+    return 0;
+
+  if (buf == limit || *buf++ != '(')
+    {
+      if (type == T_IF)
+       return 0;
+
+      error ("missing '(' after predicate");
+      return 1;
+    }
+
+  /* Drop whitespace at start.  */
+  while (buf < limit && *buf == ' ')
+    buf++;
+
+  start = buf;
+  while (buf < limit && *buf != ')')
+    buf++;
+
+  if (buf == limit)
+    {
+      error ("missing ')' to complete answer");
+      return 1;
+    }
+
+  if (buf == start)
+    {
+      error ("predicate's answer is empty");
+      return 1;
+    }
+
+  if ((type == T_ASSERT || type == T_UNASSERT) && buf + 1 != limit)
+    {
+      error ("extra text at end of directive");
+      return 1;
+    }
+
+  /* Lose trailing whitespace.  */
+  if (buf[-1] == ' ')
+    buf--;
+
+  *answerp = (struct answer *) xmalloc (sizeof (struct answer));
+  (*answerp)->answer = start;
+  (*answerp)->len = buf - start;
+
+  return 0;
+}
+
+/* Parses an assertion, returning a pointer to the hash node of the
+   predicate, or 0 on error.  If an answer was supplied, it is placed
+   in ANSWERP, otherwise it is set to 0.  */
+static HASHNODE *
+parse_assertion (buf, limit, answerp, type)
+     const unsigned char *buf, *limit;
+     struct answer **answerp;
+     int type;
+{
+  HASHNODE *result = 0;
+  const unsigned char *climit;
+  unsigned char *bp, *symname = canonicalize_text (buf, limit, &climit);
+  unsigned int len;
+
+  bp = symname;
+  if (bp < climit && is_idstart (*bp))
+    {
+      do
+       bp++;
+      while (bp < climit && is_idchar (*bp));
+    }
+  len = bp - symname;
+
+  *answerp = 0;
+  if (len == 0)
+    {
+      if (symname == climit)
+       error ("assertion without predicate");
+      else
+       error ("predicate must be an identifier");
+    }
+  /* Unfortunately, because of the way we handle #if, we don't avoid
+     macro expansion in answers.  This is not easy to fix.  */
+  else if (parse_answer (bp, climit, answerp, type) == 0)
+    {
+      unsigned char *sym = alloca (len + 1);
+      int hashcode;
+      
+      /* Prefix '#' to get it out of macro namespace.  */
+      sym[0] = '#';
+      memcpy (sym + 1, symname, len);
+
+      hashcode = hashf (sym, len + 1, HASHSIZE);
+      result = lookup (sym, len + 1, hashcode);
+      if (result == 0)
+       result = install (sym, len + 1, T_UNUSED, hashcode);
+    }
+
+  return result;
+}
+
+/* Test an assertion within a preprocessor conditional.  Returns zero
+   on error or failure, one on success.  */
+int
+test_assertion (pbuf)
+     unsigned char **pbuf;     /* NUL-terminated.  */
+{
+  unsigned char *buf = *pbuf;
+  unsigned char *limit = buf + strlen ((char *) buf);
+  struct answer *answer;
+  HASHNODE *node;
+  int result = 0;
+
+  node = parse_assertion (buf, limit, &answer, T_IF);
+  if (node)
+    {
+      result = (node->type == T_ASSERT &&
+               (answer == 0 || *find_answer (node, answer) != 0));
+
+      /* Yuk.  We update pbuf to point after the assertion test.
+        First, move past the identifier.  */
+      if (is_space (*buf))
+       buf++;
+      while (is_idchar (*buf))
+       buf++;
+      /* If we have an answer, we need to move past the parentheses.  */
+      if (answer)
+       while (*buf++ != ')')
+         ;
+      *pbuf = buf;
+    }
+
+  return result;
+}
+
+/* Handle a #error directive.  */
+static void
+do_error (buf, limit, op)
+     U_CHAR *buf;
+     U_CHAR *limit;
+     FILE_BUF *op ATTRIBUTE_UNUSED;
+{
+  error ("#error%.*s", (int) (limit - buf), buf);
+}
+
+/* Handle a #warning directive.  */
+static void
+do_warning (buf, limit, op)
+     U_CHAR *buf;
+     U_CHAR *limit;
+     FILE_BUF *op ATTRIBUTE_UNUSED;
+{
+  warning ("#warning%.*s", (int) (limit - buf), buf);
+}
+
+/* Handle a #assert directive.  */
+static void
+do_assert (buf, limit, op)
+     U_CHAR *buf;
+     U_CHAR *limit;
+     FILE_BUF *op ATTRIBUTE_UNUSED;
+{
+  struct answer *new_answer;
+  HASHNODE *node;
+  
+  node = parse_assertion (buf, limit, &new_answer, T_ASSERT);
+  if (node)
+    {
+      /* Place the new answer in the answer list.  First check there
+         is not a duplicate.  */
+      new_answer->next = 0;
+      if (node->type == T_ASSERT)
+       {
+         if (*find_answer (node, new_answer))
+           {
+             free (new_answer);
+             warning ("\"%s\" re-asserted", node->name + 1);
+             return;
+           }
+         new_answer->next = node->value.answers;
+       }
+      node->type = T_ASSERT;
+      node->value.answers = new_answer;
+    }
+}
+
+/* Function body to be provided later.  */
+static void
+do_unassert (buf, limit, op)
+     U_CHAR *buf;
+     U_CHAR *limit;
+     FILE_BUF *op ATTRIBUTE_UNUSED;
+{
+  HASHNODE *node;
+  struct answer *answer;
+  
+  node = parse_assertion (buf, limit, &answer, T_UNASSERT);
+  /* It isn't an error to #unassert something that isn't asserted.  */
+  if (node)
+    {
+      if (node->type == T_ASSERT)
+       {
+         if (answer)
+           {
+             struct answer **p = find_answer (node, answer), *temp;
+
+             /* Remove the answer from the list.  */
+             temp = *p;
+             if (temp)
+               *p = temp->next;
+
+             /* Did we free the last answer?  */
+             if (node->value.answers == 0)
+               delete_macro (node);
+           }
+         else
+           delete_macro (node);
+       }
+
+      free (answer);
+    }
+}
+
+/* Returns a pointer to the pointer to the answer in the answer chain,
+   or a pointer to NULL if the answer is not in the chain.  */
+static struct answer **
+find_answer (node, candidate)
+     HASHNODE *node;
+     const struct answer *candidate;
+{
+  struct answer **result;
+
+  for (result = &node->value.answers; *result; result = &(*result)->next)
+    {
+      struct answer *answer = *result;
+
+      if (answer->len == candidate->len
+         && !memcmp (answer->answer, candidate->answer, answer->len))
+       break;
+    }
+
+  return result;
+}
+
+/* Return a malloced buffer with leading and trailing whitespace
+   removed, and all instances of internal whitespace reduced to a
+   single space.  */
+static unsigned char *
+canonicalize_text (buf, limit, climit)
+     const unsigned char *buf, *limit, **climit;
+{
+  unsigned int len = limit - buf;
+  unsigned char *result = (unsigned char *) xmalloc (len), *dest;
+
+  for (dest = result; buf < limit;)
+    {
+      if (! is_space (*buf))
+       *dest++ = *buf++;
+      else
+       {
+         while (++buf < limit && is_space (*buf))
+           ;
+         if (dest != result && buf != limit)
+           *dest++ = ' ';
+       }
+    }
+
+  *climit = dest;
+  return result;
+}
+
 /*
  * handle #if command by
  *   1) inserting special `defined' keyword into the hash table
@@ -3016,10 +3419,9 @@ do_undef (buf, limit, op, keyword)
  *      or not, depending on the value from step 3.
  */
 static void
-do_if (buf, limit, op, keyword)
+do_if (buf, limit, op)
      U_CHAR *buf, *limit;
      FILE_BUF *op ATTRIBUTE_UNUSED;
-     struct directive *keyword ATTRIBUTE_UNUSED;
 {
   int value;
   FILE_BUF *ip = &instack[indepth];
@@ -3033,10 +3435,9 @@ do_if (buf, limit, op, keyword)
  * see the comment above do_else.
  */
 static void
-do_elif (buf, limit, op, keyword)
+do_elif (buf, limit, op)
      U_CHAR *buf, *limit;
      FILE_BUF *op;
-     struct directive *keyword ATTRIBUTE_UNUSED;
 {
   int value;
   FILE_BUF *ip = &instack[indepth];
@@ -3099,10 +3500,9 @@ eval_if_expression (buf, length)
  * on what directive is actually being processed.
  */
 static void
-do_xifdef (buf, limit, op, keyword)
+do_xifdef (buf, limit, type)
      U_CHAR *buf, *limit;
-     FILE_BUF *op ATTRIBUTE_UNUSED;
-     struct directive *keyword;
+     enum node_type type;     
 {
   int skip;
   FILE_BUF *ip = &instack[indepth];
@@ -3110,20 +3510,35 @@ do_xifdef (buf, limit, op, keyword)
 
   /* Discard leading and trailing whitespace.  */
   SKIP_WHITE_SPACE (buf);
-  while (limit != buf && is_hor_space[limit[-1]]) limit--;
+  while (limit != buf && is_nvspace (limit[-1])) limit--;
 
   /* Find the end of the identifier at the beginning.  */
-  for (end = buf; is_idchar[*end]; end++);
+  for (end = buf; is_idchar (*end); end++);
 
-  if (end == buf) {
-    skip = (keyword->type == T_IFDEF);
-  } else {
-    skip = (lookup (buf, end-buf, -1) == NULL) ^ (keyword->type == T_IFNDEF);
-  }
+  if (end == buf)
+    skip = (type == T_IFDEF);
+  else
+    skip = (lookup (buf, end-buf, -1) == NULL) ^ (type == T_IFNDEF);
 
   conditional_skip (ip, skip, T_IF);
 }
 
+static void
+do_ifdef (buf, limit, op)
+     U_CHAR *buf, *limit;
+     FILE_BUF *op ATTRIBUTE_UNUSED;
+{
+  do_xifdef (buf, limit, T_IFDEF);
+}
+
+static void
+do_ifndef (buf, limit, op)
+     U_CHAR *buf, *limit;
+     FILE_BUF *op ATTRIBUTE_UNUSED;
+{
+  do_xifdef (buf, limit, T_IFNDEF);
+}
+
 /*
  * push TYPE on stack; then, if SKIP is nonzero, skip ahead.
  */
@@ -3205,7 +3620,7 @@ skip_if_group (ip, any)
         If not, this # is not special.  */
       bp = beg_of_line;
       while (1) {
-       if (is_hor_space[*bp])
+       if (is_nvspace (*bp))
          bp++;
        else if (*bp == '\\' && bp[1] == '\n')
          bp += 2;
@@ -3229,7 +3644,7 @@ skip_if_group (ip, any)
 
       /* Skip whitespace and \-newline.  */
       while (1) {
-       if (is_hor_space[*bp])
+       if (is_nvspace (*bp))
          bp++;
        else if (*bp == '\\' && bp[1] == '\n')
          bp += 2;
@@ -3249,12 +3664,12 @@ skip_if_group (ip, any)
         symbol-constituents so that we end up with a contiguous name.  */
 
       while (1) {
-       if (is_idchar[*bp])
+       if (is_idchar (*bp))
          bp++;
        else {
          if (*bp == '\\' && bp[1] == '\n')
            name_newline_fix (bp);
-         if (is_idchar[*bp])
+         if (is_idchar (*bp))
            bp++;
          else break;
        }
@@ -3263,7 +3678,7 @@ skip_if_group (ip, any)
       for (kt = directive_table; kt->length >= 0; kt++) {
        IF_STACK_FRAME *temp;
        if (strncmp ((const char *)cp, kt->name, kt->length) == 0
-           && !is_idchar[cp[kt->length]]) {
+           && !is_idchar (cp[kt->length])) {
 
          /* If we are asked to return on next directive,
             do so now.  */
@@ -3325,11 +3740,10 @@ skip_if_group (ip, any)
  * is possible that something different would be better.
  */
 static void
-do_else (buf, limit, op, keyword)
+do_else (buf, limit, op)
      U_CHAR *buf ATTRIBUTE_UNUSED;
      U_CHAR *limit ATTRIBUTE_UNUSED;
      FILE_BUF *op;
-     struct directive *keyword ATTRIBUTE_UNUSED;
 {
   FILE_BUF *ip = &instack[indepth];
 
@@ -3359,11 +3773,10 @@ do_else (buf, limit, op, keyword)
  * unstack after #endif command
  */
 static void
-do_endif (buf, limit, op, keyword)
+do_endif (buf, limit, op)
      U_CHAR *buf ATTRIBUTE_UNUSED;
      U_CHAR *limit ATTRIBUTE_UNUSED;
      FILE_BUF *op;
-     struct directive *keyword ATTRIBUTE_UNUSED;
 {
   if (if_stack == instack[indepth].if_stack)
     error ("unbalanced #endif");
@@ -3535,6 +3948,8 @@ output_line_command (ip, op, conditional, file_change)
   sprintf (line_cmd_buf, "# %d \"%s\"", ip->lineno, ip->fname);
   if (file_change != same_file)
     strcat (line_cmd_buf, file_change == enter_file ? " 1" : " 2");
+  if (system_include_depth > 0)
+    strcat (line_cmd_buf, " 3");
   len = strlen (line_cmd_buf);
   line_cmd_buf[len++] = '\n';
   check_expand (op, len + 1);
@@ -3610,7 +4025,7 @@ macroexpand (hp, op)
     if (i == 1) {
       register const U_CHAR *bp = args[0].raw;
       register const U_CHAR *lim = bp + args[0].raw_length;
-      while (bp != lim && is_space[*bp]) bp++;
+      while (bp != lim && is_space (*bp)) bp++;
       if (bp == lim)
        i = 0;
     }
@@ -3678,10 +4093,10 @@ macroexpand (hp, op)
          int c;
          i = 0;
          while (i < arglen
-                && (c = arg->raw[i], is_space[c]))
+                && (c = arg->raw[i], is_space (c)))
            i++;
          while (i < arglen
-                && (c = arg->raw[arglen - 1], is_space[c]))
+                && (c = arg->raw[arglen - 1], is_space (c)))
            arglen--;
          for (; i < arglen; i++) {
            c = arg->raw[i];
@@ -3696,13 +4111,13 @@ macroexpand (hp, op)
            /* Internal sequences of whitespace are replaced by one space
               except within an string or char token.  */
            if (! in_string
-               && (c == '\n' ? arg->raw[i+1] == '\n' : is_space[c])) {
+               && (c == '\n' ? arg->raw[i+1] == '\n' : is_space (c))) {
              while (1) {
                /* Note that Newline Space does occur within whitespace
                   sequences; consider it part of the sequence.  */
-               if (c == '\n' && is_space[arg->raw[i+1]])
+               if (c == '\n' && is_space (arg->raw[i+1]))
                  i += 2;
-               else if (c != '\n' && is_space[c])
+               else if (c != '\n' && is_space (c))
                  i++;
                else break;
                c = arg->raw[i];
@@ -3738,8 +4153,8 @@ macroexpand (hp, op)
          const U_CHAR *l1 = p1 + arg->raw_length;
 
          if (ap->raw_before) {
-           while (p1 != l1 && is_space[*p1]) p1++;
-           while (p1 != l1 && is_idchar[*p1])
+           while (p1 != l1 && is_space (*p1)) p1++;
+           while (p1 != l1 && is_idchar (*p1))
              xbuf[totlen++] = *p1++;
            /* Delete any no-reexpansion marker that follows
               an identifier at the beginning of the argument
@@ -3751,7 +4166,7 @@ macroexpand (hp, op)
            /* Arg is concatenated after: delete trailing whitespace,
               whitespace markers, and no-reexpansion markers.  */
            while (p1 != l1) {
-             if (is_space[l1[-1]]) l1--;
+             if (is_space (l1[-1])) l1--;
              else if (l1[-1] == '-') {
                const U_CHAR *p2 = l1 - 1;
                /* If a `-' is preceded by an odd number of newlines then it
@@ -4365,7 +4780,7 @@ install (name, len, type, hash)
 
   if (len < 0) {
     p = name;
-    while (is_idchar[*p])
+    while (is_idchar (*p))
       p++;
     len = p - name;
   }
@@ -4409,7 +4824,7 @@ lookup (name, len, hash)
   register HASHNODE *bucket;
 
   if (len < 0) {
-    for (bp = name; is_idchar[*bp]; bp++) ;
+    for (bp = name; is_idchar (*bp); bp++) ;
     len = bp - name;
   }
 
@@ -4580,44 +4995,6 @@ dump_arg_n (defn, argnum)
     p++;
   }
 }
-\f
-/* Initialize syntactic classifications of characters.  */
-static void
-initialize_char_syntax ()
-{
-  register int i;
-
-  /*
-   * Set up is_idchar and is_idstart tables.  These should be
-   * faster than saying (is_alpha (c) || c == '_'), etc.
-   * Must do set up these things before calling any routines tthat
-   * refer to them.
-   */
-  for (i = 'a'; i <= 'z'; i++) {
-    is_idchar[i - 'a' + 'A'] = 1;
-    is_idchar[i] = 1;
-    is_idstart[i - 'a' + 'A'] = 1;
-    is_idstart[i] = 1;
-  }
-  for (i = '0'; i <= '9'; i++)
-    is_idchar[i] = 1;
-  is_idchar['_'] = 1;
-  is_idstart['_'] = 1;
-
-  /* horizontal space table */
-  is_hor_space[' '] = 1;
-  is_hor_space['\t'] = 1;
-  is_hor_space['\v'] = 1;
-  is_hor_space['\f'] = 1;
-  is_hor_space['\r'] = 1;
-
-  is_space[' '] = 1;
-  is_space['\t'] = 1;
-  is_space['\v'] = 1;
-  is_space['\f'] = 1;
-  is_space['\n'] = 1;
-  is_space['\r'] = 1;
-}
 
 /* Initialize the built-in macros.  */
 #define DSC(x) U x, sizeof x - 1
@@ -4657,111 +5034,102 @@ initialize_builtins ()
 #undef install_spec
 #undef install_value
 \f
-/*
- * 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-space-definition.
- */
+/* Common handler of command line directives -U, -D and -A.  */
 static void
-make_definition (str)
-     const U_CHAR *str;
+run_directive (str, len, type)
+     const char *str;
+     size_t len;
+     enum node_type type;
 {
-  FILE_BUF *ip;
   struct directive *kt;
-  U_CHAR *buf;
-  const U_CHAR *p;
-  size_t len = strlen ((const char *)str);
-
-  p = (const U_CHAR *) strchr ((const char *)str, '=');
-  if (p == NULL) {
-    /* Change -DFOO into #define FOO 1 */
-    buf = (U_CHAR *) alloca (len + 3);
-    memcpy (buf, str, len);
-    memcpy (buf + len, " 1", 3);
-    len += 2;
-  } else {
-    buf = (U_CHAR *) alloca (len + 1);
-    memcpy (buf, str, len + 1);
-    buf[p - str] = ' ';
-  }
-  
-  ip = &instack[++indepth];
-  ip->fname = "*Initialization*";
+  FILE_BUF *ip = &instack[++indepth];
+  ip->fname = "*command line*";
 
-  ip->buf = ip->bufp = buf;
+  ip->buf = ip->bufp = (U_CHAR *) str;
   ip->length = len;
   ip->lineno = 1;
   ip->macro = 0;
   ip->free_ptr = 0;
   ip->if_stack = if_stack;
 
-  for (kt = directive_table; kt->type != T_DEFINE; kt++)
+  for (kt = directive_table; kt->type != type; kt++)
     ;
 
-  /* pass NULL as output ptr to do_define since we KNOW it never
-     does any output.... */
-  do_define (buf, buf + ip->length, NULL, kt);
+  (*kt->func) ((U_CHAR *) str, (U_CHAR *) str + len, NULL);
   --indepth;
 }
 
-/* JF, this does the work for the -U option */
+/* Handle the -D option.  If STR is just an identifier, define it with
+ * value 1.  If STR has anything after the identifier, then it should
+ * be identifier-space-definition.  */
 static void
-make_undef (str)
-     U_CHAR *str;
+make_definition (str)
+     const char *str;
 {
-  FILE_BUF *ip;
-  struct directive *kt;
+  char *buf, *p;
+  size_t count;
 
-  ip = &instack[++indepth];
-  ip->fname = "*undef*";
+  /* Copy the entire option so we can modify it. 
+     Change the first "=" in the string to a space.  If there is none,
+     tack " 1" on the end.  */
 
-  ip->buf = ip->bufp = str;
-  ip->length = strlen ((const char *)str);
-  ip->lineno = 1;
-  ip->macro = 0;
-  ip->free_ptr = 0;
-  ip->if_stack = if_stack;
+  /* Length including the null.  */  
+  count = strlen (str);
+  buf = (char *) alloca (count + 2);
+  memcpy (buf, str, count);
 
-  for (kt = directive_table; kt->type != T_UNDEF; kt++)
-    ;
+  p = strchr (str, '=');
+  if (p)
+    buf[p - str] = ' ';
+  else
+    {
+      buf[count++] = ' ';
+      buf[count++] = '1';
+    }
 
-  do_undef (str, str + ip->length, NULL, kt);
-  --indepth;
+  run_directive (buf, count, T_DEFINE);
 }
-\f
-/* Add output to `deps_buffer' for the -M switch.
-   STRING points to the text to be output.
-   SIZE is the number of bytes, or 0 meaning output until a null.
-   If SIZE is nonzero, we break the line first, if it is long enough.  */
+
+/* Handle the -U option.  */
 static void
-deps_output (string, size)
-     const char *string;
-     int size;
+make_undef (str)
+     const char *str;
 {
-#ifndef MAX_OUTPUT_COLUMNS
-#define MAX_OUTPUT_COLUMNS 75
-#endif
-  if (size != 0 && deps_column != 0
-      && size + deps_column > MAX_OUTPUT_COLUMNS) {
-    deps_output ("\\\n  ", 0);
-    deps_column = 0;
-  }
+  run_directive (str, strlen (str), T_UNDEF);
+}
 
-  if (size == 0)
-    size = strlen (string);
+/* Handles the #assert (-A) and #unassert (-A-) command line options.  */
+static void
+make_assertion (str)
+     const char *str;
+{
+  enum node_type type = T_ASSERT;
+  size_t count;
+  const char *p;
 
-  if (deps_size + size + 1 > deps_allocated_size) {
-    deps_allocated_size = deps_size + size + 50;
-    deps_allocated_size *= 2;
-    deps_buffer = (char *) xrealloc (deps_buffer, deps_allocated_size);
-  }
-  memcpy (&deps_buffer[deps_size], string, size);
-  deps_size += size;
-  deps_column += size;
-  deps_buffer[deps_size] = 0;
-}
+  if (*str == '-')
+    {
+      str++;
+      type = T_UNASSERT;
+    }
+  
+  count = strlen (str);
+  p = strchr (str, '=');
+  if (p)
+    {
+      /* Copy the entire option so we can modify it.  Change the first
+        "=" in the string to a '(', and tack a ')' on the end.  */
+      char *buf = (char *) alloca (count + 1);
+
+      memcpy (buf, str, count);
+      buf[p - str] = '(';
+      buf[count++] = ')';
+      str = buf;
+    }
 
+  run_directive (str, count, type);
+}
+\f
 /* Get the file-mode and data size of the file open on FD
    and store them in *MODE_POINTER and *SIZE_POINTER.  */