OSDN Git Service

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