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