xref: /netbsd-src/external/gpl3/gdb/dist/libiberty/make-relative-prefix.c (revision 5173eb0a33e5d83890ba976253e703be4c92557c)
198b9484cSchristos /* Relative (relocatable) prefix support.
2*5173eb0aSchristos    Copyright (C) 1987-2024 Free Software Foundation, Inc.
398b9484cSchristos 
498b9484cSchristos This file is part of libiberty.
598b9484cSchristos 
698b9484cSchristos GCC is free software; you can redistribute it and/or modify it under
798b9484cSchristos the terms of the GNU General Public License as published by the Free
898b9484cSchristos Software Foundation; either version 2, or (at your option) any later
998b9484cSchristos version.
1098b9484cSchristos 
1198b9484cSchristos GCC is distributed in the hope that it will be useful, but WITHOUT ANY
1298b9484cSchristos WARRANTY; without even the implied warranty of MERCHANTABILITY or
1398b9484cSchristos FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1498b9484cSchristos for more details.
1598b9484cSchristos 
1698b9484cSchristos You should have received a copy of the GNU General Public License
1798b9484cSchristos along with GCC; see the file COPYING.  If not, write to the Free
1898b9484cSchristos Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
1998b9484cSchristos 02110-1301, USA.  */
2098b9484cSchristos 
2198b9484cSchristos /*
2298b9484cSchristos 
2398b9484cSchristos @deftypefn Extension {const char*} make_relative_prefix (const char *@var{progname}, @
2498b9484cSchristos   const char *@var{bin_prefix}, const char *@var{prefix})
2598b9484cSchristos 
2698b9484cSchristos Given three paths @var{progname}, @var{bin_prefix}, @var{prefix},
2798b9484cSchristos return the path that is in the same position relative to
2898b9484cSchristos @var{progname}'s directory as @var{prefix} is relative to
2998b9484cSchristos @var{bin_prefix}.  That is, a string starting with the directory
3098b9484cSchristos portion of @var{progname}, followed by a relative pathname of the
3198b9484cSchristos difference between @var{bin_prefix} and @var{prefix}.
3298b9484cSchristos 
3398b9484cSchristos If @var{progname} does not contain any directory separators,
3498b9484cSchristos @code{make_relative_prefix} will search @env{PATH} to find a program
3598b9484cSchristos named @var{progname}.  Also, if @var{progname} is a symbolic link,
3698b9484cSchristos the symbolic link will be resolved.
3798b9484cSchristos 
3898b9484cSchristos For example, if @var{bin_prefix} is @code{/alpha/beta/gamma/gcc/delta},
3998b9484cSchristos @var{prefix} is @code{/alpha/beta/gamma/omega/}, and @var{progname} is
4098b9484cSchristos @code{/red/green/blue/gcc}, then this function will return
4198b9484cSchristos @code{/red/green/blue/../../omega/}.
4298b9484cSchristos 
4398b9484cSchristos The return value is normally allocated via @code{malloc}.  If no
4498b9484cSchristos relative prefix can be found, return @code{NULL}.
4598b9484cSchristos 
4698b9484cSchristos @end deftypefn
4798b9484cSchristos 
4898b9484cSchristos */
4998b9484cSchristos 
5098b9484cSchristos #ifdef HAVE_CONFIG_H
5198b9484cSchristos #include "config.h"
5298b9484cSchristos #endif
5398b9484cSchristos 
5498b9484cSchristos #ifdef HAVE_STDLIB_H
5598b9484cSchristos #include <stdlib.h>
5698b9484cSchristos #endif
5798b9484cSchristos #ifdef HAVE_UNISTD_H
5898b9484cSchristos #include <unistd.h>
5998b9484cSchristos #endif
60a2e2270fSchristos #ifdef HAVE_SYS_STAT_H
61a2e2270fSchristos #include <sys/stat.h>
62a2e2270fSchristos #endif
6398b9484cSchristos 
6498b9484cSchristos #include <string.h>
6598b9484cSchristos 
6698b9484cSchristos #include "ansidecl.h"
6798b9484cSchristos #include "libiberty.h"
6898b9484cSchristos 
6998b9484cSchristos #ifndef R_OK
7098b9484cSchristos #define R_OK 4
7198b9484cSchristos #define W_OK 2
7298b9484cSchristos #define X_OK 1
7398b9484cSchristos #endif
7498b9484cSchristos 
7598b9484cSchristos #ifndef DIR_SEPARATOR
7698b9484cSchristos #  define DIR_SEPARATOR '/'
7798b9484cSchristos #endif
7898b9484cSchristos 
7998b9484cSchristos #if defined (_WIN32) || defined (__MSDOS__) \
8098b9484cSchristos     || defined (__DJGPP__) || defined (__OS2__)
8198b9484cSchristos #  define HAVE_DOS_BASED_FILE_SYSTEM
8298b9484cSchristos #  define HAVE_HOST_EXECUTABLE_SUFFIX
8398b9484cSchristos #  define HOST_EXECUTABLE_SUFFIX ".exe"
8498b9484cSchristos #  ifndef DIR_SEPARATOR_2
8598b9484cSchristos #    define DIR_SEPARATOR_2 '\\'
8698b9484cSchristos #  endif
8798b9484cSchristos #  define PATH_SEPARATOR ';'
8898b9484cSchristos #else
8998b9484cSchristos #  define PATH_SEPARATOR ':'
9098b9484cSchristos #endif
9198b9484cSchristos 
9298b9484cSchristos #ifndef DIR_SEPARATOR_2
9398b9484cSchristos #  define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
9498b9484cSchristos #else
9598b9484cSchristos #  define IS_DIR_SEPARATOR(ch) \
9698b9484cSchristos 	(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
9798b9484cSchristos #endif
9898b9484cSchristos 
9998b9484cSchristos #define DIR_UP ".."
10098b9484cSchristos 
10198b9484cSchristos static char *save_string (const char *, int);
10298b9484cSchristos static char **split_directories	(const char *, int *);
10398b9484cSchristos static void free_split_directories (char **);
10498b9484cSchristos 
10598b9484cSchristos static char *
10698b9484cSchristos save_string (const char *s, int len)
10798b9484cSchristos {
10898b9484cSchristos   char *result = (char *) malloc (len + 1);
10998b9484cSchristos 
11098b9484cSchristos   memcpy (result, s, len);
11198b9484cSchristos   result[len] = 0;
11298b9484cSchristos   return result;
11398b9484cSchristos }
11498b9484cSchristos 
11598b9484cSchristos /* Split a filename into component directories.  */
11698b9484cSchristos 
11798b9484cSchristos static char **
11898b9484cSchristos split_directories (const char *name, int *ptr_num_dirs)
11998b9484cSchristos {
12098b9484cSchristos   int num_dirs = 0;
12198b9484cSchristos   char **dirs;
12298b9484cSchristos   const char *p, *q;
12398b9484cSchristos   int ch;
12498b9484cSchristos 
1258dffb485Schristos   if (!*name)
1268dffb485Schristos     return NULL;
1278dffb485Schristos 
12898b9484cSchristos   /* Count the number of directories.  Special case MSDOS disk names as part
12998b9484cSchristos      of the initial directory.  */
13098b9484cSchristos   p = name;
13198b9484cSchristos #ifdef HAVE_DOS_BASED_FILE_SYSTEM
13298b9484cSchristos   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
13398b9484cSchristos     {
13498b9484cSchristos       p += 3;
13598b9484cSchristos       num_dirs++;
13698b9484cSchristos     }
13798b9484cSchristos #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
13898b9484cSchristos 
13998b9484cSchristos   while ((ch = *p++) != '\0')
14098b9484cSchristos     {
14198b9484cSchristos       if (IS_DIR_SEPARATOR (ch))
14298b9484cSchristos 	{
14398b9484cSchristos 	  num_dirs++;
14498b9484cSchristos 	  while (IS_DIR_SEPARATOR (*p))
14598b9484cSchristos 	    p++;
14698b9484cSchristos 	}
14798b9484cSchristos     }
14898b9484cSchristos 
14998b9484cSchristos   dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2));
15098b9484cSchristos   if (dirs == NULL)
15198b9484cSchristos     return NULL;
15298b9484cSchristos 
15398b9484cSchristos   /* Now copy the directory parts.  */
15498b9484cSchristos   num_dirs = 0;
15598b9484cSchristos   p = name;
15698b9484cSchristos #ifdef HAVE_DOS_BASED_FILE_SYSTEM
15798b9484cSchristos   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
15898b9484cSchristos     {
15998b9484cSchristos       dirs[num_dirs++] = save_string (p, 3);
16098b9484cSchristos       if (dirs[num_dirs - 1] == NULL)
16198b9484cSchristos 	{
16298b9484cSchristos 	  free (dirs);
16398b9484cSchristos 	  return NULL;
16498b9484cSchristos 	}
16598b9484cSchristos       p += 3;
16698b9484cSchristos     }
16798b9484cSchristos #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
16898b9484cSchristos 
16998b9484cSchristos   q = p;
17098b9484cSchristos   while ((ch = *p++) != '\0')
17198b9484cSchristos     {
17298b9484cSchristos       if (IS_DIR_SEPARATOR (ch))
17398b9484cSchristos 	{
17498b9484cSchristos 	  while (IS_DIR_SEPARATOR (*p))
17598b9484cSchristos 	    p++;
17698b9484cSchristos 
17798b9484cSchristos 	  dirs[num_dirs++] = save_string (q, p - q);
17898b9484cSchristos 	  if (dirs[num_dirs - 1] == NULL)
17998b9484cSchristos 	    {
18098b9484cSchristos 	      dirs[num_dirs] = NULL;
18198b9484cSchristos 	      free_split_directories (dirs);
18298b9484cSchristos 	      return NULL;
18398b9484cSchristos 	    }
18498b9484cSchristos 	  q = p;
18598b9484cSchristos 	}
18698b9484cSchristos     }
18798b9484cSchristos 
18898b9484cSchristos   if (p - 1 - q > 0)
18998b9484cSchristos     dirs[num_dirs++] = save_string (q, p - 1 - q);
19098b9484cSchristos   dirs[num_dirs] = NULL;
19198b9484cSchristos 
19298b9484cSchristos   if (dirs[num_dirs - 1] == NULL)
19398b9484cSchristos     {
19498b9484cSchristos       free_split_directories (dirs);
19598b9484cSchristos       return NULL;
19698b9484cSchristos     }
19798b9484cSchristos 
19898b9484cSchristos   if (ptr_num_dirs)
19998b9484cSchristos     *ptr_num_dirs = num_dirs;
20098b9484cSchristos   return dirs;
20198b9484cSchristos }
20298b9484cSchristos 
20398b9484cSchristos /* Release storage held by split directories.  */
20498b9484cSchristos 
20598b9484cSchristos static void
20698b9484cSchristos free_split_directories (char **dirs)
20798b9484cSchristos {
20898b9484cSchristos   int i = 0;
20998b9484cSchristos 
21098b9484cSchristos   if (dirs != NULL)
21198b9484cSchristos     {
21298b9484cSchristos       while (dirs[i] != NULL)
21398b9484cSchristos 	free (dirs[i++]);
21498b9484cSchristos 
21598b9484cSchristos       free ((char *) dirs);
21698b9484cSchristos     }
21798b9484cSchristos }
21898b9484cSchristos 
21998b9484cSchristos /* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets
22098b9484cSchristos    to PREFIX starting with the directory portion of PROGNAME and a relative
22198b9484cSchristos    pathname of the difference between BIN_PREFIX and PREFIX.
22298b9484cSchristos 
22398b9484cSchristos    For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is
22498b9484cSchristos    /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this
22598b9484cSchristos    function will return /red/green/blue/../../omega/.
22698b9484cSchristos 
22798b9484cSchristos    If no relative prefix can be found, return NULL.  */
22898b9484cSchristos 
22998b9484cSchristos static char *
23098b9484cSchristos make_relative_prefix_1 (const char *progname, const char *bin_prefix,
23198b9484cSchristos 			const char *prefix, const int resolve_links)
23298b9484cSchristos {
23398b9484cSchristos   char **prog_dirs = NULL, **bin_dirs = NULL, **prefix_dirs = NULL;
23498b9484cSchristos   int prog_num, bin_num, prefix_num;
23598b9484cSchristos   int i, n, common;
23698b9484cSchristos   int needed_len;
23798b9484cSchristos   char *ret = NULL, *ptr, *full_progname;
238ba340e45Schristos   char *alloc_ptr = NULL;
23998b9484cSchristos 
24098b9484cSchristos   if (progname == NULL || bin_prefix == NULL || prefix == NULL)
24198b9484cSchristos     return NULL;
24298b9484cSchristos 
24398b9484cSchristos   /* If there is no full pathname, try to find the program by checking in each
24498b9484cSchristos      of the directories specified in the PATH environment variable.  */
24598b9484cSchristos   if (lbasename (progname) == progname)
24698b9484cSchristos     {
24798b9484cSchristos       char *temp;
24898b9484cSchristos 
24998b9484cSchristos       temp = getenv ("PATH");
25098b9484cSchristos       if (temp)
25198b9484cSchristos 	{
25298b9484cSchristos 	  char *startp, *endp, *nstore;
25398b9484cSchristos 	  size_t prefixlen = strlen (temp) + 1;
254a2e2270fSchristos 	  size_t len;
25598b9484cSchristos 	  if (prefixlen < 2)
25698b9484cSchristos 	    prefixlen = 2;
25798b9484cSchristos 
258a2e2270fSchristos 	  len = prefixlen + strlen (progname) + 1;
259a2e2270fSchristos #ifdef HAVE_HOST_EXECUTABLE_SUFFIX
260a2e2270fSchristos 	  len += strlen (HOST_EXECUTABLE_SUFFIX);
261a2e2270fSchristos #endif
262ba340e45Schristos 	  if (len < MAX_ALLOCA_SIZE)
263a2e2270fSchristos 	    nstore = (char *) alloca (len);
264ba340e45Schristos 	  else
265ba340e45Schristos 	    alloc_ptr = nstore = (char *) malloc (len);
26698b9484cSchristos 
26798b9484cSchristos 	  startp = endp = temp;
26898b9484cSchristos 	  while (1)
26998b9484cSchristos 	    {
27098b9484cSchristos 	      if (*endp == PATH_SEPARATOR || *endp == 0)
27198b9484cSchristos 		{
27298b9484cSchristos 		  if (endp == startp)
27398b9484cSchristos 		    {
27498b9484cSchristos 		      nstore[0] = '.';
27598b9484cSchristos 		      nstore[1] = DIR_SEPARATOR;
27698b9484cSchristos 		      nstore[2] = '\0';
27798b9484cSchristos 		    }
27898b9484cSchristos 		  else
27998b9484cSchristos 		    {
280a2e2270fSchristos 		      memcpy (nstore, startp, endp - startp);
28198b9484cSchristos 		      if (! IS_DIR_SEPARATOR (endp[-1]))
28298b9484cSchristos 			{
28398b9484cSchristos 			  nstore[endp - startp] = DIR_SEPARATOR;
28498b9484cSchristos 			  nstore[endp - startp + 1] = 0;
28598b9484cSchristos 			}
28698b9484cSchristos 		      else
28798b9484cSchristos 			nstore[endp - startp] = 0;
28898b9484cSchristos 		    }
28998b9484cSchristos 		  strcat (nstore, progname);
29098b9484cSchristos 		  if (! access (nstore, X_OK)
29198b9484cSchristos #ifdef HAVE_HOST_EXECUTABLE_SUFFIX
29298b9484cSchristos                       || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
29398b9484cSchristos #endif
29498b9484cSchristos 		      )
29598b9484cSchristos 		    {
296a2e2270fSchristos #if defined (HAVE_SYS_STAT_H) && defined (S_ISREG)
297a2e2270fSchristos 		      struct stat st;
298a2e2270fSchristos 		      if (stat (nstore, &st) >= 0 && S_ISREG (st.st_mode))
299a2e2270fSchristos #endif
300a2e2270fSchristos 			{
30198b9484cSchristos 			  progname = nstore;
30298b9484cSchristos 			  break;
30398b9484cSchristos 			}
304a2e2270fSchristos 		    }
30598b9484cSchristos 
30698b9484cSchristos 		  if (*endp == 0)
30798b9484cSchristos 		    break;
30898b9484cSchristos 		  endp = startp = endp + 1;
30998b9484cSchristos 		}
31098b9484cSchristos 	      else
31198b9484cSchristos 		endp++;
31298b9484cSchristos 	    }
31398b9484cSchristos 	}
31498b9484cSchristos     }
31598b9484cSchristos 
31698b9484cSchristos   if (resolve_links)
31798b9484cSchristos     full_progname = lrealpath (progname);
31898b9484cSchristos   else
31998b9484cSchristos     full_progname = strdup (progname);
32098b9484cSchristos   if (full_progname == NULL)
321ba340e45Schristos     goto bailout;
32298b9484cSchristos 
32398b9484cSchristos   prog_dirs = split_directories (full_progname, &prog_num);
32498b9484cSchristos   free (full_progname);
32598b9484cSchristos   if (prog_dirs == NULL)
326ba340e45Schristos     goto bailout;
32798b9484cSchristos 
32898b9484cSchristos   bin_dirs = split_directories (bin_prefix, &bin_num);
32998b9484cSchristos   if (bin_dirs == NULL)
33098b9484cSchristos     goto bailout;
33198b9484cSchristos 
33298b9484cSchristos   /* Remove the program name from comparison of directory names.  */
33398b9484cSchristos   prog_num--;
33498b9484cSchristos 
33598b9484cSchristos   /* If we are still installed in the standard location, we don't need to
33698b9484cSchristos      specify relative directories.  Also, if argv[0] still doesn't contain
33798b9484cSchristos      any directory specifiers after the search above, then there is not much
33898b9484cSchristos      we can do.  */
33998b9484cSchristos   if (prog_num == bin_num)
34098b9484cSchristos     {
34198b9484cSchristos       for (i = 0; i < bin_num; i++)
34298b9484cSchristos 	{
34398b9484cSchristos 	  if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
34498b9484cSchristos 	    break;
34598b9484cSchristos 	}
34698b9484cSchristos 
34798b9484cSchristos       if (prog_num <= 0 || i == bin_num)
34898b9484cSchristos 	goto bailout;
34998b9484cSchristos     }
35098b9484cSchristos 
35198b9484cSchristos   prefix_dirs = split_directories (prefix, &prefix_num);
35298b9484cSchristos   if (prefix_dirs == NULL)
35398b9484cSchristos     goto bailout;
35498b9484cSchristos 
35598b9484cSchristos   /* Find how many directories are in common between bin_prefix & prefix.  */
35698b9484cSchristos   n = (prefix_num < bin_num) ? prefix_num : bin_num;
35798b9484cSchristos   for (common = 0; common < n; common++)
35898b9484cSchristos     {
35998b9484cSchristos       if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
36098b9484cSchristos 	break;
36198b9484cSchristos     }
36298b9484cSchristos 
36398b9484cSchristos   /* If there are no common directories, there can be no relative prefix.  */
36498b9484cSchristos   if (common == 0)
36598b9484cSchristos     goto bailout;
36698b9484cSchristos 
36798b9484cSchristos   /* Two passes: first figure out the size of the result string, and
36898b9484cSchristos      then construct it.  */
36998b9484cSchristos   needed_len = 0;
37098b9484cSchristos   for (i = 0; i < prog_num; i++)
37198b9484cSchristos     needed_len += strlen (prog_dirs[i]);
37298b9484cSchristos   needed_len += sizeof (DIR_UP) * (bin_num - common);
37398b9484cSchristos   for (i = common; i < prefix_num; i++)
37498b9484cSchristos     needed_len += strlen (prefix_dirs[i]);
37598b9484cSchristos   needed_len += 1; /* Trailing NUL.  */
37698b9484cSchristos 
37798b9484cSchristos   ret = (char *) malloc (needed_len);
37898b9484cSchristos   if (ret == NULL)
37998b9484cSchristos     goto bailout;
38098b9484cSchristos 
38198b9484cSchristos   /* Build up the pathnames in argv[0].  */
38298b9484cSchristos   *ret = '\0';
38398b9484cSchristos   for (i = 0; i < prog_num; i++)
38498b9484cSchristos     strcat (ret, prog_dirs[i]);
38598b9484cSchristos 
38698b9484cSchristos   /* Now build up the ..'s.  */
38798b9484cSchristos   ptr = ret + strlen(ret);
38898b9484cSchristos   for (i = common; i < bin_num; i++)
38998b9484cSchristos     {
39098b9484cSchristos       strcpy (ptr, DIR_UP);
39198b9484cSchristos       ptr += sizeof (DIR_UP) - 1;
39298b9484cSchristos       *(ptr++) = DIR_SEPARATOR;
39398b9484cSchristos     }
39498b9484cSchristos   *ptr = '\0';
39598b9484cSchristos 
39698b9484cSchristos   /* Put in directories to move over to prefix.  */
39798b9484cSchristos   for (i = common; i < prefix_num; i++)
39898b9484cSchristos     strcat (ret, prefix_dirs[i]);
39998b9484cSchristos 
40098b9484cSchristos  bailout:
40198b9484cSchristos   free_split_directories (prog_dirs);
40298b9484cSchristos   free_split_directories (bin_dirs);
40398b9484cSchristos   free_split_directories (prefix_dirs);
404ba340e45Schristos   free (alloc_ptr);
40598b9484cSchristos 
40698b9484cSchristos   return ret;
40798b9484cSchristos }
40898b9484cSchristos 
40998b9484cSchristos 
41098b9484cSchristos /* Do the full job, including symlink resolution.
41198b9484cSchristos    This path will find files installed in the same place as the
41298b9484cSchristos    program even when a soft link has been made to the program
41398b9484cSchristos    from somwhere else. */
41498b9484cSchristos 
41598b9484cSchristos char *
41698b9484cSchristos make_relative_prefix (const char *progname, const char *bin_prefix,
41798b9484cSchristos 		      const char *prefix)
41898b9484cSchristos {
41998b9484cSchristos   return make_relative_prefix_1 (progname, bin_prefix, prefix, 1);
42098b9484cSchristos }
42198b9484cSchristos 
42298b9484cSchristos /* Make the relative pathname without attempting to resolve any links.
42398b9484cSchristos    '..' etc may also be left in the pathname.
42498b9484cSchristos    This will find the files the user meant the program to find if the
42598b9484cSchristos    installation is patched together with soft links. */
42698b9484cSchristos 
42798b9484cSchristos char *
42898b9484cSchristos make_relative_prefix_ignore_links (const char *progname,
42998b9484cSchristos 				   const char *bin_prefix,
43098b9484cSchristos 				   const char *prefix)
43198b9484cSchristos {
43298b9484cSchristos   return make_relative_prefix_1 (progname, bin_prefix, prefix, 0);
43398b9484cSchristos }
43498b9484cSchristos 
435