make-relative-prefix.c revision 130562
1193323Sed/* Relative (relocatable) prefix support. 2193323Sed Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 3193323Sed 1999, 2000, 2001, 2002 Free Software Foundation, Inc. 4193323Sed 5193323SedThis file is part of libiberty. 6193323Sed 7193323SedGCC is free software; you can redistribute it and/or modify it under 8193323Sedthe terms of the GNU General Public License as published by the Free 9193323SedSoftware Foundation; either version 2, or (at your option) any later 10193323Sedversion. 11193323Sed 12203954SrdivackyGCC is distributed in the hope that it will be useful, but WITHOUT ANY 13193323SedWARRANTY; without even the implied warranty of MERCHANTABILITY or 14193323SedFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15193323Sedfor more details. 16193323Sed 17193323SedYou should have received a copy of the GNU General Public License 18193323Sedalong with GCC; see the file COPYING. If not, write to the Free 19234353SdimSoftware Foundation, 59 Temple Place - Suite 330, Boston, MA 20249423Sdim02111-1307, USA. */ 21239462Sdim 22249423Sdim/* 23249423Sdim 24239462Sdim@deftypefn Extension {const char*} make_relative_prefix (const char *@var{progname}, const char *@var{bin_prefix}, const char *@var{prefix}) 25243830Sdim 26193323SedGiven three paths @var{progname}, @var{bin_prefix}, @var{prefix}, 27193323Sedreturn the path that is in the same position relative to 28193323Sed@var{progname}'s directory as @var{prefix} is relative to 29193323Sed@var{bin_prefix}. That is, a string starting with the directory 30239462Sdimportion of @var{progname}, followed by a relative pathname of the 31193323Seddifference between @var{bin_prefix} and @var{prefix}. 32193323Sed 33193323SedIf @var{progname} does not contain any directory separators, 34198090Srdivacky@code{make_relative_prefix} will search @env{PATH} to find a program 35249423Sdimnamed @var{progname}. Also, if @var{progname} is a symbolic link, 36249423Sdimthe symbolic link will be resolved. 37249423Sdim 38193323SedFor example, if @var{bin_prefix} is @code{/alpha/beta/gamma/gcc/delta}, 39193323Sed@var{prefix} is @code{/alpha/beta/gamma/omega/}, and @var{progname} is 40193323Sed@code{/red/green/blue/gcc}, then this function will return 41226633Sdim@code{/red/green/blue/../../omega/}. 42226633Sdim 43193323SedThe return value is normally allocated via @code{malloc}. If no 44193323Sedrelative prefix can be found, return @code{NULL}. 45193323Sed 46193323Sed@end deftypefn 47193323Sed 48193323Sed*/ 49193323Sed 50218893Sdim#ifdef HAVE_CONFIG_H 51193323Sed#include "config.h" 52193323Sed#endif 53194612Sed 54193323Sed#ifdef HAVE_STDLIB_H 55193323Sed#include <stdlib.h> 56193323Sed#endif 57198892Srdivacky#ifdef HAVE_UNISTD_H 58193323Sed#include <unistd.h> 59193323Sed#endif 60193323Sed 61193323Sed#include <string.h> 62193323Sed 63193323Sed#include "ansidecl.h" 64193323Sed#include "libiberty.h" 65193323Sed 66193323Sed#ifndef R_OK 67218893Sdim#define R_OK 4 68218893Sdim#define W_OK 2 69218893Sdim#define X_OK 1 70218893Sdim#endif 71193323Sed 72193323Sed#ifndef DIR_SEPARATOR 73218893Sdim# define DIR_SEPARATOR '/' 74218893Sdim#endif 75218893Sdim 76226633Sdim#if defined (_WIN32) || defined (__MSDOS__) \ 77218893Sdim || defined (__DJGPP__) || defined (__OS2__) 78218893Sdim# define HAVE_DOS_BASED_FILE_SYSTEM 79218893Sdim# define HAVE_HOST_EXECUTABLE_SUFFIX 80249423Sdim# define HOST_EXECUTABLE_SUFFIX ".exe" 81249423Sdim# ifndef DIR_SEPARATOR_2 82249423Sdim# define DIR_SEPARATOR_2 '\\' 83249423Sdim# endif 84249423Sdim# define PATH_SEPARATOR ';' 85249423Sdim#else 86249423Sdim# define PATH_SEPARATOR ':' 87194612Sed#endif 88194612Sed 89249423Sdim#ifndef DIR_SEPARATOR_2 90249423Sdim# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) 91249423Sdim#else 92249423Sdim# define IS_DIR_SEPARATOR(ch) \ 93249423Sdim (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) 94249423Sdim#endif 95249423Sdim 96249423Sdim#define DIR_UP ".." 97249423Sdim 98193323Sedstatic char *save_string PARAMS ((const char *, int)); 99193323Sedstatic char **split_directories PARAMS ((const char *, int *)); 100193323Sedstatic void free_split_directories PARAMS ((char **)); 101193323Sed 102193323Sedstatic char * 103218893Sdimsave_string (s, len) 104193323Sed const char *s; 105193323Sed int len; 106193323Sed{ 107193323Sed char *result = malloc (len + 1); 108193323Sed 109193323Sed memcpy (result, s, len); 110193323Sed result[len] = 0; 111193323Sed return result; 112193323Sed} 113193323Sed 114193323Sed/* Split a filename into component directories. */ 115193323Sed 116239462Sdimstatic char ** 117239462Sdimsplit_directories (name, ptr_num_dirs) 118239462Sdim const char *name; 119239462Sdim int *ptr_num_dirs; 120239462Sdim{ 121239462Sdim int num_dirs = 0; 122239462Sdim char **dirs; 123239462Sdim const char *p, *q; 124239462Sdim int ch; 125239462Sdim 126239462Sdim /* Count the number of directories. Special case MSDOS disk names as part 127239462Sdim of the initial directory. */ 128239462Sdim p = name; 129239462Sdim#ifdef HAVE_DOS_BASED_FILE_SYSTEM 130239462Sdim if (name[1] == ':' && IS_DIR_SEPARATOR (name[2])) 131239462Sdim { 132239462Sdim p += 3; 133239462Sdim num_dirs++; 134239462Sdim } 135239462Sdim#endif /* HAVE_DOS_BASED_FILE_SYSTEM */ 136239462Sdim 137243830Sdim while ((ch = *p++) != '\0') 138239462Sdim { 139239462Sdim if (IS_DIR_SEPARATOR (ch)) 140239462Sdim { 141243830Sdim num_dirs++; 142239462Sdim while (IS_DIR_SEPARATOR (*p)) 143239462Sdim p++; 144239462Sdim } 145239462Sdim } 146239462Sdim 147239462Sdim dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2)); 148239462Sdim if (dirs == NULL) 149239462Sdim return NULL; 150239462Sdim 151239462Sdim /* Now copy the directory parts. */ 152239462Sdim num_dirs = 0; 153239462Sdim p = name; 154239462Sdim#ifdef HAVE_DOS_BASED_FILE_SYSTEM 155239462Sdim if (name[1] == ':' && IS_DIR_SEPARATOR (name[2])) 156239462Sdim { 157239462Sdim dirs[num_dirs++] = save_string (p, 3); 158239462Sdim if (dirs[num_dirs - 1] == NULL) 159239462Sdim { 160239462Sdim free (dirs); 161239462Sdim return NULL; 162239462Sdim } 163239462Sdim p += 3; 164239462Sdim } 165239462Sdim#endif /* HAVE_DOS_BASED_FILE_SYSTEM */ 166239462Sdim 167239462Sdim q = p; 168239462Sdim while ((ch = *p++) != '\0') 169239462Sdim { 170239462Sdim if (IS_DIR_SEPARATOR (ch)) 171239462Sdim { 172239462Sdim while (IS_DIR_SEPARATOR (*p)) 173239462Sdim p++; 174239462Sdim 175239462Sdim dirs[num_dirs++] = save_string (q, p - q); 176239462Sdim if (dirs[num_dirs - 1] == NULL) 177239462Sdim { 178239462Sdim dirs[num_dirs] = NULL; 179239462Sdim free_split_directories (dirs); 180239462Sdim return NULL; 181239462Sdim } 182243830Sdim q = p; 183239462Sdim } 184239462Sdim } 185239462Sdim 186239462Sdim if (p - 1 - q > 0) 187239462Sdim dirs[num_dirs++] = save_string (q, p - 1 - q); 188239462Sdim dirs[num_dirs] = NULL; 189239462Sdim 190239462Sdim if (dirs[num_dirs - 1] == NULL) 191239462Sdim { 192239462Sdim free_split_directories (dirs); 193239462Sdim return NULL; 194239462Sdim } 195243830Sdim 196243830Sdim if (ptr_num_dirs) 197239462Sdim *ptr_num_dirs = num_dirs; 198239462Sdim return dirs; 199239462Sdim} 200239462Sdim 201239462Sdim/* Release storage held by split directories. */ 202239462Sdim 203239462Sdimstatic void 204239462Sdimfree_split_directories (dirs) 205239462Sdim char **dirs; 206239462Sdim{ 207239462Sdim int i = 0; 208239462Sdim 209239462Sdim while (dirs[i] != NULL) 210218893Sdim free (dirs[i++]); 211218893Sdim 212243830Sdim free ((char *) dirs); 213239462Sdim} 214239462Sdim 215239462Sdim/* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets 216243830Sdim to PREFIX starting with the directory portion of PROGNAME and a relative 217239462Sdim pathname of the difference between BIN_PREFIX and PREFIX. 218239462Sdim 219239462Sdim For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is 220239462Sdim /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this 221239462Sdim function will return /red/green/blue/../../omega/. 222239462Sdim 223239462Sdim If no relative prefix can be found, return NULL. */ 224239462Sdim 225239462Sdimchar * 226239462Sdimmake_relative_prefix (progname, bin_prefix, prefix) 227239462Sdim const char *progname; 228239462Sdim const char *bin_prefix; 229239462Sdim const char *prefix; 230239462Sdim{ 231239462Sdim char **prog_dirs, **bin_dirs, **prefix_dirs; 232239462Sdim int prog_num, bin_num, prefix_num; 233239462Sdim int i, n, common; 234239462Sdim int needed_len; 235239462Sdim char *ret, *ptr, *full_progname = NULL; 236239462Sdim 237239462Sdim if (progname == NULL || bin_prefix == NULL || prefix == NULL) 238239462Sdim return NULL; 239239462Sdim 240239462Sdim /* If there is no full pathname, try to find the program by checking in each 241239462Sdim of the directories specified in the PATH environment variable. */ 242239462Sdim if (lbasename (progname) == progname) 243239462Sdim { 244239462Sdim char *temp; 245239462Sdim 246239462Sdim temp = getenv ("PATH"); 247239462Sdim if (temp) 248239462Sdim { 249239462Sdim char *startp, *endp, *nstore; 250239462Sdim size_t prefixlen = strlen (temp) + 1; 251239462Sdim if (prefixlen < 2) 252239462Sdim prefixlen = 2; 253239462Sdim 254239462Sdim nstore = (char *) alloca (prefixlen + strlen (progname) + 1); 255239462Sdim 256239462Sdim startp = endp = temp; 257239462Sdim while (1) 258239462Sdim { 259239462Sdim if (*endp == PATH_SEPARATOR || *endp == 0) 260239462Sdim { 261239462Sdim if (endp == startp) 262239462Sdim { 263239462Sdim nstore[0] = '.'; 264221345Sdim nstore[1] = DIR_SEPARATOR; 265221345Sdim nstore[2] = '\0'; 266218893Sdim } 267218893Sdim else 268218893Sdim { 269218893Sdim strncpy (nstore, startp, endp - startp); 270218893Sdim if (! IS_DIR_SEPARATOR (endp[-1])) 271234353Sdim { 272234353Sdim nstore[endp - startp] = DIR_SEPARATOR; 273218893Sdim nstore[endp - startp + 1] = 0; 274218893Sdim } 275218893Sdim else 276218893Sdim nstore[endp - startp] = 0; 277218893Sdim } 278218893Sdim strcat (nstore, progname); 279234353Sdim if (! access (nstore, X_OK) 280234353Sdim#ifdef HAVE_HOST_EXECUTABLE_SUFFIX 281234353Sdim || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK) 282234353Sdim#endif 283234353Sdim ) 284218893Sdim { 285218893Sdim progname = nstore; 286218893Sdim break; 287239462Sdim } 288239462Sdim 289239462Sdim if (*endp == 0) 290243830Sdim break; 291218893Sdim endp = startp = endp + 1; 292218893Sdim } 293218893Sdim else 294218893Sdim endp++; 295226633Sdim } 296226633Sdim } 297226633Sdim } 298226633Sdim 299218893Sdim full_progname = lrealpath (progname); 300226633Sdim if (full_progname == NULL) 301226633Sdim return NULL; 302226633Sdim 303226633Sdim prog_dirs = split_directories (full_progname, &prog_num); 304226633Sdim bin_dirs = split_directories (bin_prefix, &bin_num); 305226633Sdim free (full_progname); 306226633Sdim if (bin_dirs == NULL || prog_dirs == NULL) 307226633Sdim return NULL; 308226633Sdim 309226633Sdim /* Remove the program name from comparison of directory names. */ 310226633Sdim prog_num--; 311226633Sdim 312226633Sdim /* If we are still installed in the standard location, we don't need to 313226633Sdim specify relative directories. Also, if argv[0] still doesn't contain 314218893Sdim any directory specifiers after the search above, then there is not much 315218893Sdim we can do. */ 316218893Sdim if (prog_num == bin_num) 317218893Sdim { 318218893Sdim for (i = 0; i < bin_num; i++) 319218893Sdim { 320218893Sdim if (strcmp (prog_dirs[i], bin_dirs[i]) != 0) 321218893Sdim break; 322218893Sdim } 323218893Sdim 324218893Sdim if (prog_num <= 0 || i == bin_num) 325218893Sdim { 326218893Sdim free_split_directories (prog_dirs); 327218893Sdim free_split_directories (bin_dirs); 328218893Sdim prog_dirs = bin_dirs = (char **) 0; 329221345Sdim return NULL; 330221345Sdim } 331221345Sdim } 332221345Sdim 333221345Sdim prefix_dirs = split_directories (prefix, &prefix_num); 334221345Sdim if (prefix_dirs == NULL) 335218893Sdim { 336218893Sdim free_split_directories (prog_dirs); 337218893Sdim free_split_directories (bin_dirs); 338218893Sdim return NULL; 339223017Sdim } 340221345Sdim 341221345Sdim /* Find how many directories are in common between bin_prefix & prefix. */ 342221345Sdim n = (prefix_num < bin_num) ? prefix_num : bin_num; 343221345Sdim for (common = 0; common < n; common++) 344221345Sdim { 345221345Sdim if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0) 346221345Sdim break; 347221345Sdim } 348221345Sdim 349221345Sdim /* If there are no common directories, there can be no relative prefix. */ 350221345Sdim if (common == 0) 351218893Sdim { 352218893Sdim free_split_directories (prog_dirs); 353218893Sdim free_split_directories (bin_dirs); 354218893Sdim free_split_directories (prefix_dirs); 355218893Sdim return NULL; 356218893Sdim } 357218893Sdim 358218893Sdim /* Two passes: first figure out the size of the result string, and 359218893Sdim 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