Home | History | Annotate | Line # | Download | only in libiberty
make-relative-prefix.c revision 1.6
      1  1.1  christos /* Relative (relocatable) prefix support.
      2  1.6  christos    Copyright (C) 1987-2018 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.1  christos   /* Count the number of directories.  Special case MSDOS disk names as part
    126  1.1  christos      of the initial directory.  */
    127  1.1  christos   p = name;
    128  1.1  christos #ifdef HAVE_DOS_BASED_FILE_SYSTEM
    129  1.1  christos   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
    130  1.1  christos     {
    131  1.1  christos       p += 3;
    132  1.1  christos       num_dirs++;
    133  1.1  christos     }
    134  1.1  christos #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
    135  1.1  christos 
    136  1.1  christos   while ((ch = *p++) != '\0')
    137  1.1  christos     {
    138  1.1  christos       if (IS_DIR_SEPARATOR (ch))
    139  1.1  christos 	{
    140  1.1  christos 	  num_dirs++;
    141  1.1  christos 	  while (IS_DIR_SEPARATOR (*p))
    142  1.1  christos 	    p++;
    143  1.1  christos 	}
    144  1.1  christos     }
    145  1.1  christos 
    146  1.1  christos   dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2));
    147  1.1  christos   if (dirs == NULL)
    148  1.1  christos     return NULL;
    149  1.1  christos 
    150  1.1  christos   /* Now copy the directory parts.  */
    151  1.1  christos   num_dirs = 0;
    152  1.1  christos   p = name;
    153  1.1  christos #ifdef HAVE_DOS_BASED_FILE_SYSTEM
    154  1.1  christos   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
    155  1.1  christos     {
    156  1.1  christos       dirs[num_dirs++] = save_string (p, 3);
    157  1.1  christos       if (dirs[num_dirs - 1] == NULL)
    158  1.1  christos 	{
    159  1.1  christos 	  free (dirs);
    160  1.1  christos 	  return NULL;
    161  1.1  christos 	}
    162  1.1  christos       p += 3;
    163  1.1  christos     }
    164  1.1  christos #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
    165  1.1  christos 
    166  1.1  christos   q = p;
    167  1.1  christos   while ((ch = *p++) != '\0')
    168  1.1  christos     {
    169  1.1  christos       if (IS_DIR_SEPARATOR (ch))
    170  1.1  christos 	{
    171  1.1  christos 	  while (IS_DIR_SEPARATOR (*p))
    172  1.1  christos 	    p++;
    173  1.1  christos 
    174  1.1  christos 	  dirs[num_dirs++] = save_string (q, p - q);
    175  1.1  christos 	  if (dirs[num_dirs - 1] == NULL)
    176  1.1  christos 	    {
    177  1.1  christos 	      dirs[num_dirs] = NULL;
    178  1.1  christos 	      free_split_directories (dirs);
    179  1.1  christos 	      return NULL;
    180  1.1  christos 	    }
    181  1.1  christos 	  q = p;
    182  1.1  christos 	}
    183  1.1  christos     }
    184  1.1  christos 
    185  1.1  christos   if (p - 1 - q > 0)
    186  1.1  christos     dirs[num_dirs++] = save_string (q, p - 1 - q);
    187  1.1  christos   dirs[num_dirs] = NULL;
    188  1.1  christos 
    189  1.1  christos   if (dirs[num_dirs - 1] == NULL)
    190  1.1  christos     {
    191  1.1  christos       free_split_directories (dirs);
    192  1.1  christos       return NULL;
    193  1.1  christos     }
    194  1.1  christos 
    195  1.1  christos   if (ptr_num_dirs)
    196  1.1  christos     *ptr_num_dirs = num_dirs;
    197  1.1  christos   return dirs;
    198  1.1  christos }
    199  1.1  christos 
    200  1.1  christos /* Release storage held by split directories.  */
    201  1.1  christos 
    202  1.1  christos static void
    203  1.1  christos free_split_directories (char **dirs)
    204  1.1  christos {
    205  1.1  christos   int i = 0;
    206  1.1  christos 
    207  1.1  christos   if (dirs != NULL)
    208  1.1  christos     {
    209  1.1  christos       while (dirs[i] != NULL)
    210  1.1  christos 	free (dirs[i++]);
    211  1.1  christos 
    212  1.1  christos       free ((char *) dirs);
    213  1.1  christos     }
    214  1.1  christos }
    215  1.1  christos 
    216  1.1  christos /* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets
    217  1.1  christos    to PREFIX starting with the directory portion of PROGNAME and a relative
    218  1.1  christos    pathname of the difference between BIN_PREFIX and PREFIX.
    219  1.1  christos 
    220  1.1  christos    For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is
    221  1.1  christos    /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this
    222  1.1  christos    function will return /red/green/blue/../../omega/.
    223  1.1  christos 
    224  1.1  christos    If no relative prefix can be found, return NULL.  */
    225  1.1  christos 
    226  1.1  christos static char *
    227  1.1  christos make_relative_prefix_1 (const char *progname, const char *bin_prefix,
    228  1.1  christos 			const char *prefix, const int resolve_links)
    229  1.1  christos {
    230  1.1  christos   char **prog_dirs = NULL, **bin_dirs = NULL, **prefix_dirs = NULL;
    231  1.1  christos   int prog_num, bin_num, prefix_num;
    232  1.1  christos   int i, n, common;
    233  1.1  christos   int needed_len;
    234  1.1  christos   char *ret = NULL, *ptr, *full_progname;
    235  1.6  christos   char *alloc_ptr = NULL;
    236  1.1  christos 
    237  1.1  christos   if (progname == NULL || bin_prefix == NULL || prefix == NULL)
    238  1.1  christos     return NULL;
    239  1.1  christos 
    240  1.1  christos   /* If there is no full pathname, try to find the program by checking in each
    241  1.1  christos      of the directories specified in the PATH environment variable.  */
    242  1.1  christos   if (lbasename (progname) == progname)
    243  1.1  christos     {
    244  1.1  christos       char *temp;
    245  1.1  christos 
    246  1.1  christos       temp = getenv ("PATH");
    247  1.1  christos       if (temp)
    248  1.1  christos 	{
    249  1.1  christos 	  char *startp, *endp, *nstore;
    250  1.1  christos 	  size_t prefixlen = strlen (temp) + 1;
    251  1.1  christos 	  size_t len;
    252  1.1  christos 	  if (prefixlen < 2)
    253  1.1  christos 	    prefixlen = 2;
    254  1.1  christos 
    255  1.1  christos 	  len = prefixlen + strlen (progname) + 1;
    256  1.1  christos #ifdef HAVE_HOST_EXECUTABLE_SUFFIX
    257  1.1  christos 	  len += strlen (HOST_EXECUTABLE_SUFFIX);
    258  1.1  christos #endif
    259  1.6  christos 	  if (len < MAX_ALLOCA_SIZE)
    260  1.6  christos 	    nstore = (char *) alloca (len);
    261  1.6  christos 	  else
    262  1.6  christos 	    alloc_ptr = nstore = (char *) malloc (len);
    263  1.1  christos 
    264  1.1  christos 	  startp = endp = temp;
    265  1.1  christos 	  while (1)
    266  1.1  christos 	    {
    267  1.1  christos 	      if (*endp == PATH_SEPARATOR || *endp == 0)
    268  1.1  christos 		{
    269  1.1  christos 		  if (endp == startp)
    270  1.1  christos 		    {
    271  1.1  christos 		      nstore[0] = '.';
    272  1.1  christos 		      nstore[1] = DIR_SEPARATOR;
    273  1.1  christos 		      nstore[2] = '\0';
    274  1.1  christos 		    }
    275  1.1  christos 		  else
    276  1.1  christos 		    {
    277  1.1  christos 		      memcpy (nstore, startp, endp - startp);
    278  1.1  christos 		      if (! IS_DIR_SEPARATOR (endp[-1]))
    279  1.1  christos 			{
    280  1.1  christos 			  nstore[endp - startp] = DIR_SEPARATOR;
    281  1.1  christos 			  nstore[endp - startp + 1] = 0;
    282  1.1  christos 			}
    283  1.1  christos 		      else
    284  1.1  christos 			nstore[endp - startp] = 0;
    285  1.1  christos 		    }
    286  1.1  christos 		  strcat (nstore, progname);
    287  1.1  christos 		  if (! access (nstore, X_OK)
    288  1.1  christos #ifdef HAVE_HOST_EXECUTABLE_SUFFIX
    289  1.1  christos                       || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
    290  1.1  christos #endif
    291  1.1  christos 		      )
    292  1.1  christos 		    {
    293  1.1  christos #if defined (HAVE_SYS_STAT_H) && defined (S_ISREG)
    294  1.1  christos 		      struct stat st;
    295  1.1  christos 		      if (stat (nstore, &st) >= 0 && S_ISREG (st.st_mode))
    296  1.1  christos #endif
    297  1.1  christos 			{
    298  1.1  christos 			  progname = nstore;
    299  1.1  christos 			  break;
    300  1.1  christos 			}
    301  1.1  christos 		    }
    302  1.1  christos 
    303  1.1  christos 		  if (*endp == 0)
    304  1.1  christos 		    break;
    305  1.1  christos 		  endp = startp = endp + 1;
    306  1.1  christos 		}
    307  1.1  christos 	      else
    308  1.1  christos 		endp++;
    309  1.1  christos 	    }
    310  1.1  christos 	}
    311  1.1  christos     }
    312  1.1  christos 
    313  1.1  christos   if (resolve_links)
    314  1.1  christos     full_progname = lrealpath (progname);
    315  1.1  christos   else
    316  1.1  christos     full_progname = strdup (progname);
    317  1.1  christos   if (full_progname == NULL)
    318  1.6  christos     goto bailout;
    319  1.1  christos 
    320  1.1  christos   prog_dirs = split_directories (full_progname, &prog_num);
    321  1.1  christos   free (full_progname);
    322  1.1  christos   if (prog_dirs == NULL)
    323  1.6  christos     goto bailout;
    324  1.1  christos 
    325  1.1  christos   bin_dirs = split_directories (bin_prefix, &bin_num);
    326  1.1  christos   if (bin_dirs == NULL)
    327  1.1  christos     goto bailout;
    328  1.1  christos 
    329  1.1  christos   /* Remove the program name from comparison of directory names.  */
    330  1.1  christos   prog_num--;
    331  1.1  christos 
    332  1.1  christos   /* If we are still installed in the standard location, we don't need to
    333  1.1  christos      specify relative directories.  Also, if argv[0] still doesn't contain
    334  1.1  christos      any directory specifiers after the search above, then there is not much
    335  1.1  christos      we can do.  */
    336  1.1  christos   if (prog_num == bin_num)
    337  1.1  christos     {
    338  1.1  christos       for (i = 0; i < bin_num; i++)
    339  1.1  christos 	{
    340  1.1  christos 	  if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
    341  1.1  christos 	    break;
    342  1.1  christos 	}
    343  1.1  christos 
    344  1.1  christos       if (prog_num <= 0 || i == bin_num)
    345  1.1  christos 	goto bailout;
    346  1.1  christos     }
    347  1.1  christos 
    348  1.1  christos   prefix_dirs = split_directories (prefix, &prefix_num);
    349  1.1  christos   if (prefix_dirs == NULL)
    350  1.1  christos     goto bailout;
    351  1.1  christos 
    352  1.1  christos   /* Find how many directories are in common between bin_prefix & prefix.  */
    353  1.1  christos   n = (prefix_num < bin_num) ? prefix_num : bin_num;
    354  1.1  christos   for (common = 0; common < n; common++)
    355  1.1  christos     {
    356  1.1  christos       if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
    357  1.1  christos 	break;
    358  1.1  christos     }
    359  1.1  christos 
    360  1.1  christos   /* If there are no common directories, there can be no relative prefix.  */
    361  1.1  christos   if (common == 0)
    362  1.1  christos     goto bailout;
    363  1.1  christos 
    364  1.1  christos   /* Two passes: first figure out the size of the result string, and
    365  1.1  christos      then construct it.  */
    366  1.1  christos   needed_len = 0;
    367  1.1  christos   for (i = 0; i < prog_num; i++)
    368  1.1  christos     needed_len += strlen (prog_dirs[i]);
    369  1.1  christos   needed_len += sizeof (DIR_UP) * (bin_num - common);
    370  1.1  christos   for (i = common; i < prefix_num; i++)
    371  1.1  christos     needed_len += strlen (prefix_dirs[i]);
    372  1.1  christos   needed_len += 1; /* Trailing NUL.  */
    373  1.1  christos 
    374  1.1  christos   ret = (char *) malloc (needed_len);
    375  1.1  christos   if (ret == NULL)
    376  1.1  christos     goto bailout;
    377  1.1  christos 
    378  1.1  christos   /* Build up the pathnames in argv[0].  */
    379  1.1  christos   *ret = '\0';
    380  1.1  christos   for (i = 0; i < prog_num; i++)
    381  1.1  christos     strcat (ret, prog_dirs[i]);
    382  1.1  christos 
    383  1.1  christos   /* Now build up the ..'s.  */
    384  1.1  christos   ptr = ret + strlen(ret);
    385  1.1  christos   for (i = common; i < bin_num; i++)
    386  1.1  christos     {
    387  1.1  christos       strcpy (ptr, DIR_UP);
    388  1.1  christos       ptr += sizeof (DIR_UP) - 1;
    389  1.1  christos       *(ptr++) = DIR_SEPARATOR;
    390  1.1  christos     }
    391  1.1  christos   *ptr = '\0';
    392  1.1  christos 
    393  1.1  christos   /* Put in directories to move over to prefix.  */
    394  1.1  christos   for (i = common; i < prefix_num; i++)
    395  1.1  christos     strcat (ret, prefix_dirs[i]);
    396  1.1  christos 
    397  1.1  christos  bailout:
    398  1.1  christos   free_split_directories (prog_dirs);
    399  1.1  christos   free_split_directories (bin_dirs);
    400  1.1  christos   free_split_directories (prefix_dirs);
    401  1.6  christos   free (alloc_ptr);
    402  1.1  christos 
    403  1.1  christos   return ret;
    404  1.1  christos }
    405  1.1  christos 
    406  1.1  christos 
    407  1.1  christos /* Do the full job, including symlink resolution.
    408  1.1  christos    This path will find files installed in the same place as the
    409  1.1  christos    program even when a soft link has been made to the program
    410  1.1  christos    from somwhere else. */
    411  1.1  christos 
    412  1.1  christos char *
    413  1.1  christos make_relative_prefix (const char *progname, const char *bin_prefix,
    414  1.1  christos 		      const char *prefix)
    415  1.1  christos {
    416  1.1  christos   return make_relative_prefix_1 (progname, bin_prefix, prefix, 1);
    417  1.1  christos }
    418  1.1  christos 
    419  1.1  christos /* Make the relative pathname without attempting to resolve any links.
    420  1.1  christos    '..' etc may also be left in the pathname.
    421  1.1  christos    This will find the files the user meant the program to find if the
    422  1.1  christos    installation is patched together with soft links. */
    423  1.1  christos 
    424  1.1  christos char *
    425  1.1  christos make_relative_prefix_ignore_links (const char *progname,
    426  1.1  christos 				   const char *bin_prefix,
    427  1.1  christos 				   const char *prefix)
    428  1.1  christos {
    429  1.1  christos   return make_relative_prefix_1 (progname, bin_prefix, prefix, 0);
    430  1.1  christos }
    431  1.1  christos 
    432