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