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