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