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 5This file is part of libiberty. 6 7GCC is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 2, or (at your option) any later 10version. 11 12GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17You should have received a copy of the GNU General Public License 18along with GCC; see the file COPYING. If not, write to the Free 19Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 2002110-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 27Given three paths @var{progname}, @var{bin_prefix}, @var{prefix}, 28return 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 31portion of @var{progname}, followed by a relative pathname of the 32difference between @var{bin_prefix} and @var{prefix}. 33 34If @var{progname} does not contain any directory separators, 35@code{make_relative_prefix} will search @env{PATH} to find a program 36named @var{progname}. Also, if @var{progname} is a symbolic link, 37the symbolic link will be resolved. 38 39For 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 44The return value is normally allocated via @code{malloc}. If no 45relative 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 <stdlib.h> 57#endif 58#ifdef HAVE_UNISTD_H 59#include <unistd.h> 60#endif 61#ifdef HAVE_SYS_STAT_H 62#include <sys/stat.h> 63#endif 64 65#include <string.h> 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" 85# ifndef DIR_SEPARATOR_2 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 102static char *save_string (const char *, int); 103static char **split_directories (const char *, int *); 104static void free_split_directories (char **); 105 106static char * 107save_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 118static char ** 119split_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 203static void 204free_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 227static char * 228make_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 char *alloc_ptr = NULL; 237 238 if (progname == NULL || bin_prefix == NULL || prefix == NULL) 239 return NULL; 240 241 /* If there is no full pathname, try to find the program by checking in each 242 of the directories specified in the PATH environment variable. */ 243 if (lbasename (progname) == progname) 244 { 245 char *temp; 246 247 temp = getenv ("PATH"); 248 if (temp) 249 { 250 char *startp, *endp, *nstore; 251 size_t prefixlen = strlen (temp) + 1; 252 size_t len; 253 if (prefixlen < 2) 254 prefixlen = 2; 255 256 len = prefixlen + strlen (progname) + 1; 257#ifdef HAVE_HOST_EXECUTABLE_SUFFIX 258 len += strlen (HOST_EXECUTABLE_SUFFIX); 259#endif 260 if (len < MAX_ALLOCA_SIZE) 261 nstore = (char *) alloca (len); 262 else 263 alloc_ptr = nstore = (char *) malloc (len); 264 265 startp = endp = temp; 266 while (1) 267 { 268 if (*endp == PATH_SEPARATOR || *endp == 0) 269 { 270 if (endp == startp) 271 { 272 nstore[0] = '.'; 273 nstore[1] = DIR_SEPARATOR; 274 nstore[2] = '\0'; 275 } 276 else 277 { 278 memcpy (nstore, startp, endp - startp); 279 if (! IS_DIR_SEPARATOR (endp[-1])) 280 { 281 nstore[endp - startp] = DIR_SEPARATOR; 282 nstore[endp - startp + 1] = 0; 283 } 284 else 285 nstore[endp - startp] = 0; 286 } 287 strcat (nstore, progname); 288 if (! access (nstore, X_OK) 289#ifdef HAVE_HOST_EXECUTABLE_SUFFIX 290 || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK) 291#endif 292 ) 293 { 294#if defined (HAVE_SYS_STAT_H) && defined (S_ISREG) 295 struct stat st; 296 if (stat (nstore, &st) >= 0 && S_ISREG (st.st_mode)) 297#endif 298 { 299 progname = nstore; 300 break; 301 } 302 } 303 304 if (*endp == 0) 305 break; 306 endp = startp = endp + 1; 307 } 308 else 309 endp++; 310 } 311 } 312 } 313 314 if (resolve_links) 315 full_progname = lrealpath (progname); 316 else 317 full_progname = strdup (progname); 318 if (full_progname == NULL) 319 goto bailout; 320 321 prog_dirs = split_directories (full_progname, &prog_num); 322 free (full_progname); 323 if (prog_dirs == NULL) 324 goto bailout; 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 free (alloc_ptr); 403 404 return ret; 405} 406 407 408/* Do the full job, including symlink resolution. 409 This path will find files installed in the same place as the 410 program even when a soft link has been made to the program 411 from somwhere else. */ 412 413char * 414make_relative_prefix (const char *progname, const char *bin_prefix, 415 const char *prefix) 416{ 417 return make_relative_prefix_1 (progname, bin_prefix, prefix, 1); 418} 419 420/* Make the relative pathname without attempting to resolve any links. 421 '..' etc may also be left in the pathname. 422 This will find the files the user meant the program to find if the 423 installation is patched together with soft links. */ 424 425char * 426make_relative_prefix_ignore_links (const char *progname, 427 const char *bin_prefix, 428 const char *prefix) 429{ 430 return make_relative_prefix_1 (progname, bin_prefix, prefix, 0); 431} 432 433