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