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