1169695Skan/* Relative (relocatable) prefix support.
2169695Skan   Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
3169695Skan   1999, 2000, 2001, 2002 Free Software Foundation, Inc.
4169695Skan
5169695SkanThis file is part of libiberty.
6169695Skan
7169695SkanGCC is free software; you can redistribute it and/or modify it under
8169695Skanthe terms of the GNU General Public License as published by the Free
9169695SkanSoftware Foundation; either version 2, or (at your option) any later
10169695Skanversion.
11169695Skan
12169695SkanGCC is distributed in the hope that it will be useful, but WITHOUT ANY
13169695SkanWARRANTY; without even the implied warranty of MERCHANTABILITY or
14169695SkanFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15169695Skanfor more details.
16169695Skan
17169695SkanYou should have received a copy of the GNU General Public License
18169695Skanalong with GCC; see the file COPYING.  If not, write to the Free
19169695SkanSoftware Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
20169695Skan02110-1301, USA.  */
21169695Skan
22169695Skan/*
23169695Skan
24169695Skan@deftypefn Extension {const char*} make_relative_prefix (const char *@var{progname}, const char *@var{bin_prefix}, const char *@var{prefix})
25169695Skan
26169695SkanGiven three paths @var{progname}, @var{bin_prefix}, @var{prefix},
27169695Skanreturn the path that is in the same position relative to
28169695Skan@var{progname}'s directory as @var{prefix} is relative to
29169695Skan@var{bin_prefix}.  That is, a string starting with the directory
30169695Skanportion of @var{progname}, followed by a relative pathname of the
31169695Skandifference between @var{bin_prefix} and @var{prefix}.
32169695Skan
33169695SkanIf @var{progname} does not contain any directory separators,
34169695Skan@code{make_relative_prefix} will search @env{PATH} to find a program
35169695Skannamed @var{progname}.  Also, if @var{progname} is a symbolic link,
36169695Skanthe symbolic link will be resolved.
37169695Skan
38169695SkanFor example, if @var{bin_prefix} is @code{/alpha/beta/gamma/gcc/delta},
39169695Skan@var{prefix} is @code{/alpha/beta/gamma/omega/}, and @var{progname} is
40169695Skan@code{/red/green/blue/gcc}, then this function will return
41169695Skan@code{/red/green/blue/../../omega/}.
42169695Skan
43169695SkanThe return value is normally allocated via @code{malloc}.  If no
44169695Skanrelative prefix can be found, return @code{NULL}.
45169695Skan
46169695Skan@end deftypefn
47169695Skan
48169695Skan*/
49169695Skan
50169695Skan#ifdef HAVE_CONFIG_H
51169695Skan#include "config.h"
52169695Skan#endif
53169695Skan
54169695Skan#ifdef HAVE_STDLIB_H
55169695Skan#include <stdlib.h>
56169695Skan#endif
57169695Skan#ifdef HAVE_UNISTD_H
58169695Skan#include <unistd.h>
59169695Skan#endif
60169695Skan
61169695Skan#include <string.h>
62169695Skan
63169695Skan#include "ansidecl.h"
64169695Skan#include "libiberty.h"
65169695Skan
66169695Skan#ifndef R_OK
67169695Skan#define R_OK 4
68169695Skan#define W_OK 2
69169695Skan#define X_OK 1
70169695Skan#endif
71169695Skan
72169695Skan#ifndef DIR_SEPARATOR
73169695Skan#  define DIR_SEPARATOR '/'
74169695Skan#endif
75169695Skan
76169695Skan#if defined (_WIN32) || defined (__MSDOS__) \
77169695Skan    || defined (__DJGPP__) || defined (__OS2__)
78169695Skan#  define HAVE_DOS_BASED_FILE_SYSTEM
79169695Skan#  define HAVE_HOST_EXECUTABLE_SUFFIX
80169695Skan#  define HOST_EXECUTABLE_SUFFIX ".exe"
81169695Skan#  ifndef DIR_SEPARATOR_2
82169695Skan#    define DIR_SEPARATOR_2 '\\'
83169695Skan#  endif
84169695Skan#  define PATH_SEPARATOR ';'
85169695Skan#else
86169695Skan#  define PATH_SEPARATOR ':'
87169695Skan#endif
88169695Skan
89169695Skan#ifndef DIR_SEPARATOR_2
90169695Skan#  define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
91169695Skan#else
92169695Skan#  define IS_DIR_SEPARATOR(ch) \
93169695Skan	(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
94169695Skan#endif
95169695Skan
96169695Skan#define DIR_UP ".."
97169695Skan
98169695Skanstatic char *save_string (const char *, int);
99169695Skanstatic char **split_directories	(const char *, int *);
100169695Skanstatic void free_split_directories (char **);
101169695Skan
102169695Skanstatic char *
103169695Skansave_string (const char *s, int len)
104169695Skan{
105169695Skan  char *result = (char *) malloc (len + 1);
106169695Skan
107169695Skan  memcpy (result, s, len);
108169695Skan  result[len] = 0;
109169695Skan  return result;
110169695Skan}
111169695Skan
112169695Skan/* Split a filename into component directories.  */
113169695Skan
114169695Skanstatic char **
115169695Skansplit_directories (const char *name, int *ptr_num_dirs)
116169695Skan{
117169695Skan  int num_dirs = 0;
118169695Skan  char **dirs;
119169695Skan  const char *p, *q;
120169695Skan  int ch;
121169695Skan
122169695Skan  /* Count the number of directories.  Special case MSDOS disk names as part
123169695Skan     of the initial directory.  */
124169695Skan  p = name;
125169695Skan#ifdef HAVE_DOS_BASED_FILE_SYSTEM
126169695Skan  if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
127169695Skan    {
128169695Skan      p += 3;
129169695Skan      num_dirs++;
130169695Skan    }
131169695Skan#endif /* HAVE_DOS_BASED_FILE_SYSTEM */
132169695Skan
133169695Skan  while ((ch = *p++) != '\0')
134169695Skan    {
135169695Skan      if (IS_DIR_SEPARATOR (ch))
136169695Skan	{
137169695Skan	  num_dirs++;
138169695Skan	  while (IS_DIR_SEPARATOR (*p))
139169695Skan	    p++;
140169695Skan	}
141169695Skan    }
142169695Skan
143169695Skan  dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2));
144169695Skan  if (dirs == NULL)
145169695Skan    return NULL;
146169695Skan
147169695Skan  /* Now copy the directory parts.  */
148169695Skan  num_dirs = 0;
149169695Skan  p = name;
150169695Skan#ifdef HAVE_DOS_BASED_FILE_SYSTEM
151169695Skan  if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
152169695Skan    {
153169695Skan      dirs[num_dirs++] = save_string (p, 3);
154169695Skan      if (dirs[num_dirs - 1] == NULL)
155169695Skan	{
156169695Skan	  free (dirs);
157169695Skan	  return NULL;
158169695Skan	}
159169695Skan      p += 3;
160169695Skan    }
161169695Skan#endif /* HAVE_DOS_BASED_FILE_SYSTEM */
162169695Skan
163169695Skan  q = p;
164169695Skan  while ((ch = *p++) != '\0')
165169695Skan    {
166169695Skan      if (IS_DIR_SEPARATOR (ch))
167169695Skan	{
168169695Skan	  while (IS_DIR_SEPARATOR (*p))
169169695Skan	    p++;
170169695Skan
171169695Skan	  dirs[num_dirs++] = save_string (q, p - q);
172169695Skan	  if (dirs[num_dirs - 1] == NULL)
173169695Skan	    {
174169695Skan	      dirs[num_dirs] = NULL;
175169695Skan	      free_split_directories (dirs);
176169695Skan	      return NULL;
177169695Skan	    }
178169695Skan	  q = p;
179169695Skan	}
180169695Skan    }
181169695Skan
182169695Skan  if (p - 1 - q > 0)
183169695Skan    dirs[num_dirs++] = save_string (q, p - 1 - q);
184169695Skan  dirs[num_dirs] = NULL;
185169695Skan
186169695Skan  if (dirs[num_dirs - 1] == NULL)
187169695Skan    {
188169695Skan      free_split_directories (dirs);
189169695Skan      return NULL;
190169695Skan    }
191169695Skan
192169695Skan  if (ptr_num_dirs)
193169695Skan    *ptr_num_dirs = num_dirs;
194169695Skan  return dirs;
195169695Skan}
196169695Skan
197169695Skan/* Release storage held by split directories.  */
198169695Skan
199169695Skanstatic void
200169695Skanfree_split_directories (char **dirs)
201169695Skan{
202169695Skan  int i = 0;
203169695Skan
204169695Skan  while (dirs[i] != NULL)
205169695Skan    free (dirs[i++]);
206169695Skan
207169695Skan  free ((char *) dirs);
208169695Skan}
209169695Skan
210169695Skan/* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets
211169695Skan   to PREFIX starting with the directory portion of PROGNAME and a relative
212169695Skan   pathname of the difference between BIN_PREFIX and PREFIX.
213169695Skan
214169695Skan   For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is
215169695Skan   /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this
216169695Skan   function will return /red/green/blue/../../omega/.
217169695Skan
218169695Skan   If no relative prefix can be found, return NULL.  */
219169695Skan
220169695Skanchar *
221169695Skanmake_relative_prefix (const char *progname,
222169695Skan                      const char *bin_prefix, const char *prefix)
223169695Skan{
224169695Skan  char **prog_dirs, **bin_dirs, **prefix_dirs;
225169695Skan  int prog_num, bin_num, prefix_num;
226169695Skan  int i, n, common;
227169695Skan  int needed_len;
228169695Skan  char *ret, *ptr, *full_progname = NULL;
229169695Skan
230169695Skan  if (progname == NULL || bin_prefix == NULL || prefix == NULL)
231169695Skan    return NULL;
232169695Skan
233169695Skan  /* If there is no full pathname, try to find the program by checking in each
234169695Skan     of the directories specified in the PATH environment variable.  */
235169695Skan  if (lbasename (progname) == progname)
236169695Skan    {
237169695Skan      char *temp;
238169695Skan
239169695Skan      temp = getenv ("PATH");
240169695Skan      if (temp)
241169695Skan	{
242169695Skan	  char *startp, *endp, *nstore;
243169695Skan	  size_t prefixlen = strlen (temp) + 1;
244169695Skan	  if (prefixlen < 2)
245169695Skan	    prefixlen = 2;
246169695Skan
247169695Skan	  nstore = (char *) alloca (prefixlen + strlen (progname) + 1);
248169695Skan
249169695Skan	  startp = endp = temp;
250169695Skan	  while (1)
251169695Skan	    {
252169695Skan	      if (*endp == PATH_SEPARATOR || *endp == 0)
253169695Skan		{
254169695Skan		  if (endp == startp)
255169695Skan		    {
256169695Skan		      nstore[0] = '.';
257169695Skan		      nstore[1] = DIR_SEPARATOR;
258169695Skan		      nstore[2] = '\0';
259169695Skan		    }
260169695Skan		  else
261169695Skan		    {
262169695Skan		      strncpy (nstore, startp, endp - startp);
263169695Skan		      if (! IS_DIR_SEPARATOR (endp[-1]))
264169695Skan			{
265169695Skan			  nstore[endp - startp] = DIR_SEPARATOR;
266169695Skan			  nstore[endp - startp + 1] = 0;
267169695Skan			}
268169695Skan		      else
269169695Skan			nstore[endp - startp] = 0;
270169695Skan		    }
271169695Skan		  strcat (nstore, progname);
272169695Skan		  if (! access (nstore, X_OK)
273169695Skan#ifdef HAVE_HOST_EXECUTABLE_SUFFIX
274169695Skan                      || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
275169695Skan#endif
276169695Skan		      )
277169695Skan		    {
278169695Skan		      progname = nstore;
279169695Skan		      break;
280169695Skan		    }
281169695Skan
282169695Skan		  if (*endp == 0)
283169695Skan		    break;
284169695Skan		  endp = startp = endp + 1;
285169695Skan		}
286169695Skan	      else
287169695Skan		endp++;
288169695Skan	    }
289169695Skan	}
290169695Skan    }
291169695Skan
292169695Skan  full_progname = lrealpath (progname);
293169695Skan  if (full_progname == NULL)
294169695Skan    return NULL;
295169695Skan
296169695Skan  prog_dirs = split_directories (full_progname, &prog_num);
297169695Skan  bin_dirs = split_directories (bin_prefix, &bin_num);
298169695Skan  free (full_progname);
299169695Skan  if (bin_dirs == NULL || prog_dirs == NULL)
300169695Skan    return NULL;
301169695Skan
302169695Skan  /* Remove the program name from comparison of directory names.  */
303169695Skan  prog_num--;
304169695Skan
305169695Skan  /* If we are still installed in the standard location, we don't need to
306169695Skan     specify relative directories.  Also, if argv[0] still doesn't contain
307169695Skan     any directory specifiers after the search above, then there is not much
308169695Skan     we can do.  */
309169695Skan  if (prog_num == bin_num)
310169695Skan    {
311169695Skan      for (i = 0; i < bin_num; i++)
312169695Skan	{
313169695Skan	  if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
314169695Skan	    break;
315169695Skan	}
316169695Skan
317169695Skan      if (prog_num <= 0 || i == bin_num)
318169695Skan	{
319169695Skan	  free_split_directories (prog_dirs);
320169695Skan	  free_split_directories (bin_dirs);
321169695Skan	  prog_dirs = bin_dirs = (char **) 0;
322169695Skan	  return NULL;
323169695Skan	}
324169695Skan    }
325169695Skan
326169695Skan  prefix_dirs = split_directories (prefix, &prefix_num);
327169695Skan  if (prefix_dirs == NULL)
328169695Skan    {
329169695Skan      free_split_directories (prog_dirs);
330169695Skan      free_split_directories (bin_dirs);
331169695Skan      return NULL;
332169695Skan    }
333169695Skan
334169695Skan  /* Find how many directories are in common between bin_prefix & prefix.  */
335169695Skan  n = (prefix_num < bin_num) ? prefix_num : bin_num;
336169695Skan  for (common = 0; common < n; common++)
337169695Skan    {
338169695Skan      if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
339169695Skan	break;
340169695Skan    }
341169695Skan
342169695Skan  /* If there are no common directories, there can be no relative prefix.  */
343169695Skan  if (common == 0)
344169695Skan    {
345169695Skan      free_split_directories (prog_dirs);
346169695Skan      free_split_directories (bin_dirs);
347169695Skan      free_split_directories (prefix_dirs);
348169695Skan      return NULL;
349169695Skan    }
350169695Skan
351169695Skan  /* Two passes: first figure out the size of the result string, and
352169695Skan     then construct it.  */
353169695Skan  needed_len = 0;
354169695Skan  for (i = 0; i < prog_num; i++)
355169695Skan    needed_len += strlen (prog_dirs[i]);
356169695Skan  needed_len += sizeof (DIR_UP) * (bin_num - common);
357169695Skan  for (i = common; i < prefix_num; i++)
358169695Skan    needed_len += strlen (prefix_dirs[i]);
359169695Skan  needed_len += 1; /* Trailing NUL.  */
360169695Skan
361169695Skan  ret = (char *) malloc (needed_len);
362169695Skan  if (ret == NULL)
363169695Skan    return NULL;
364169695Skan
365169695Skan  /* Build up the pathnames in argv[0].  */
366169695Skan  *ret = '\0';
367169695Skan  for (i = 0; i < prog_num; i++)
368169695Skan    strcat (ret, prog_dirs[i]);
369169695Skan
370169695Skan  /* Now build up the ..'s.  */
371169695Skan  ptr = ret + strlen(ret);
372169695Skan  for (i = common; i < bin_num; i++)
373169695Skan    {
374169695Skan      strcpy (ptr, DIR_UP);
375169695Skan      ptr += sizeof (DIR_UP) - 1;
376169695Skan      *(ptr++) = DIR_SEPARATOR;
377169695Skan    }
378169695Skan  *ptr = '\0';
379169695Skan
380169695Skan  /* Put in directories to move over to prefix.  */
381169695Skan  for (i = common; i < prefix_num; i++)
382169695Skan    strcat (ret, prefix_dirs[i]);
383169695Skan
384169695Skan  free_split_directories (prog_dirs);
385169695Skan  free_split_directories (bin_dirs);
386169695Skan  free_split_directories (prefix_dirs);
387169695Skan
388169695Skan  return ret;
389169695Skan}
390