$NetBSD: patch-ai,v 1.1 2003/07/12 11:39:53 jtb Exp $

--- ../gcc-2.95.3/gcc/p/gpc.c.orig
+++ ../gcc-2.95.3/gcc/p/gpc.c
@@ -245,9 +245,13 @@
 static void clear_failure_queue PROTO((void));
 static int check_live_switch    PROTO((int, int));
 static const char *handle_braces PROTO((const char *));
+static const struct spec_function *lookup_spec_function PROTO((const char *));
+static const char *eval_spec_function PROTO((const char *, const char *));
+static const char *handle_spec_function PROTO((const char *));
 static char *save_string        PROTO((const char *, int));
 extern int do_spec              PROTO((const char *));
 static int do_spec_1            PROTO((const char *, int, const char *));
+static int do_spec_2		PROTO((const char *));
 static const char *find_file    PROTO((const char *));
 static int is_directory         PROTO((const char *, const char *, int));
 static void validate_switches   PROTO((const char *));
@@ -280,6 +284,7 @@
 static void process_command             PROTO ((int, char **));
 static int execute                      PROTO ((void));
 static void unused_prefix_warnings      PROTO ((struct path_prefix *));
+static void alloc_args			PROTO ((void));
 static void clear_args                  PROTO ((void));
 static void fatal_error                 PROTO ((int));
 
@@ -290,6 +295,9 @@
 extern char *xrealloc PROTO((void *, size_t));
 #endif
 
+
+static const char *if_exists_spec_function PROTO ((int, const char **));
+
 /* Specs are strings containing lines, each of which (if not blank)
 is made up of a program name, and arguments separated by spaces.
 The program name must be exact and start from root, since no path
@@ -373,6 +381,12 @@
  %*     substitute the variable part of a matched option.  (See below.)
         Note that each comma in the substituted string is replaced by
         a single space.
+ %:function(args)
+	Call the named function FUNCTION, passing it ARGS.  ARGS is
+	first processed as a nested spec string, then split into an
+	argument vector in the usual fashion.  The function returns
+	a string which is processed as if it had appeared literally
+	as part of the current spec.
  %{S}   substitutes the -S switch, if that switch was given to CC.
         If that switch was not specified, this substitutes nothing.
         Here S is a metasyntactic variable.
@@ -1370,6 +1384,24 @@
 static struct spec_list *specs = (struct spec_list *)0;
 
 
+/* The mapping of a spec function name to the C function that
+   implements it.  */
+struct spec_function
+{
+  const char *name;
+  const char *(*func) PROTO ((int, const char **));
+};
+
+/* List of static spec functions.  */
+
+static const struct spec_function static_spec_functions[] =
+{
+  { "if-exists",		if_exists_spec_function },
+  { 0, 0 }
+};
+
+static int processing_spec_function;
+
 /* Initialize the specs lookup routines.  */
 
 static void
@@ -1598,6 +1630,15 @@
 
 static char *multilib_dir;
 
+/* Allocate the argument vector.  */
+
+static void
+alloc_args ()
+{
+  argbuf_length = 10;
+  argbuf = (char **) xmalloc (argbuf_length * sizeof (char *));
+}
+
 #ifdef GPC
 /* Print a help screen and exit */
 static void print_message_and_exit PROTO((void));
@@ -2482,6 +2523,9 @@
 
   struct command *commands;     /* each command buffer with above info.  */
 
+  if (processing_spec_function)
+    abort ();
+
   /* Count # of piped commands.  */
   for (n_commands = 1, i = 0; i < argbuf_index; i++)
     if (strcmp (argbuf[i], "|") == 0)
@@ -2665,6 +2709,11 @@
 
 static int n_infiles;
 
+/* This counts the number of libraries added by lang_specific_driver, so that
+   we can tell if there were any user supplied any files or libraries.  */
+
+static int added_libraries;
+
 /* And a vector of corresponding output files is made up later.  */
 
 static const char **outfiles;
@@ -2851,7 +2900,7 @@
               strncpy (nstore, startp, endp-startp);
               if (endp == startp)
                 strcpy (nstore, concat (".", dir_separator_str, NULL_PTR));
-              else if (endp[-1] != '/' && endp[-1] != DIR_SEPARATOR)
+	      else if (!IS_DIR_SEPARATOR (endp[-1]))
                 {
                   nstore[endp-startp] = DIR_SEPARATOR;
                   nstore[endp-startp+1] = 0;
@@ -2882,7 +2931,7 @@
               strncpy (nstore, startp, endp-startp);
               if (endp == startp)
                 strcpy (nstore, concat (".", dir_separator_str, NULL_PTR));
-              else if (endp[-1] != '/' && endp[-1] != DIR_SEPARATOR)
+	      else if (!IS_DIR_SEPARATOR (endp[-1]))
                 {
                   nstore[endp-startp] = DIR_SEPARATOR;
                   nstore[endp-startp+1] = 0;
@@ -2915,7 +2964,7 @@
               strncpy (nstore, startp, endp-startp);
               if (endp == startp)
                 strcpy (nstore, concat (".", dir_separator_str, NULL_PTR));
-              else if (endp[-1] != '/' && endp[-1] != DIR_SEPARATOR)
+	      else if (!IS_DIR_SEPARATOR (endp[-1]))
                 {
                   nstore[endp-startp] = DIR_SEPARATOR;
                   nstore[endp-startp+1] = 0;
@@ -3518,14 +3567,7 @@
 {
   int value;
 
-  clear_args ();
-  arg_going = 0;
-  delete_this_arg = 0;
-  this_is_output_file = 0;
-  this_is_library_file = 0;
-  input_from_pipe = 0;
-
-  value = do_spec_1 (spec, 0, NULL_PTR);
+  value = do_spec_2 (spec);
 
   /* Force out any unfinished command.
      If -pipe, this forces out the last command if it ended in `|'.  */
@@ -3541,6 +3583,20 @@
   return value;
 }
 
+static int
+do_spec_2 (spec)
+     const char *spec;
+{
+  clear_args ();
+  arg_going = 0;
+  delete_this_arg = 0;
+  this_is_output_file = 0;
+  this_is_library_file = 0;
+  input_from_pipe = 0;
+
+  return do_spec_1 (spec, 0, NULL_PTR);
+}
+
 /* Process the sub-spec SPEC as a portion of a larger spec.
    This is like processing a whole spec except that we do
    not initialize at the beginning and we do not supply a
@@ -4258,6 +4314,12 @@
               return -1;
             break;
 
+	  case ':':
+	    p = handle_spec_function (p);
+	    if (p == 0)
+	      return -1;
+	    break;
+
           case '%':
             obstack_1grow (&obstack, '%');
             break;
@@ -4414,7 +4476,173 @@
         arg_going = 1;
       }
 
-  return 0;             /* End of string */
+  /* End of string.  If we are processing a spec function, we need to
+     end any pending argument.  */
+  if (processing_spec_function && arg_going)
+    {
+      obstack_1grow (&obstack, 0);
+      string = obstack_finish (&obstack);
+      if (this_is_library_file)
+	string = find_file (string);
+      store_arg (string, delete_this_arg, this_is_output_file);
+      if (this_is_output_file)
+	outfiles[input_file_number] = string;
+      arg_going = 0;
+    }
+
+  return 0;
+}
+
+/* Look up a spec function.  */
+
+static const struct spec_function *
+lookup_spec_function (name)
+     const char *name;
+{
+  static const struct spec_function * const spec_function_tables[] =
+  {
+    static_spec_functions,
+  };
+  const struct spec_function *sf;
+  unsigned int i;
+
+  for (i = 0; i < ARRAY_SIZE (spec_function_tables); i++)
+    {
+      for (sf = spec_function_tables[i]; sf->name != NULL; sf++)
+	if (strcmp (sf->name, name) == 0)
+	  return sf;
+    }
+
+  return NULL;
+}
+
+/* Evaluate a spec function.  */
+
+static const char *
+eval_spec_function (func, args)
+     const char *func, *args;
+{
+  const struct spec_function *sf;
+  const char *funcval;
+
+  /* Saved spec processing context.  */
+  int save_argbuf_index;
+  int save_argbuf_length;
+  char **save_argbuf;
+
+  int save_arg_going;
+  int save_delete_this_arg;
+  int save_this_is_output_file;
+  int save_this_is_library_file;
+  int save_input_from_pipe;
+
+
+  sf = lookup_spec_function (func);
+  if (sf == NULL)
+    fatal ("unknown spec function `%s'", func);
+
+  /* Push the spec processing context.  */
+  save_argbuf_index = argbuf_index;
+  save_argbuf_length = argbuf_length;
+  save_argbuf = argbuf;
+
+  save_arg_going = arg_going;
+  save_delete_this_arg = delete_this_arg;
+  save_this_is_output_file = this_is_output_file;
+  save_this_is_library_file = this_is_library_file;
+  save_input_from_pipe = input_from_pipe;
+
+  /* Create a new spec processing context, and build the function
+     arguments.  */
+
+  alloc_args ();
+  if (do_spec_2 (args) < 0)
+    fatal ("error in args to spec function `%s'", func);
+
+  /* argbuf_index is an index for the next argument to be inserted, and
+     so contains the count of the args already inserted.  */
+
+  funcval = (*sf->func) (argbuf_index, (const char **) argbuf);
+
+  /* Pop the spec processing context.  */
+  argbuf_index = save_argbuf_index;
+  argbuf_length = save_argbuf_length;
+  free (argbuf);
+  argbuf = save_argbuf;
+
+  arg_going = save_arg_going;
+  delete_this_arg = save_delete_this_arg;
+  this_is_output_file = save_this_is_output_file;
+  this_is_library_file = save_this_is_library_file;
+  input_from_pipe = save_input_from_pipe;
+
+  return funcval;
+}
+
+/* Handle a spec function call of the form:
+
+   %:function(args)
+
+   ARGS is processed as a spec in a separate context and split into an
+   argument vector in the normal fashion.  The function returns a string
+   containing a spec which we then process in the caller's context, or
+   NULL if no processing is required.  */
+
+static const char *
+handle_spec_function (p)
+     const char *p;
+{
+  char *func, *args;
+  const char *endp, *funcval;
+  int count;
+
+  processing_spec_function++;
+
+  /* Get the function name.  */
+  for (endp = p; *endp != '\0'; endp++)
+    {
+      if (*endp == '(')		/* ) */
+	break;
+      /* Only allow [A-Za-z0-9], -, and _ in function names.  */
+      if (!ISALNUM (*endp) && !(*endp == '-' || *endp == '_'))
+	fatal ("malformed spec function name");
+    }
+  if (*endp != '(')		/* ) */
+    fatal ("no arguments for spec function");
+  func = save_string (p, endp - p);
+  p = ++endp;
+
+  /* Get the arguments.  */
+  for (count = 0; *endp != '\0'; endp++)
+    {
+      /* ( */
+      if (*endp == ')')
+	{
+	  if (count == 0)
+	    break;
+	  count--;
+	}
+      else if (*endp == '(')	/* ) */
+	count++;
+    }
+  /* ( */
+  if (*endp != ')')
+    fatal ("malformed spec function arguments");
+  args = save_string (p, endp - p);
+  p = ++endp;
+
+  /* p now points to just past the end of the spec function expression.  */
+
+  funcval = eval_spec_function (func, args);
+  if (funcval != NULL && do_spec_1 (funcval, 0, NULL) < 0)
+    p = NULL;
+
+  free (func);
+  free (args);
+
+  processing_spec_function--;
+
+  return p;
 }
 
 /* Return 0 if we call do_spec_1 and that returns -1.  */
@@ -5146,7 +5374,8 @@
 #endif /* GPC */
 
   p = argv[0] + strlen (argv[0]);
-  while (p != argv[0] && p[-1] != '/' && p[-1] != DIR_SEPARATOR) --p;
+  while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
+    --p;
   programname = p;
 
 #ifdef GPC
@@ -5166,8 +5395,8 @@
     signal (SIGPIPE, fatal_error);
 #endif
 
-  argbuf_length = 10;
-  argbuf = (char **) xmalloc (argbuf_length * sizeof (char *));
+  /* Allocate the argument vector.  */
+  alloc_args ();
 
   obstack_init (&obstack);
 
@@ -5469,7 +5698,7 @@
 #endif
     }
 
-  if (n_infiles == 0)
+  if (n_infiles == added_libraries)
     fatal ("No input files");
 
   /* Make a place to record the compiler output file names
@@ -6380,3 +6609,25 @@
       ++p;
     }
 }
+
+/* if-exists built-in spec function.
+
+   Checks to see if the file specified by the absolute pathname in
+   ARGS exists.  Returns that pathname if found.
+
+   The usual use for this function is to check for a library file
+   (whose name has been expanded with %s).  */
+
+#define IS_ABSOLUTE_PATHNAME(cp)	((cp)[0] == '/')
+
+static const char *
+if_exists_spec_function (argc, argv)
+     int argc;
+     const char **argv;
+{
+  /* Must have only one argument.  */
+  if (argc == 1 && IS_ABSOLUTE_PATHNAME (argv[0]) && ! access (argv[0], R_OK))
+    return argv[0];
+
+  return NULL;
+}
