1/* Relative (relocatable) prefix support.
2   Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
3   1999, 2000, 2001, 2002, 2006, 2012 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#ifdef HAVE_SYS_STAT_H
62#include <sys/stat.h>
63#endif
64
65#include <string.h>
66
67#include "ansidecl.h"
68#include "libiberty.h"
69
70#ifndef R_OK
71#define R_OK 4
72#define W_OK 2
73#define X_OK 1
74#endif
75
76#ifndef DIR_SEPARATOR
77#  define DIR_SEPARATOR '/'
78#endif
79
80#if defined (_WIN32) || defined (__MSDOS__) \
81    || defined (__DJGPP__) || defined (__OS2__)
82#  define HAVE_DOS_BASED_FILE_SYSTEM
83#  define HAVE_HOST_EXECUTABLE_SUFFIX
84#  define HOST_EXECUTABLE_SUFFIX ".exe"
85#  ifndef DIR_SEPARATOR_2
86#    define DIR_SEPARATOR_2 '\\'
87#  endif
88#  define PATH_SEPARATOR ';'
89#else
90#  define PATH_SEPARATOR ':'
91#endif
92
93#ifndef DIR_SEPARATOR_2
94#  define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
95#else
96#  define IS_DIR_SEPARATOR(ch) \
97	(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
98#endif
99
100#define DIR_UP ".."
101
102static char *save_string (const char *, int);
103static char **split_directories	(const char *, int *);
104static void free_split_directories (char **);
105
106static char *
107save_string (const char *s, int len)
108{
109  char *result = (char *) malloc (len + 1);
110
111  memcpy (result, s, len);
112  result[len] = 0;
113  return result;
114}
115
116/* Split a filename into component directories.  */
117
118static char **
119split_directories (const char *name, int *ptr_num_dirs)
120{
121  int num_dirs = 0;
122  char **dirs;
123  const char *p, *q;
124  int ch;
125
126  /* Count the number of directories.  Special case MSDOS disk names as part
127     of the initial directory.  */
128  p = name;
129#ifdef HAVE_DOS_BASED_FILE_SYSTEM
130  if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
131    {
132      p += 3;
133      num_dirs++;
134    }
135#endif /* HAVE_DOS_BASED_FILE_SYSTEM */
136
137  while ((ch = *p++) != '\0')
138    {
139      if (IS_DIR_SEPARATOR (ch))
140	{
141	  num_dirs++;
142	  while (IS_DIR_SEPARATOR (*p))
143	    p++;
144	}
145    }
146
147  dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2));
148  if (dirs == NULL)
149    return NULL;
150
151  /* Now copy the directory parts.  */
152  num_dirs = 0;
153  p = name;
154#ifdef HAVE_DOS_BASED_FILE_SYSTEM
155  if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
156    {
157      dirs[num_dirs++] = save_string (p, 3);
158      if (dirs[num_dirs - 1] == NULL)
159	{
160	  free (dirs);
161	  return NULL;
162	}
163      p += 3;
164    }
165#endif /* HAVE_DOS_BASED_FILE_SYSTEM */
166
167  q = p;
168  while ((ch = *p++) != '\0')
169    {
170      if (IS_DIR_SEPARATOR (ch))
171	{
172	  while (IS_DIR_SEPARATOR (*p))
173	    p++;
174
175	  dirs[num_dirs++] = save_string (q, p - q);
176	  if (dirs[num_dirs - 1] == NULL)
177	    {
178	      dirs[num_dirs] = NULL;
179	      free_split_directories (dirs);
180	      return NULL;
181	    }
182	  q = p;
183	}
184    }
185
186  if (p - 1 - q > 0)
187    dirs[num_dirs++] = save_string (q, p - 1 - q);
188  dirs[num_dirs] = NULL;
189
190  if (dirs[num_dirs - 1] == NULL)
191    {
192      free_split_directories (dirs);
193      return NULL;
194    }
195
196  if (ptr_num_dirs)
197    *ptr_num_dirs = num_dirs;
198  return dirs;
199}
200
201/* Release storage held by split directories.  */
202
203static void
204free_split_directories (char **dirs)
205{
206  int i = 0;
207
208  if (dirs != NULL)
209    {
210      while (dirs[i] != NULL)
211	free (dirs[i++]);
212
213      free ((char *) dirs);
214    }
215}
216
217/* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets
218   to PREFIX starting with the directory portion of PROGNAME and a relative
219   pathname of the difference between BIN_PREFIX and PREFIX.
220
221   For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is
222   /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this
223   function will return /red/green/blue/../../omega/.
224
225   If no relative prefix can be found, return NULL.  */
226
227static char *
228make_relative_prefix_1 (const char *progname, const char *bin_prefix,
229			const char *prefix, const int resolve_links)
230{
231  char **prog_dirs = NULL, **bin_dirs = NULL, **prefix_dirs = NULL;
232  int prog_num, bin_num, prefix_num;
233  int i, n, common;
234  int needed_len;
235  char *ret = NULL, *ptr, *full_progname;
236
237  if (progname == NULL || bin_prefix == NULL || prefix == NULL)
238    return NULL;
239
240  /* If there is no full pathname, try to find the program by checking in each
241     of the directories specified in the PATH environment variable.  */
242  if (lbasename (progname) == progname)
243    {
244      char *temp;
245
246      temp = getenv ("PATH");
247      if (temp)
248	{
249	  char *startp, *endp, *nstore;
250	  size_t prefixlen = strlen (temp) + 1;
251	  size_t len;
252	  if (prefixlen < 2)
253	    prefixlen = 2;
254
255	  len = prefixlen + strlen (progname) + 1;
256#ifdef HAVE_HOST_EXECUTABLE_SUFFIX
257	  len += strlen (HOST_EXECUTABLE_SUFFIX);
258#endif
259	  nstore = (char *) alloca (len);
260
261	  startp = endp = temp;
262	  while (1)
263	    {
264	      if (*endp == PATH_SEPARATOR || *endp == 0)
265		{
266		  if (endp == startp)
267		    {
268		      nstore[0] = '.';
269		      nstore[1] = DIR_SEPARATOR;
270		      nstore[2] = '\0';
271		    }
272		  else
273		    {
274		      memcpy (nstore, startp, endp - startp);
275		      if (! IS_DIR_SEPARATOR (endp[-1]))
276			{
277			  nstore[endp - startp] = DIR_SEPARATOR;
278			  nstore[endp - startp + 1] = 0;
279			}
280		      else
281			nstore[endp - startp] = 0;
282		    }
283		  strcat (nstore, progname);
284		  if (! access (nstore, X_OK)
285#ifdef HAVE_HOST_EXECUTABLE_SUFFIX
286                      || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
287#endif
288		      )
289		    {
290#if defined (HAVE_SYS_STAT_H) && defined (S_ISREG)
291		      struct stat st;
292		      if (stat (nstore, &st) >= 0 && S_ISREG (st.st_mode))
293#endif
294			{
295			  progname = nstore;
296			  break;
297			}
298		    }
299
300		  if (*endp == 0)
301		    break;
302		  endp = startp = endp + 1;
303		}
304	      else
305		endp++;
306	    }
307	}
308    }
309
310  if (resolve_links)
311    full_progname = lrealpath (progname);
312  else
313    full_progname = strdup (progname);
314  if (full_progname == NULL)
315    return NULL;
316
317  prog_dirs = split_directories (full_progname, &prog_num);
318  free (full_progname);
319  if (prog_dirs == NULL)
320    return NULL;
321
322  bin_dirs = split_directories (bin_prefix, &bin_num);
323  if (bin_dirs == NULL)
324    goto bailout;
325
326  /* Remove the program name from comparison of directory names.  */
327  prog_num--;
328
329  /* If we are still installed in the standard location, we don't need to
330     specify relative directories.  Also, if argv[0] still doesn't contain
331     any directory specifiers after the search above, then there is not much
332     we can do.  */
333  if (prog_num == bin_num)
334    {
335      for (i = 0; i < bin_num; i++)
336	{
337	  if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
338	    break;
339	}
340
341      if (prog_num <= 0 || i == bin_num)
342	goto bailout;
343    }
344
345  prefix_dirs = split_directories (prefix, &prefix_num);
346  if (prefix_dirs == NULL)
347    goto bailout;
348
349  /* Find how many directories are in common between bin_prefix & prefix.  */
350  n = (prefix_num < bin_num) ? prefix_num : bin_num;
351  for (common = 0; common < n; common++)
352    {
353      if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
354	break;
355    }
356
357  /* If there are no common directories, there can be no relative prefix.  */
358  if (common == 0)
359    goto bailout;
360
361  /* Two passes: first figure out the size of the result string, and
362     then construct it.  */
363  needed_len = 0;
364  for (i = 0; i < prog_num; i++)
365    needed_len += strlen (prog_dirs[i]);
366  needed_len += sizeof (DIR_UP) * (bin_num - common);
367  for (i = common; i < prefix_num; i++)
368    needed_len += strlen (prefix_dirs[i]);
369  needed_len += 1; /* Trailing NUL.  */
370
371  ret = (char *) malloc (needed_len);
372  if (ret == NULL)
373    goto bailout;
374
375  /* Build up the pathnames in argv[0].  */
376  *ret = '\0';
377  for (i = 0; i < prog_num; i++)
378    strcat (ret, prog_dirs[i]);
379
380  /* Now build up the ..'s.  */
381  ptr = ret + strlen(ret);
382  for (i = common; i < bin_num; i++)
383    {
384      strcpy (ptr, DIR_UP);
385      ptr += sizeof (DIR_UP) - 1;
386      *(ptr++) = DIR_SEPARATOR;
387    }
388  *ptr = '\0';
389
390  /* Put in directories to move over to prefix.  */
391  for (i = common; i < prefix_num; i++)
392    strcat (ret, prefix_dirs[i]);
393
394 bailout:
395  free_split_directories (prog_dirs);
396  free_split_directories (bin_dirs);
397  free_split_directories (prefix_dirs);
398
399  return ret;
400}
401
402
403/* Do the full job, including symlink resolution.
404   This path will find files installed in the same place as the
405   program even when a soft link has been made to the program
406   from somwhere else. */
407
408char *
409make_relative_prefix (const char *progname, const char *bin_prefix,
410		      const char *prefix)
411{
412  return make_relative_prefix_1 (progname, bin_prefix, prefix, 1);
413}
414
415/* Make the relative pathname without attempting to resolve any links.
416   '..' etc may also be left in the pathname.
417   This will find the files the user meant the program to find if the
418   installation is patched together with soft links. */
419
420char *
421make_relative_prefix_ignore_links (const char *progname,
422				   const char *bin_prefix,
423				   const char *prefix)
424{
425  return make_relative_prefix_1 (progname, bin_prefix, prefix, 0);
426}
427
428