Home | History | Annotate | Line # | Download | only in libiberty
      1   1.1  christos /* Relative (relocatable) prefix support.
      2  1.10  christos    Copyright (C) 1987-2025 Free Software Foundation, Inc.
      3   1.1  christos 
      4   1.1  christos This file is part of libiberty.
      5   1.1  christos 
      6   1.1  christos GCC is free software; you can redistribute it and/or modify it under
      7   1.1  christos the terms of the GNU General Public License as published by the Free
      8   1.1  christos Software Foundation; either version 2, or (at your option) any later
      9   1.1  christos version.
     10   1.1  christos 
     11   1.1  christos GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     12   1.1  christos WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13   1.1  christos FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14   1.1  christos for more details.
     15   1.1  christos 
     16   1.1  christos You should have received a copy of the GNU General Public License
     17   1.1  christos along with GCC; see the file COPYING.  If not, write to the Free
     18   1.1  christos Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
     19   1.1  christos 02110-1301, USA.  */
     20   1.1  christos 
     21   1.1  christos /*
     22   1.1  christos 
     23   1.1  christos @deftypefn Extension {const char*} make_relative_prefix (const char *@var{progname}, @
     24   1.1  christos   const char *@var{bin_prefix}, const char *@var{prefix})
     25   1.1  christos 
     26   1.1  christos Given three paths @var{progname}, @var{bin_prefix}, @var{prefix},
     27   1.1  christos return the path that is in the same position relative to
     28   1.1  christos @var{progname}'s directory as @var{prefix} is relative to
     29   1.1  christos @var{bin_prefix}.  That is, a string starting with the directory
     30   1.1  christos portion of @var{progname}, followed by a relative pathname of the
     31   1.1  christos difference between @var{bin_prefix} and @var{prefix}.
     32   1.1  christos 
     33   1.1  christos If @var{progname} does not contain any directory separators,
     34   1.1  christos @code{make_relative_prefix} will search @env{PATH} to find a program
     35   1.1  christos named @var{progname}.  Also, if @var{progname} is a symbolic link,
     36   1.1  christos the symbolic link will be resolved.
     37   1.1  christos 
     38   1.1  christos For example, if @var{bin_prefix} is @code{/alpha/beta/gamma/gcc/delta},
     39   1.1  christos @var{prefix} is @code{/alpha/beta/gamma/omega/}, and @var{progname} is
     40   1.1  christos @code{/red/green/blue/gcc}, then this function will return
     41   1.1  christos @code{/red/green/blue/../../omega/}.
     42   1.1  christos 
     43   1.1  christos The return value is normally allocated via @code{malloc}.  If no
     44   1.1  christos relative prefix can be found, return @code{NULL}.
     45   1.1  christos 
     46   1.1  christos @end deftypefn
     47   1.1  christos 
     48   1.1  christos */
     49   1.1  christos 
     50   1.1  christos #ifdef HAVE_CONFIG_H
     51   1.1  christos #include "config.h"
     52   1.1  christos #endif
     53   1.1  christos 
     54   1.1  christos #ifdef HAVE_STDLIB_H
     55   1.1  christos #include <stdlib.h>
     56   1.1  christos #endif
     57   1.1  christos #ifdef HAVE_UNISTD_H
     58   1.1  christos #include <unistd.h>
     59   1.1  christos #endif
     60   1.1  christos #ifdef HAVE_SYS_STAT_H
     61   1.1  christos #include <sys/stat.h>
     62   1.1  christos #endif
     63   1.1  christos 
     64   1.1  christos #include <string.h>
     65   1.1  christos 
     66   1.1  christos #include "ansidecl.h"
     67   1.1  christos #include "libiberty.h"
     68   1.1  christos 
     69   1.1  christos #ifndef R_OK
     70   1.1  christos #define R_OK 4
     71   1.1  christos #define W_OK 2
     72   1.1  christos #define X_OK 1
     73   1.1  christos #endif
     74   1.1  christos 
     75   1.1  christos #ifndef DIR_SEPARATOR
     76   1.1  christos #  define DIR_SEPARATOR '/'
     77   1.1  christos #endif
     78   1.1  christos 
     79   1.1  christos #if defined (_WIN32) || defined (__MSDOS__) \
     80   1.1  christos     || defined (__DJGPP__) || defined (__OS2__)
     81   1.1  christos #  define HAVE_DOS_BASED_FILE_SYSTEM
     82   1.1  christos #  define HAVE_HOST_EXECUTABLE_SUFFIX
     83   1.1  christos #  define HOST_EXECUTABLE_SUFFIX ".exe"
     84   1.1  christos #  ifndef DIR_SEPARATOR_2
     85   1.1  christos #    define DIR_SEPARATOR_2 '\\'
     86   1.1  christos #  endif
     87   1.1  christos #  define PATH_SEPARATOR ';'
     88   1.1  christos #else
     89   1.1  christos #  define PATH_SEPARATOR ':'
     90   1.1  christos #endif
     91   1.1  christos 
     92   1.1  christos #ifndef DIR_SEPARATOR_2
     93   1.1  christos #  define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
     94   1.1  christos #else
     95   1.1  christos #  define IS_DIR_SEPARATOR(ch) \
     96   1.1  christos 	(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
     97   1.1  christos #endif
     98   1.1  christos 
     99   1.1  christos #define DIR_UP ".."
    100   1.1  christos 
    101   1.1  christos static char *save_string (const char *, int);
    102   1.1  christos static char **split_directories	(const char *, int *);
    103   1.1  christos static void free_split_directories (char **);
    104   1.1  christos 
    105   1.1  christos static char *
    106   1.1  christos save_string (const char *s, int len)
    107   1.1  christos {
    108   1.1  christos   char *result = (char *) malloc (len + 1);
    109   1.1  christos 
    110   1.1  christos   memcpy (result, s, len);
    111   1.1  christos   result[len] = 0;
    112   1.1  christos   return result;
    113   1.1  christos }
    114   1.1  christos 
    115   1.1  christos /* Split a filename into component directories.  */
    116   1.1  christos 
    117   1.1  christos static char **
    118   1.1  christos split_directories (const char *name, int *ptr_num_dirs)
    119   1.1  christos {
    120   1.1  christos   int num_dirs = 0;
    121   1.1  christos   char **dirs;
    122   1.1  christos   const char *p, *q;
    123   1.1  christos   int ch;
    124   1.1  christos 
    125   1.7  christos   if (!*name)
    126   1.7  christos     return NULL;
    127   1.7  christos 
    128   1.1  christos   /* Count the number of directories.  Special case MSDOS disk names as part
    129   1.1  christos      of the initial directory.  */
    130   1.1  christos   p = name;
    131   1.1  christos #ifdef HAVE_DOS_BASED_FILE_SYSTEM
    132   1.1  christos   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
    133   1.1  christos     {
    134   1.1  christos       p += 3;
    135   1.1  christos       num_dirs++;
    136   1.1  christos     }
    137   1.1  christos #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
    138   1.1  christos 
    139   1.1  christos   while ((ch = *p++) != '\0')
    140   1.1  christos     {
    141   1.1  christos       if (IS_DIR_SEPARATOR (ch))
    142   1.1  christos 	{
    143   1.1  christos 	  num_dirs++;
    144   1.1  christos 	  while (IS_DIR_SEPARATOR (*p))
    145   1.1  christos 	    p++;
    146   1.1  christos 	}
    147   1.1  christos     }
    148   1.1  christos 
    149   1.1  christos   dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2));
    150   1.1  christos   if (dirs == NULL)
    151   1.1  christos     return NULL;
    152   1.1  christos 
    153   1.1  christos   /* Now copy the directory parts.  */
    154   1.1  christos   num_dirs = 0;
    155   1.1  christos   p = name;
    156   1.1  christos #ifdef HAVE_DOS_BASED_FILE_SYSTEM
    157   1.1  christos   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
    158   1.1  christos     {
    159   1.1  christos       dirs[num_dirs++] = save_string (p, 3);
    160   1.1  christos       if (dirs[num_dirs - 1] == NULL)
    161   1.1  christos 	{
    162   1.1  christos 	  free (dirs);
    163   1.1  christos 	  return NULL;
    164   1.1  christos 	}
    165   1.1  christos       p += 3;
    166   1.1  christos     }
    167   1.1  christos #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
    168   1.1  christos 
    169   1.1  christos   q = p;
    170   1.1  christos   while ((ch = *p++) != '\0')
    171   1.1  christos     {
    172   1.1  christos       if (IS_DIR_SEPARATOR (ch))
    173   1.1  christos 	{
    174   1.1  christos 	  while (IS_DIR_SEPARATOR (*p))
    175   1.1  christos 	    p++;
    176   1.1  christos 
    177   1.1  christos 	  dirs[num_dirs++] = save_string (q, p - q);
    178   1.1  christos 	  if (dirs[num_dirs - 1] == NULL)
    179   1.1  christos 	    {
    180   1.1  christos 	      dirs[num_dirs] = NULL;
    181   1.1  christos 	      free_split_directories (dirs);
    182   1.1  christos 	      return NULL;
    183   1.1  christos 	    }
    184   1.1  christos 	  q = p;
    185   1.1  christos 	}
    186   1.1  christos     }
    187   1.1  christos 
    188   1.1  christos   if (p - 1 - q > 0)
    189   1.1  christos     dirs[num_dirs++] = save_string (q, p - 1 - q);
    190   1.1  christos   dirs[num_dirs] = NULL;
    191   1.1  christos 
    192   1.1  christos   if (dirs[num_dirs - 1] == NULL)
    193   1.1  christos     {
    194   1.1  christos       free_split_directories (dirs);
    195   1.1  christos       return NULL;
    196   1.1  christos     }
    197   1.1  christos 
    198   1.1  christos   if (ptr_num_dirs)
    199   1.1  christos     *ptr_num_dirs = num_dirs;
    200   1.1  christos   return dirs;
    201   1.1  christos }
    202   1.1  christos 
    203   1.1  christos /* Release storage held by split directories.  */
    204   1.1  christos 
    205   1.1  christos static void
    206   1.1  christos free_split_directories (char **dirs)
    207   1.1  christos {
    208   1.1  christos   int i = 0;
    209   1.1  christos 
    210   1.1  christos   if (dirs != NULL)
    211   1.1  christos     {
    212   1.1  christos       while (dirs[i] != NULL)
    213   1.1  christos 	free (dirs[i++]);
    214   1.1  christos 
    215   1.1  christos       free ((char *) dirs);
    216   1.1  christos     }
    217   1.1  christos }
    218   1.1  christos 
    219   1.1  christos /* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets
    220   1.1  christos    to PREFIX starting with the directory portion of PROGNAME and a relative
    221   1.1  christos    pathname of the difference between BIN_PREFIX and PREFIX.
    222   1.1  christos 
    223   1.1  christos    For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is
    224   1.1  christos    /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this
    225   1.1  christos    function will return /red/green/blue/../../omega/.
    226   1.1  christos 
    227   1.1  christos    If no relative prefix can be found, return NULL.  */
    228   1.1  christos 
    229   1.1  christos static char *
    230   1.1  christos make_relative_prefix_1 (const char *progname, const char *bin_prefix,
    231   1.1  christos 			const char *prefix, const int resolve_links)
    232   1.1  christos {
    233   1.1  christos   char **prog_dirs = NULL, **bin_dirs = NULL, **prefix_dirs = NULL;
    234   1.1  christos   int prog_num, bin_num, prefix_num;
    235   1.1  christos   int i, n, common;
    236   1.1  christos   int needed_len;
    237   1.1  christos   char *ret = NULL, *ptr, *full_progname;
    238   1.6  christos   char *alloc_ptr = NULL;
    239   1.1  christos 
    240   1.1  christos   if (progname == NULL || bin_prefix == NULL || prefix == NULL)
    241   1.1  christos     return NULL;
    242   1.1  christos 
    243   1.1  christos   /* If there is no full pathname, try to find the program by checking in each
    244   1.1  christos      of the directories specified in the PATH environment variable.  */
    245   1.1  christos   if (lbasename (progname) == progname)
    246   1.1  christos     {
    247   1.1  christos       char *temp;
    248   1.1  christos 
    249   1.1  christos       temp = getenv ("PATH");
    250   1.1  christos       if (temp)
    251   1.1  christos 	{
    252   1.1  christos 	  char *startp, *endp, *nstore;
    253   1.1  christos 	  size_t prefixlen = strlen (temp) + 1;
    254   1.1  christos 	  size_t len;
    255   1.1  christos 	  if (prefixlen < 2)
    256   1.1  christos 	    prefixlen = 2;
    257   1.1  christos 
    258   1.1  christos 	  len = prefixlen + strlen (progname) + 1;
    259   1.1  christos #ifdef HAVE_HOST_EXECUTABLE_SUFFIX
    260   1.1  christos 	  len += strlen (HOST_EXECUTABLE_SUFFIX);
    261   1.1  christos #endif
    262   1.6  christos 	  if (len < MAX_ALLOCA_SIZE)
    263   1.6  christos 	    nstore = (char *) alloca (len);
    264   1.6  christos 	  else
    265   1.6  christos 	    alloc_ptr = nstore = (char *) malloc (len);
    266   1.1  christos 
    267   1.1  christos 	  startp = endp = temp;
    268   1.1  christos 	  while (1)
    269   1.1  christos 	    {
    270   1.1  christos 	      if (*endp == PATH_SEPARATOR || *endp == 0)
    271   1.1  christos 		{
    272   1.1  christos 		  if (endp == startp)
    273   1.1  christos 		    {
    274   1.1  christos 		      nstore[0] = '.';
    275   1.1  christos 		      nstore[1] = DIR_SEPARATOR;
    276   1.1  christos 		      nstore[2] = '\0';
    277   1.1  christos 		    }
    278   1.1  christos 		  else
    279   1.1  christos 		    {
    280   1.1  christos 		      memcpy (nstore, startp, endp - startp);
    281   1.1  christos 		      if (! IS_DIR_SEPARATOR (endp[-1]))
    282   1.1  christos 			{
    283   1.1  christos 			  nstore[endp - startp] = DIR_SEPARATOR;
    284   1.1  christos 			  nstore[endp - startp + 1] = 0;
    285   1.1  christos 			}
    286   1.1  christos 		      else
    287   1.1  christos 			nstore[endp - startp] = 0;
    288   1.1  christos 		    }
    289   1.1  christos 		  strcat (nstore, progname);
    290   1.1  christos 		  if (! access (nstore, X_OK)
    291   1.1  christos #ifdef HAVE_HOST_EXECUTABLE_SUFFIX
    292   1.1  christos                       || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
    293   1.1  christos #endif
    294   1.1  christos 		      )
    295   1.1  christos 		    {
    296   1.1  christos #if defined (HAVE_SYS_STAT_H) && defined (S_ISREG)
    297   1.1  christos 		      struct stat st;
    298   1.1  christos 		      if (stat (nstore, &st) >= 0 && S_ISREG (st.st_mode))
    299   1.1  christos #endif
    300   1.1  christos 			{
    301   1.1  christos 			  progname = nstore;
    302   1.1  christos 			  break;
    303   1.1  christos 			}
    304   1.1  christos 		    }
    305   1.1  christos 
    306   1.1  christos 		  if (*endp == 0)
    307   1.1  christos 		    break;
    308   1.1  christos 		  endp = startp = endp + 1;
    309   1.1  christos 		}
    310   1.1  christos 	      else
    311   1.1  christos 		endp++;
    312   1.1  christos 	    }
    313   1.1  christos 	}
    314   1.1  christos     }
    315   1.1  christos 
    316   1.1  christos   if (resolve_links)
    317   1.1  christos     full_progname = lrealpath (progname);
    318   1.1  christos   else
    319   1.1  christos     full_progname = strdup (progname);
    320   1.1  christos   if (full_progname == NULL)
    321   1.6  christos     goto bailout;
    322   1.1  christos 
    323   1.1  christos   prog_dirs = split_directories (full_progname, &prog_num);
    324   1.1  christos   free (full_progname);
    325   1.1  christos   if (prog_dirs == NULL)
    326   1.6  christos     goto bailout;
    327   1.1  christos 
    328   1.1  christos   bin_dirs = split_directories (bin_prefix, &bin_num);
    329   1.1  christos   if (bin_dirs == NULL)
    330   1.1  christos     goto bailout;
    331   1.1  christos 
    332   1.1  christos   /* Remove the program name from comparison of directory names.  */
    333   1.1  christos   prog_num--;
    334   1.1  christos 
    335   1.1  christos   /* If we are still installed in the standard location, we don't need to
    336   1.1  christos      specify relative directories.  Also, if argv[0] still doesn't contain
    337   1.1  christos      any directory specifiers after the search above, then there is not much
    338   1.1  christos      we can do.  */
    339   1.1  christos   if (prog_num == bin_num)
    340   1.1  christos     {
    341   1.1  christos       for (i = 0; i < bin_num; i++)
    342   1.1  christos 	{
    343   1.1  christos 	  if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
    344   1.1  christos 	    break;
    345   1.1  christos 	}
    346   1.1  christos 
    347   1.1  christos       if (prog_num <= 0 || i == bin_num)
    348   1.1  christos 	goto bailout;
    349   1.1  christos     }
    350   1.1  christos 
    351   1.1  christos   prefix_dirs = split_directories (prefix, &prefix_num);
    352   1.1  christos   if (prefix_dirs == NULL)
    353   1.1  christos     goto bailout;
    354   1.1  christos 
    355   1.1  christos   /* Find how many directories are in common between bin_prefix & prefix.  */
    356   1.1  christos   n = (prefix_num < bin_num) ? prefix_num : bin_num;
    357   1.1  christos   for (common = 0; common < n; common++)
    358   1.1  christos     {
    359   1.1  christos       if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
    360   1.1  christos 	break;
    361   1.1  christos     }
    362   1.1  christos 
    363   1.1  christos   /* If there are no common directories, there can be no relative prefix.  */
    364   1.1  christos   if (common == 0)
    365   1.1  christos     goto bailout;
    366   1.1  christos 
    367   1.1  christos   /* Two passes: first figure out the size of the result string, and
    368   1.1  christos      then construct it.  */
    369   1.1  christos   needed_len = 0;
    370   1.1  christos   for (i = 0; i < prog_num; i++)
    371   1.1  christos     needed_len += strlen (prog_dirs[i]);
    372   1.1  christos   needed_len += sizeof (DIR_UP) * (bin_num - common);
    373   1.1  christos   for (i = common; i < prefix_num; i++)
    374   1.1  christos     needed_len += strlen (prefix_dirs[i]);
    375   1.1  christos   needed_len += 1; /* Trailing NUL.  */
    376   1.1  christos 
    377   1.1  christos   ret = (char *) malloc (needed_len);
    378   1.1  christos   if (ret == NULL)
    379   1.1  christos     goto bailout;
    380   1.1  christos 
    381   1.1  christos   /* Build up the pathnames in argv[0].  */
    382   1.1  christos   *ret = '\0';
    383   1.1  christos   for (i = 0; i < prog_num; i++)
    384   1.1  christos     strcat (ret, prog_dirs[i]);
    385   1.1  christos 
    386   1.1  christos   /* Now build up the ..'s.  */
    387   1.1  christos   ptr = ret + strlen(ret);
    388   1.1  christos   for (i = common; i < bin_num; i++)
    389   1.1  christos     {
    390   1.1  christos       strcpy (ptr, DIR_UP);
    391   1.1  christos       ptr += sizeof (DIR_UP) - 1;
    392   1.1  christos       *(ptr++) = DIR_SEPARATOR;
    393   1.1  christos     }
    394   1.1  christos   *ptr = '\0';
    395   1.1  christos 
    396   1.1  christos   /* Put in directories to move over to prefix.  */
    397   1.1  christos   for (i = common; i < prefix_num; i++)
    398   1.1  christos     strcat (ret, prefix_dirs[i]);
    399   1.1  christos 
    400   1.1  christos  bailout:
    401   1.1  christos   free_split_directories (prog_dirs);
    402   1.1  christos   free_split_directories (bin_dirs);
    403   1.1  christos   free_split_directories (prefix_dirs);
    404   1.6  christos   free (alloc_ptr);
    405   1.1  christos 
    406   1.1  christos   return ret;
    407   1.1  christos }
    408   1.1  christos 
    409   1.1  christos 
    410   1.1  christos /* Do the full job, including symlink resolution.
    411   1.1  christos    This path will find files installed in the same place as the
    412   1.1  christos    program even when a soft link has been made to the program
    413   1.1  christos    from somwhere else. */
    414   1.1  christos 
    415   1.1  christos char *
    416   1.1  christos make_relative_prefix (const char *progname, const char *bin_prefix,
    417   1.1  christos 		      const char *prefix)
    418   1.1  christos {
    419   1.1  christos   return make_relative_prefix_1 (progname, bin_prefix, prefix, 1);
    420   1.1  christos }
    421   1.1  christos 
    422   1.1  christos /* Make the relative pathname without attempting to resolve any links.
    423   1.1  christos    '..' etc may also be left in the pathname.
    424   1.1  christos    This will find the files the user meant the program to find if the
    425   1.1  christos    installation is patched together with soft links. */
    426   1.1  christos 
    427   1.1  christos char *
    428   1.1  christos make_relative_prefix_ignore_links (const char *progname,
    429   1.1  christos 				   const char *bin_prefix,
    430   1.1  christos 				   const char *prefix)
    431   1.1  christos {
    432   1.1  christos   return make_relative_prefix_1 (progname, bin_prefix, prefix, 0);
    433   1.1  christos }
    434   1.1  christos 
    435