198b9484cSchristos /* Relative (relocatable) prefix support. 2*5173eb0aSchristos Copyright (C) 1987-2024 Free Software Foundation, Inc. 398b9484cSchristos 498b9484cSchristos This file is part of libiberty. 598b9484cSchristos 698b9484cSchristos GCC is free software; you can redistribute it and/or modify it under 798b9484cSchristos the terms of the GNU General Public License as published by the Free 898b9484cSchristos Software Foundation; either version 2, or (at your option) any later 998b9484cSchristos version. 1098b9484cSchristos 1198b9484cSchristos GCC is distributed in the hope that it will be useful, but WITHOUT ANY 1298b9484cSchristos WARRANTY; without even the implied warranty of MERCHANTABILITY or 1398b9484cSchristos FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1498b9484cSchristos for more details. 1598b9484cSchristos 1698b9484cSchristos You should have received a copy of the GNU General Public License 1798b9484cSchristos along with GCC; see the file COPYING. If not, write to the Free 1898b9484cSchristos Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 1998b9484cSchristos 02110-1301, USA. */ 2098b9484cSchristos 2198b9484cSchristos /* 2298b9484cSchristos 2398b9484cSchristos @deftypefn Extension {const char*} make_relative_prefix (const char *@var{progname}, @ 2498b9484cSchristos const char *@var{bin_prefix}, const char *@var{prefix}) 2598b9484cSchristos 2698b9484cSchristos Given three paths @var{progname}, @var{bin_prefix}, @var{prefix}, 2798b9484cSchristos return the path that is in the same position relative to 2898b9484cSchristos @var{progname}'s directory as @var{prefix} is relative to 2998b9484cSchristos @var{bin_prefix}. That is, a string starting with the directory 3098b9484cSchristos portion of @var{progname}, followed by a relative pathname of the 3198b9484cSchristos difference between @var{bin_prefix} and @var{prefix}. 3298b9484cSchristos 3398b9484cSchristos If @var{progname} does not contain any directory separators, 3498b9484cSchristos @code{make_relative_prefix} will search @env{PATH} to find a program 3598b9484cSchristos named @var{progname}. Also, if @var{progname} is a symbolic link, 3698b9484cSchristos the symbolic link will be resolved. 3798b9484cSchristos 3898b9484cSchristos For example, if @var{bin_prefix} is @code{/alpha/beta/gamma/gcc/delta}, 3998b9484cSchristos @var{prefix} is @code{/alpha/beta/gamma/omega/}, and @var{progname} is 4098b9484cSchristos @code{/red/green/blue/gcc}, then this function will return 4198b9484cSchristos @code{/red/green/blue/../../omega/}. 4298b9484cSchristos 4398b9484cSchristos The return value is normally allocated via @code{malloc}. If no 4498b9484cSchristos relative prefix can be found, return @code{NULL}. 4598b9484cSchristos 4698b9484cSchristos @end deftypefn 4798b9484cSchristos 4898b9484cSchristos */ 4998b9484cSchristos 5098b9484cSchristos #ifdef HAVE_CONFIG_H 5198b9484cSchristos #include "config.h" 5298b9484cSchristos #endif 5398b9484cSchristos 5498b9484cSchristos #ifdef HAVE_STDLIB_H 5598b9484cSchristos #include <stdlib.h> 5698b9484cSchristos #endif 5798b9484cSchristos #ifdef HAVE_UNISTD_H 5898b9484cSchristos #include <unistd.h> 5998b9484cSchristos #endif 60a2e2270fSchristos #ifdef HAVE_SYS_STAT_H 61a2e2270fSchristos #include <sys/stat.h> 62a2e2270fSchristos #endif 6398b9484cSchristos 6498b9484cSchristos #include <string.h> 6598b9484cSchristos 6698b9484cSchristos #include "ansidecl.h" 6798b9484cSchristos #include "libiberty.h" 6898b9484cSchristos 6998b9484cSchristos #ifndef R_OK 7098b9484cSchristos #define R_OK 4 7198b9484cSchristos #define W_OK 2 7298b9484cSchristos #define X_OK 1 7398b9484cSchristos #endif 7498b9484cSchristos 7598b9484cSchristos #ifndef DIR_SEPARATOR 7698b9484cSchristos # define DIR_SEPARATOR '/' 7798b9484cSchristos #endif 7898b9484cSchristos 7998b9484cSchristos #if defined (_WIN32) || defined (__MSDOS__) \ 8098b9484cSchristos || defined (__DJGPP__) || defined (__OS2__) 8198b9484cSchristos # define HAVE_DOS_BASED_FILE_SYSTEM 8298b9484cSchristos # define HAVE_HOST_EXECUTABLE_SUFFIX 8398b9484cSchristos # define HOST_EXECUTABLE_SUFFIX ".exe" 8498b9484cSchristos # ifndef DIR_SEPARATOR_2 8598b9484cSchristos # define DIR_SEPARATOR_2 '\\' 8698b9484cSchristos # endif 8798b9484cSchristos # define PATH_SEPARATOR ';' 8898b9484cSchristos #else 8998b9484cSchristos # define PATH_SEPARATOR ':' 9098b9484cSchristos #endif 9198b9484cSchristos 9298b9484cSchristos #ifndef DIR_SEPARATOR_2 9398b9484cSchristos # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) 9498b9484cSchristos #else 9598b9484cSchristos # define IS_DIR_SEPARATOR(ch) \ 9698b9484cSchristos (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) 9798b9484cSchristos #endif 9898b9484cSchristos 9998b9484cSchristos #define DIR_UP ".." 10098b9484cSchristos 10198b9484cSchristos static char *save_string (const char *, int); 10298b9484cSchristos static char **split_directories (const char *, int *); 10398b9484cSchristos static void free_split_directories (char **); 10498b9484cSchristos 10598b9484cSchristos static char * 10698b9484cSchristos save_string (const char *s, int len) 10798b9484cSchristos { 10898b9484cSchristos char *result = (char *) malloc (len + 1); 10998b9484cSchristos 11098b9484cSchristos memcpy (result, s, len); 11198b9484cSchristos result[len] = 0; 11298b9484cSchristos return result; 11398b9484cSchristos } 11498b9484cSchristos 11598b9484cSchristos /* Split a filename into component directories. */ 11698b9484cSchristos 11798b9484cSchristos static char ** 11898b9484cSchristos split_directories (const char *name, int *ptr_num_dirs) 11998b9484cSchristos { 12098b9484cSchristos int num_dirs = 0; 12198b9484cSchristos char **dirs; 12298b9484cSchristos const char *p, *q; 12398b9484cSchristos int ch; 12498b9484cSchristos 1258dffb485Schristos if (!*name) 1268dffb485Schristos return NULL; 1278dffb485Schristos 12898b9484cSchristos /* Count the number of directories. Special case MSDOS disk names as part 12998b9484cSchristos of the initial directory. */ 13098b9484cSchristos p = name; 13198b9484cSchristos #ifdef HAVE_DOS_BASED_FILE_SYSTEM 13298b9484cSchristos if (name[1] == ':' && IS_DIR_SEPARATOR (name[2])) 13398b9484cSchristos { 13498b9484cSchristos p += 3; 13598b9484cSchristos num_dirs++; 13698b9484cSchristos } 13798b9484cSchristos #endif /* HAVE_DOS_BASED_FILE_SYSTEM */ 13898b9484cSchristos 13998b9484cSchristos while ((ch = *p++) != '\0') 14098b9484cSchristos { 14198b9484cSchristos if (IS_DIR_SEPARATOR (ch)) 14298b9484cSchristos { 14398b9484cSchristos num_dirs++; 14498b9484cSchristos while (IS_DIR_SEPARATOR (*p)) 14598b9484cSchristos p++; 14698b9484cSchristos } 14798b9484cSchristos } 14898b9484cSchristos 14998b9484cSchristos dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2)); 15098b9484cSchristos if (dirs == NULL) 15198b9484cSchristos return NULL; 15298b9484cSchristos 15398b9484cSchristos /* Now copy the directory parts. */ 15498b9484cSchristos num_dirs = 0; 15598b9484cSchristos p = name; 15698b9484cSchristos #ifdef HAVE_DOS_BASED_FILE_SYSTEM 15798b9484cSchristos if (name[1] == ':' && IS_DIR_SEPARATOR (name[2])) 15898b9484cSchristos { 15998b9484cSchristos dirs[num_dirs++] = save_string (p, 3); 16098b9484cSchristos if (dirs[num_dirs - 1] == NULL) 16198b9484cSchristos { 16298b9484cSchristos free (dirs); 16398b9484cSchristos return NULL; 16498b9484cSchristos } 16598b9484cSchristos p += 3; 16698b9484cSchristos } 16798b9484cSchristos #endif /* HAVE_DOS_BASED_FILE_SYSTEM */ 16898b9484cSchristos 16998b9484cSchristos q = p; 17098b9484cSchristos while ((ch = *p++) != '\0') 17198b9484cSchristos { 17298b9484cSchristos if (IS_DIR_SEPARATOR (ch)) 17398b9484cSchristos { 17498b9484cSchristos while (IS_DIR_SEPARATOR (*p)) 17598b9484cSchristos p++; 17698b9484cSchristos 17798b9484cSchristos dirs[num_dirs++] = save_string (q, p - q); 17898b9484cSchristos if (dirs[num_dirs - 1] == NULL) 17998b9484cSchristos { 18098b9484cSchristos dirs[num_dirs] = NULL; 18198b9484cSchristos free_split_directories (dirs); 18298b9484cSchristos return NULL; 18398b9484cSchristos } 18498b9484cSchristos q = p; 18598b9484cSchristos } 18698b9484cSchristos } 18798b9484cSchristos 18898b9484cSchristos if (p - 1 - q > 0) 18998b9484cSchristos dirs[num_dirs++] = save_string (q, p - 1 - q); 19098b9484cSchristos dirs[num_dirs] = NULL; 19198b9484cSchristos 19298b9484cSchristos if (dirs[num_dirs - 1] == NULL) 19398b9484cSchristos { 19498b9484cSchristos free_split_directories (dirs); 19598b9484cSchristos return NULL; 19698b9484cSchristos } 19798b9484cSchristos 19898b9484cSchristos if (ptr_num_dirs) 19998b9484cSchristos *ptr_num_dirs = num_dirs; 20098b9484cSchristos return dirs; 20198b9484cSchristos } 20298b9484cSchristos 20398b9484cSchristos /* Release storage held by split directories. */ 20498b9484cSchristos 20598b9484cSchristos static void 20698b9484cSchristos free_split_directories (char **dirs) 20798b9484cSchristos { 20898b9484cSchristos int i = 0; 20998b9484cSchristos 21098b9484cSchristos if (dirs != NULL) 21198b9484cSchristos { 21298b9484cSchristos while (dirs[i] != NULL) 21398b9484cSchristos free (dirs[i++]); 21498b9484cSchristos 21598b9484cSchristos free ((char *) dirs); 21698b9484cSchristos } 21798b9484cSchristos } 21898b9484cSchristos 21998b9484cSchristos /* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets 22098b9484cSchristos to PREFIX starting with the directory portion of PROGNAME and a relative 22198b9484cSchristos pathname of the difference between BIN_PREFIX and PREFIX. 22298b9484cSchristos 22398b9484cSchristos For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is 22498b9484cSchristos /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this 22598b9484cSchristos function will return /red/green/blue/../../omega/. 22698b9484cSchristos 22798b9484cSchristos If no relative prefix can be found, return NULL. */ 22898b9484cSchristos 22998b9484cSchristos static char * 23098b9484cSchristos make_relative_prefix_1 (const char *progname, const char *bin_prefix, 23198b9484cSchristos const char *prefix, const int resolve_links) 23298b9484cSchristos { 23398b9484cSchristos char **prog_dirs = NULL, **bin_dirs = NULL, **prefix_dirs = NULL; 23498b9484cSchristos int prog_num, bin_num, prefix_num; 23598b9484cSchristos int i, n, common; 23698b9484cSchristos int needed_len; 23798b9484cSchristos char *ret = NULL, *ptr, *full_progname; 238ba340e45Schristos char *alloc_ptr = NULL; 23998b9484cSchristos 24098b9484cSchristos if (progname == NULL || bin_prefix == NULL || prefix == NULL) 24198b9484cSchristos return NULL; 24298b9484cSchristos 24398b9484cSchristos /* If there is no full pathname, try to find the program by checking in each 24498b9484cSchristos of the directories specified in the PATH environment variable. */ 24598b9484cSchristos if (lbasename (progname) == progname) 24698b9484cSchristos { 24798b9484cSchristos char *temp; 24898b9484cSchristos 24998b9484cSchristos temp = getenv ("PATH"); 25098b9484cSchristos if (temp) 25198b9484cSchristos { 25298b9484cSchristos char *startp, *endp, *nstore; 25398b9484cSchristos size_t prefixlen = strlen (temp) + 1; 254a2e2270fSchristos size_t len; 25598b9484cSchristos if (prefixlen < 2) 25698b9484cSchristos prefixlen = 2; 25798b9484cSchristos 258a2e2270fSchristos len = prefixlen + strlen (progname) + 1; 259a2e2270fSchristos #ifdef HAVE_HOST_EXECUTABLE_SUFFIX 260a2e2270fSchristos len += strlen (HOST_EXECUTABLE_SUFFIX); 261a2e2270fSchristos #endif 262ba340e45Schristos if (len < MAX_ALLOCA_SIZE) 263a2e2270fSchristos nstore = (char *) alloca (len); 264ba340e45Schristos else 265ba340e45Schristos alloc_ptr = nstore = (char *) malloc (len); 26698b9484cSchristos 26798b9484cSchristos startp = endp = temp; 26898b9484cSchristos while (1) 26998b9484cSchristos { 27098b9484cSchristos if (*endp == PATH_SEPARATOR || *endp == 0) 27198b9484cSchristos { 27298b9484cSchristos if (endp == startp) 27398b9484cSchristos { 27498b9484cSchristos nstore[0] = '.'; 27598b9484cSchristos nstore[1] = DIR_SEPARATOR; 27698b9484cSchristos nstore[2] = '\0'; 27798b9484cSchristos } 27898b9484cSchristos else 27998b9484cSchristos { 280a2e2270fSchristos memcpy (nstore, startp, endp - startp); 28198b9484cSchristos if (! IS_DIR_SEPARATOR (endp[-1])) 28298b9484cSchristos { 28398b9484cSchristos nstore[endp - startp] = DIR_SEPARATOR; 28498b9484cSchristos nstore[endp - startp + 1] = 0; 28598b9484cSchristos } 28698b9484cSchristos else 28798b9484cSchristos nstore[endp - startp] = 0; 28898b9484cSchristos } 28998b9484cSchristos strcat (nstore, progname); 29098b9484cSchristos if (! access (nstore, X_OK) 29198b9484cSchristos #ifdef HAVE_HOST_EXECUTABLE_SUFFIX 29298b9484cSchristos || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK) 29398b9484cSchristos #endif 29498b9484cSchristos ) 29598b9484cSchristos { 296a2e2270fSchristos #if defined (HAVE_SYS_STAT_H) && defined (S_ISREG) 297a2e2270fSchristos struct stat st; 298a2e2270fSchristos if (stat (nstore, &st) >= 0 && S_ISREG (st.st_mode)) 299a2e2270fSchristos #endif 300a2e2270fSchristos { 30198b9484cSchristos progname = nstore; 30298b9484cSchristos break; 30398b9484cSchristos } 304a2e2270fSchristos } 30598b9484cSchristos 30698b9484cSchristos if (*endp == 0) 30798b9484cSchristos break; 30898b9484cSchristos endp = startp = endp + 1; 30998b9484cSchristos } 31098b9484cSchristos else 31198b9484cSchristos endp++; 31298b9484cSchristos } 31398b9484cSchristos } 31498b9484cSchristos } 31598b9484cSchristos 31698b9484cSchristos if (resolve_links) 31798b9484cSchristos full_progname = lrealpath (progname); 31898b9484cSchristos else 31998b9484cSchristos full_progname = strdup (progname); 32098b9484cSchristos if (full_progname == NULL) 321ba340e45Schristos goto bailout; 32298b9484cSchristos 32398b9484cSchristos prog_dirs = split_directories (full_progname, &prog_num); 32498b9484cSchristos free (full_progname); 32598b9484cSchristos if (prog_dirs == NULL) 326ba340e45Schristos goto bailout; 32798b9484cSchristos 32898b9484cSchristos bin_dirs = split_directories (bin_prefix, &bin_num); 32998b9484cSchristos if (bin_dirs == NULL) 33098b9484cSchristos goto bailout; 33198b9484cSchristos 33298b9484cSchristos /* Remove the program name from comparison of directory names. */ 33398b9484cSchristos prog_num--; 33498b9484cSchristos 33598b9484cSchristos /* If we are still installed in the standard location, we don't need to 33698b9484cSchristos specify relative directories. Also, if argv[0] still doesn't contain 33798b9484cSchristos any directory specifiers after the search above, then there is not much 33898b9484cSchristos we can do. */ 33998b9484cSchristos if (prog_num == bin_num) 34098b9484cSchristos { 34198b9484cSchristos for (i = 0; i < bin_num; i++) 34298b9484cSchristos { 34398b9484cSchristos if (strcmp (prog_dirs[i], bin_dirs[i]) != 0) 34498b9484cSchristos break; 34598b9484cSchristos } 34698b9484cSchristos 34798b9484cSchristos if (prog_num <= 0 || i == bin_num) 34898b9484cSchristos goto bailout; 34998b9484cSchristos } 35098b9484cSchristos 35198b9484cSchristos prefix_dirs = split_directories (prefix, &prefix_num); 35298b9484cSchristos if (prefix_dirs == NULL) 35398b9484cSchristos goto bailout; 35498b9484cSchristos 35598b9484cSchristos /* Find how many directories are in common between bin_prefix & prefix. */ 35698b9484cSchristos n = (prefix_num < bin_num) ? prefix_num : bin_num; 35798b9484cSchristos for (common = 0; common < n; common++) 35898b9484cSchristos { 35998b9484cSchristos if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0) 36098b9484cSchristos break; 36198b9484cSchristos } 36298b9484cSchristos 36398b9484cSchristos /* If there are no common directories, there can be no relative prefix. */ 36498b9484cSchristos if (common == 0) 36598b9484cSchristos goto bailout; 36698b9484cSchristos 36798b9484cSchristos /* Two passes: first figure out the size of the result string, and 36898b9484cSchristos then construct it. */ 36998b9484cSchristos needed_len = 0; 37098b9484cSchristos for (i = 0; i < prog_num; i++) 37198b9484cSchristos needed_len += strlen (prog_dirs[i]); 37298b9484cSchristos needed_len += sizeof (DIR_UP) * (bin_num - common); 37398b9484cSchristos for (i = common; i < prefix_num; i++) 37498b9484cSchristos needed_len += strlen (prefix_dirs[i]); 37598b9484cSchristos needed_len += 1; /* Trailing NUL. */ 37698b9484cSchristos 37798b9484cSchristos ret = (char *) malloc (needed_len); 37898b9484cSchristos if (ret == NULL) 37998b9484cSchristos goto bailout; 38098b9484cSchristos 38198b9484cSchristos /* Build up the pathnames in argv[0]. */ 38298b9484cSchristos *ret = '\0'; 38398b9484cSchristos for (i = 0; i < prog_num; i++) 38498b9484cSchristos strcat (ret, prog_dirs[i]); 38598b9484cSchristos 38698b9484cSchristos /* Now build up the ..'s. */ 38798b9484cSchristos ptr = ret + strlen(ret); 38898b9484cSchristos for (i = common; i < bin_num; i++) 38998b9484cSchristos { 39098b9484cSchristos strcpy (ptr, DIR_UP); 39198b9484cSchristos ptr += sizeof (DIR_UP) - 1; 39298b9484cSchristos *(ptr++) = DIR_SEPARATOR; 39398b9484cSchristos } 39498b9484cSchristos *ptr = '\0'; 39598b9484cSchristos 39698b9484cSchristos /* Put in directories to move over to prefix. */ 39798b9484cSchristos for (i = common; i < prefix_num; i++) 39898b9484cSchristos strcat (ret, prefix_dirs[i]); 39998b9484cSchristos 40098b9484cSchristos bailout: 40198b9484cSchristos free_split_directories (prog_dirs); 40298b9484cSchristos free_split_directories (bin_dirs); 40398b9484cSchristos free_split_directories (prefix_dirs); 404ba340e45Schristos free (alloc_ptr); 40598b9484cSchristos 40698b9484cSchristos return ret; 40798b9484cSchristos } 40898b9484cSchristos 40998b9484cSchristos 41098b9484cSchristos /* Do the full job, including symlink resolution. 41198b9484cSchristos This path will find files installed in the same place as the 41298b9484cSchristos program even when a soft link has been made to the program 41398b9484cSchristos from somwhere else. */ 41498b9484cSchristos 41598b9484cSchristos char * 41698b9484cSchristos make_relative_prefix (const char *progname, const char *bin_prefix, 41798b9484cSchristos const char *prefix) 41898b9484cSchristos { 41998b9484cSchristos return make_relative_prefix_1 (progname, bin_prefix, prefix, 1); 42098b9484cSchristos } 42198b9484cSchristos 42298b9484cSchristos /* Make the relative pathname without attempting to resolve any links. 42398b9484cSchristos '..' etc may also be left in the pathname. 42498b9484cSchristos This will find the files the user meant the program to find if the 42598b9484cSchristos installation is patched together with soft links. */ 42698b9484cSchristos 42798b9484cSchristos char * 42898b9484cSchristos make_relative_prefix_ignore_links (const char *progname, 42998b9484cSchristos const char *bin_prefix, 43098b9484cSchristos const char *prefix) 43198b9484cSchristos { 43298b9484cSchristos return make_relative_prefix_1 (progname, bin_prefix, prefix, 0); 43398b9484cSchristos } 43498b9484cSchristos 435