OSDN Git Service

contrib:
[pf3gnuchains/gcc-fork.git] / gcc / tradcpp.c
index 3f48210..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
@@ -23,12 +23,14 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "system.h"
 #include "version.h"
 #include "cppdefault.h"
+#include "tradcpp.h"
+#include "mkdeps.h"
 
 typedef unsigned char U_CHAR;
 
 /* Name under which this program was invoked.  */
 
-char *progname;
+static const char *progname;
 
 /* Current maximum length of directory names in the search path
    for include files.  (Altered as we get more of them.)  */
@@ -39,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;
@@ -59,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;
@@ -71,7 +97,7 @@ int no_output;
 
 /* Value of __USER_LABEL_PREFIX__.  Target-dependent, also controlled
    by -f(no-)leading-underscore.  */
-const char *user_label_prefix;
+static const char *user_label_prefix;
 
 /* I/O buffer structure.
    The `fname' field is nonzero for source files and #include files
@@ -184,7 +210,15 @@ struct definition {
      with comma-space between them.
      The only use of this is that we warn on redefinition
      if this differs between the old and new definitions.  */
-  U_CHAR *argnames;
+  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
@@ -192,9 +226,9 @@ struct definition {
 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
@@ -214,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__' */
@@ -241,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,
@@ -257,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. */
@@ -304,42 +353,36 @@ struct arglist {
 
 /* Function prototypes.  */
 
-void do_define PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-void do_line   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-void do_include        PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-void do_undef  PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-void do_if     PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-void do_xifdef PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-void do_else   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-void do_elif   PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-void do_endif  PARAMS ((U_CHAR *, U_CHAR *, FILE_BUF *, struct directive *));
-
-struct hashnode *install PARAMS ((const U_CHAR *, int, enum node_type, int));
-struct hashnode *lookup  PARAMS ((const U_CHAR *, int, int));
-int hashf               PARAMS ((const U_CHAR *, int, int));
-int compare_defs        PARAMS ((DEFINITION *, DEFINITION *));
-int comp_def_part       PARAMS ((int, U_CHAR *, int, U_CHAR *, int, int));
-void delete_macro       PARAMS ((HASHNODE *));
+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));
+static int compare_defs         PARAMS ((DEFINITION *, DEFINITION *));
+static int comp_def_part        PARAMS ((int, const U_CHAR *, int,
+                                         const U_CHAR *, int, int));
+static void delete_macro        PARAMS ((HASHNODE *));
 
 /* First arg to v_message.  */
 enum msgtype { WARNING = 0, ERROR, FATAL };
-void v_message          PARAMS ((enum msgtype mtype, int line,
-                                 const char *msgid, va_list ap))
+static void v_message           PARAMS ((enum msgtype mtype, int line,
+                                         const char *msgid, va_list ap))
      ATTRIBUTE_PRINTF (3, 0);
 
-void warning            PARAMS ((const char *msgid, ...)) ATTRIBUTE_PRINTF_1;
-void error              PARAMS ((const char *msgid, ...)) ATTRIBUTE_PRINTF_1;
-void fatal              PARAMS ((const char *msgid, ...)) ATTRIBUTE_NORETURN
-     ATTRIBUTE_PRINTF_1;
-void error_with_line    PARAMS ((int, const char *msgid, ...))
-     ATTRIBUTE_PRINTF_2;
-void error_from_errno   PARAMS ((const char *msgid));
-
-void perror_with_name   PARAMS ((const char *msgid));
-void pfatal_with_name   PARAMS ((const char *msgid)) ATTRIBUTE_NORETURN;
-void fancy_abort        PARAMS ((int, const char *)) ATTRIBUTE_NORETURN;
-
-int line_for_error      PARAMS ((int));
+static int line_for_error       PARAMS ((int));
 
 /* We know perfectly well which file this is, so we don't need to
    use __FILE__.  */
@@ -350,39 +393,42 @@ int line_for_error         PARAMS ((int));
 #define abort() fancy_abort(__LINE__, 0);
 #endif
 
-void macroexpand               PARAMS ((HASHNODE *, FILE_BUF *));
-void special_symbol            PARAMS ((HASHNODE *, FILE_BUF *));
-void dump_all_macros           PARAMS ((void));
-void dump_defn_1               PARAMS ((U_CHAR *, int, int));
-void dump_arg_n                        PARAMS ((DEFINITION *, int));
-void conditional_skip          PARAMS ((FILE_BUF *, int, enum node_type));
-void skip_if_group             PARAMS ((FILE_BUF *, int));
-void output_line_command       PARAMS ((FILE_BUF *, FILE_BUF *,
+static void macroexpand        PARAMS ((HASHNODE *, FILE_BUF *));
+static void special_symbol     PARAMS ((HASHNODE *, FILE_BUF *));
+static void dump_all_macros    PARAMS ((void));
+static void dump_defn_1                PARAMS ((const U_CHAR *, int, int));
+static void dump_arg_n         PARAMS ((DEFINITION *, int));
+static void conditional_skip   PARAMS ((FILE_BUF *, int, enum node_type));
+static void skip_if_group      PARAMS ((FILE_BUF *, int));
+static void output_line_command PARAMS ((FILE_BUF *, FILE_BUF *,
                                         int, enum file_change_code));
 
-int eval_if_expression         PARAMS ((U_CHAR *, int));
-int parse_c_expression         PARAMS ((char *));  /* in tradcif.y */
-
-void initialize_char_syntax    PARAMS ((void));
-void initialize_builtins       PARAMS ((void));
-void make_definition           PARAMS ((U_CHAR *));
-void make_undef                        PARAMS ((U_CHAR *));
-
-void grow_outbuf       PARAMS ((FILE_BUF *, int));
-int handle_directive   PARAMS ((FILE_BUF *, FILE_BUF *));
-void finclude          PARAMS ((int, const char *, FILE_BUF *));
-void deps_output       PARAMS ((const char *, int));
-void rescan            PARAMS ((FILE_BUF *, int));
-void newline_fix       PARAMS ((U_CHAR *));
-void name_newline_fix  PARAMS ((U_CHAR *));
-U_CHAR *macarg1        PARAMS ((U_CHAR *, U_CHAR *, int *, int *, int *));
-const char *macarg     PARAMS ((struct argdata *));
-int discard_comments   PARAMS ((U_CHAR *, int, int));
-int file_size_and_mode PARAMS ((int, int *, long *));
-
-U_CHAR *skip_to_end_of_comment PARAMS ((FILE_BUF *, int *));
-U_CHAR *skip_quoted_string     PARAMS ((U_CHAR *, U_CHAR *, int,
-                                       int *, int *, int *));
+static int eval_if_expression  PARAMS ((const U_CHAR *, int));
+
+static void output_deps                PARAMS ((void));
+static void initialize_builtins        PARAMS ((void));
+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 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 *));
+static U_CHAR *macarg1         PARAMS ((U_CHAR *, const U_CHAR *, int *,
+                                        int *, int *));
+static const char *macarg      PARAMS ((struct argdata *));
+static int discard_comments    PARAMS ((U_CHAR *, int, int));
+static int file_size_and_mode  PARAMS ((int, int *, long *));
+
+static U_CHAR *skip_to_end_of_comment PARAMS ((FILE_BUF *, int *));
+static U_CHAR *skip_quoted_string     PARAMS ((const U_CHAR *, const U_CHAR *,
+                                              int, int *, int *, int *));
 
 int main               PARAMS ((int, char **));
 
@@ -395,33 +441,28 @@ 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 */
 
-FILE_BUF expand_to_temp_buffer PARAMS ((U_CHAR *, U_CHAR *, int));
-DEFINITION *collect_expansion  PARAMS ((U_CHAR *, U_CHAR *, int,
-                                       struct arglist *));
+static FILE_BUF expand_to_temp_buffer PARAMS ((const U_CHAR *, const U_CHAR *, int));
+static DEFINITION *collect_expansion  PARAMS ((U_CHAR *, U_CHAR *, int,
+                                              struct arglist *));
 
 /* Stack of conditionals currently in progress
    (including both successful and failing conditionals).  */
@@ -437,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;
@@ -464,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.  */
   {
@@ -495,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.  */
 
@@ -519,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':
@@ -533,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;
 
@@ -547,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 */
@@ -591,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':
@@ -608,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;
@@ -700,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 ((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 */
@@ -754,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.  */
 
@@ -777,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;
@@ -939,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");
@@ -965,13 +966,98 @@ 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.
    Potentially-embarrassing characters are / and *
    (because a backslash-newline inside a comment delimiter
    would cause it not to be recognized).  */
-void
+static void
 newline_fix (bp)
      U_CHAR *bp;
 {
@@ -1006,7 +1092,7 @@ newline_fix (bp)
 
 /* Like newline_fix but for use within a directive-name.
    Move any backslash-newlines up past any following symbol constituents.  */
-void
+static void
 name_newline_fix (bp)
      U_CHAR *bp;
 {
@@ -1022,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.  */
@@ -1075,7 +1161,7 @@ name_newline_fix (bp)
  * explicitly, and before RECACHE, since RECACHE uses OBP.
  */
 
-void
+static void
 rescan (op, output_marks)
      FILE_BUF *op;
      int output_marks;
@@ -1370,7 +1456,7 @@ do { ip = &instack[indepth];              \
            ibp += 2;
          }
          c = *ibp++;
-         if (!isalnum (c) && c != '.' && c != '_') {
+         if (!ISALNUM (c) && c != '.' && c != '_') {
            --ibp;
            break;
          }
@@ -1433,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)
@@ -1484,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;
        }
@@ -1612,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) {
@@ -1711,9 +1797,9 @@ hashcollision:
  * OUTPUT_MARKS is 1 for macroexpanding a macro argument separately
  * before substitution; it is 0 for other uses.
  */
-FILE_BUF
+static FILE_BUF
 expand_to_temp_buffer (buf, limit, output_marks)
-     U_CHAR *buf, *limit;
+     const U_CHAR *buf, *limit;
      int output_marks;
 {
   register FILE_BUF *ip;
@@ -1729,7 +1815,7 @@ expand_to_temp_buffer (buf, limit, output_marks)
 
   buf1 = (U_CHAR *) alloca (length + 1);
   {
-    register U_CHAR *p1 = buf;
+    register const U_CHAR *p1 = buf;
     register U_CHAR *p2 = buf1;
 
     while (p1 != limit)
@@ -1789,7 +1875,7 @@ expand_to_temp_buffer (buf, limit, output_marks)
  * Otherwise, returns zero, without advancing the input pointer.
  */
 
-int
+static int
 handle_directive (ip, op)
      FILE_BUF *ip, *op;
 {
@@ -1807,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;
@@ -1824,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;
     }
@@ -1851,7 +1937,7 @@ handle_directive (ip, op)
    */
   for (kt = directive_table; kt->length > 0; kt++) {
     if (kt->length == ident_length
-       && !strncmp (kt->name, (char *)ident, ident_length)) {
+       && !strncmp (kt->name, (const char *)ident, ident_length)) {
       register U_CHAR *buf;
       register U_CHAR *limit = ip->buf + ip->length;
       int unterminated = 0;
@@ -1966,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);
              }
@@ -1982,7 +2068,7 @@ handle_directive (ip, op)
          case '\'':
          case '\"':
            {
-             register U_CHAR *bp1
+             register const U_CHAR *bp1
                = skip_quoted_string (xp - 1, limit, ip->lineno, 0, 0, 0);
              while (xp != bp1)
                *cp++ = *xp++;
@@ -2018,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;
@@ -2036,12 +2122,12 @@ monthnames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  * expand things like __FILE__.  Place the expansion into the output
  * buffer *without* rescanning.
  */
-void
+static void
 special_symbol (hp, op)
      HASHNODE *hp;
      FILE_BUF *op;
 {
-  char *buf = 0;
+  const char *buf;
   time_t t;
   int i, len;
   int true_indepth;
@@ -2070,56 +2156,70 @@ special_symbol (hp, op)
 
       if (string)
        {
-         buf = (char *) alloca (3 + strlen (string));
-         sprintf (buf, "\"%s\"", string);
+         char *tmp = (char *) alloca (3 + strlen (string));
+         sprintf (tmp, "\"%s\"", string);
+         buf = tmp;
        }
       else
-       buf = (char *) "";
+       buf = "";
 
       break;
     }
 
   case T_INCLUDE_LEVEL:
-    true_indepth = 0;
-    for (i = indepth; i >= 0; i--)
-      if (instack[i].fname != NULL)
-        true_indepth++;
-
-    buf = (char *) alloca (8); /* Eigth bytes ought to be more than enough */
-    sprintf (buf, "%d", true_indepth - 1);
+    {
+      char *tmp = (char *) alloca (8); /* Eigth bytes ought to be more than enough */
+      true_indepth = 0;
+      for (i = indepth; i >= 0; i--)
+       if (instack[i].fname != NULL)
+         true_indepth++;
+
+    sprintf (tmp, "%d", true_indepth - 1);
+    buf = tmp;
     break;
+    }
 
   case T_VERSION:
-    buf = (char *) alloca (3 + strlen (version_string));
-    sprintf (buf, "\"%s\"", version_string);
-    break;
+    {
+      char *tmp = (char *) alloca (3 + strlen (version_string));
+      sprintf (tmp, "\"%s\"", version_string);
+      buf = tmp;
+      break;
+    }
 
   case T_CONST:
-    buf = (char *) hp->value.cpval;
+    buf = hp->value.cpval;
     break;
 
   case T_SPECLINE:
-    buf = (char *) alloca (10);
-    sprintf (buf, "%d", ip->lineno);
-    break;
+    {
+      char *tmp = (char *) alloca (10);
+      sprintf (tmp, "%d", ip->lineno);
+      buf = tmp;
+      break;
+    }
 
   case T_DATE:
   case T_TIME:
-    if (timebuf == NULL) {
-      t = time (0);
-      timebuf = localtime (&t);
+    {
+      char *tmp = (char *) alloca (20);
+
+      if (timebuf == NULL) {
+       t = time (0);
+       timebuf = localtime (&t);
+      }
+      if (hp->type == T_DATE)
+       sprintf (tmp, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon],
+                timebuf->tm_mday, timebuf->tm_year + 1900);
+      else
+       sprintf (tmp, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min,
+                timebuf->tm_sec);
+      buf = tmp;
+      break;
     }
-    buf = (char *) alloca (20);
-    if (hp->type == T_DATE)
-      sprintf (buf, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon],
-             timebuf->tm_mday, timebuf->tm_year + 1900);
-    else
-      sprintf (buf, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min,
-             timebuf->tm_sec);
-    break;
 
   case T_SPEC_DEFINED:
-    buf = (char *) " 0 ";              /* Assume symbol is not defined */
+    buf = " 0 ";                       /* Assume symbol is not defined */
     ip = &instack[indepth];
     SKIP_WHITE_SPACE (ip->bufp);
     if (*ip->bufp == '(') {
@@ -2128,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 = (char *) " 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) {
@@ -2164,11 +2268,10 @@ oops:
  * Process include file by reading it in and calling rescan.
  * Expects to see "fname" or <fname> on the input.
  */
-void
-do_include (buf, limit, op, keyword)
+static void
+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 */
@@ -2192,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 '\"':
@@ -2271,8 +2374,8 @@ get_filename:
 
   /* If specified file name is absolute, just open it.  */
 
-  if (*fbeg == '/') {
-    strncpy (fname, (char *)fbeg, flen);
+  if (IS_ABSOLUTE_PATHNAME (fbeg)) {
+    strncpy (fname, (const char *)fbeg, flen);
     fname[flen] = 0;
     f = open (fname, O_RDONLY, 0666);
   } else {
@@ -2287,28 +2390,46 @@ get_filename:
       } else {
        fname[0] = 0;
       }
-      strncat (fname, (char *)fbeg, flen);
+      strncat (fname, (const char *)fbeg, flen);
       if ((f = open (fname, O_RDONLY, 0666)) >= 0)
        break;
     }
   }
 
   if (f < 0) {
-    strncpy (fname, (char *)fbeg, flen);
+    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.
@@ -2338,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)
@@ -2360,7 +2479,7 @@ get_filename:
 /* Process the contents of include file FNAME, already open on descriptor F,
    with output to OP.  */
 
-void
+static void
 finclude (f, fname, op)
      int f;
      const char *fname;
@@ -2440,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;
@@ -2455,11 +2576,10 @@ BUF points to the contents of the #define command, as a continguous string.
 LIMIT points to the first character past the end of the definition.
 KEYWORD is the keyword-table entry for #define.  */
 
-void
-do_define (buf, limit, op, keyword)
+static void
+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 */
@@ -2472,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 ((char *)symname, "defined", 7) && sym_length == 7)
-      error ("defining `defined' as a macro");
+    if (! strncmp ((const char *)symname, "defined", 7) && sym_length == 7)
+      {
+       error ("\"defined\" cannot be used as a macro name");
+       return;
+      }
   }
 
   /* lossage will occur if identifiers or control keywords are broken
@@ -2514,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;
@@ -2539,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);
@@ -2547,27 +2674,30 @@ do_define (buf, limit, op, keyword)
     /* Now set defn->argnames to the result of concatenating
        the argument names in reverse order
        with comma-space between them.  */
-    defn->argnames = (U_CHAR *) xmalloc (arglengths + 1);
     {
       struct arglist *temp;
       int i = 0;
+      U_CHAR *tmp = (U_CHAR *) xmalloc (arglengths + 1);
+
       for (temp = arg_ptrs; temp; temp = temp->next) {
-       memcpy (&defn->argnames[i], temp->name, temp->length);
+       memcpy (&tmp[i], temp->name, temp->length);
        i += temp->length;
        if (temp->next != 0) {
-         defn->argnames[i++] = ',';
-         defn->argnames[i++] = ' ';
+         tmp[i++] = ',';
+         tmp[i++] = ' ';
        }
       }
-      defn->argnames[i] = 0;
+      tmp[i] = 0;
+      defn->argnames = tmp;
+      
     }
   } 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);
-    defn->argnames = (U_CHAR *) "";
+    defn->argnames = (const U_CHAR *) "";
   }
 
   hashcode = hashf (symname, sym_length, HASHSIZE);
@@ -2591,7 +2721,7 @@ do_define (buf, limit, op, keyword)
 /*
  * return zero if two DEFINITIONs are isomorphic
  */
-int
+static int
 compare_defs (d1, d2)
      DEFINITION *d1, *d2;
 {
@@ -2602,12 +2732,12 @@ compare_defs (d1, d2)
 
   if (d1->nargs != d2->nargs)
     return 1;
-  if (strcmp ((char *)d1->argnames, (char *)d2->argnames))
+  if (strcmp ((const char *)d1->argnames, (const char *)d2->argnames))
     return 1;
   for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2;
        a1 = a1->next, a2 = a2->next) {
     if (!((a1->nchars == a2->nchars
-          && ! strncmp ((char *)p1, (char *)p2, a1->nchars))
+          && ! strncmp ((const char *)p1, (const char *)p2, a1->nchars))
          || ! comp_def_part (first, p1, a1->nchars, p2, a2->nchars, 0))
        || a1->argno != a2->argno
        || a1->stringify != a2->stringify
@@ -2634,27 +2764,27 @@ compare_defs (d1, d2)
     so ignore leading whitespace entirely.
    LAST means these parts are the last of a macro definition;
     so ignore trailing whitespace entirely.  */
-int
+static int
 comp_def_part (first, beg1, len1, beg2, len2, last)
      int first;
-     U_CHAR *beg1, *beg2;
+     const U_CHAR *beg1, *beg2;
      int len1, len2;
      int last;
 {
-  register U_CHAR *end1 = beg1 + len1;
-  register U_CHAR *end2 = beg2 + len2;
+  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;
@@ -2683,7 +2813,7 @@ from the argument.  */
    If there is no trailing whitespace, a Newline Space is added at the end
    to prevent concatenation that would be contrary to the standard.  */
 
-DEFINITION *
+static DEFINITION *
 collect_expansion (buf, end, nargs, arglist)
      U_CHAR *buf, *end;
      int nargs;
@@ -2711,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.
@@ -2731,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++;
   }
@@ -2781,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) {
@@ -2797,7 +2927,8 @@ collect_expansion (buf, end, nargs, arglist)
 
          if (arg->name[0] == c
              && arg->length == id_len
-             && strncmp ((char *)arg->name, (char *)id_beg, id_len) == 0) {
+             && strncmp ((const char *)arg->name,
+                         (const char *)id_beg, id_len) == 0) {
            /* make a pat node for this arg and append it to the end of
               the pat list */
            tpat = (struct reflist *) xmalloc (sizeof (struct reflist));
@@ -2841,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++;
     }
@@ -2862,11 +2993,10 @@ collect_expansion (buf, end, nargs, arglist)
  * in its very own hash table.
  */
 #define FNAME_HASHSIZE 37
-void
-do_line (buf, limit, op, keyword)
+static void
+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];
@@ -2881,7 +3011,7 @@ do_line (buf, limit, op, keyword)
   bp = tem.buf;
   SKIP_WHITE_SPACE (bp);
 
-  if (!isdigit (*bp)) {
+  if (!ISDIGIT (*bp)) {
     error ("invalid format #line command");
     return;
   }
@@ -2889,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 ((char *)bp) - 1;
+  new_lineno = atoi ((const char *)bp);
 
   /* skip over the line number.  */
-  while (isdigit (*bp))
+  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 == '\"') {
@@ -2945,7 +3068,7 @@ do_line (buf, limit, op, keyword)
       &fname_table[hashf (fname, fname_length, FNAME_HASHSIZE)];
     for (hp = *hash_bucket; hp != NULL; hp = hp->next)
       if (hp->length == fname_length &&
-         strncmp (hp->value.cpval, (char *)fname, fname_length) == 0) {
+         strncmp (hp->value.cpval, (const char *)fname, fname_length) == 0) {
        ip->fname = hp->value.cpval;
        break;
       }
@@ -2967,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));
 }
 
@@ -2975,18 +3099,17 @@ do_line (buf, limit, op, keyword)
  * 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.
  */
-void
-do_undef (buf, limit, op, keyword)
+static void
+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 ((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) {
@@ -2996,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
@@ -3008,11 +3418,10 @@ do_undef (buf, limit, op, keyword)
  *   5) call conditional_skip to skip til the next #endif (etc.),
  *      or not, depending on the value from step 3.
  */
-void
-do_if (buf, limit, op, keyword)
+static void
+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];
@@ -3025,11 +3434,10 @@ do_if (buf, limit, op, keyword)
  * handle a #elif directive by not changing  if_stack  either.
  * see the comment above do_else.
  */
-void
-do_elif (buf, limit, op, keyword)
+static void
+do_elif (buf, limit, op)
      U_CHAR *buf, *limit;
      FILE_BUF *op;
-     struct directive *keyword ATTRIBUTE_UNUSED;
 {
   int value;
   FILE_BUF *ip = &instack[indepth];
@@ -3066,9 +3474,9 @@ do_elif (buf, limit, op, keyword)
  * evaluate a #if expression in BUF, of length LENGTH,
  * then parse the result as a C expression and return the value as an int.
  */
-int
+static int
 eval_if_expression (buf, length)
-     U_CHAR *buf;
+     const U_CHAR *buf;
      int length;
 {
   FILE_BUF temp_obuf;
@@ -3079,7 +3487,7 @@ eval_if_expression (buf, length)
   temp_obuf = expand_to_temp_buffer (buf, buf + length, 0);
   delete_macro (save_defined); /* clean up special symbol */
 
-  value = parse_c_expression ((char *)temp_obuf.buf);
+  value = parse_c_expression ((const char *)temp_obuf.buf);
 
   free (temp_obuf.buf);
 
@@ -3091,11 +3499,10 @@ eval_if_expression (buf, length)
  * then do or don't skip to the #endif/#else/#elif depending
  * on what directive is actually being processed.
  */
-void
-do_xifdef (buf, limit, op, keyword)
+static void
+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];
@@ -3103,24 +3510,39 @@ 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.
  */
-void
+static void
 conditional_skip (ip, skip, type)
      FILE_BUF *ip;
      int skip;
@@ -3150,7 +3572,7 @@ conditional_skip (ip, skip, type)
  * leaves input ptr at the sharp sign found.
  * If ANY is nonzero, return at next directive of any sort.
  */
-void
+static void
 skip_if_group (ip, any)
      FILE_BUF *ip;
      int any;
@@ -3198,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;
@@ -3222,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;
@@ -3242,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;
        }
@@ -3255,8 +3677,8 @@ skip_if_group (ip, any)
 
       for (kt = directive_table; kt->length >= 0; kt++) {
        IF_STACK_FRAME *temp;
-       if (strncmp ((char *)cp, kt->name, kt->length) == 0
-           && !is_idchar[cp[kt->length]]) {
+       if (strncmp ((const char *)cp, kt->name, kt->length) == 0
+           && !is_idchar (cp[kt->length])) {
 
          /* If we are asked to return on next directive,
             do so now.  */
@@ -3317,12 +3739,11 @@ skip_if_group (ip, any)
  * for missing #endif's etc. will point to the original #if.  It
  * is possible that something different would be better.
  */
-void
-do_else (buf, limit, op, keyword)
+static void
+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];
 
@@ -3351,12 +3772,11 @@ do_else (buf, limit, op, keyword)
 /*
  * unstack after #endif command
  */
-void
-do_endif (buf, limit, op, keyword)
+static void
+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");
@@ -3375,7 +3795,7 @@ do_endif (buf, limit, op, keyword)
  * Don't use this routine (or the next one) if bumping the line
  * counter is not sufficient to deal with newlines in the string.
  */
-U_CHAR *
+static U_CHAR *
 skip_to_end_of_comment (ip, line_counter)
      register FILE_BUF *ip;
      int *line_counter;                /* place to remember newlines, or NULL */
@@ -3438,10 +3858,10 @@ skip_to_end_of_comment (ip, line_counter)
  *
  * If EOFP is nonzero, set *EOFP to 1 if the string is unterminated.
  */
-U_CHAR *
+static U_CHAR *
 skip_quoted_string (bp, limit, start_line, count_newlines, backslash_newlines_p, eofp)
-     register U_CHAR *bp;
-     register U_CHAR *limit;
+     register const U_CHAR *bp;
+     register const U_CHAR *limit;
      int start_line;
      int *count_newlines;
      int *backslash_newlines_p;
@@ -3482,7 +3902,7 @@ skip_quoted_string (bp, limit, start_line, count_newlines, backslash_newlines_p,
     } else if (c == match)
       break;
   }
-  return bp;
+  return (U_CHAR *) bp;
 }
 \f
 /*
@@ -3493,7 +3913,7 @@ skip_quoted_string (bp, limit, start_line, count_newlines, backslash_newlines_p,
  * FILE_CHANGE says whether we are entering a file, leaving, or neither.
  */
 
-void
+static void
 output_line_command (ip, op, conditional, file_change)
      FILE_BUF *ip, *op;
      int conditional;
@@ -3528,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);
@@ -3547,7 +3969,7 @@ output_line_command (ip, op, conditional, file_change)
    If macro wants arguments, caller has already verified that
    an argument list follows; arguments come from the input stack.  */
 
-void
+static void
 macroexpand (hp, op)
      HASHNODE *hp;
      FILE_BUF *op;
@@ -3601,9 +4023,9 @@ macroexpand (hp, op)
 
     /* If we got one arg but it was just whitespace, call that 0 args.  */
     if (i == 1) {
-      register U_CHAR *bp = args[0].raw;
-      register U_CHAR *lim = bp + args[0].raw_length;
-      while (bp != lim && is_space[*bp]) bp++;
+      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++;
       if (bp == lim)
        i = 0;
     }
@@ -3671,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];
@@ -3689,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];
@@ -3719,7 +4141,7 @@ macroexpand (hp, op)
            /* Escape these chars */
            if (c == '\"' || (in_string && c == '\\'))
              xbuf[totlen++] = '\\';
-           if (isprint (c))
+           if (ISPRINT (c))
              xbuf[totlen++] = c;
            else {
              sprintf ((char *) &xbuf[totlen], "\\%03o", (unsigned int) c);
@@ -3727,12 +4149,12 @@ macroexpand (hp, op)
            }
          }
        } else {
-         U_CHAR *p1 = arg->raw;
-         U_CHAR *l1 = p1 + arg->raw_length;
+         const U_CHAR *p1 = arg->raw;
+         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
@@ -3744,9 +4166,9 @@ 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] == '-') {
-               U_CHAR *p2 = l1 - 1;
+               const U_CHAR *p2 = l1 - 1;
                /* If a `-' is preceded by an odd number of newlines then it
                   and the last newline are a no-reexpansion marker.  */
                while (p2 != p1 && p2[-1] == '\n') p2--;
@@ -3810,7 +4232,7 @@ macroexpand (hp, op)
  * Return nonzero to indicate a syntax error.
  */
 
-const char *
+static const char *
 macarg (argptr)
      register struct argdata *argptr;
 {
@@ -3898,7 +4320,7 @@ macarg (argptr)
 
   if (argptr != 0) {
     FILE_BUF obuf;
-    register U_CHAR *buf, *lim;
+    register const U_CHAR *buf, *lim;
     register int totlen;
 
     obuf = expand_to_temp_buffer (argptr->raw,
@@ -3921,7 +4343,7 @@ macarg (argptr)
         in case we need to keep it all.  */
       if (c == '\"' || c == '\\') /* escape these chars */
        totlen++;
-      else if (!isprint (c))
+      else if (!ISPRINT (c))
        totlen += 3;
     }
     argptr->stringified_length = totlen;
@@ -3941,10 +4363,10 @@ macarg (argptr)
    Increment *NEWLINES each time a newline is passed.
    Set *COMMENTS to 1 if a comment is seen.  */
 
-U_CHAR *
+static U_CHAR *
 macarg1 (start, limit, depthptr, newlines, comments)
      U_CHAR *start;
-     register U_CHAR *limit;
+     register const U_CHAR *limit;
      int *depthptr, *newlines, *comments;
 {
   register U_CHAR *bp = start;
@@ -4029,7 +4451,7 @@ macarg1 (start, limit, depthptr, newlines, comments)
    We assume that that much extra space is available past the end
    of the string.  */
 
-int
+static int
 discard_comments (start, length, newlines)
      U_CHAR *start;
      int length;
@@ -4037,7 +4459,7 @@ discard_comments (start, length, newlines)
 {
   register U_CHAR *ibp;
   register U_CHAR *obp;
-  register U_CHAR *limit;
+  register const U_CHAR *limit;
   register int c;
 
   /* If we have newlines to duplicate, copy everything
@@ -4122,7 +4544,7 @@ discard_comments (start, length, newlines)
 \f
 
 /* Core error handling routine.  */
-void
+static void
 v_message (mtype, line, msgid, ap)
      enum msgtype mtype;
      int line;
@@ -4275,7 +4697,7 @@ pfatal_with_name (name)
    If the current level is for a file, we return LINE.
    But if the current level is not for a file, LINE is meaningless.
    In that case, we return the lineno of the innermost file.  */
-int
+static int
 line_for_error (line)
      int line;
 {
@@ -4302,7 +4724,7 @@ line_for_error (line)
  * should work ok.
  */
 
-void
+static void
 grow_outbuf (obuf, needed)
      register FILE_BUF *obuf;
      register int needed;
@@ -4343,7 +4765,7 @@ grow_outbuf (obuf, needed)
  *
  * caller must set the value, if any is desired.
  */
-HASHNODE *
+static HASHNODE *
 install (name, len, type, hash)
      const U_CHAR *name;
      int len;
@@ -4358,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;
   }
@@ -4402,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;
   }
 
@@ -4412,7 +4834,7 @@ lookup (name, len, hash)
   bucket = hashtab[hash];
   while (bucket) {
     if (bucket->length == len
-       && strncmp ((char *)bucket->name, (char *)name, len) == 0)
+       && strncmp ((const char *)bucket->name, (const char *)name, len) == 0)
       return bucket;
     bucket = bucket->next;
   }
@@ -4432,7 +4854,7 @@ lookup (name, len, hash)
    In any case, this is necessary, because a macro can be #undef'd
    in the middle of reading the arguments to a call to it.
    If #undef freed the DEFINITION, that would crash.  */
-void
+static void
 delete_macro (hp)
      HASHNODE *hp;
 {
@@ -4454,7 +4876,7 @@ delete_macro (hp)
  * return hash function on name.  must be compatible with the one
  * computed a step at a time, elsewhere
  */
-int
+static int
 hashf (name, len, hashsize)
      register const U_CHAR *name;
      register int len;
@@ -4470,7 +4892,7 @@ hashf (name, len, hashsize)
 \f
 /* Dump all macro definitions as #defines to stdout.  */
 
-void
+static void
 dump_all_macros ()
 {
   int bucket;
@@ -4533,20 +4955,20 @@ dump_all_macros ()
    Output characters START thru LENGTH.
    Discard newlines outside of strings, thus
    converting funny-space markers to ordinary spaces.  */
-void
+static void
 dump_defn_1 (base, start, length)
-     U_CHAR *base;
+     const U_CHAR *base;
      int start;
      int length;
 {
-  U_CHAR *p = base + start;
-  U_CHAR *limit = base + start + length;
+  const U_CHAR *p = base + start;
+  const U_CHAR *limit = base + start + length;
 
   while (p < limit) {
     if (*p != '\n')
       putchar (*p);
     else if (*p == '\"' || *p =='\'') {
-      U_CHAR *p1 = skip_quoted_string (p, limit, 0, 0, 0, 0);
+      const U_CHAR *p1 = skip_quoted_string (p, limit, 0, 0, 0, 0);
       fwrite (p, p1 - p, 1, stdout);
       p = p1 - 1;
     }
@@ -4557,14 +4979,14 @@ dump_defn_1 (base, start, length)
 /* Print the name of argument number ARGNUM of macro definition DEFN.
    Recall that DEFN->argnames contains all the arg names
    concatenated in reverse order with comma-space in between.  */
-void
+static void
 dump_arg_n (defn, argnum)
      DEFINITION *defn;
      int argnum;
 {
-  register U_CHAR *p = defn->argnames;
+  register const U_CHAR *p = defn->argnames;
   while (argnum + 1 < defn->nargs) {
-    p = (U_CHAR *) strchr ((char *)p, ' ') + 1;
+    p = (const U_CHAR *) strchr ((const char *)p, ' ') + 1;
     argnum++;
   }
 
@@ -4573,44 +4995,6 @@ dump_arg_n (defn, argnum)
     p++;
   }
 }
-\f
-/* Initialize syntactic classifications of characters.  */
-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
@@ -4618,7 +5002,7 @@ initialize_char_syntax ()
  install(DSC(name), type, -1);
 #define install_value(name, val) \
  hp = install(DSC(name), T_CONST, -1); hp->value.cpval = val;
-void
+static void
 initialize_builtins ()
 {
   HASHNODE *hp;
@@ -4650,114 +5034,106 @@ 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.
- */
-void
-make_definition (str)
-     U_CHAR *str;
+/* Common handler of command line directives -U, -D and -A.  */
+static void
+run_directive (str, len, type)
+     const char *str;
+     size_t len;
+     enum node_type type;
 {
-  FILE_BUF *ip;
   struct directive *kt;
-  U_CHAR *buf, *p;
-  size_t len = strlen ((char *)str);
-
-  p = (U_CHAR *) strchr ((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 */
-void
-make_undef (str)
-     U_CHAR *str;
+/* 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_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 ((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.  */
-void
-deps_output (string, size)
-     const char *string;
-     int size;
+
+/* Handle the -U option.  */
+static void
+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.  */
 
-int
+static int
 file_size_and_mode (fd, mode_pointer, size_pointer)
      int fd;
      int *mode_pointer;