1130561Sobrien/* Relative (relocatable) prefix support.
2130561Sobrien   Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
3218822Sdim   1999, 2000, 2001, 2002, 2006 Free Software Foundation, Inc.
4130561Sobrien
5130561SobrienThis file is part of libiberty.
6130561Sobrien
7130561SobrienGCC is free software; you can redistribute it and/or modify it under
8130561Sobrienthe terms of the GNU General Public License as published by the Free
9130561SobrienSoftware Foundation; either version 2, or (at your option) any later
10130561Sobrienversion.
11130561Sobrien
12130561SobrienGCC is distributed in the hope that it will be useful, but WITHOUT ANY
13130561SobrienWARRANTY; without even the implied warranty of MERCHANTABILITY or
14130561SobrienFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15130561Sobrienfor more details.
16130561Sobrien
17130561SobrienYou should have received a copy of the GNU General Public License
18130561Sobrienalong with GCC; see the file COPYING.  If not, write to the Free
19218822SdimSoftware Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
20218822Sdim02110-1301, USA.  */
21130561Sobrien
22130561Sobrien/*
23130561Sobrien
24130561Sobrien@deftypefn Extension {const char*} make_relative_prefix (const char *@var{progname}, const char *@var{bin_prefix}, const char *@var{prefix})
25130561Sobrien
26130561SobrienGiven three paths @var{progname}, @var{bin_prefix}, @var{prefix},
27130561Sobrienreturn the path that is in the same position relative to
28130561Sobrien@var{progname}'s directory as @var{prefix} is relative to
29130561Sobrien@var{bin_prefix}.  That is, a string starting with the directory
30130561Sobrienportion of @var{progname}, followed by a relative pathname of the
31130561Sobriendifference between @var{bin_prefix} and @var{prefix}.
32130561Sobrien
33130561SobrienIf @var{progname} does not contain any directory separators,
34130561Sobrien@code{make_relative_prefix} will search @env{PATH} to find a program
35130561Sobriennamed @var{progname}.  Also, if @var{progname} is a symbolic link,
36130561Sobrienthe symbolic link will be resolved.
37130561Sobrien
38130561SobrienFor example, if @var{bin_prefix} is @code{/alpha/beta/gamma/gcc/delta},
39130561Sobrien@var{prefix} is @code{/alpha/beta/gamma/omega/}, and @var{progname} is
40130561Sobrien@code{/red/green/blue/gcc}, then this function will return
41130561Sobrien@code{/red/green/blue/../../omega/}.
42130561Sobrien
43130561SobrienThe return value is normally allocated via @code{malloc}.  If no
44130561Sobrienrelative prefix can be found, return @code{NULL}.
45130561Sobrien
46130561Sobrien@end deftypefn
47130561Sobrien
48130561Sobrien*/
49130561Sobrien
50130561Sobrien#ifdef HAVE_CONFIG_H
51130561Sobrien#include "config.h"
52130561Sobrien#endif
53130561Sobrien
54130561Sobrien#ifdef HAVE_STDLIB_H
55130561Sobrien#include <stdlib.h>
56130561Sobrien#endif
57130561Sobrien#ifdef HAVE_UNISTD_H
58130561Sobrien#include <unistd.h>
59130561Sobrien#endif
60130561Sobrien
61130561Sobrien#include <string.h>
62130561Sobrien
63130561Sobrien#include "ansidecl.h"
64130561Sobrien#include "libiberty.h"
65130561Sobrien
66130561Sobrien#ifndef R_OK
67130561Sobrien#define R_OK 4
68130561Sobrien#define W_OK 2
69130561Sobrien#define X_OK 1
70130561Sobrien#endif
71130561Sobrien
72130561Sobrien#ifndef DIR_SEPARATOR
73130561Sobrien#  define DIR_SEPARATOR '/'
74130561Sobrien#endif
75130561Sobrien
76130561Sobrien#if defined (_WIN32) || defined (__MSDOS__) \
77130561Sobrien    || defined (__DJGPP__) || defined (__OS2__)
78130561Sobrien#  define HAVE_DOS_BASED_FILE_SYSTEM
79130561Sobrien#  define HAVE_HOST_EXECUTABLE_SUFFIX
80130561Sobrien#  define HOST_EXECUTABLE_SUFFIX ".exe"
81130561Sobrien#  ifndef DIR_SEPARATOR_2
82130561Sobrien#    define DIR_SEPARATOR_2 '\\'
83130561Sobrien#  endif
84130561Sobrien#  define PATH_SEPARATOR ';'
85130561Sobrien#else
86130561Sobrien#  define PATH_SEPARATOR ':'
87130561Sobrien#endif
88130561Sobrien
89130561Sobrien#ifndef DIR_SEPARATOR_2
90130561Sobrien#  define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
91130561Sobrien#else
92130561Sobrien#  define IS_DIR_SEPARATOR(ch) \
93130561Sobrien	(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
94130561Sobrien#endif
95130561Sobrien
96130561Sobrien#define DIR_UP ".."
97130561Sobrien
98218822Sdimstatic char *save_string (const char *, int);
99218822Sdimstatic char **split_directories	(const char *, int *);
100218822Sdimstatic void free_split_directories (char **);
101130561Sobrien
102130561Sobrienstatic char *
103218822Sdimsave_string (const char *s, int len)
104130561Sobrien{
105218822Sdim  char *result = (char *) malloc (len + 1);
106130561Sobrien
107130561Sobrien  memcpy (result, s, len);
108130561Sobrien  result[len] = 0;
109130561Sobrien  return result;
110130561Sobrien}
111130561Sobrien
112130561Sobrien/* Split a filename into component directories.  */
113130561Sobrien
114130561Sobrienstatic char **
115218822Sdimsplit_directories (const char *name, int *ptr_num_dirs)
116130561Sobrien{
117130561Sobrien  int num_dirs = 0;
118130561Sobrien  char **dirs;
119130561Sobrien  const char *p, *q;
120130561Sobrien  int ch;
121130561Sobrien
122130561Sobrien  /* Count the number of directories.  Special case MSDOS disk names as part
123130561Sobrien     of the initial directory.  */
124130561Sobrien  p = name;
125130561Sobrien#ifdef HAVE_DOS_BASED_FILE_SYSTEM
126130561Sobrien  if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
127130561Sobrien    {
128130561Sobrien      p += 3;
129130561Sobrien      num_dirs++;
130130561Sobrien    }
131130561Sobrien#endif /* HAVE_DOS_BASED_FILE_SYSTEM */
132130561Sobrien
133130561Sobrien  while ((ch = *p++) != '\0')
134130561Sobrien    {
135130561Sobrien      if (IS_DIR_SEPARATOR (ch))
136130561Sobrien	{
137130561Sobrien	  num_dirs++;
138130561Sobrien	  while (IS_DIR_SEPARATOR (*p))
139130561Sobrien	    p++;
140130561Sobrien	}
141130561Sobrien    }
142130561Sobrien
143130561Sobrien  dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2));
144130561Sobrien  if (dirs == NULL)
145130561Sobrien    return NULL;
146130561Sobrien
147130561Sobrien  /* Now copy the directory parts.  */
148130561Sobrien  num_dirs = 0;
149130561Sobrien  p = name;
150130561Sobrien#ifdef HAVE_DOS_BASED_FILE_SYSTEM
151130561Sobrien  if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
152130561Sobrien    {
153130561Sobrien      dirs[num_dirs++] = save_string (p, 3);
154130561Sobrien      if (dirs[num_dirs - 1] == NULL)
155130561Sobrien	{
156130561Sobrien	  free (dirs);
157130561Sobrien	  return NULL;
158130561Sobrien	}
159130561Sobrien      p += 3;
160130561Sobrien    }
161130561Sobrien#endif /* HAVE_DOS_BASED_FILE_SYSTEM */
162130561Sobrien
163130561Sobrien  q = p;
164130561Sobrien  while ((ch = *p++) != '\0')
165130561Sobrien    {
166130561Sobrien      if (IS_DIR_SEPARATOR (ch))
167130561Sobrien	{
168130561Sobrien	  while (IS_DIR_SEPARATOR (*p))
169130561Sobrien	    p++;
170130561Sobrien
171130561Sobrien	  dirs[num_dirs++] = save_string (q, p - q);
172130561Sobrien	  if (dirs[num_dirs - 1] == NULL)
173130561Sobrien	    {
174130561Sobrien	      dirs[num_dirs] = NULL;
175130561Sobrien	      free_split_directories (dirs);
176130561Sobrien	      return NULL;
177130561Sobrien	    }
178130561Sobrien	  q = p;
179130561Sobrien	}
180130561Sobrien    }
181130561Sobrien
182130561Sobrien  if (p - 1 - q > 0)
183130561Sobrien    dirs[num_dirs++] = save_string (q, p - 1 - q);
184130561Sobrien  dirs[num_dirs] = NULL;
185130561Sobrien
186130561Sobrien  if (dirs[num_dirs - 1] == NULL)
187130561Sobrien    {
188130561Sobrien      free_split_directories (dirs);
189130561Sobrien      return NULL;
190130561Sobrien    }
191130561Sobrien
192130561Sobrien  if (ptr_num_dirs)
193130561Sobrien    *ptr_num_dirs = num_dirs;
194130561Sobrien  return dirs;
195130561Sobrien}
196130561Sobrien
197130561Sobrien/* Release storage held by split directories.  */
198130561Sobrien
199130561Sobrienstatic void
200218822Sdimfree_split_directories (char **dirs)
201130561Sobrien{
202130561Sobrien  int i = 0;
203130561Sobrien
204130561Sobrien  while (dirs[i] != NULL)
205130561Sobrien    free (dirs[i++]);
206130561Sobrien
207130561Sobrien  free ((char *) dirs);
208130561Sobrien}
209130561Sobrien
210130561Sobrien/* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets
211130561Sobrien   to PREFIX starting with the directory portion of PROGNAME and a relative
212130561Sobrien   pathname of the difference between BIN_PREFIX and PREFIX.
213130561Sobrien
214130561Sobrien   For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is
215130561Sobrien   /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this
216130561Sobrien   function will return /red/green/blue/../../omega/.
217130561Sobrien
218130561Sobrien   If no relative prefix can be found, return NULL.  */
219130561Sobrien
220218822Sdimstatic char *
221218822Sdimmake_relative_prefix_1 (const char *progname, const char *bin_prefix,
222218822Sdim			const char *prefix, const int resolve_links)
223130561Sobrien{
224130561Sobrien  char **prog_dirs, **bin_dirs, **prefix_dirs;
225130561Sobrien  int prog_num, bin_num, prefix_num;
226130561Sobrien  int i, n, common;
227130561Sobrien  int needed_len;
228130561Sobrien  char *ret, *ptr, *full_progname = NULL;
229130561Sobrien
230130561Sobrien  if (progname == NULL || bin_prefix == NULL || prefix == NULL)
231130561Sobrien    return NULL;
232130561Sobrien
233130561Sobrien  /* If there is no full pathname, try to find the program by checking in each
234130561Sobrien     of the directories specified in the PATH environment variable.  */
235130561Sobrien  if (lbasename (progname) == progname)
236130561Sobrien    {
237130561Sobrien      char *temp;
238130561Sobrien
239130561Sobrien      temp = getenv ("PATH");
240130561Sobrien      if (temp)
241130561Sobrien	{
242130561Sobrien	  char *startp, *endp, *nstore;
243130561Sobrien	  size_t prefixlen = strlen (temp) + 1;
244130561Sobrien	  if (prefixlen < 2)
245130561Sobrien	    prefixlen = 2;
246130561Sobrien
247130561Sobrien	  nstore = (char *) alloca (prefixlen + strlen (progname) + 1);
248130561Sobrien
249130561Sobrien	  startp = endp = temp;
250130561Sobrien	  while (1)
251130561Sobrien	    {
252130561Sobrien	      if (*endp == PATH_SEPARATOR || *endp == 0)
253130561Sobrien		{
254130561Sobrien		  if (endp == startp)
255130561Sobrien		    {
256130561Sobrien		      nstore[0] = '.';
257130561Sobrien		      nstore[1] = DIR_SEPARATOR;
258130561Sobrien		      nstore[2] = '\0';
259130561Sobrien		    }
260130561Sobrien		  else
261130561Sobrien		    {
262130561Sobrien		      strncpy (nstore, startp, endp - startp);
263130561Sobrien		      if (! IS_DIR_SEPARATOR (endp[-1]))
264130561Sobrien			{
265130561Sobrien			  nstore[endp - startp] = DIR_SEPARATOR;
266130561Sobrien			  nstore[endp - startp + 1] = 0;
267130561Sobrien			}
268130561Sobrien		      else
269130561Sobrien			nstore[endp - startp] = 0;
270130561Sobrien		    }
271130561Sobrien		  strcat (nstore, progname);
272130561Sobrien		  if (! access (nstore, X_OK)
273130561Sobrien#ifdef HAVE_HOST_EXECUTABLE_SUFFIX
274130561Sobrien                      || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
275130561Sobrien#endif
276130561Sobrien		      )
277130561Sobrien		    {
278130561Sobrien		      progname = nstore;
279130561Sobrien		      break;
280130561Sobrien		    }
281130561Sobrien
282130561Sobrien		  if (*endp == 0)
283130561Sobrien		    break;
284130561Sobrien		  endp = startp = endp + 1;
285130561Sobrien		}
286130561Sobrien	      else
287130561Sobrien		endp++;
288130561Sobrien	    }
289130561Sobrien	}
290130561Sobrien    }
291130561Sobrien
292218822Sdim  if ( resolve_links )
293218822Sdim    {
294218822Sdim      full_progname = lrealpath (progname);
295218822Sdim      if (full_progname == NULL)
296218822Sdim	return NULL;
297218822Sdim    }
298218822Sdim  else
299218822Sdim    full_progname = strdup(progname);
300130561Sobrien
301130561Sobrien  prog_dirs = split_directories (full_progname, &prog_num);
302130561Sobrien  bin_dirs = split_directories (bin_prefix, &bin_num);
303130561Sobrien  free (full_progname);
304130561Sobrien  if (bin_dirs == NULL || prog_dirs == NULL)
305130561Sobrien    return NULL;
306130561Sobrien
307130561Sobrien  /* Remove the program name from comparison of directory names.  */
308130561Sobrien  prog_num--;
309130561Sobrien
310130561Sobrien  /* If we are still installed in the standard location, we don't need to
311130561Sobrien     specify relative directories.  Also, if argv[0] still doesn't contain
312130561Sobrien     any directory specifiers after the search above, then there is not much
313130561Sobrien     we can do.  */
314130561Sobrien  if (prog_num == bin_num)
315130561Sobrien    {
316130561Sobrien      for (i = 0; i < bin_num; i++)
317130561Sobrien	{
318130561Sobrien	  if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
319130561Sobrien	    break;
320130561Sobrien	}
321130561Sobrien
322130561Sobrien      if (prog_num <= 0 || i == bin_num)
323130561Sobrien	{
324130561Sobrien	  free_split_directories (prog_dirs);
325130561Sobrien	  free_split_directories (bin_dirs);
326130561Sobrien	  prog_dirs = bin_dirs = (char **) 0;
327130561Sobrien	  return NULL;
328130561Sobrien	}
329130561Sobrien    }
330130561Sobrien
331130561Sobrien  prefix_dirs = split_directories (prefix, &prefix_num);
332130561Sobrien  if (prefix_dirs == NULL)
333130561Sobrien    {
334130561Sobrien      free_split_directories (prog_dirs);
335130561Sobrien      free_split_directories (bin_dirs);
336130561Sobrien      return NULL;
337130561Sobrien    }
338130561Sobrien
339130561Sobrien  /* Find how many directories are in common between bin_prefix & prefix.  */
340130561Sobrien  n = (prefix_num < bin_num) ? prefix_num : bin_num;
341130561Sobrien  for (common = 0; common < n; common++)
342130561Sobrien    {
343130561Sobrien      if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
344130561Sobrien	break;
345130561Sobrien    }
346130561Sobrien
347130561Sobrien  /* If there are no common directories, there can be no relative prefix.  */
348130561Sobrien  if (common == 0)
349130561Sobrien    {
350130561Sobrien      free_split_directories (prog_dirs);
351130561Sobrien      free_split_directories (bin_dirs);
352130561Sobrien      free_split_directories (prefix_dirs);
353130561Sobrien      return NULL;
354130561Sobrien    }
355130561Sobrien
356130561Sobrien  /* Two passes: first figure out the size of the result string, and
357130561Sobrien     then construct it.  */
358130561Sobrien  needed_len = 0;
359130561Sobrien  for (i = 0; i < prog_num; i++)
360130561Sobrien    needed_len += strlen (prog_dirs[i]);
361130561Sobrien  needed_len += sizeof (DIR_UP) * (bin_num - common);
362130561Sobrien  for (i = common; i < prefix_num; i++)
363130561Sobrien    needed_len += strlen (prefix_dirs[i]);
364130561Sobrien  needed_len += 1; /* Trailing NUL.  */
365130561Sobrien
366130561Sobrien  ret = (char *) malloc (needed_len);
367130561Sobrien  if (ret == NULL)
368130561Sobrien    return NULL;
369130561Sobrien
370130561Sobrien  /* Build up the pathnames in argv[0].  */
371130561Sobrien  *ret = '\0';
372130561Sobrien  for (i = 0; i < prog_num; i++)
373130561Sobrien    strcat (ret, prog_dirs[i]);
374130561Sobrien
375130561Sobrien  /* Now build up the ..'s.  */
376130561Sobrien  ptr = ret + strlen(ret);
377130561Sobrien  for (i = common; i < bin_num; i++)
378130561Sobrien    {
379130561Sobrien      strcpy (ptr, DIR_UP);
380130561Sobrien      ptr += sizeof (DIR_UP) - 1;
381130561Sobrien      *(ptr++) = DIR_SEPARATOR;
382130561Sobrien    }
383130561Sobrien  *ptr = '\0';
384130561Sobrien
385130561Sobrien  /* Put in directories to move over to prefix.  */
386130561Sobrien  for (i = common; i < prefix_num; i++)
387130561Sobrien    strcat (ret, prefix_dirs[i]);
388130561Sobrien
389130561Sobrien  free_split_directories (prog_dirs);
390130561Sobrien  free_split_directories (bin_dirs);
391130561Sobrien  free_split_directories (prefix_dirs);
392130561Sobrien
393130561Sobrien  return ret;
394130561Sobrien}
395218822Sdim
396218822Sdim
397218822Sdim/* Do the full job, including symlink resolution.
398218822Sdim   This path will find files installed in the same place as the
399218822Sdim   program even when a soft link has been made to the program
400218822Sdim   from somwhere else. */
401218822Sdim
402218822Sdimchar *
403218822Sdimmake_relative_prefix (const char *progname, const char *bin_prefix,
404218822Sdim		      const char *prefix)
405218822Sdim{
406218822Sdim  return make_relative_prefix_1 (progname, bin_prefix, prefix, 1);
407218822Sdim}
408218822Sdim
409218822Sdim/* Make the relative pathname without attempting to resolve any links.
410218822Sdim   '..' etc may also be left in the pathname.
411218822Sdim   This will find the files the user meant the program to find if the
412218822Sdim   installation is patched together with soft links. */
413218822Sdim
414218822Sdimchar *
415218822Sdimmake_relative_prefix_ignore_links (const char *progname,
416218822Sdim				   const char *bin_prefix,
417218822Sdim				   const char *prefix)
418218822Sdim{
419218822Sdim  return make_relative_prefix_1 (progname, bin_prefix, prefix, 0);
420218822Sdim}
421218822Sdim
422