1 /* Relative (relocatable) prefix support. 2 Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 3 1999, 2000, 2001, 2002 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 char * 221 make_relative_prefix (const char *progname, 222 const char *bin_prefix, const char *prefix) 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 full_progname = lrealpath (progname); 293 if (full_progname == NULL) 294 return NULL; 295 296 prog_dirs = split_directories (full_progname, &prog_num); 297 bin_dirs = split_directories (bin_prefix, &bin_num); 298 free (full_progname); 299 if (bin_dirs == NULL || prog_dirs == NULL) 300 return NULL; 301 302 /* Remove the program name from comparison of directory names. */ 303 prog_num--; 304 305 /* If we are still installed in the standard location, we don't need to 306 specify relative directories. Also, if argv[0] still doesn't contain 307 any directory specifiers after the search above, then there is not much 308 we can do. */ 309 if (prog_num == bin_num) 310 { 311 for (i = 0; i < bin_num; i++) 312 { 313 if (strcmp (prog_dirs[i], bin_dirs[i]) != 0) 314 break; 315 } 316 317 if (prog_num <= 0 || i == bin_num) 318 { 319 free_split_directories (prog_dirs); 320 free_split_directories (bin_dirs); 321 prog_dirs = bin_dirs = (char **) 0; 322 return NULL; 323 } 324 } 325 326 prefix_dirs = split_directories (prefix, &prefix_num); 327 if (prefix_dirs == NULL) 328 { 329 free_split_directories (prog_dirs); 330 free_split_directories (bin_dirs); 331 return NULL; 332 } 333 334 /* Find how many directories are in common between bin_prefix & prefix. */ 335 n = (prefix_num < bin_num) ? prefix_num : bin_num; 336 for (common = 0; common < n; common++) 337 { 338 if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0) 339 break; 340 } 341 342 /* If there are no common directories, there can be no relative prefix. */ 343 if (common == 0) 344 { 345 free_split_directories (prog_dirs); 346 free_split_directories (bin_dirs); 347 free_split_directories (prefix_dirs); 348 return NULL; 349 } 350 351 /* Two passes: first figure out the size of the result string, and 352 then construct it. */ 353 needed_len = 0; 354 for (i = 0; i < prog_num; i++) 355 needed_len += strlen (prog_dirs[i]); 356 needed_len += sizeof (DIR_UP) * (bin_num - common); 357 for (i = common; i < prefix_num; i++) 358 needed_len += strlen (prefix_dirs[i]); 359 needed_len += 1; /* Trailing NUL. */ 360 361 ret = (char *) malloc (needed_len); 362 if (ret == NULL) 363 return NULL; 364 365 /* Build up the pathnames in argv[0]. */ 366 *ret = '\0'; 367 for (i = 0; i < prog_num; i++) 368 strcat (ret, prog_dirs[i]); 369 370 /* Now build up the ..'s. */ 371 ptr = ret + strlen(ret); 372 for (i = common; i < bin_num; i++) 373 { 374 strcpy (ptr, DIR_UP); 375 ptr += sizeof (DIR_UP) - 1; 376 *(ptr++) = DIR_SEPARATOR; 377 } 378 *ptr = '\0'; 379 380 /* Put in directories to move over to prefix. */ 381 for (i = common; i < prefix_num; i++) 382 strcat (ret, prefix_dirs[i]); 383 384 free_split_directories (prog_dirs); 385 free_split_directories (bin_dirs); 386 free_split_directories (prefix_dirs); 387 388 return ret; 389 } 390