Back to index

cell-binutils  2.17cvs20070401
make-relative-prefix.c
Go to the documentation of this file.
00001 /* Relative (relocatable) prefix support.
00002    Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
00003    1999, 2000, 2001, 2002, 2006 Free Software Foundation, Inc.
00004 
00005 This file is part of libiberty.
00006 
00007 GCC is free software; you can redistribute it and/or modify it under
00008 the terms of the GNU General Public License as published by the Free
00009 Software Foundation; either version 2, or (at your option) any later
00010 version.
00011 
00012 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
00013 WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with GCC; see the file COPYING.  If not, write to the Free
00019 Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
00020 02110-1301, USA.  */
00021 
00022 /*
00023 
00024 @deftypefn Extension {const char*} make_relative_prefix (const char *@var{progname}, const char *@var{bin_prefix}, const char *@var{prefix})
00025 
00026 Given three paths @var{progname}, @var{bin_prefix}, @var{prefix},
00027 return the path that is in the same position relative to
00028 @var{progname}'s directory as @var{prefix} is relative to
00029 @var{bin_prefix}.  That is, a string starting with the directory
00030 portion of @var{progname}, followed by a relative pathname of the
00031 difference between @var{bin_prefix} and @var{prefix}.
00032 
00033 If @var{progname} does not contain any directory separators,
00034 @code{make_relative_prefix} will search @env{PATH} to find a program
00035 named @var{progname}.  Also, if @var{progname} is a symbolic link,
00036 the symbolic link will be resolved.
00037 
00038 For example, if @var{bin_prefix} is @code{/alpha/beta/gamma/gcc/delta},
00039 @var{prefix} is @code{/alpha/beta/gamma/omega/}, and @var{progname} is
00040 @code{/red/green/blue/gcc}, then this function will return
00041 @code{/red/green/blue/../../omega/}.
00042 
00043 The return value is normally allocated via @code{malloc}.  If no
00044 relative prefix can be found, return @code{NULL}.
00045 
00046 @end deftypefn
00047 
00048 */
00049 
00050 #ifdef HAVE_CONFIG_H
00051 #include "config.h"
00052 #endif
00053 
00054 #ifdef HAVE_STDLIB_H
00055 #include <stdlib.h>
00056 #endif
00057 #ifdef HAVE_UNISTD_H
00058 #include <unistd.h>
00059 #endif
00060 
00061 #include <string.h>
00062 
00063 #include "ansidecl.h"
00064 #include "libiberty.h"
00065 
00066 #ifndef R_OK
00067 #define R_OK 4
00068 #define W_OK 2
00069 #define X_OK 1
00070 #endif
00071 
00072 #ifndef DIR_SEPARATOR
00073 #  define DIR_SEPARATOR '/'
00074 #endif
00075 
00076 #if defined (_WIN32) || defined (__MSDOS__) \
00077     || defined (__DJGPP__) || defined (__OS2__)
00078 #  define HAVE_DOS_BASED_FILE_SYSTEM
00079 #  define HAVE_HOST_EXECUTABLE_SUFFIX
00080 #  define HOST_EXECUTABLE_SUFFIX ".exe"
00081 #  ifndef DIR_SEPARATOR_2 
00082 #    define DIR_SEPARATOR_2 '\\'
00083 #  endif
00084 #  define PATH_SEPARATOR ';'
00085 #else
00086 #  define PATH_SEPARATOR ':'
00087 #endif
00088 
00089 #ifndef DIR_SEPARATOR_2
00090 #  define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
00091 #else
00092 #  define IS_DIR_SEPARATOR(ch) \
00093        (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
00094 #endif
00095 
00096 #define DIR_UP ".."
00097 
00098 static char *save_string (const char *, int);
00099 static char **split_directories    (const char *, int *);
00100 static void free_split_directories (char **);
00101 
00102 static char *
00103 save_string (const char *s, int len)
00104 {
00105   char *result = (char *) malloc (len + 1);
00106 
00107   memcpy (result, s, len);
00108   result[len] = 0;
00109   return result;
00110 }
00111 
00112 /* Split a filename into component directories.  */
00113 
00114 static char **
00115 split_directories (const char *name, int *ptr_num_dirs)
00116 {
00117   int num_dirs = 0;
00118   char **dirs;
00119   const char *p, *q;
00120   int ch;
00121 
00122   /* Count the number of directories.  Special case MSDOS disk names as part
00123      of the initial directory.  */
00124   p = name;
00125 #ifdef HAVE_DOS_BASED_FILE_SYSTEM
00126   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
00127     {
00128       p += 3;
00129       num_dirs++;
00130     }
00131 #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
00132 
00133   while ((ch = *p++) != '\0')
00134     {
00135       if (IS_DIR_SEPARATOR (ch))
00136        {
00137          num_dirs++;
00138          while (IS_DIR_SEPARATOR (*p))
00139            p++;
00140        }
00141     }
00142 
00143   dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2));
00144   if (dirs == NULL)
00145     return NULL;
00146 
00147   /* Now copy the directory parts.  */
00148   num_dirs = 0;
00149   p = name;
00150 #ifdef HAVE_DOS_BASED_FILE_SYSTEM
00151   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
00152     {
00153       dirs[num_dirs++] = save_string (p, 3);
00154       if (dirs[num_dirs - 1] == NULL)
00155        {
00156          free (dirs);
00157          return NULL;
00158        }
00159       p += 3;
00160     }
00161 #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
00162 
00163   q = p;
00164   while ((ch = *p++) != '\0')
00165     {
00166       if (IS_DIR_SEPARATOR (ch))
00167        {
00168          while (IS_DIR_SEPARATOR (*p))
00169            p++;
00170 
00171          dirs[num_dirs++] = save_string (q, p - q);
00172          if (dirs[num_dirs - 1] == NULL)
00173            {
00174              dirs[num_dirs] = NULL;
00175              free_split_directories (dirs);
00176              return NULL;
00177            }
00178          q = p;
00179        }
00180     }
00181 
00182   if (p - 1 - q > 0)
00183     dirs[num_dirs++] = save_string (q, p - 1 - q);
00184   dirs[num_dirs] = NULL;
00185 
00186   if (dirs[num_dirs - 1] == NULL)
00187     {
00188       free_split_directories (dirs);
00189       return NULL;
00190     }
00191 
00192   if (ptr_num_dirs)
00193     *ptr_num_dirs = num_dirs;
00194   return dirs;
00195 }
00196 
00197 /* Release storage held by split directories.  */
00198 
00199 static void
00200 free_split_directories (char **dirs)
00201 {
00202   int i = 0;
00203 
00204   while (dirs[i] != NULL)
00205     free (dirs[i++]);
00206 
00207   free ((char *) dirs);
00208 }
00209 
00210 /* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets
00211    to PREFIX starting with the directory portion of PROGNAME and a relative
00212    pathname of the difference between BIN_PREFIX and PREFIX.
00213 
00214    For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is
00215    /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this
00216    function will return /red/green/blue/../../omega/.
00217 
00218    If no relative prefix can be found, return NULL.  */
00219 
00220 static char *
00221 make_relative_prefix_1 (const char *progname, const char *bin_prefix,
00222                      const char *prefix, const int resolve_links)
00223 {
00224   char **prog_dirs, **bin_dirs, **prefix_dirs;
00225   int prog_num, bin_num, prefix_num;
00226   int i, n, common;
00227   int needed_len;
00228   char *ret, *ptr, *full_progname = NULL;
00229 
00230   if (progname == NULL || bin_prefix == NULL || prefix == NULL)
00231     return NULL;
00232 
00233   /* If there is no full pathname, try to find the program by checking in each
00234      of the directories specified in the PATH environment variable.  */
00235   if (lbasename (progname) == progname)
00236     {
00237       char *temp;
00238 
00239       temp = getenv ("PATH");
00240       if (temp)
00241        {
00242          char *startp, *endp, *nstore;
00243          size_t prefixlen = strlen (temp) + 1;
00244          if (prefixlen < 2)
00245            prefixlen = 2;
00246 
00247          nstore = (char *) alloca (prefixlen + strlen (progname) + 1);
00248 
00249          startp = endp = temp;
00250          while (1)
00251            {
00252              if (*endp == PATH_SEPARATOR || *endp == 0)
00253               {
00254                 if (endp == startp)
00255                   {
00256                     nstore[0] = '.';
00257                     nstore[1] = DIR_SEPARATOR;
00258                     nstore[2] = '\0';
00259                   }
00260                 else
00261                   {
00262                     strncpy (nstore, startp, endp - startp);
00263                     if (! IS_DIR_SEPARATOR (endp[-1]))
00264                      {
00265                        nstore[endp - startp] = DIR_SEPARATOR;
00266                        nstore[endp - startp + 1] = 0;
00267                      }
00268                     else
00269                      nstore[endp - startp] = 0;
00270                   }
00271                 strcat (nstore, progname);
00272                 if (! access (nstore, X_OK)
00273 #ifdef HAVE_HOST_EXECUTABLE_SUFFIX
00274                       || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
00275 #endif
00276                     )
00277                   {
00278                     progname = nstore;
00279                     break;
00280                   }
00281 
00282                 if (*endp == 0)
00283                   break;
00284                 endp = startp = endp + 1;
00285               }
00286              else
00287               endp++;
00288            }
00289        }
00290     }
00291 
00292   if ( resolve_links )
00293     {
00294       full_progname = lrealpath (progname);
00295       if (full_progname == NULL)
00296        return NULL;
00297     }
00298   else
00299     full_progname = strdup(progname);
00300 
00301   prog_dirs = split_directories (full_progname, &prog_num);
00302   bin_dirs = split_directories (bin_prefix, &bin_num);
00303   free (full_progname);
00304   if (bin_dirs == NULL || prog_dirs == NULL)
00305     return NULL;
00306 
00307   /* Remove the program name from comparison of directory names.  */
00308   prog_num--;
00309 
00310   /* If we are still installed in the standard location, we don't need to
00311      specify relative directories.  Also, if argv[0] still doesn't contain
00312      any directory specifiers after the search above, then there is not much
00313      we can do.  */
00314   if (prog_num == bin_num)
00315     {
00316       for (i = 0; i < bin_num; i++)
00317        {
00318          if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
00319            break;
00320        }
00321 
00322       if (prog_num <= 0 || i == bin_num)
00323        {
00324          free_split_directories (prog_dirs);
00325          free_split_directories (bin_dirs);
00326          prog_dirs = bin_dirs = (char **) 0;
00327          return NULL;
00328        }
00329     }
00330 
00331   prefix_dirs = split_directories (prefix, &prefix_num);
00332   if (prefix_dirs == NULL)
00333     {
00334       free_split_directories (prog_dirs);
00335       free_split_directories (bin_dirs);
00336       return NULL;
00337     }
00338 
00339   /* Find how many directories are in common between bin_prefix & prefix.  */
00340   n = (prefix_num < bin_num) ? prefix_num : bin_num;
00341   for (common = 0; common < n; common++)
00342     {
00343       if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
00344        break;
00345     }
00346 
00347   /* If there are no common directories, there can be no relative prefix.  */
00348   if (common == 0)
00349     {
00350       free_split_directories (prog_dirs);
00351       free_split_directories (bin_dirs);
00352       free_split_directories (prefix_dirs);
00353       return NULL;
00354     }
00355 
00356   /* Two passes: first figure out the size of the result string, and
00357      then construct it.  */
00358   needed_len = 0;
00359   for (i = 0; i < prog_num; i++)
00360     needed_len += strlen (prog_dirs[i]);
00361   needed_len += sizeof (DIR_UP) * (bin_num - common);
00362   for (i = common; i < prefix_num; i++)
00363     needed_len += strlen (prefix_dirs[i]);
00364   needed_len += 1; /* Trailing NUL.  */
00365 
00366   ret = (char *) malloc (needed_len);
00367   if (ret == NULL)
00368     return NULL;
00369 
00370   /* Build up the pathnames in argv[0].  */
00371   *ret = '\0';
00372   for (i = 0; i < prog_num; i++)
00373     strcat (ret, prog_dirs[i]);
00374 
00375   /* Now build up the ..'s.  */
00376   ptr = ret + strlen(ret);
00377   for (i = common; i < bin_num; i++)
00378     {
00379       strcpy (ptr, DIR_UP);
00380       ptr += sizeof (DIR_UP) - 1;
00381       *(ptr++) = DIR_SEPARATOR;
00382     }
00383   *ptr = '\0';
00384 
00385   /* Put in directories to move over to prefix.  */
00386   for (i = common; i < prefix_num; i++)
00387     strcat (ret, prefix_dirs[i]);
00388 
00389   free_split_directories (prog_dirs);
00390   free_split_directories (bin_dirs);
00391   free_split_directories (prefix_dirs);
00392 
00393   return ret;
00394 }
00395 
00396 
00397 /* Do the full job, including symlink resolution.
00398    This path will find files installed in the same place as the
00399    program even when a soft link has been made to the program
00400    from somwhere else. */
00401 
00402 char *
00403 make_relative_prefix (progname, bin_prefix, prefix)
00404      const char *progname;
00405      const char *bin_prefix;
00406      const char *prefix;
00407 {
00408   return make_relative_prefix_1 (progname, bin_prefix, prefix, 1);
00409 }
00410 
00411 /* Make the relative pathname without attempting to resolve any links.
00412    '..' etc may also be left in the pathname.
00413    This will find the files the user meant the program to find if the
00414    installation is patched together with soft links. */
00415 
00416 char *
00417 make_relative_prefix_ignore_links (progname, bin_prefix, prefix)
00418      const char *progname;
00419      const char *bin_prefix;
00420      const char *prefix;
00421 {
00422   return make_relative_prefix_1 (progname, bin_prefix, prefix, 0);
00423 }
00424