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