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 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, 59 Temple Place - Suite 330, Boston, MA 2002111-1307, 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 26Given three paths @var{progname}, @var{bin_prefix}, @var{prefix}, 27return 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 30portion of @var{progname}, followed by a relative pathname of the 31difference between @var{bin_prefix} and @var{prefix}. 32 33If @var{progname} does not contain any directory separators, 34@code{make_relative_prefix} will search @env{PATH} to find a program 35named @var{progname}. Also, if @var{progname} is a symbolic link, 36the symbolic link will be resolved. 37 38For 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 43The return value is normally allocated via @code{malloc}. If no 44relative 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 98static char *save_string PARAMS ((const char *, int)); 99static char **split_directories PARAMS ((const char *, int *)); 100static void free_split_directories PARAMS ((char **)); 101 102static char * 103save_string (s, len) 104 const char *s; 105 int len; 106{ 107 char *result = malloc (len + 1); 108 109 memcpy (result, s, len); 110 result[len] = 0; 111 return result; 112} 113 114/* Split a filename into component directories. */ 115 116static char ** 117split_directories (name, ptr_num_dirs) 118 const char *name; 119 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 (dirs) 205 char **dirs; 206{ 207 int i = 0; 208 209 while (dirs[i] != NULL) 210 free (dirs[i++]); 211 212 free ((char *) dirs); 213} 214 215/* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets 216 to PREFIX starting with the directory portion of PROGNAME and a relative 217 pathname of the difference between BIN_PREFIX and PREFIX. 218 219 For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is 220 /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this 221 function will return /red/green/blue/../../omega/. 222 223 If no relative prefix can be found, return NULL. */ 224 225char * 226make_relative_prefix (progname, bin_prefix, prefix) 227 const char *progname; 228 const char *bin_prefix; 229 const char *prefix; 230{ 231 char **prog_dirs, **bin_dirs, **prefix_dirs; 232 int prog_num, bin_num, prefix_num; 233 int i, n, common; 234 int needed_len; 235 char *ret, *ptr, *full_progname = NULL; 236 237 if (progname == NULL || bin_prefix == NULL || prefix == NULL) 238 return NULL; 239 240 /* If there is no full pathname, try to find the program by checking in each 241 of the directories specified in the PATH environment variable. */ 242 if (lbasename (progname) == progname) 243 { 244 char *temp; 245 246 temp = getenv ("PATH"); 247 if (temp) 248 { 249 char *startp, *endp, *nstore; 250 size_t prefixlen = strlen (temp) + 1; 251 if (prefixlen < 2) 252 prefixlen = 2; 253 254 nstore = (char *) alloca (prefixlen + strlen (progname) + 1); 255 256 startp = endp = temp; 257 while (1) 258 { 259 if (*endp == PATH_SEPARATOR || *endp == 0) 260 { 261 if (endp == startp) 262 { 263 nstore[0] = '.'; 264 nstore[1] = DIR_SEPARATOR; 265 nstore[2] = '\0'; 266 } 267 else 268 { 269 strncpy (nstore, startp, endp - startp); 270 if (! IS_DIR_SEPARATOR (endp[-1])) 271 { 272 nstore[endp - startp] = DIR_SEPARATOR; 273 nstore[endp - startp + 1] = 0; 274 } 275 else 276 nstore[endp - startp] = 0; 277 } 278 strcat (nstore, progname); 279 if (! access (nstore, X_OK) 280#ifdef HAVE_HOST_EXECUTABLE_SUFFIX 281 || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK) 282#endif 283 ) 284 { 285 progname = nstore; 286 break; 287 } 288 289 if (*endp == 0) 290 break; 291 endp = startp = endp + 1; 292 } 293 else 294 endp++; 295 } 296 } 297 } 298 299 full_progname = lrealpath (progname); 300 if (full_progname == NULL) 301 return NULL; 302 303 prog_dirs = split_directories (full_progname, &prog_num); 304 bin_dirs = split_directories (bin_prefix, &bin_num); 305 free (full_progname); 306 if (bin_dirs == NULL || prog_dirs == NULL) 307 return NULL; 308 309 /* Remove the program name from comparison of directory names. */ 310 prog_num--; 311 312 /* If we are still installed in the standard location, we don't need to 313 specify relative directories. Also, if argv[0] still doesn't contain 314 any directory specifiers after the search above, then there is not much 315 we can do. */ 316 if (prog_num == bin_num) 317 { 318 for (i = 0; i < bin_num; i++) 319 { 320 if (strcmp (prog_dirs[i], bin_dirs[i]) != 0) 321 break; 322 } 323 324 if (prog_num <= 0 || i == bin_num) 325 { 326 free_split_directories (prog_dirs); 327 free_split_directories (bin_dirs); 328 prog_dirs = bin_dirs = (char **) 0; 329 return NULL; 330 } 331 } 332 333 prefix_dirs = split_directories (prefix, &prefix_num); 334 if (prefix_dirs == NULL) 335 { 336 free_split_directories (prog_dirs); 337 free_split_directories (bin_dirs); 338 return NULL; 339 } 340 341 /* Find how many directories are in common between bin_prefix & prefix. */ 342 n = (prefix_num < bin_num) ? prefix_num : bin_num; 343 for (common = 0; common < n; common++) 344 { 345 if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0) 346 break; 347 } 348 349 /* If there are no common directories, there can be no relative prefix. */ 350 if (common == 0) 351 { 352 free_split_directories (prog_dirs); 353 free_split_directories (bin_dirs); 354 free_split_directories (prefix_dirs); 355 return NULL; 356 } 357 358 /* Two passes: first figure out the size of the result string, and 359 then construct it. */ 360 needed_len = 0; 361 for (i = 0; i < prog_num; i++) 362 needed_len += strlen (prog_dirs[i]); 363 needed_len += sizeof (DIR_UP) * (bin_num - common); 364 for (i = common; i < prefix_num; i++) 365 needed_len += strlen (prefix_dirs[i]); 366 needed_len += 1; /* Trailing NUL. */ 367 368 ret = (char *) malloc (needed_len); 369 if (ret == NULL) 370 return NULL; 371 372 /* Build up the pathnames in argv[0]. */ 373 *ret = '\0'; 374 for (i = 0; i < prog_num; i++) 375 strcat (ret, prog_dirs[i]); 376 377 /* Now build up the ..'s. */ 378 ptr = ret + strlen(ret); 379 for (i = common; i < bin_num; i++) 380 { 381 strcpy (ptr, DIR_UP); 382 ptr += sizeof (DIR_UP) - 1; 383 *(ptr++) = DIR_SEPARATOR; 384 } 385 *ptr = '\0'; 386 387 /* Put in directories to move over to prefix. */ 388 for (i = common; i < prefix_num; i++) 389 strcat (ret, prefix_dirs[i]); 390 391 free_split_directories (prog_dirs); 392 free_split_directories (bin_dirs); 393 free_split_directories (prefix_dirs); 394 395 return ret; 396} 397