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