19588ddcfSespie /* Relative (relocatable) prefix support.
29588ddcfSespie    Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
39588ddcfSespie    1999, 2000, 2001, 2002 Free Software Foundation, Inc.
49588ddcfSespie 
59588ddcfSespie This file is part of libiberty.
69588ddcfSespie 
79588ddcfSespie GCC is free software; you can redistribute it and/or modify it under
89588ddcfSespie the terms of the GNU General Public License as published by the Free
99588ddcfSespie Software Foundation; either version 2, or (at your option) any later
109588ddcfSespie version.
119588ddcfSespie 
129588ddcfSespie GCC is distributed in the hope that it will be useful, but WITHOUT ANY
139588ddcfSespie WARRANTY; without even the implied warranty of MERCHANTABILITY or
149588ddcfSespie FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
159588ddcfSespie for more details.
169588ddcfSespie 
179588ddcfSespie You should have received a copy of the GNU General Public License
189588ddcfSespie along with GCC; see the file COPYING.  If not, write to the Free
19*20fce977Smiod Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
20*20fce977Smiod 02110-1301, USA.  */
219588ddcfSespie 
229588ddcfSespie /*
239588ddcfSespie 
249588ddcfSespie @deftypefn Extension {const char*} make_relative_prefix (const char *@var{progname}, const char *@var{bin_prefix}, const char *@var{prefix})
259588ddcfSespie 
2632d62142Sespie Given three paths @var{progname}, @var{bin_prefix}, @var{prefix},
2732d62142Sespie return the path that is in the same position relative to
2832d62142Sespie @var{progname}'s directory as @var{prefix} is relative to
2932d62142Sespie @var{bin_prefix}.  That is, a string starting with the directory
3032d62142Sespie portion of @var{progname}, followed by a relative pathname of the
3132d62142Sespie difference between @var{bin_prefix} and @var{prefix}.
329588ddcfSespie 
3332d62142Sespie If @var{progname} does not contain any directory separators,
3432d62142Sespie @code{make_relative_prefix} will search @env{PATH} to find a program
3532d62142Sespie named @var{progname}.  Also, if @var{progname} is a symbolic link,
3632d62142Sespie the symbolic link will be resolved.
379588ddcfSespie 
3832d62142Sespie For example, if @var{bin_prefix} is @code{/alpha/beta/gamma/gcc/delta},
3932d62142Sespie @var{prefix} is @code{/alpha/beta/gamma/omega/}, and @var{progname} is
4032d62142Sespie @code{/red/green/blue/gcc}, then this function will return
4132d62142Sespie @code{/red/green/blue/../../omega/}.
4232d62142Sespie 
4332d62142Sespie The return value is normally allocated via @code{malloc}.  If no
4432d62142Sespie relative prefix can be found, return @code{NULL}.
459588ddcfSespie 
469588ddcfSespie @end deftypefn
479588ddcfSespie 
489588ddcfSespie */
499588ddcfSespie 
509588ddcfSespie #ifdef HAVE_CONFIG_H
519588ddcfSespie #include "config.h"
529588ddcfSespie #endif
539588ddcfSespie 
549588ddcfSespie #ifdef HAVE_STDLIB_H
559588ddcfSespie #include <stdlib.h>
569588ddcfSespie #endif
579588ddcfSespie #ifdef HAVE_UNISTD_H
589588ddcfSespie #include <unistd.h>
599588ddcfSespie #endif
609588ddcfSespie 
619588ddcfSespie #include <string.h>
629588ddcfSespie 
639588ddcfSespie #include "ansidecl.h"
649588ddcfSespie #include "libiberty.h"
659588ddcfSespie 
669588ddcfSespie #ifndef R_OK
679588ddcfSespie #define R_OK 4
689588ddcfSespie #define W_OK 2
699588ddcfSespie #define X_OK 1
709588ddcfSespie #endif
719588ddcfSespie 
729588ddcfSespie #ifndef DIR_SEPARATOR
739588ddcfSespie #  define DIR_SEPARATOR '/'
749588ddcfSespie #endif
759588ddcfSespie 
769588ddcfSespie #if defined (_WIN32) || defined (__MSDOS__) \
779588ddcfSespie     || defined (__DJGPP__) || defined (__OS2__)
789588ddcfSespie #  define HAVE_DOS_BASED_FILE_SYSTEM
799588ddcfSespie #  define HAVE_HOST_EXECUTABLE_SUFFIX
809588ddcfSespie #  define HOST_EXECUTABLE_SUFFIX ".exe"
819588ddcfSespie #  ifndef DIR_SEPARATOR_2
829588ddcfSespie #    define DIR_SEPARATOR_2 '\\'
839588ddcfSespie #  endif
849588ddcfSespie #  define PATH_SEPARATOR ';'
859588ddcfSespie #else
869588ddcfSespie #  define PATH_SEPARATOR ':'
879588ddcfSespie #endif
889588ddcfSespie 
899588ddcfSespie #ifndef DIR_SEPARATOR_2
909588ddcfSespie #  define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
919588ddcfSespie #else
929588ddcfSespie #  define IS_DIR_SEPARATOR(ch) \
939588ddcfSespie 	(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
949588ddcfSespie #endif
959588ddcfSespie 
969588ddcfSespie #define DIR_UP ".."
979588ddcfSespie 
98*20fce977Smiod static char *save_string (const char *, int);
99*20fce977Smiod static char **split_directories	(const char *, int *);
100*20fce977Smiod static void free_split_directories (char **);
1019588ddcfSespie 
1029588ddcfSespie static char *
save_string(const char * s,int len)103*20fce977Smiod save_string (const char *s, int len)
1049588ddcfSespie {
105*20fce977Smiod   char *result = (char *) malloc (len + 1);
1069588ddcfSespie 
1079588ddcfSespie   memcpy (result, s, len);
1089588ddcfSespie   result[len] = 0;
1099588ddcfSespie   return result;
1109588ddcfSespie }
1119588ddcfSespie 
1129588ddcfSespie /* Split a filename into component directories.  */
1139588ddcfSespie 
1149588ddcfSespie static char **
split_directories(const char * name,int * ptr_num_dirs)115*20fce977Smiod split_directories (const char *name, int *ptr_num_dirs)
1169588ddcfSespie {
1179588ddcfSespie   int num_dirs = 0;
1189588ddcfSespie   char **dirs;
1199588ddcfSespie   const char *p, *q;
1209588ddcfSespie   int ch;
1219588ddcfSespie 
1229588ddcfSespie   /* Count the number of directories.  Special case MSDOS disk names as part
1239588ddcfSespie      of the initial directory.  */
1249588ddcfSespie   p = name;
1259588ddcfSespie #ifdef HAVE_DOS_BASED_FILE_SYSTEM
1269588ddcfSespie   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
1279588ddcfSespie     {
1289588ddcfSespie       p += 3;
1299588ddcfSespie       num_dirs++;
1309588ddcfSespie     }
1319588ddcfSespie #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
1329588ddcfSespie 
1339588ddcfSespie   while ((ch = *p++) != '\0')
1349588ddcfSespie     {
1359588ddcfSespie       if (IS_DIR_SEPARATOR (ch))
1369588ddcfSespie 	{
1379588ddcfSespie 	  num_dirs++;
1389588ddcfSespie 	  while (IS_DIR_SEPARATOR (*p))
1399588ddcfSespie 	    p++;
1409588ddcfSespie 	}
1419588ddcfSespie     }
1429588ddcfSespie 
1439588ddcfSespie   dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2));
1449588ddcfSespie   if (dirs == NULL)
1459588ddcfSespie     return NULL;
1469588ddcfSespie 
1479588ddcfSespie   /* Now copy the directory parts.  */
1489588ddcfSespie   num_dirs = 0;
1499588ddcfSespie   p = name;
1509588ddcfSespie #ifdef HAVE_DOS_BASED_FILE_SYSTEM
1519588ddcfSespie   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
1529588ddcfSespie     {
1539588ddcfSespie       dirs[num_dirs++] = save_string (p, 3);
1549588ddcfSespie       if (dirs[num_dirs - 1] == NULL)
1559588ddcfSespie 	{
1569588ddcfSespie 	  free (dirs);
1579588ddcfSespie 	  return NULL;
1589588ddcfSespie 	}
1599588ddcfSespie       p += 3;
1609588ddcfSespie     }
1619588ddcfSespie #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
1629588ddcfSespie 
1639588ddcfSespie   q = p;
1649588ddcfSespie   while ((ch = *p++) != '\0')
1659588ddcfSespie     {
1669588ddcfSespie       if (IS_DIR_SEPARATOR (ch))
1679588ddcfSespie 	{
1689588ddcfSespie 	  while (IS_DIR_SEPARATOR (*p))
1699588ddcfSespie 	    p++;
1709588ddcfSespie 
1719588ddcfSespie 	  dirs[num_dirs++] = save_string (q, p - q);
1729588ddcfSespie 	  if (dirs[num_dirs - 1] == NULL)
1739588ddcfSespie 	    {
1749588ddcfSespie 	      dirs[num_dirs] = NULL;
1759588ddcfSespie 	      free_split_directories (dirs);
1769588ddcfSespie 	      return NULL;
1779588ddcfSespie 	    }
1789588ddcfSespie 	  q = p;
1799588ddcfSespie 	}
1809588ddcfSespie     }
1819588ddcfSespie 
1829588ddcfSespie   if (p - 1 - q > 0)
1839588ddcfSespie     dirs[num_dirs++] = save_string (q, p - 1 - q);
1849588ddcfSespie   dirs[num_dirs] = NULL;
1859588ddcfSespie 
1869588ddcfSespie   if (dirs[num_dirs - 1] == NULL)
1879588ddcfSespie     {
1889588ddcfSespie       free_split_directories (dirs);
1899588ddcfSespie       return NULL;
1909588ddcfSespie     }
1919588ddcfSespie 
1929588ddcfSespie   if (ptr_num_dirs)
1939588ddcfSespie     *ptr_num_dirs = num_dirs;
1949588ddcfSespie   return dirs;
1959588ddcfSespie }
1969588ddcfSespie 
1979588ddcfSespie /* Release storage held by split directories.  */
1989588ddcfSespie 
1999588ddcfSespie static void
free_split_directories(char ** dirs)200*20fce977Smiod free_split_directories (char **dirs)
2019588ddcfSespie {
2029588ddcfSespie   int i = 0;
2039588ddcfSespie 
2049588ddcfSespie   while (dirs[i] != NULL)
2059588ddcfSespie     free (dirs[i++]);
2069588ddcfSespie 
2079588ddcfSespie   free ((char *) dirs);
2089588ddcfSespie }
2099588ddcfSespie 
2109588ddcfSespie /* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets
2119588ddcfSespie    to PREFIX starting with the directory portion of PROGNAME and a relative
2129588ddcfSespie    pathname of the difference between BIN_PREFIX and PREFIX.
2139588ddcfSespie 
2149588ddcfSespie    For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is
2159588ddcfSespie    /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this
2169588ddcfSespie    function will return /red/green/blue/../../omega/.
2179588ddcfSespie 
2189588ddcfSespie    If no relative prefix can be found, return NULL.  */
2199588ddcfSespie 
2209588ddcfSespie char *
make_relative_prefix(const char * progname,const char * bin_prefix,const char * prefix)221*20fce977Smiod make_relative_prefix (const char *progname,
222*20fce977Smiod                       const char *bin_prefix, const char *prefix)
2239588ddcfSespie {
2249588ddcfSespie   char **prog_dirs, **bin_dirs, **prefix_dirs;
2259588ddcfSespie   int prog_num, bin_num, prefix_num;
2269588ddcfSespie   int i, n, common;
2279588ddcfSespie   int needed_len;
22832d62142Sespie   char *ret, *ptr, *full_progname = NULL;
2299588ddcfSespie 
2309588ddcfSespie   if (progname == NULL || bin_prefix == NULL || prefix == NULL)
2319588ddcfSespie     return NULL;
2329588ddcfSespie 
2339588ddcfSespie   /* If there is no full pathname, try to find the program by checking in each
2349588ddcfSespie      of the directories specified in the PATH environment variable.  */
23532d62142Sespie   if (lbasename (progname) == progname)
2369588ddcfSespie     {
2379588ddcfSespie       char *temp;
2389588ddcfSespie 
2399588ddcfSespie       temp = getenv ("PATH");
2409588ddcfSespie       if (temp)
2419588ddcfSespie 	{
2429588ddcfSespie 	  char *startp, *endp, *nstore;
2439588ddcfSespie 	  size_t prefixlen = strlen (temp) + 1;
2449588ddcfSespie 	  if (prefixlen < 2)
2459588ddcfSespie 	    prefixlen = 2;
2469588ddcfSespie 
2479588ddcfSespie 	  nstore = (char *) alloca (prefixlen + strlen (progname) + 1);
2489588ddcfSespie 
2499588ddcfSespie 	  startp = endp = temp;
2509588ddcfSespie 	  while (1)
2519588ddcfSespie 	    {
2529588ddcfSespie 	      if (*endp == PATH_SEPARATOR || *endp == 0)
2539588ddcfSespie 		{
2549588ddcfSespie 		  if (endp == startp)
2559588ddcfSespie 		    {
2569588ddcfSespie 		      nstore[0] = '.';
2579588ddcfSespie 		      nstore[1] = DIR_SEPARATOR;
2589588ddcfSespie 		      nstore[2] = '\0';
2599588ddcfSespie 		    }
2609588ddcfSespie 		  else
2619588ddcfSespie 		    {
2629588ddcfSespie 		      strncpy (nstore, startp, endp - startp);
2639588ddcfSespie 		      if (! IS_DIR_SEPARATOR (endp[-1]))
2649588ddcfSespie 			{
2659588ddcfSespie 			  nstore[endp - startp] = DIR_SEPARATOR;
2669588ddcfSespie 			  nstore[endp - startp + 1] = 0;
2679588ddcfSespie 			}
2689588ddcfSespie 		      else
2699588ddcfSespie 			nstore[endp - startp] = 0;
2709588ddcfSespie 		    }
2719588ddcfSespie 		  strcat (nstore, progname);
2729588ddcfSespie 		  if (! access (nstore, X_OK)
2739588ddcfSespie #ifdef HAVE_HOST_EXECUTABLE_SUFFIX
2749588ddcfSespie                       || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
2759588ddcfSespie #endif
2769588ddcfSespie 		      )
2779588ddcfSespie 		    {
2789588ddcfSespie 		      progname = nstore;
2799588ddcfSespie 		      break;
2809588ddcfSespie 		    }
2819588ddcfSespie 
2829588ddcfSespie 		  if (*endp == 0)
2839588ddcfSespie 		    break;
2849588ddcfSespie 		  endp = startp = endp + 1;
2859588ddcfSespie 		}
2869588ddcfSespie 	      else
2879588ddcfSespie 		endp++;
2889588ddcfSespie 	    }
2899588ddcfSespie 	}
2909588ddcfSespie     }
2919588ddcfSespie 
29232d62142Sespie   full_progname = lrealpath (progname);
29332d62142Sespie   if (full_progname == NULL)
29432d62142Sespie     return NULL;
29532d62142Sespie 
29632d62142Sespie   prog_dirs = split_directories (full_progname, &prog_num);
29732d62142Sespie   bin_dirs = split_directories (bin_prefix, &bin_num);
29832d62142Sespie   free (full_progname);
29932d62142Sespie   if (bin_dirs == NULL || prog_dirs == NULL)
30032d62142Sespie     return NULL;
30132d62142Sespie 
3029588ddcfSespie   /* Remove the program name from comparison of directory names.  */
3039588ddcfSespie   prog_num--;
3049588ddcfSespie 
3059588ddcfSespie   /* If we are still installed in the standard location, we don't need to
3069588ddcfSespie      specify relative directories.  Also, if argv[0] still doesn't contain
3079588ddcfSespie      any directory specifiers after the search above, then there is not much
3089588ddcfSespie      we can do.  */
3099588ddcfSespie   if (prog_num == bin_num)
3109588ddcfSespie     {
3119588ddcfSespie       for (i = 0; i < bin_num; i++)
3129588ddcfSespie 	{
3139588ddcfSespie 	  if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
3149588ddcfSespie 	    break;
3159588ddcfSespie 	}
3169588ddcfSespie 
3179588ddcfSespie       if (prog_num <= 0 || i == bin_num)
3189588ddcfSespie 	{
3199588ddcfSespie 	  free_split_directories (prog_dirs);
3209588ddcfSespie 	  free_split_directories (bin_dirs);
3219588ddcfSespie 	  prog_dirs = bin_dirs = (char **) 0;
3229588ddcfSespie 	  return NULL;
3239588ddcfSespie 	}
3249588ddcfSespie     }
3259588ddcfSespie 
3269588ddcfSespie   prefix_dirs = split_directories (prefix, &prefix_num);
3279588ddcfSespie   if (prefix_dirs == NULL)
3289588ddcfSespie     {
3299588ddcfSespie       free_split_directories (prog_dirs);
3309588ddcfSespie       free_split_directories (bin_dirs);
3319588ddcfSespie       return NULL;
3329588ddcfSespie     }
3339588ddcfSespie 
3349588ddcfSespie   /* Find how many directories are in common between bin_prefix & prefix.  */
3359588ddcfSespie   n = (prefix_num < bin_num) ? prefix_num : bin_num;
3369588ddcfSespie   for (common = 0; common < n; common++)
3379588ddcfSespie     {
3389588ddcfSespie       if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
3399588ddcfSespie 	break;
3409588ddcfSespie     }
3419588ddcfSespie 
3429588ddcfSespie   /* If there are no common directories, there can be no relative prefix.  */
3439588ddcfSespie   if (common == 0)
3449588ddcfSespie     {
3459588ddcfSespie       free_split_directories (prog_dirs);
3469588ddcfSespie       free_split_directories (bin_dirs);
3479588ddcfSespie       free_split_directories (prefix_dirs);
3489588ddcfSespie       return NULL;
3499588ddcfSespie     }
3509588ddcfSespie 
3519588ddcfSespie   /* Two passes: first figure out the size of the result string, and
3529588ddcfSespie      then construct it.  */
3539588ddcfSespie   needed_len = 0;
3549588ddcfSespie   for (i = 0; i < prog_num; i++)
3559588ddcfSespie     needed_len += strlen (prog_dirs[i]);
3569588ddcfSespie   needed_len += sizeof (DIR_UP) * (bin_num - common);
3579588ddcfSespie   for (i = common; i < prefix_num; i++)
3589588ddcfSespie     needed_len += strlen (prefix_dirs[i]);
3599588ddcfSespie   needed_len += 1; /* Trailing NUL.  */
3609588ddcfSespie 
3619588ddcfSespie   ret = (char *) malloc (needed_len);
3629588ddcfSespie   if (ret == NULL)
3639588ddcfSespie     return NULL;
3649588ddcfSespie 
3659588ddcfSespie   /* Build up the pathnames in argv[0].  */
3669588ddcfSespie   *ret = '\0';
3679588ddcfSespie   for (i = 0; i < prog_num; i++)
3689588ddcfSespie     strcat (ret, prog_dirs[i]);
3699588ddcfSespie 
3709588ddcfSespie   /* Now build up the ..'s.  */
3719588ddcfSespie   ptr = ret + strlen(ret);
3729588ddcfSespie   for (i = common; i < bin_num; i++)
3739588ddcfSespie     {
3749588ddcfSespie       strcpy (ptr, DIR_UP);
3759588ddcfSespie       ptr += sizeof (DIR_UP) - 1;
3769588ddcfSespie       *(ptr++) = DIR_SEPARATOR;
3779588ddcfSespie     }
3789588ddcfSespie   *ptr = '\0';
3799588ddcfSespie 
3809588ddcfSespie   /* Put in directories to move over to prefix.  */
3819588ddcfSespie   for (i = common; i < prefix_num; i++)
3829588ddcfSespie     strcat (ret, prefix_dirs[i]);
3839588ddcfSespie 
3849588ddcfSespie   free_split_directories (prog_dirs);
3859588ddcfSespie   free_split_directories (bin_dirs);
3869588ddcfSespie   free_split_directories (prefix_dirs);
3879588ddcfSespie 
3889588ddcfSespie   return ret;
3899588ddcfSespie }
390