make-relative-prefix.c revision 1.6 1 1.1 christos /* Relative (relocatable) prefix support.
2 1.6 christos Copyright (C) 1987-2018 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.1 christos /* Count the number of directories. Special case MSDOS disk names as part
126 1.1 christos of the initial directory. */
127 1.1 christos p = name;
128 1.1 christos #ifdef HAVE_DOS_BASED_FILE_SYSTEM
129 1.1 christos if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
130 1.1 christos {
131 1.1 christos p += 3;
132 1.1 christos num_dirs++;
133 1.1 christos }
134 1.1 christos #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
135 1.1 christos
136 1.1 christos while ((ch = *p++) != '\0')
137 1.1 christos {
138 1.1 christos if (IS_DIR_SEPARATOR (ch))
139 1.1 christos {
140 1.1 christos num_dirs++;
141 1.1 christos while (IS_DIR_SEPARATOR (*p))
142 1.1 christos p++;
143 1.1 christos }
144 1.1 christos }
145 1.1 christos
146 1.1 christos dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2));
147 1.1 christos if (dirs == NULL)
148 1.1 christos return NULL;
149 1.1 christos
150 1.1 christos /* Now copy the directory parts. */
151 1.1 christos num_dirs = 0;
152 1.1 christos p = name;
153 1.1 christos #ifdef HAVE_DOS_BASED_FILE_SYSTEM
154 1.1 christos if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
155 1.1 christos {
156 1.1 christos dirs[num_dirs++] = save_string (p, 3);
157 1.1 christos if (dirs[num_dirs - 1] == NULL)
158 1.1 christos {
159 1.1 christos free (dirs);
160 1.1 christos return NULL;
161 1.1 christos }
162 1.1 christos p += 3;
163 1.1 christos }
164 1.1 christos #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
165 1.1 christos
166 1.1 christos q = p;
167 1.1 christos while ((ch = *p++) != '\0')
168 1.1 christos {
169 1.1 christos if (IS_DIR_SEPARATOR (ch))
170 1.1 christos {
171 1.1 christos while (IS_DIR_SEPARATOR (*p))
172 1.1 christos p++;
173 1.1 christos
174 1.1 christos dirs[num_dirs++] = save_string (q, p - q);
175 1.1 christos if (dirs[num_dirs - 1] == NULL)
176 1.1 christos {
177 1.1 christos dirs[num_dirs] = NULL;
178 1.1 christos free_split_directories (dirs);
179 1.1 christos return NULL;
180 1.1 christos }
181 1.1 christos q = p;
182 1.1 christos }
183 1.1 christos }
184 1.1 christos
185 1.1 christos if (p - 1 - q > 0)
186 1.1 christos dirs[num_dirs++] = save_string (q, p - 1 - q);
187 1.1 christos dirs[num_dirs] = NULL;
188 1.1 christos
189 1.1 christos if (dirs[num_dirs - 1] == NULL)
190 1.1 christos {
191 1.1 christos free_split_directories (dirs);
192 1.1 christos return NULL;
193 1.1 christos }
194 1.1 christos
195 1.1 christos if (ptr_num_dirs)
196 1.1 christos *ptr_num_dirs = num_dirs;
197 1.1 christos return dirs;
198 1.1 christos }
199 1.1 christos
200 1.1 christos /* Release storage held by split directories. */
201 1.1 christos
202 1.1 christos static void
203 1.1 christos free_split_directories (char **dirs)
204 1.1 christos {
205 1.1 christos int i = 0;
206 1.1 christos
207 1.1 christos if (dirs != NULL)
208 1.1 christos {
209 1.1 christos while (dirs[i] != NULL)
210 1.1 christos free (dirs[i++]);
211 1.1 christos
212 1.1 christos free ((char *) dirs);
213 1.1 christos }
214 1.1 christos }
215 1.1 christos
216 1.1 christos /* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets
217 1.1 christos to PREFIX starting with the directory portion of PROGNAME and a relative
218 1.1 christos pathname of the difference between BIN_PREFIX and PREFIX.
219 1.1 christos
220 1.1 christos For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is
221 1.1 christos /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this
222 1.1 christos function will return /red/green/blue/../../omega/.
223 1.1 christos
224 1.1 christos If no relative prefix can be found, return NULL. */
225 1.1 christos
226 1.1 christos static char *
227 1.1 christos make_relative_prefix_1 (const char *progname, const char *bin_prefix,
228 1.1 christos const char *prefix, const int resolve_links)
229 1.1 christos {
230 1.1 christos char **prog_dirs = NULL, **bin_dirs = NULL, **prefix_dirs = NULL;
231 1.1 christos int prog_num, bin_num, prefix_num;
232 1.1 christos int i, n, common;
233 1.1 christos int needed_len;
234 1.1 christos char *ret = NULL, *ptr, *full_progname;
235 1.6 christos char *alloc_ptr = NULL;
236 1.1 christos
237 1.1 christos if (progname == NULL || bin_prefix == NULL || prefix == NULL)
238 1.1 christos return NULL;
239 1.1 christos
240 1.1 christos /* If there is no full pathname, try to find the program by checking in each
241 1.1 christos of the directories specified in the PATH environment variable. */
242 1.1 christos if (lbasename (progname) == progname)
243 1.1 christos {
244 1.1 christos char *temp;
245 1.1 christos
246 1.1 christos temp = getenv ("PATH");
247 1.1 christos if (temp)
248 1.1 christos {
249 1.1 christos char *startp, *endp, *nstore;
250 1.1 christos size_t prefixlen = strlen (temp) + 1;
251 1.1 christos size_t len;
252 1.1 christos if (prefixlen < 2)
253 1.1 christos prefixlen = 2;
254 1.1 christos
255 1.1 christos len = prefixlen + strlen (progname) + 1;
256 1.1 christos #ifdef HAVE_HOST_EXECUTABLE_SUFFIX
257 1.1 christos len += strlen (HOST_EXECUTABLE_SUFFIX);
258 1.1 christos #endif
259 1.6 christos if (len < MAX_ALLOCA_SIZE)
260 1.6 christos nstore = (char *) alloca (len);
261 1.6 christos else
262 1.6 christos alloc_ptr = nstore = (char *) malloc (len);
263 1.1 christos
264 1.1 christos startp = endp = temp;
265 1.1 christos while (1)
266 1.1 christos {
267 1.1 christos if (*endp == PATH_SEPARATOR || *endp == 0)
268 1.1 christos {
269 1.1 christos if (endp == startp)
270 1.1 christos {
271 1.1 christos nstore[0] = '.';
272 1.1 christos nstore[1] = DIR_SEPARATOR;
273 1.1 christos nstore[2] = '\0';
274 1.1 christos }
275 1.1 christos else
276 1.1 christos {
277 1.1 christos memcpy (nstore, startp, endp - startp);
278 1.1 christos if (! IS_DIR_SEPARATOR (endp[-1]))
279 1.1 christos {
280 1.1 christos nstore[endp - startp] = DIR_SEPARATOR;
281 1.1 christos nstore[endp - startp + 1] = 0;
282 1.1 christos }
283 1.1 christos else
284 1.1 christos nstore[endp - startp] = 0;
285 1.1 christos }
286 1.1 christos strcat (nstore, progname);
287 1.1 christos if (! access (nstore, X_OK)
288 1.1 christos #ifdef HAVE_HOST_EXECUTABLE_SUFFIX
289 1.1 christos || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
290 1.1 christos #endif
291 1.1 christos )
292 1.1 christos {
293 1.1 christos #if defined (HAVE_SYS_STAT_H) && defined (S_ISREG)
294 1.1 christos struct stat st;
295 1.1 christos if (stat (nstore, &st) >= 0 && S_ISREG (st.st_mode))
296 1.1 christos #endif
297 1.1 christos {
298 1.1 christos progname = nstore;
299 1.1 christos break;
300 1.1 christos }
301 1.1 christos }
302 1.1 christos
303 1.1 christos if (*endp == 0)
304 1.1 christos break;
305 1.1 christos endp = startp = endp + 1;
306 1.1 christos }
307 1.1 christos else
308 1.1 christos endp++;
309 1.1 christos }
310 1.1 christos }
311 1.1 christos }
312 1.1 christos
313 1.1 christos if (resolve_links)
314 1.1 christos full_progname = lrealpath (progname);
315 1.1 christos else
316 1.1 christos full_progname = strdup (progname);
317 1.1 christos if (full_progname == NULL)
318 1.6 christos goto bailout;
319 1.1 christos
320 1.1 christos prog_dirs = split_directories (full_progname, &prog_num);
321 1.1 christos free (full_progname);
322 1.1 christos if (prog_dirs == NULL)
323 1.6 christos goto bailout;
324 1.1 christos
325 1.1 christos bin_dirs = split_directories (bin_prefix, &bin_num);
326 1.1 christos if (bin_dirs == NULL)
327 1.1 christos goto bailout;
328 1.1 christos
329 1.1 christos /* Remove the program name from comparison of directory names. */
330 1.1 christos prog_num--;
331 1.1 christos
332 1.1 christos /* If we are still installed in the standard location, we don't need to
333 1.1 christos specify relative directories. Also, if argv[0] still doesn't contain
334 1.1 christos any directory specifiers after the search above, then there is not much
335 1.1 christos we can do. */
336 1.1 christos if (prog_num == bin_num)
337 1.1 christos {
338 1.1 christos for (i = 0; i < bin_num; i++)
339 1.1 christos {
340 1.1 christos if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
341 1.1 christos break;
342 1.1 christos }
343 1.1 christos
344 1.1 christos if (prog_num <= 0 || i == bin_num)
345 1.1 christos goto bailout;
346 1.1 christos }
347 1.1 christos
348 1.1 christos prefix_dirs = split_directories (prefix, &prefix_num);
349 1.1 christos if (prefix_dirs == NULL)
350 1.1 christos goto bailout;
351 1.1 christos
352 1.1 christos /* Find how many directories are in common between bin_prefix & prefix. */
353 1.1 christos n = (prefix_num < bin_num) ? prefix_num : bin_num;
354 1.1 christos for (common = 0; common < n; common++)
355 1.1 christos {
356 1.1 christos if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
357 1.1 christos break;
358 1.1 christos }
359 1.1 christos
360 1.1 christos /* If there are no common directories, there can be no relative prefix. */
361 1.1 christos if (common == 0)
362 1.1 christos goto bailout;
363 1.1 christos
364 1.1 christos /* Two passes: first figure out the size of the result string, and
365 1.1 christos then construct it. */
366 1.1 christos needed_len = 0;
367 1.1 christos for (i = 0; i < prog_num; i++)
368 1.1 christos needed_len += strlen (prog_dirs[i]);
369 1.1 christos needed_len += sizeof (DIR_UP) * (bin_num - common);
370 1.1 christos for (i = common; i < prefix_num; i++)
371 1.1 christos needed_len += strlen (prefix_dirs[i]);
372 1.1 christos needed_len += 1; /* Trailing NUL. */
373 1.1 christos
374 1.1 christos ret = (char *) malloc (needed_len);
375 1.1 christos if (ret == NULL)
376 1.1 christos goto bailout;
377 1.1 christos
378 1.1 christos /* Build up the pathnames in argv[0]. */
379 1.1 christos *ret = '\0';
380 1.1 christos for (i = 0; i < prog_num; i++)
381 1.1 christos strcat (ret, prog_dirs[i]);
382 1.1 christos
383 1.1 christos /* Now build up the ..'s. */
384 1.1 christos ptr = ret + strlen(ret);
385 1.1 christos for (i = common; i < bin_num; i++)
386 1.1 christos {
387 1.1 christos strcpy (ptr, DIR_UP);
388 1.1 christos ptr += sizeof (DIR_UP) - 1;
389 1.1 christos *(ptr++) = DIR_SEPARATOR;
390 1.1 christos }
391 1.1 christos *ptr = '\0';
392 1.1 christos
393 1.1 christos /* Put in directories to move over to prefix. */
394 1.1 christos for (i = common; i < prefix_num; i++)
395 1.1 christos strcat (ret, prefix_dirs[i]);
396 1.1 christos
397 1.1 christos bailout:
398 1.1 christos free_split_directories (prog_dirs);
399 1.1 christos free_split_directories (bin_dirs);
400 1.1 christos free_split_directories (prefix_dirs);
401 1.6 christos free (alloc_ptr);
402 1.1 christos
403 1.1 christos return ret;
404 1.1 christos }
405 1.1 christos
406 1.1 christos
407 1.1 christos /* Do the full job, including symlink resolution.
408 1.1 christos This path will find files installed in the same place as the
409 1.1 christos program even when a soft link has been made to the program
410 1.1 christos from somwhere else. */
411 1.1 christos
412 1.1 christos char *
413 1.1 christos make_relative_prefix (const char *progname, const char *bin_prefix,
414 1.1 christos const char *prefix)
415 1.1 christos {
416 1.1 christos return make_relative_prefix_1 (progname, bin_prefix, prefix, 1);
417 1.1 christos }
418 1.1 christos
419 1.1 christos /* Make the relative pathname without attempting to resolve any links.
420 1.1 christos '..' etc may also be left in the pathname.
421 1.1 christos This will find the files the user meant the program to find if the
422 1.1 christos installation is patched together with soft links. */
423 1.1 christos
424 1.1 christos char *
425 1.1 christos make_relative_prefix_ignore_links (const char *progname,
426 1.1 christos const char *bin_prefix,
427 1.1 christos const char *prefix)
428 1.1 christos {
429 1.1 christos return make_relative_prefix_1 (progname, bin_prefix, prefix, 0);
430 1.1 christos }
431 1.1 christos
432