Subversion Repositories Kolibri OS

Rev

Rev 5191 | Details | Compare with Previous | Last modification | View Log | RSS feed

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