0,0 → 1,427 |
/* Relative (relocatable) prefix support. |
Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, |
1999, 2000, 2001, 2002, 2006, 2012 Free Software Foundation, Inc. |
|
This file is part of libiberty. |
|
GCC is free software; you can redistribute it and/or modify it under |
the terms of the GNU General Public License as published by the Free |
Software Foundation; either version 2, or (at your option) any later |
version. |
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
WARRANTY; without even the implied warranty of MERCHANTABILITY or |
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
for more details. |
|
You should have received a copy of the GNU General Public License |
along with GCC; see the file COPYING. If not, write to the Free |
Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
02110-1301, USA. */ |
|
/* |
|
@deftypefn Extension {const char*} make_relative_prefix (const char *@var{progname}, @ |
const char *@var{bin_prefix}, const char *@var{prefix}) |
|
Given three paths @var{progname}, @var{bin_prefix}, @var{prefix}, |
return the path that is in the same position relative to |
@var{progname}'s directory as @var{prefix} is relative to |
@var{bin_prefix}. That is, a string starting with the directory |
portion of @var{progname}, followed by a relative pathname of the |
difference between @var{bin_prefix} and @var{prefix}. |
|
If @var{progname} does not contain any directory separators, |
@code{make_relative_prefix} will search @env{PATH} to find a program |
named @var{progname}. Also, if @var{progname} is a symbolic link, |
the symbolic link will be resolved. |
|
For example, if @var{bin_prefix} is @code{/alpha/beta/gamma/gcc/delta}, |
@var{prefix} is @code{/alpha/beta/gamma/omega/}, and @var{progname} is |
@code{/red/green/blue/gcc}, then this function will return |
@code{/red/green/blue/../../omega/}. |
|
The return value is normally allocated via @code{malloc}. If no |
relative prefix can be found, return @code{NULL}. |
|
@end deftypefn |
|
*/ |
|
#ifdef HAVE_CONFIG_H |
#include "config.h" |
#endif |
|
#ifdef HAVE_STDLIB_H |
#include <stdlib.h> |
#endif |
#ifdef HAVE_UNISTD_H |
#include <unistd.h> |
#endif |
#ifdef HAVE_SYS_STAT_H |
#include <sys/stat.h> |
#endif |
|
#include <string.h> |
|
#include "ansidecl.h" |
#include "libiberty.h" |
|
#ifndef R_OK |
#define R_OK 4 |
#define W_OK 2 |
#define X_OK 1 |
#endif |
|
#ifndef DIR_SEPARATOR |
# define DIR_SEPARATOR '/' |
#endif |
|
#if defined (_WIN32) || defined (__MSDOS__) \ |
|| defined (__DJGPP__) || defined (__OS2__) |
# define HAVE_DOS_BASED_FILE_SYSTEM |
# define HAVE_HOST_EXECUTABLE_SUFFIX |
# define HOST_EXECUTABLE_SUFFIX ".exe" |
# ifndef DIR_SEPARATOR_2 |
# define DIR_SEPARATOR_2 '\\' |
# endif |
# define PATH_SEPARATOR ';' |
#else |
# define PATH_SEPARATOR ':' |
#endif |
|
#ifndef DIR_SEPARATOR_2 |
# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) |
#else |
# define IS_DIR_SEPARATOR(ch) \ |
(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) |
#endif |
|
#define DIR_UP ".." |
|
static char *save_string (const char *, int); |
static char **split_directories (const char *, int *); |
static void free_split_directories (char **); |
|
static char * |
save_string (const char *s, int len) |
{ |
char *result = (char *) malloc (len + 1); |
|
memcpy (result, s, len); |
result[len] = 0; |
return result; |
} |
|
/* Split a filename into component directories. */ |
|
static char ** |
split_directories (const char *name, int *ptr_num_dirs) |
{ |
int num_dirs = 0; |
char **dirs; |
const char *p, *q; |
int ch; |
|
/* Count the number of directories. Special case MSDOS disk names as part |
of the initial directory. */ |
p = name; |
#ifdef HAVE_DOS_BASED_FILE_SYSTEM |
if (name[1] == ':' && IS_DIR_SEPARATOR (name[2])) |
{ |
p += 3; |
num_dirs++; |
} |
#endif /* HAVE_DOS_BASED_FILE_SYSTEM */ |
|
while ((ch = *p++) != '\0') |
{ |
if (IS_DIR_SEPARATOR (ch)) |
{ |
num_dirs++; |
while (IS_DIR_SEPARATOR (*p)) |
p++; |
} |
} |
|
dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2)); |
if (dirs == NULL) |
return NULL; |
|
/* Now copy the directory parts. */ |
num_dirs = 0; |
p = name; |
#ifdef HAVE_DOS_BASED_FILE_SYSTEM |
if (name[1] == ':' && IS_DIR_SEPARATOR (name[2])) |
{ |
dirs[num_dirs++] = save_string (p, 3); |
if (dirs[num_dirs - 1] == NULL) |
{ |
free (dirs); |
return NULL; |
} |
p += 3; |
} |
#endif /* HAVE_DOS_BASED_FILE_SYSTEM */ |
|
q = p; |
while ((ch = *p++) != '\0') |
{ |
if (IS_DIR_SEPARATOR (ch)) |
{ |
while (IS_DIR_SEPARATOR (*p)) |
p++; |
|
dirs[num_dirs++] = save_string (q, p - q); |
if (dirs[num_dirs - 1] == NULL) |
{ |
dirs[num_dirs] = NULL; |
free_split_directories (dirs); |
return NULL; |
} |
q = p; |
} |
} |
|
if (p - 1 - q > 0) |
dirs[num_dirs++] = save_string (q, p - 1 - q); |
dirs[num_dirs] = NULL; |
|
if (dirs[num_dirs - 1] == NULL) |
{ |
free_split_directories (dirs); |
return NULL; |
} |
|
if (ptr_num_dirs) |
*ptr_num_dirs = num_dirs; |
return dirs; |
} |
|
/* Release storage held by split directories. */ |
|
static void |
free_split_directories (char **dirs) |
{ |
int i = 0; |
|
if (dirs != NULL) |
{ |
while (dirs[i] != NULL) |
free (dirs[i++]); |
|
free ((char *) dirs); |
} |
} |
|
/* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets |
to PREFIX starting with the directory portion of PROGNAME and a relative |
pathname of the difference between BIN_PREFIX and PREFIX. |
|
For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is |
/alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this |
function will return /red/green/blue/../../omega/. |
|
If no relative prefix can be found, return NULL. */ |
|
static char * |
make_relative_prefix_1 (const char *progname, const char *bin_prefix, |
const char *prefix, const int resolve_links) |
{ |
char **prog_dirs = NULL, **bin_dirs = NULL, **prefix_dirs = NULL; |
int prog_num, bin_num, prefix_num; |
int i, n, common; |
int needed_len; |
char *ret = NULL, *ptr, *full_progname; |
|
if (progname == NULL || bin_prefix == NULL || prefix == NULL) |
return NULL; |
|
/* If there is no full pathname, try to find the program by checking in each |
of the directories specified in the PATH environment variable. */ |
if (lbasename (progname) == progname) |
{ |
char *temp; |
|
temp = getenv ("PATH"); |
if (temp) |
{ |
char *startp, *endp, *nstore; |
size_t prefixlen = strlen (temp) + 1; |
size_t len; |
if (prefixlen < 2) |
prefixlen = 2; |
|
len = prefixlen + strlen (progname) + 1; |
#ifdef HAVE_HOST_EXECUTABLE_SUFFIX |
len += strlen (HOST_EXECUTABLE_SUFFIX); |
#endif |
nstore = (char *) alloca (len); |
|
startp = endp = temp; |
while (1) |
{ |
if (*endp == PATH_SEPARATOR || *endp == 0) |
{ |
if (endp == startp) |
{ |
nstore[0] = '.'; |
nstore[1] = DIR_SEPARATOR; |
nstore[2] = '\0'; |
} |
else |
{ |
memcpy (nstore, startp, endp - startp); |
if (! IS_DIR_SEPARATOR (endp[-1])) |
{ |
nstore[endp - startp] = DIR_SEPARATOR; |
nstore[endp - startp + 1] = 0; |
} |
else |
nstore[endp - startp] = 0; |
} |
strcat (nstore, progname); |
if (! access (nstore, X_OK) |
#ifdef HAVE_HOST_EXECUTABLE_SUFFIX |
|| ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK) |
#endif |
) |
{ |
#if defined (HAVE_SYS_STAT_H) && defined (S_ISREG) |
struct stat st; |
if (stat (nstore, &st) >= 0 && S_ISREG (st.st_mode)) |
#endif |
{ |
progname = nstore; |
break; |
} |
} |
|
if (*endp == 0) |
break; |
endp = startp = endp + 1; |
} |
else |
endp++; |
} |
} |
} |
|
if (resolve_links) |
full_progname = lrealpath (progname); |
else |
full_progname = strdup (progname); |
if (full_progname == NULL) |
return NULL; |
|
prog_dirs = split_directories (full_progname, &prog_num); |
free (full_progname); |
if (prog_dirs == NULL) |
return NULL; |
|
bin_dirs = split_directories (bin_prefix, &bin_num); |
if (bin_dirs == NULL) |
goto bailout; |
|
/* Remove the program name from comparison of directory names. */ |
prog_num--; |
|
/* If we are still installed in the standard location, we don't need to |
specify relative directories. Also, if argv[0] still doesn't contain |
any directory specifiers after the search above, then there is not much |
we can do. */ |
if (prog_num == bin_num) |
{ |
for (i = 0; i < bin_num; i++) |
{ |
if (strcmp (prog_dirs[i], bin_dirs[i]) != 0) |
break; |
} |
|
if (prog_num <= 0 || i == bin_num) |
goto bailout; |
} |
|
prefix_dirs = split_directories (prefix, &prefix_num); |
if (prefix_dirs == NULL) |
goto bailout; |
|
/* Find how many directories are in common between bin_prefix & prefix. */ |
n = (prefix_num < bin_num) ? prefix_num : bin_num; |
for (common = 0; common < n; common++) |
{ |
if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0) |
break; |
} |
|
/* If there are no common directories, there can be no relative prefix. */ |
if (common == 0) |
goto bailout; |
|
/* Two passes: first figure out the size of the result string, and |
then construct it. */ |
needed_len = 0; |
for (i = 0; i < prog_num; i++) |
needed_len += strlen (prog_dirs[i]); |
needed_len += sizeof (DIR_UP) * (bin_num - common); |
for (i = common; i < prefix_num; i++) |
needed_len += strlen (prefix_dirs[i]); |
needed_len += 1; /* Trailing NUL. */ |
|
ret = (char *) malloc (needed_len); |
if (ret == NULL) |
goto bailout; |
|
/* Build up the pathnames in argv[0]. */ |
*ret = '\0'; |
for (i = 0; i < prog_num; i++) |
strcat (ret, prog_dirs[i]); |
|
/* Now build up the ..'s. */ |
ptr = ret + strlen(ret); |
for (i = common; i < bin_num; i++) |
{ |
strcpy (ptr, DIR_UP); |
ptr += sizeof (DIR_UP) - 1; |
*(ptr++) = DIR_SEPARATOR; |
} |
*ptr = '\0'; |
|
/* Put in directories to move over to prefix. */ |
for (i = common; i < prefix_num; i++) |
strcat (ret, prefix_dirs[i]); |
|
bailout: |
free_split_directories (prog_dirs); |
free_split_directories (bin_dirs); |
free_split_directories (prefix_dirs); |
|
return ret; |
} |
|
|
/* Do the full job, including symlink resolution. |
This path will find files installed in the same place as the |
program even when a soft link has been made to the program |
from somwhere else. */ |
|
char * |
make_relative_prefix (const char *progname, const char *bin_prefix, |
const char *prefix) |
{ |
return make_relative_prefix_1 (progname, bin_prefix, prefix, 1); |
} |
|
/* Make the relative pathname without attempting to resolve any links. |
'..' etc may also be left in the pathname. |
This will find the files the user meant the program to find if the |
installation is patched together with soft links. */ |
|
char * |
make_relative_prefix_ignore_links (const char *progname, |
const char *bin_prefix, |
const char *prefix) |
{ |
return make_relative_prefix_1 (progname, bin_prefix, prefix, 0); |
} |
|