From 067277c389ff24af29f0ce9a1b31d7d002ae9fe8 Mon Sep 17 00:00:00 2001
From: amodra <amodra@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Fri, 16 Dec 2005 09:46:22 +0000
Subject: [PATCH] 	PR driver/20425 	* gcc.c (for_each_path): New
 function. 	(add_to_obstack, file_at_path): New functions. 	(struct
 file_at_path_info, struct add_to_obstack_info): New. 	(build_search_list):
 Rewrite using for_each_path.  Constify struct 	path_prefix pointer.  Add
 do_multi param.  Adjust all callers. 	(find_a_file): Similarly, but just
 change existing param to bool. 	(putenv_from_prefixes): Add do_multi
 param, make "paths" const. 	(do_spec_path): Delete. 	(struct
 spec_path_info): New. 	(spec_path): New function. 	(do_spec_1): Use
 for_each_path for %D and %I. 	(find_file): Adjust for find_a_file changes. 
 (main): Search multilibs for specs.  Print multilib lib path for 
 -print-search-dirs.  Likewise add multilibs to LIBRARY_PATH_ENV. 
 (read_specs): Search multilibs for %include and %include_noerr. 
 (is_directory): Remove second string param.  Change last param 	to a
 bool.  Don't use concat.  Remove SMALL_ARG_MAX test, always 	check path is
 a dir.  Update all callers. 	* doc/hostconfig.texi (SMALL_ARG_MAX): Remove
 mention. 	* system.h (SMALL_ARG_MAX): Poison. 	* config.gcc: Don't
 define SMALL_ARG_MAX. 	* config.host: Likewise. 	* config.build:
 Likewise.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@108635 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/ChangeLog           |  27 ++
 gcc/config.build        |   5 -
 gcc/config.gcc          |   3 -
 gcc/config.host         |   7 -
 gcc/doc/hostconfig.texi |   5 -
 gcc/gcc.c               | 665 +++++++++++++++++++++-------------------
 gcc/system.h            |   3 +-
 7 files changed, 376 insertions(+), 339 deletions(-)

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index bfa1e941d70c..f8d5db8025a6 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,30 @@
+2005-12-16  Alan Modra  <amodra@bigpond.net.au>
+
+	PR driver/20425
+	* gcc.c (for_each_path): New function.
+	(add_to_obstack, file_at_path): New functions.
+	(struct file_at_path_info, struct add_to_obstack_info): New.
+	(build_search_list): Rewrite using for_each_path.  Constify struct
+	path_prefix pointer.  Add do_multi param.  Adjust all callers.
+	(find_a_file): Similarly, but just change existing param to bool.
+	(putenv_from_prefixes): Add do_multi param, make "paths" const.
+	(do_spec_path): Delete.
+	(struct spec_path_info): New.
+	(spec_path): New function.
+	(do_spec_1): Use for_each_path for %D and %I.
+	(find_file): Adjust for find_a_file changes.
+	(main): Search multilibs for specs.  Print multilib lib path for
+	-print-search-dirs.  Likewise add multilibs to LIBRARY_PATH_ENV.
+	(read_specs): Search multilibs for %include and %include_noerr.
+	(is_directory): Remove second string param.  Change last param
+	to a bool.  Don't use concat.  Remove SMALL_ARG_MAX test, always
+	check path is a dir.  Update all callers.
+	* doc/hostconfig.texi (SMALL_ARG_MAX): Remove mention.
+	* system.h (SMALL_ARG_MAX): Poison.
+	* config.gcc: Don't define SMALL_ARG_MAX.
+	* config.host: Likewise.
+	* config.build: Likewise.
+
 2005-12-16  Paolo Bonzini  <bonzini@gnu.org>
 
 	* combine.c (combine_simplify_rtx <case NOT, NEG, TRUNCATE,
diff --git a/gcc/config.build b/gcc/config.build
index 5f2e70e18783..4a8f409e4319 100644
--- a/gcc/config.build
+++ b/gcc/config.build
@@ -90,15 +90,10 @@ case $build in
     build_install_headers_dir=install-headers-cpio
     ;;
   i[34567]86-sequent-ptx4* | i[34567]86-sequent-sysv4* )
-    build_xm_defines="SMALL_ARG_MAX"
     build_install_headers_dir=install-headers-cpio
     ;;
-  i[34567]86-*-solaris2*)
-    build_xm_defines="SMALL_ARG_MAX"
-    ;;
   i[34567]86-*-sysv4*) 
     # Intel x86 running system V r4
-    build_xm_defines="SMALL_ARG_MAX"
     build_install_headers_dir=install-headers-cpio
     ;;
   i[34567]86-*-udk*) 
diff --git a/gcc/config.gcc b/gcc/config.gcc
index 01521f266504..e443dd0023f0 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -984,7 +984,6 @@ i[34567]86-sequent-ptx4* | i[34567]86-sequent-sysv4*)
 	then
 		tm_file="${tm_file} usegas.h"
 	fi
-	xm_defines="SMALL_ARG_MAX"
 	tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h ptx4.h i386/ptx4-i.h"
 	tmake_file=t-svr4
 	extra_parts="crtbegin.o crtend.o"
@@ -1115,7 +1114,6 @@ i[34567]86-*-sco3.2v5*)	# 80386 running SCO Open Server 5
 	use_fixproto=yes
 	;;
 i[34567]86-*-solaris2*)
-	xm_defines="SMALL_ARG_MAX"
 	tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h svr4.h i386/sysv4.h sol2.h"
 	case ${target} in
 	*-*-solaris2.[0-6] | *-*-solaris2.[0-6].*)
@@ -1182,7 +1180,6 @@ i[34567]86-*-sysv5*)           # Intel x86 on System V Release 5
 	use_fixproto=yes
        ;;
 i[34567]86-*-sysv4*)		# Intel 80386's running system V.4
-	xm_defines="SMALL_ARG_MAX"
 	tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h svr4.h i386/sysv4.h i386/sysv4-cpp.h"
 	if test x$stabs = xyes
 	then
diff --git a/gcc/config.host b/gcc/config.host
index 026cd09a91d9..8d227dd68f6f 100644
--- a/gcc/config.host
+++ b/gcc/config.host
@@ -132,17 +132,10 @@ case ${host} in
   i370-*-opened* | i370-*-mvs* ) # IBM 360/370/390 Architecture
     host_xm_defines='FATAL_EXIT_CODE=12'
     ;;
-  i[34567]86-sequent-ptx4*)
-    host_xm_defines="SMALL_ARG_MAX"
-    ;;
   i[34567]86-*-solaris2*)
-    host_xm_defines="SMALL_ARG_MAX"
     out_host_hook_obj=host-solaris.o
     host_xmake_file=x-solaris
     ;;
-  i[34567]86-*-sysv4*) # Intel 80386's running System V Release 4
-    host_xm_defines="SMALL_ARG_MAX"
-    ;;
   i[34567]86-pc-msdosdjgpp*)
     host_xm_file=i386/xm-djgpp.h
     host_exeext=.exe
diff --git a/gcc/doc/hostconfig.texi b/gcc/doc/hostconfig.texi
index 98dadafe360c..1c97ac278e72 100644
--- a/gcc/doc/hostconfig.texi
+++ b/gcc/doc/hostconfig.texi
@@ -208,11 +208,6 @@ initialization when @code{collect2} is being initialized.
 If defined, a C statement (sans semicolon) that performs host-dependent
 initialization when a compilation driver is being initialized.
 
-@item SMALL_ARG_MAX
-Define this macro if the host system has a small limit on the total
-size of an argument vector.  This causes the driver to take more care
-not to pass unnecessary arguments to subprocesses.
-
 @item HOST_LONG_LONG_FORMAT
 If defined, the string used to indicate an argument of type @code{long
 long} to functions like @code{printf}.  The default value is
diff --git a/gcc/gcc.c b/gcc/gcc.c
index f89e51bb479a..56f12901fef5 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -287,10 +287,12 @@ static char *load_specs (const char *);
 static void read_specs (const char *, int);
 static void set_spec (const char *, const char *);
 static struct compiler *lookup_compiler (const char *, size_t, const char *);
-static char *build_search_list (struct path_prefix *, const char *, int);
-static void putenv_from_prefixes (struct path_prefix *, const char *);
+static char *build_search_list (const struct path_prefix *, const char *,
+				bool, bool);
+static void putenv_from_prefixes (const struct path_prefix *, const char *,
+				  bool);
 static int access_check (const char *, int);
-static char *find_a_file (struct path_prefix *, const char *, int, int);
+static char *find_a_file (const struct path_prefix *, const char *, int, bool);
 static void add_prefix (struct path_prefix *, const char *, const char *,
 			int, int, int);
 static void add_sysrooted_prefix (struct path_prefix *, const char *,
@@ -313,13 +315,12 @@ static const char *eval_spec_function (const char *, const char *);
 static const char *handle_spec_function (const char *);
 static char *save_string (const char *, int);
 static void set_collect_gcc_options (void);
-static void do_spec_path (struct prefix_list *, const char *, int, int, int, const char *, const char *);
 static int do_spec_1 (const char *, int, const char *);
 static int do_spec_2 (const char *);
 static void do_option_spec (const char *, const char *);
 static void do_self_spec (const char *);
 static const char *find_file (const char *);
-static int is_directory (const char *, const char *, int);
+static int is_directory (const char *, bool);
 static const char *validate_switches (const char *);
 static void validate_all_switches (void);
 static inline void validate_switches_from_spec (const char *);
@@ -2041,7 +2042,7 @@ read_specs (const char *filename, int main_p)
 		       (long) (p1 - buffer + 1));
 
 	      p[-2] = '\0';
-	      new_filename = find_a_file (&startfile_prefixes, p1, R_OK, 0);
+	      new_filename = find_a_file (&startfile_prefixes, p1, R_OK, true);
 	      read_specs (new_filename ? new_filename : p1, FALSE);
 	      continue;
 	    }
@@ -2060,7 +2061,7 @@ read_specs (const char *filename, int main_p)
 		       (long) (p1 - buffer + 1));
 
 	      p[-2] = '\0';
-	      new_filename = find_a_file (&startfile_prefixes, p1, R_OK, 0);
+	      new_filename = find_a_file (&startfile_prefixes, p1, R_OK, true);
 	      if (new_filename)
 		read_specs (new_filename, FALSE);
 	      else if (verbose_flag)
@@ -2357,64 +2358,213 @@ clear_failure_queue (void)
   failure_delete_queue = 0;
 }
 
-/* Build a list of search directories from PATHS.
-   PREFIX is a string to prepend to the list.
-   If CHECK_DIR_P is nonzero we ensure the directory exists.
-   This is used mostly by putenv_from_prefixes so we use `collect_obstack'.
-   It is also used by the --print-search-dirs flag.  */
-
-static char *
-build_search_list (struct path_prefix *paths, const char *prefix,
-		   int check_dir_p)
+/* Call CALLBACK for each path in PATHS, breaking out early if CALLBACK
+   returns non-NULL.
+   If DO_MULTI is true iterate over the paths twice, first with multilib
+   suffix then without, otherwise iterate over the paths once without
+   adding a multilib suffix.  When DO_MULTI is true, some attempt is made
+   to avoid visiting the same path twice, but we could do better.  For
+   instance, /usr/lib/../lib is considered different from /usr/lib.
+   At least EXTRA_SPACE chars past the end of the path passed to
+   CALLBACK are available for use by the callback.
+   CALLBACK_INFO allows extra parameters to be passed to CALLBACK.
+
+   Returns the value returned by CALLBACK.  */
+
+static void *
+for_each_path (const struct path_prefix *paths,
+	       bool do_multi,
+	       size_t extra_space,
+	       void *(*callback) (char *, void *),
+	       void *callback_info)
 {
-  int suffix_len = (machine_suffix) ? strlen (machine_suffix) : 0;
-  int just_suffix_len
-    = (just_machine_suffix) ? strlen (just_machine_suffix) : 0;
-  int first_time = TRUE;
-  struct prefix_list *pprefix;
-
-  obstack_grow (&collect_obstack, prefix, strlen (prefix));
-  obstack_1grow (&collect_obstack, '=');
-
-  for (pprefix = paths->plist; pprefix != 0; pprefix = pprefix->next)
+  struct prefix_list *pl;
+  const char *multi_dir = NULL;
+  const char *multi_os_dir = NULL;
+  const char *multi_suffix;
+  const char *just_multi_suffix;
+  char *path = NULL;
+  void *ret = NULL;
+  bool skip_multi_dir = false;
+  bool skip_multi_os_dir = false;
+
+  multi_suffix = machine_suffix;
+  just_multi_suffix = just_machine_suffix;
+  if (do_multi && multilib_dir && strcmp (multilib_dir, ".") != 0)
     {
-      int len = strlen (pprefix->prefix);
+      multi_dir = concat (multilib_dir, dir_separator_str, NULL);
+      multi_suffix = concat (multi_suffix, multi_dir, NULL);
+      just_multi_suffix = concat (just_multi_suffix, multi_dir, NULL);
+    }
+  if (do_multi && multilib_os_dir && strcmp (multilib_os_dir, ".") != 0)
+    multi_os_dir = concat (multilib_os_dir, dir_separator_str, NULL);
 
-      if (machine_suffix
-	  && (! check_dir_p
-	      || is_directory (pprefix->prefix, machine_suffix, 0)))
+  while (1)
+    {
+      size_t multi_dir_len = 0;
+      size_t multi_os_dir_len = 0;
+      size_t suffix_len;
+      size_t just_suffix_len;
+      size_t len;
+
+      if (multi_dir)
+	multi_dir_len = strlen (multi_dir);
+      if (multi_os_dir)
+	multi_os_dir_len = strlen (multi_os_dir);
+      suffix_len = strlen (multi_suffix);
+      just_suffix_len = strlen (just_multi_suffix);
+
+      if (path == NULL)
 	{
-	  if (!first_time)
-	    obstack_1grow (&collect_obstack, PATH_SEPARATOR);
-
-	  first_time = FALSE;
-	  obstack_grow (&collect_obstack, pprefix->prefix, len);
-	  obstack_grow (&collect_obstack, machine_suffix, suffix_len);
+	  len = paths->max_len + extra_space + 1;
+	  if (suffix_len > multi_os_dir_len)
+	    len += suffix_len;
+	  else
+	    len += multi_os_dir_len;
+	  path = xmalloc (len);
 	}
 
-      if (just_machine_suffix
-	  && pprefix->require_machine_suffix == 2
-	  && (! check_dir_p
-	      || is_directory (pprefix->prefix, just_machine_suffix, 0)))
+      for (pl = paths->plist; pl != 0; pl = pl->next)
 	{
-	  if (! first_time)
-	    obstack_1grow (&collect_obstack, PATH_SEPARATOR);
+	  len = strlen (pl->prefix);
+	  memcpy (path, pl->prefix, len);
 
-	  first_time = FALSE;
-	  obstack_grow (&collect_obstack, pprefix->prefix, len);
-	  obstack_grow (&collect_obstack, just_machine_suffix,
-			just_suffix_len);
+	  /* Look first in MACHINE/VERSION subdirectory.  */
+	  if (!skip_multi_dir)
+	    {
+	      memcpy (path + len, multi_suffix, suffix_len + 1);
+	      ret = callback (path, callback_info);
+	      if (ret)
+		break;
+	    }
+
+	  /* Some paths are tried with just the machine (ie. target)
+	     subdir.  This is used for finding as, ld, etc.  */
+	  if (!skip_multi_dir
+	      && pl->require_machine_suffix == 2)
+	    {
+	      memcpy (path + len, just_multi_suffix, just_suffix_len + 1);
+	      ret = callback (path, callback_info);
+	      if (ret)
+		break;
+	    }
+
+	  /* Now try the base path.  */
+	  if (!pl->require_machine_suffix
+	      && !(pl->os_multilib ? skip_multi_os_dir : skip_multi_dir))
+	    {
+	      const char *this_multi;
+	      size_t this_multi_len;
+
+	      if (pl->os_multilib)
+		{
+		  this_multi = multi_os_dir;
+		  this_multi_len = multi_os_dir_len;
+		}
+	      else
+		{
+		  this_multi = multi_dir;
+		  this_multi_len = multi_dir_len;
+		}
+
+	      if (this_multi_len)
+		memcpy (path + len, this_multi, this_multi_len + 1);
+	      else
+		path[len] = '\0';
+
+	      ret = callback (path, callback_info);
+	      if (ret)
+		break;
+	    }
 	}
+      if (pl)
+	break;
 
-      if (! pprefix->require_machine_suffix)
-	{
-	  if (! first_time)
-	    obstack_1grow (&collect_obstack, PATH_SEPARATOR);
+      if (multi_dir == NULL && multi_os_dir == NULL)
+	break;
 
-	  first_time = FALSE;
-	  obstack_grow (&collect_obstack, pprefix->prefix, len);
+      /* Run through the paths again, this time without multilibs.
+	 Don't repeat any we have already seen.  */
+      if (multi_dir)
+	{
+	  free ((char *) multi_dir);
+	  multi_dir = NULL;
+	  free ((char *) multi_suffix);
+	  multi_suffix = machine_suffix;
+	  free ((char *) just_multi_suffix);
+	  just_multi_suffix = just_machine_suffix;
 	}
+      else
+	skip_multi_dir = true;
+      if (multi_os_dir)
+	{
+	  free ((char *) multi_os_dir);
+	  multi_os_dir = NULL;
+	}
+      else
+	skip_multi_os_dir = true;
+    }
+
+  if (multi_dir)
+    {
+      free ((char *) multi_dir);
+      free ((char *) multi_suffix);
+      free ((char *) just_multi_suffix);
     }
+  if (multi_os_dir)
+    free ((char *) multi_os_dir);
+  if (ret != path)
+    free (path);
+  return ret;
+}
+
+/* Callback for build_search_list.  Adds path to obstack being built.  */
+
+struct add_to_obstack_info {
+  struct obstack *ob;
+  bool check_dir;
+  bool first_time;
+};
+
+static void *
+add_to_obstack (char *path, void *data)
+{
+  struct add_to_obstack_info *info = data;
+
+  if (info->check_dir && !is_directory (path, false))
+    return NULL;
+
+  if (!info->first_time)
+    obstack_1grow (info->ob, PATH_SEPARATOR);
+
+  obstack_grow (info->ob, path, strlen (path));
+
+  info->first_time = false;
+  return NULL;
+}
+
+/* Build a list of search directories from PATHS.
+   PREFIX is a string to prepend to the list.
+   If CHECK_DIR_P is true we ensure the directory exists.
+   If DO_MULTI is true, multilib paths are output first, then
+   non-multilib paths.
+   This is used mostly by putenv_from_prefixes so we use `collect_obstack'.
+   It is also used by the --print-search-dirs flag.  */
+
+static char *
+build_search_list (const struct path_prefix *paths, const char *prefix,
+		   bool check_dir, bool do_multi)
+{
+  struct add_to_obstack_info info;
+
+  info.ob = &collect_obstack;
+  info.check_dir = check_dir;
+  info.first_time = true;
+
+  obstack_grow (&collect_obstack, prefix, strlen (prefix));
+  obstack_1grow (&collect_obstack, '=');
+
+  for_each_path (paths, do_multi, 0, add_to_obstack, &info);
 
   obstack_1grow (&collect_obstack, '\0');
   return XOBFINISH (&collect_obstack, char *);
@@ -2424,9 +2574,10 @@ build_search_list (struct path_prefix *paths, const char *prefix,
    for collect.  */
 
 static void
-putenv_from_prefixes (struct path_prefix *paths, const char *env_var)
+putenv_from_prefixes (const struct path_prefix *paths, const char *env_var,
+		      bool do_multi)
 {
-  putenv (build_search_list (paths, env_var, 1));
+  putenv (build_search_list (paths, env_var, true, do_multi));
 }
 
 /* Check whether NAME can be accessed in MODE.  This is like access,
@@ -2447,20 +2598,53 @@ access_check (const char *name, int mode)
   return access (name, mode);
 }
 
+/* Callback for find_a_file.  Appends the file name to the directory
+   path.  If the resulting file exists in the right mode, return the
+   full pathname to the file.  */
+
+struct file_at_path_info {
+  const char *name;
+  const char *suffix;
+  int name_len;
+  int suffix_len;
+  int mode;
+};
+
+static void *
+file_at_path (char *path, void *data)
+{
+  struct file_at_path_info *info = data;
+  size_t len = strlen (path);
+
+  memcpy (path + len, info->name, info->name_len);
+  len += info->name_len;
+
+  /* Some systems have a suffix for executable files.
+     So try appending that first.  */
+  if (info->suffix_len)
+    {
+      memcpy (path + len, info->suffix, info->suffix_len + 1);
+      if (access_check (path, info->mode) == 0)
+	return path;
+    }
+
+  path[len] = '\0';
+  if (access_check (path, info->mode) == 0)
+    return path;
+
+  return NULL;
+}
+
 /* Search for NAME using the prefix list PREFIXES.  MODE is passed to
-   access to check permissions.
+   access to check permissions.  If DO_MULTI is true, search multilib
+   paths then non-multilib paths, otherwise do not search multilib paths.
    Return 0 if not found, otherwise return its name, allocated with malloc.  */
 
 static char *
-find_a_file (struct path_prefix *pprefix, const char *name, int mode,
-	     int multilib)
+find_a_file (const struct path_prefix *pprefix, const char *name, int mode,
+	     bool do_multi)
 {
-  char *temp;
-  const char *const file_suffix =
-    ((mode & X_OK) != 0 ? HOST_EXECUTABLE_SUFFIX : "");
-  struct prefix_list *pl;
-  int len = pprefix->max_len + strlen (name) + strlen (file_suffix) + 1;
-  const char *multilib_name, *multilib_os_name;
+  struct file_at_path_info info;
 
 #ifdef DEFAULT_ASSEMBLER
   if (! strcmp (name, "as") && access (DEFAULT_ASSEMBLER, mode) == 0)
@@ -2472,112 +2656,24 @@ find_a_file (struct path_prefix *pprefix, const char *name, int mode,
     return xstrdup (DEFAULT_LINKER);
 #endif
 
-  if (machine_suffix)
-    len += strlen (machine_suffix);
-
-  multilib_name = name;
-  multilib_os_name = name;
-  if (multilib && multilib_os_dir)
-    {
-      int len1 = multilib_dir ? strlen (multilib_dir) + 1 : 0;
-      int len2 = strlen (multilib_os_dir) + 1;
-
-      len += len1 > len2 ? len1 : len2;
-      if (multilib_dir)
-	multilib_name = ACONCAT ((multilib_dir, dir_separator_str, name,
-				  NULL));
-      if (strcmp (multilib_os_dir, ".") != 0)
-	multilib_os_name = ACONCAT ((multilib_os_dir, dir_separator_str, name,
-				    NULL));
-    }
-
-  temp = xmalloc (len);
-
   /* Determine the filename to execute (special case for absolute paths).  */
 
   if (IS_ABSOLUTE_PATH (name))
     {
       if (access (name, mode) == 0)
-	{
-	  strcpy (temp, name);
-	  return temp;
-	}
-    }
-  else
-    for (pl = pprefix->plist; pl; pl = pl->next)
-      {
-	const char *this_name
-	  = pl->os_multilib ? multilib_os_name : multilib_name;
-
-	if (machine_suffix)
-	  {
-	    /* Some systems have a suffix for executable files.
-	       So try appending that first.  */
-	    if (file_suffix[0] != 0)
-	      {
-		strcpy (temp, pl->prefix);
-		strcat (temp, machine_suffix);
-		strcat (temp, multilib_name);
-		strcat (temp, file_suffix);
-		if (access_check (temp, mode) == 0)
-		  return temp;
-	      }
-
-	    /* Now try just the multilib_name.  */
-	    strcpy (temp, pl->prefix);
-	    strcat (temp, machine_suffix);
-	    strcat (temp, multilib_name);
-	    if (access_check (temp, mode) == 0)
-	      return temp;
-	  }
-
-	/* Certain prefixes are tried with just the machine type,
-	   not the version.  This is used for finding as, ld, etc.  */
-	if (just_machine_suffix && pl->require_machine_suffix == 2)
-	  {
-	    /* Some systems have a suffix for executable files.
-	       So try appending that first.  */
-	    if (file_suffix[0] != 0)
-	      {
-		strcpy (temp, pl->prefix);
-		strcat (temp, just_machine_suffix);
-		strcat (temp, multilib_name);
-		strcat (temp, file_suffix);
-		if (access_check (temp, mode) == 0)
-		  return temp;
-	      }
+	return xstrdup (name);
 
-	    strcpy (temp, pl->prefix);
-	    strcat (temp, just_machine_suffix);
-	    strcat (temp, multilib_name);
-	    if (access_check (temp, mode) == 0)
-	      return temp;
-	  }
-
-	/* Certain prefixes can't be used without the machine suffix
-	   when the machine or version is explicitly specified.  */
-	if (! pl->require_machine_suffix)
-	  {
-	    /* Some systems have a suffix for executable files.
-	       So try appending that first.  */
-	    if (file_suffix[0] != 0)
-	      {
-		strcpy (temp, pl->prefix);
-		strcat (temp, this_name);
-		strcat (temp, file_suffix);
-		if (access_check (temp, mode) == 0)
-		  return temp;
-	      }
+      return NULL;
+    }
 
-	    strcpy (temp, pl->prefix);
-	    strcat (temp, this_name);
-	    if (access_check (temp, mode) == 0)
-	      return temp;
-	  }
-      }
+  info.name = name;
+  info.suffix = (mode & X_OK) != 0 ? HOST_EXECUTABLE_SUFFIX : "";
+  info.name_len = strlen (info.name);
+  info.suffix_len = strlen (info.suffix);
+  info.mode = mode;
 
-  free (temp);
-  return 0;
+  return for_each_path (pprefix, do_multi, info.name_len + info.suffix_len,
+			file_at_path, &info);
 }
 
 /* Ranking of prefixes in the sort list. -B prefixes are put before
@@ -2696,7 +2792,7 @@ execute (void)
 
   commands[0].prog = argbuf[0]; /* first command.  */
   commands[0].argv = &argbuf[0];
-  string = find_a_file (&exec_prefixes, commands[0].prog, X_OK, 0);
+  string = find_a_file (&exec_prefixes, commands[0].prog, X_OK, false);
 
   if (string)
     commands[0].argv[0] = string;
@@ -2711,7 +2807,7 @@ execute (void)
 	commands[n_commands].prog = argbuf[i + 1];
 	commands[n_commands].argv = &argbuf[i + 1];
 	string = find_a_file (&exec_prefixes, commands[n_commands].prog,
-			      X_OK, 0);
+			      X_OK, false);
 	if (string)
 	  commands[n_commands].argv[0] = string;
 	n_commands++;
@@ -3649,7 +3745,7 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
 		   if appending a directory separator actually makes a
 		   valid directory name.  */
 		if (! IS_DIR_SEPARATOR (value [len - 1])
-		    && is_directory (value, "", 0))
+		    && is_directory (value, false))
 		  {
 		    char *tmp = xmalloc (len + 2);
 		    strcpy (tmp, value);
@@ -4397,108 +4493,55 @@ do_self_spec (const char *spec)
     }
 }
 
-void
-do_spec_path (struct prefix_list *pl, const char *option,
-	      int omit_if_relative, int separate_options,
-	      int only_subdir,
-	      const char *dir_for_machine_suffix,
-	      const char *dir_for_no_suffix)
+/* Callback for processing %D and %I specs.  */
+
+struct spec_path_info {
+  const char *option;
+  const char *append;
+  size_t append_len;
+  bool omit_relative;
+  bool separate_options;
+};
+
+static void *
+spec_path (char *path, void *data)
 {
-  static size_t bufsize = 0;
-  static char *buffer;
-  int idx;
-  bool multilib_p = false;
-
-  /* Used on systems which record the specified -L dirs
-     and use them to search for dynamic linking.  */
-  /* Relative directories always come from -B,
-     and it is better not to use them for searching
-     at run time.  In particular, stage1 loses.  */
-  if (omit_if_relative
-      && !IS_ABSOLUTE_PATH (pl->prefix))
-    return;
+  struct spec_path_info *info = data;
+  size_t len = 0;
+  char save = 0;
 
-  /* Try subdirectory if there is one.  */
-  if (machine_suffix && dir_for_machine_suffix)
-    {
-      if (strlen (pl->prefix) + strlen (machine_suffix)
-	  >= bufsize)
-	bufsize = (strlen (pl->prefix)
-		  + strlen (machine_suffix)) * 2 + 1;
-      buffer = xrealloc (buffer, bufsize);
-      strcpy (buffer, pl->prefix);
-      strcat (buffer, machine_suffix);
-      if (is_directory (buffer, dir_for_machine_suffix, 1))
-	{
-	  multilib_p = true;
-	  do_spec_1 (option, separate_options, NULL);
-	  if (separate_options)
-	    do_spec_1 (" ", 0, NULL);
-	  do_spec_1 (buffer, 1, NULL);
-	  do_spec_1 (dir_for_machine_suffix, 1, NULL);
-	  /* Make this a separate argument.  */
-	  do_spec_1 (" ", 0, NULL);
-	}
-    }
-  if (!pl->require_machine_suffix && dir_for_no_suffix)
+  if (info->omit_relative && !IS_ABSOLUTE_PATH (path))
+    return NULL;
+
+  if (info->append_len != 0)
     {
-      if (is_directory (pl->prefix, dir_for_no_suffix, 1))
-	{
-	  multilib_p = true;
-	  do_spec_1 (option, separate_options, NULL);
-	  if (separate_options)
-	    do_spec_1 (" ", 0, NULL);
-	  do_spec_1 (pl->prefix, 1, NULL);
-	  do_spec_1 (dir_for_no_suffix, 1, NULL);
-	  /* Make this a separate argument.  */
-	  do_spec_1 (" ", 0, NULL);
-	}
+      len = strlen (path);
+      memcpy (path + len, info->append, info->append_len + 1);
     }
 
-  if (only_subdir || multilib_p)
-    return;
+  if (!is_directory (path, true))
+    return NULL;
 
-  if (machine_suffix)
-    {
-      if (is_directory (pl->prefix, machine_suffix, 1))
-	{
-	  do_spec_1 (option, separate_options, NULL);
-	  if (separate_options)
-	    do_spec_1 (" ", 0, NULL);
-	  do_spec_1 (pl->prefix, 1, NULL);
-	  /* Remove slash from machine_suffix.  */
-	  if (strlen (machine_suffix) >= bufsize)
-	    bufsize = strlen (machine_suffix) * 2 + 1;
-	  buffer = xrealloc (buffer, bufsize);
-	  strcpy (buffer, machine_suffix);
-	  idx = strlen (buffer);
-	  if (IS_DIR_SEPARATOR (buffer[idx - 1]))
-	    buffer[idx - 1] = 0;
-	  do_spec_1 (buffer, 1, NULL);
-	  /* Make this a separate argument.  */
-	  do_spec_1 (" ", 0, NULL);
-	}
-    }
-  if (!pl->require_machine_suffix)
+  do_spec_1 (info->option, 1, NULL);
+  if (info->separate_options)
+    do_spec_1 (" ", 0, NULL);
+
+  if (info->append_len == 0)
     {
-      if (is_directory (pl->prefix, "", 1))
-	{
-	  do_spec_1 (option, separate_options, NULL);
-	  if (separate_options)
-	    do_spec_1 (" ", 0, NULL);
-	  /* Remove slash from pl->prefix.  */
-	  if (strlen (pl->prefix) >= bufsize)
-	    bufsize = strlen (pl->prefix) * 2 + 1;
-	  buffer = xrealloc (buffer, bufsize);
-	  strcpy (buffer, pl->prefix);
-	  idx = strlen (buffer);
-	  if (IS_DIR_SEPARATOR (buffer[idx - 1]))
-	    buffer[idx - 1] = 0;
-	  do_spec_1 (buffer, 1, NULL);
-	  /* Make this a separate argument.  */
-	  do_spec_1 (" ", 0, NULL);
-	}
+      len = strlen (path);
+      save = path[len - 1];
+      if (IS_DIR_SEPARATOR (path[len - 1]))
+	path[len - 1] = '\0';
     }
+
+  do_spec_1 (path, 1, NULL);
+  do_spec_1 (" ", 0, NULL);
+
+  /* Must not damage the original path.  */
+  if (info->append_len == 0)
+    path[len - 1] = save;
+
+  return NULL;
 }
 
 /* Process the sub-spec SPEC as a portion of a larger spec.
@@ -4636,23 +4679,23 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part)
 	     that we search for startfiles.  */
 	  case 'D':
 	    {
-	      struct prefix_list *pl = startfile_prefixes.plist;
+	      struct spec_path_info info;
 
-	      for (; pl; pl = pl->next)
-		{
-		  const char *no_suffix_multilib_dir;
-
-		  no_suffix_multilib_dir = pl->os_multilib ? multilib_os_dir
-					   : multilib_dir;
-		  /* Do not separate options, include non-multilibbed variant.  */
-		  do_spec_path (pl, "-L",
+	      info.option = "-L";
+	      info.append_len = 0;
 #ifdef RELATIVE_PREFIX_NOT_LINKDIR
-				1,
+	      /* Used on systems which record the specified -L dirs
+		 and use them to search for dynamic linking.
+		 Relative directories always come from -B,
+		 and it is better not to use them for searching
+		 at run time.  In particular, stage1 loses.  */
+	      info.omit_relative = true;
 #else
-				0,
+	      info.omit_relative = false;
 #endif
-				0, 0, multilib_dir, no_suffix_multilib_dir);
-		}
+	      info.separate_options = false;
+
+	      for_each_path (&startfile_prefixes, true, 0, spec_path, &info);
 	    }
 	    break;
 
@@ -4889,7 +4932,7 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part)
 
 	  case 'I':
 	    {
-	      struct prefix_list *pl = include_prefixes.plist;
+	      struct spec_path_info info;
 
 	      if (gcc_exec_prefix)
 		{
@@ -4912,9 +4955,14 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part)
 		  do_spec_1 (" ", 0, NULL);
 		}
 
-	      for (; pl; pl = pl->next)
-		/* Separate options, don't include non-suffixed variant.  */
-		do_spec_path (pl, "-isystem", 0, 1, 1, "include", "include");
+	      info.option = "-isystem";
+	      info.append = "include";
+	      info.append_len = strlen (info.append);
+	      info.omit_relative = false;
+	      info.separate_options = true;
+
+	      for_each_path (&include_prefixes, false, info.append_len,
+			     spec_path, &info);
 	    }
 	    break;
 
@@ -5900,48 +5948,27 @@ give_switch (int switchnum, int omit_first_word)
 static const char *
 find_file (const char *name)
 {
-  char *newname;
-
-  /* Try multilib_dir if it is defined.  */
-  if (multilib_os_dir != NULL)
-    {
-      newname = find_a_file (&startfile_prefixes, name, R_OK, 1);
-
-      /* If we don't find it in the multi library dir, then fall
-	 through and look for it in the normal places.  */
-      if (newname != NULL)
-	return newname;
-    }
-
-  newname = find_a_file (&startfile_prefixes, name, R_OK, 0);
+  char *newname = find_a_file (&startfile_prefixes, name, R_OK, true);
   return newname ? newname : name;
 }
 
 /* Determine whether a directory exists.  If LINKER, return 0 for
-   certain fixed names not needed by the linker.  If not LINKER, it is
-   only important to return 0 if the host machine has a small ARG_MAX
-   limit.  */
+   certain fixed names not needed by the linker.  */
 
 static int
-is_directory (const char *path1, const char *path2, int linker)
+is_directory (const char *path1, bool linker)
 {
-  int len1 = strlen (path1);
-  int len2 = strlen (path2);
-  char *path = alloca (3 + len1 + len2);
+  int len1;
+  char *path;
   char *cp;
   struct stat st;
 
-#ifndef SMALL_ARG_MAX
-  if (! linker)
-    return 1;
-#endif
-
-  /* Construct the path from the two parts.  Ensure the string ends with "/.".
-     The resulting path will be a directory even if the given path is a
-     symbolic link.  */
+  /* Ensure the string ends with "/.".  The resulting path will be a
+     directory even if the given path is a symbolic link.  */
+  len1 = strlen (path1);
+  path = alloca (3 + len1);
   memcpy (path, path1, len1);
-  memcpy (path + len1, path2, len2);
-  cp = path + len1 + len2;
+  cp = path + len1;
   if (!IS_DIR_SEPARATOR (cp[-1]))
     *cp++ = DIR_SEPARATOR;
   *cp++ = '.';
@@ -5949,13 +5976,13 @@ is_directory (const char *path1, const char *path2, int linker)
 
   /* Exclude directories that the linker is known to search.  */
   if (linker
+      && IS_DIR_SEPARATOR (path[0])
       && ((cp - path == 6
-	   && strcmp (path, concat (dir_separator_str, "lib",
-				    dir_separator_str, ".", NULL)) == 0)
+	   && strncmp (path + 1, "lib", 3) == 0)
 	  || (cp - path == 10
-	      && strcmp (path, concat (dir_separator_str, "usr",
-				       dir_separator_str, "lib",
-				       dir_separator_str, ".", NULL)) == 0)))
+	      && strncmp (path + 1, "usr", 3) == 0
+	      && IS_DIR_SEPARATOR (path[4])
+	      && strncmp (path + 5, "lib", 3) == 0)))
     return 0;
 
   return (stat (path, &st) >= 0 && S_ISDIR (st.st_mode));
@@ -6147,7 +6174,7 @@ main (int argc, char **argv)
 			   spec_version, dir_separator_str, NULL);
   just_machine_suffix = concat (spec_machine, dir_separator_str, NULL);
 
-  specs_file = find_a_file (&startfile_prefixes, "specs", R_OK, 0);
+  specs_file = find_a_file (&startfile_prefixes, "specs", R_OK, true);
   /* Read the specs file unless it is a default one.  */
   if (specs_file != 0 && strcmp (specs_file, "specs"))
     read_specs (specs_file, TRUE);
@@ -6283,7 +6310,7 @@ main (int argc, char **argv)
   for (uptr = user_specs_head; uptr; uptr = uptr->next)
     {
       char *filename = find_a_file (&startfile_prefixes, uptr->filename,
-				    R_OK, 0);
+				    R_OK, true);
       read_specs (filename ? filename : uptr->filename, FALSE);
     }
 
@@ -6312,8 +6339,10 @@ main (int argc, char **argv)
   if (print_search_dirs)
     {
       printf (_("install: %s%s\n"), standard_exec_prefix, machine_suffix);
-      printf (_("programs: %s\n"), build_search_list (&exec_prefixes, "", 0));
-      printf (_("libraries: %s\n"), build_search_list (&startfile_prefixes, "", 0));
+      printf (_("programs: %s\n"),
+	      build_search_list (&exec_prefixes, "", false, false));
+      printf (_("libraries: %s\n"),
+	      build_search_list (&startfile_prefixes, "", false, true));
       return (0);
     }
 
@@ -6630,14 +6659,14 @@ main (int argc, char **argv)
       /* We'll use ld if we can't find collect2.  */
       if (! strcmp (linker_name_spec, "collect2"))
 	{
-	  char *s = find_a_file (&exec_prefixes, "collect2", X_OK, 0);
+	  char *s = find_a_file (&exec_prefixes, "collect2", X_OK, false);
 	  if (s == NULL)
 	    linker_name_spec = "ld";
 	}
       /* Rebuild the COMPILER_PATH and LIBRARY_PATH environment variables
 	 for collect.  */
-      putenv_from_prefixes (&exec_prefixes, "COMPILER_PATH");
-      putenv_from_prefixes (&startfile_prefixes, LIBRARY_PATH_ENV);
+      putenv_from_prefixes (&exec_prefixes, "COMPILER_PATH", false);
+      putenv_from_prefixes (&startfile_prefixes, LIBRARY_PATH_ENV, true);
 
       value = do_spec (link_command_spec);
       if (value < 0)
diff --git a/gcc/system.h b/gcc/system.h
index 65d6e041645b..ca9c8b4ce5ea 100644
--- a/gcc/system.h
+++ b/gcc/system.h
@@ -730,7 +730,8 @@ extern void fancy_abort (const char *, int, const char *) ATTRIBUTE_NORETURN;
 	TARGET_OPTIONS TARGET_SWITCHES EXTRA_CC_MODES FINALIZE_PIC	   \
 	PREDICATE_CODES SPECIAL_MODE_PREDICATES HOST_PTR_PRINTF		   \
 	EXTRA_SECTIONS EXTRA_SECTION_FUNCTIONS READONLY_DATA_SECTION	   \
-	TARGET_ASM_EXCEPTION_SECTION TARGET_ASM_EH_FRAME_SECTION
+	TARGET_ASM_EXCEPTION_SECTION TARGET_ASM_EH_FRAME_SECTION	   \
+	SMALL_ARG_MAX
 
 /* Hooks that are no longer used.  */
  #pragma GCC poison LANG_HOOKS_FUNCTION_MARK LANG_HOOKS_FUNCTION_FREE	\
-- 
GitLab