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  char *alloc_ptr = NULL;
237
238  if (progname == NULL || bin_prefix == NULL || prefix == NULL)
239    return NULL;
240
241  /* If there is no full pathname, try to find the program by checking in each
242     of the directories specified in the PATH environment variable.  */
243  if (lbasename (progname) == progname)
244    {
245      char *temp;
246
247      temp = getenv ("PATH");
248      if (temp)
249	{
250	  char *startp, *endp, *nstore;
251	  size_t prefixlen = strlen (temp) + 1;
252	  size_t len;
253	  if (prefixlen < 2)
254	    prefixlen = 2;
255
256	  len = prefixlen + strlen (progname) + 1;
257#ifdef HAVE_HOST_EXECUTABLE_SUFFIX
258	  len += strlen (HOST_EXECUTABLE_SUFFIX);
259#endif
260	  if (len < MAX_ALLOCA_SIZE)
261	    nstore = (char *) alloca (len);
262	  else
263	    alloc_ptr = nstore = (char *) malloc (len);
264
265	  startp = endp = temp;
266	  while (1)
267	    {
268	      if (*endp == PATH_SEPARATOR || *endp == 0)
269		{
270		  if (endp == startp)
271		    {
272		      nstore[0] = '.';
273		      nstore[1] = DIR_SEPARATOR;
274		      nstore[2] = '\0';
275		    }
276		  else
277		    {
278		      memcpy (nstore, startp, endp - startp);
279		      if (! IS_DIR_SEPARATOR (endp[-1]))
280			{
281			  nstore[endp - startp] = DIR_SEPARATOR;
282			  nstore[endp - startp + 1] = 0;
283			}
284		      else
285			nstore[endp - startp] = 0;
286		    }
287		  strcat (nstore, progname);
288		  if (! access (nstore, X_OK)
289#ifdef HAVE_HOST_EXECUTABLE_SUFFIX
290                      || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
291#endif
292		      )
293		    {
294#if defined (HAVE_SYS_STAT_H) && defined (S_ISREG)
295		      struct stat st;
296		      if (stat (nstore, &st) >= 0 && S_ISREG (st.st_mode))
297#endif
298			{
299			  progname = nstore;
300			  break;
301			}
302		    }
303
304		  if (*endp == 0)
305		    break;
306		  endp = startp = endp + 1;
307		}
308	      else
309		endp++;
310	    }
311	}
312    }
313
314  if (resolve_links)
315    full_progname = lrealpath (progname);
316  else
317    full_progname = strdup (progname);
318  if (full_progname == NULL)
319    goto bailout;
320
321  prog_dirs = split_directories (full_progname, &prog_num);
322  free (full_progname);
323  if (prog_dirs == NULL)
324    goto bailout;
325
326  bin_dirs = split_directories (bin_prefix, &bin_num);
327  if (bin_dirs == NULL)
328    goto bailout;
329
330  /* Remove the program name from comparison of directory names.  */
331  prog_num--;
332
333  /* If we are still installed in the standard location, we don't need to
334     specify relative directories.  Also, if argv[0] still doesn't contain
335     any directory specifiers after the search above, then there is not much
336     we can do.  */
337  if (prog_num == bin_num)
338    {
339      for (i = 0; i < bin_num; i++)
340	{
341	  if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
342	    break;
343	}
344
345      if (prog_num <= 0 || i == bin_num)
346	goto bailout;
347    }
348
349  prefix_dirs = split_directories (prefix, &prefix_num);
350  if (prefix_dirs == NULL)
351    goto bailout;
352
353  /* Find how many directories are in common between bin_prefix & prefix.  */
354  n = (prefix_num < bin_num) ? prefix_num : bin_num;
355  for (common = 0; common < n; common++)
356    {
357      if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
358	break;
359    }
360
361  /* If there are no common directories, there can be no relative prefix.  */
362  if (common == 0)
363    goto bailout;
364
365  /* Two passes: first figure out the size of the result string, and
366     then construct it.  */
367  needed_len = 0;
368  for (i = 0; i < prog_num; i++)
369    needed_len += strlen (prog_dirs[i]);
370  needed_len += sizeof (DIR_UP) * (bin_num - common);
371  for (i = common; i < prefix_num; i++)
372    needed_len += strlen (prefix_dirs[i]);
373  needed_len += 1; /* Trailing NUL.  */
374
375  ret = (char *) malloc (needed_len);
376  if (ret == NULL)
377    goto bailout;
378
379  /* Build up the pathnames in argv[0].  */
380  *ret = '\0';
381  for (i = 0; i < prog_num; i++)
382    strcat (ret, prog_dirs[i]);
383
384  /* Now build up the ..'s.  */
385  ptr = ret + strlen(ret);
386  for (i = common; i < bin_num; i++)
387    {
388      strcpy (ptr, DIR_UP);
389      ptr += sizeof (DIR_UP) - 1;
390      *(ptr++) = DIR_SEPARATOR;
391    }
392  *ptr = '\0';
393
394  /* Put in directories to move over to prefix.  */
395  for (i = common; i < prefix_num; i++)
396    strcat (ret, prefix_dirs[i]);
397
398 bailout:
399  free_split_directories (prog_dirs);
400  free_split_directories (bin_dirs);
401  free_split_directories (prefix_dirs);
402  free (alloc_ptr);
403
404  return ret;
405}
406
407
408/* Do the full job, including symlink resolution.
409   This path will find files installed in the same place as the
410   program even when a soft link has been made to the program
411   from somwhere else. */
412
413char *
414make_relative_prefix (const char *progname, const char *bin_prefix,
415		      const char *prefix)
416{
417  return make_relative_prefix_1 (progname, bin_prefix, prefix, 1);
418}
419
420/* Make the relative pathname without attempting to resolve any links.
421   '..' etc may also be left in the pathname.
422   This will find the files the user meant the program to find if the
423   installation is patched together with soft links. */
424
425char *
426make_relative_prefix_ignore_links (const char *progname,
427				   const char *bin_prefix,
428				   const char *prefix)
429{
430  return make_relative_prefix_1 (progname, bin_prefix, prefix, 0);
431}
432
433