1/* Utility to update paths from internal to external forms. 2 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 3 Free Software Foundation, Inc. 4 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it under 8the terms of the GNU Library General Public License as published by 9the Free Software Foundation; either version 2 of the License, or (at 10your option) any later version. 11 12GCC is distributed in the hope that it will be useful, 13but WITHOUT ANY WARRANTY; without even the implied warranty of 14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15Library General Public License for more details. 16 17You should have received a copy of the GNU Library General Public 18License along with GCC; see the file COPYING. If not, write to the Free 19Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20Boston, MA 02110-1301, USA. */ 21 22/* This file contains routines to update a path, both to canonicalize 23 the directory format and to handle any prefix translation. 24 25 This file must be compiled with -DPREFIX= to specify the "prefix" 26 value used by configure. If a filename does not begin with this 27 prefix, it will not be affected other than by directory canonicalization. 28 29 Each caller of 'update_path' may specify both a filename and 30 a translation prefix and consist of the name of the package that contains 31 the file ("@GCC", "@BINUTIL", "@GNU", etc). 32 33 If the prefix is not specified, the filename will only undergo 34 directory canonicalization. 35 36 If it is specified, the string given by PREFIX will be replaced 37 by the specified prefix (with a '@' in front unless the prefix begins 38 with a '$') and further translation will be done as follows 39 until none of the two conditions below are met: 40 41 1) If the filename begins with '@', the string between the '@' and 42 the end of the name or the first '/' or directory separator will 43 be considered a "key" and looked up as follows: 44 45 -- If this is a Win32 OS, then the Registry will be examined for 46 an entry of "key" in 47 48 HKEY_LOCAL_MACHINE\SOFTWARE\Free Software Foundation\<KEY> 49 50 if found, that value will be used. <KEY> defaults to GCC version 51 string, but can be overridden at configuration time. 52 53 -- If not found (or not a Win32 OS), the environment variable 54 key_ROOT (the value of "key" concatenated with the constant "_ROOT") 55 is tried. If that fails, then PREFIX (see above) is used. 56 57 2) If the filename begins with a '$', the rest of the string up 58 to the end or the first '/' or directory separator will be used 59 as an environment variable, whose value will be returned. 60 61 Once all this is done, any '/' will be converted to DIR_SEPARATOR, 62 if they are different. 63 64 NOTE: using resolve_keyed_path under Win32 requires linking with 65 advapi32.dll. */ 66 67 68#include "config.h" 69#include "system.h" 70#include "coretypes.h" 71#include "tm.h" 72#if defined(_WIN32) && defined(ENABLE_WIN32_REGISTRY) 73#include <windows.h> 74#endif 75#include "prefix.h" 76 77static const char *std_prefix = PREFIX; 78 79static const char *get_key_value (char *); 80static char *translate_name (char *); 81static char *save_string (const char *, int); 82static void tr (char *, int, int); 83 84#if defined(_WIN32) && defined(ENABLE_WIN32_REGISTRY) 85static char *lookup_key (char *); 86static HKEY reg_key = (HKEY) INVALID_HANDLE_VALUE; 87#endif 88 89/* Given KEY, as above, return its value. */ 90 91static const char * 92get_key_value (char *key) 93{ 94 const char *prefix = 0; 95 char *temp = 0; 96 97#if defined(_WIN32) && defined(ENABLE_WIN32_REGISTRY) 98 prefix = lookup_key (key); 99#endif 100 101 if (prefix == 0) 102 prefix = getenv (temp = concat (key, "_ROOT", NULL)); 103 104 if (prefix == 0) 105 prefix = std_prefix; 106 107 if (temp) 108 free (temp); 109 110 return prefix; 111} 112 113/* Return a copy of a string that has been placed in the heap. */ 114 115static char * 116save_string (const char *s, int len) 117{ 118 char *result = XNEWVEC (char, len + 1); 119 120 memcpy (result, s, len); 121 result[len] = 0; 122 return result; 123} 124 125#if defined(_WIN32) && defined(ENABLE_WIN32_REGISTRY) 126 127#ifndef WIN32_REGISTRY_KEY 128# define WIN32_REGISTRY_KEY BASEVER 129#endif 130 131/* Look up "key" in the registry, as above. */ 132 133static char * 134lookup_key (char *key) 135{ 136 char *dst; 137 DWORD size; 138 DWORD type; 139 LONG res; 140 141 if (reg_key == (HKEY) INVALID_HANDLE_VALUE) 142 { 143 res = RegOpenKeyExA (HKEY_LOCAL_MACHINE, "SOFTWARE", 0, 144 KEY_READ, ®_key); 145 146 if (res == ERROR_SUCCESS) 147 res = RegOpenKeyExA (reg_key, "Free Software Foundation", 0, 148 KEY_READ, ®_key); 149 150 if (res == ERROR_SUCCESS) 151 res = RegOpenKeyExA (reg_key, WIN32_REGISTRY_KEY, 0, 152 KEY_READ, ®_key); 153 154 if (res != ERROR_SUCCESS) 155 { 156 reg_key = (HKEY) INVALID_HANDLE_VALUE; 157 return 0; 158 } 159 } 160 161 size = 32; 162 dst = xmalloc (size); 163 164 res = RegQueryValueExA (reg_key, key, 0, &type, (LPBYTE) dst, &size); 165 if (res == ERROR_MORE_DATA && type == REG_SZ) 166 { 167 dst = xrealloc (dst, size); 168 res = RegQueryValueExA (reg_key, key, 0, &type, (LPBYTE) dst, &size); 169 } 170 171 if (type != REG_SZ || res != ERROR_SUCCESS) 172 { 173 free (dst); 174 dst = 0; 175 } 176 177 return dst; 178} 179#endif 180 181/* If NAME, a malloc-ed string, starts with a '@' or '$', apply the 182 translation rules above and return a newly malloc-ed name. 183 Otherwise, return the given name. */ 184 185static char * 186translate_name (char *name) 187{ 188 char code; 189 char *key, *old_name; 190 const char *prefix; 191 int keylen; 192 193 for (;;) 194 { 195 code = name[0]; 196 if (code != '@' && code != '$') 197 break; 198 199 for (keylen = 0; 200 (name[keylen + 1] != 0 && !IS_DIR_SEPARATOR (name[keylen + 1])); 201 keylen++) 202 ; 203 204 key = (char *) alloca (keylen + 1); 205 strncpy (key, &name[1], keylen); 206 key[keylen] = 0; 207 208 if (code == '@') 209 { 210 prefix = get_key_value (key); 211 if (prefix == 0) 212 prefix = std_prefix; 213 } 214 else 215 prefix = getenv (key); 216 217 if (prefix == 0) 218 prefix = PREFIX; 219 220 /* We used to strip trailing DIR_SEPARATORs here, but that can 221 sometimes yield a result with no separator when one was coded 222 and intended by the user, causing two path components to run 223 together. */ 224 225 old_name = name; 226 name = concat (prefix, &name[keylen + 1], NULL); 227 free (old_name); 228 } 229 230 return name; 231} 232 233/* In a NUL-terminated STRING, replace character C1 with C2 in-place. */ 234static void 235tr (char *string, int c1, int c2) 236{ 237 do 238 { 239 if (*string == c1) 240 *string = c2; 241 } 242 while (*string++); 243} 244 245/* Update PATH using KEY if PATH starts with PREFIX as a directory. 246 The returned string is always malloc-ed, and the caller is 247 responsible for freeing it. */ 248 249char * 250update_path (const char *path, const char *key) 251{ 252 char *result, *p; 253 const int len = strlen (std_prefix); 254 255 if (! strncmp (path, std_prefix, len) 256 && (IS_DIR_SEPARATOR(path[len]) 257 || path[len] == '\0') 258 && key != 0) 259 { 260 bool free_key = false; 261 262 if (key[0] != '$') 263 { 264 key = concat ("@", key, NULL); 265 free_key = true; 266 } 267 268 result = concat (key, &path[len], NULL); 269 if (free_key) 270 free ((char *) key); 271 result = translate_name (result); 272 } 273 else 274 result = xstrdup (path); 275 276#ifndef ALWAYS_STRIP_DOTDOT 277#define ALWAYS_STRIP_DOTDOT 0 278#endif 279 280 p = result; 281 while (1) 282 { 283 char *src, *dest; 284 285 p = strchr (p, '.'); 286 if (p == NULL) 287 break; 288 /* Look for `/../' */ 289 if (p[1] == '.' 290 && IS_DIR_SEPARATOR (p[2]) 291 && (p != result && IS_DIR_SEPARATOR (p[-1]))) 292 { 293 *p = 0; 294 if (!ALWAYS_STRIP_DOTDOT && access (result, X_OK) == 0) 295 { 296 *p = '.'; 297 break; 298 } 299 else 300 { 301 /* We can't access the dir, so we won't be able to 302 access dir/.. either. Strip out `dir/../'. If `dir' 303 turns out to be `.', strip one more path component. */ 304 dest = p; 305 do 306 { 307 --dest; 308 while (dest != result && IS_DIR_SEPARATOR (*dest)) 309 --dest; 310 while (dest != result && !IS_DIR_SEPARATOR (dest[-1])) 311 --dest; 312 } 313 while (dest != result && *dest == '.'); 314 /* If we have something like `./..' or `/..', don't 315 strip anything more. */ 316 if (*dest == '.' || IS_DIR_SEPARATOR (*dest)) 317 { 318 *p = '.'; 319 break; 320 } 321 src = p + 3; 322 while (IS_DIR_SEPARATOR (*src)) 323 ++src; 324 p = dest; 325 while ((*dest++ = *src++) != 0) 326 ; 327 } 328 } 329 else 330 ++p; 331 } 332 333#ifdef UPDATE_PATH_HOST_CANONICALIZE 334 /* Perform host dependent canonicalization when needed. */ 335 UPDATE_PATH_HOST_CANONICALIZE (result); 336#endif 337 338#ifdef DIR_SEPARATOR_2 339 /* Convert DIR_SEPARATOR_2 to DIR_SEPARATOR. */ 340 if (DIR_SEPARATOR_2 != DIR_SEPARATOR) 341 tr (result, DIR_SEPARATOR_2, DIR_SEPARATOR); 342#endif 343 344#if defined (DIR_SEPARATOR) && !defined (DIR_SEPARATOR_2) 345 if (DIR_SEPARATOR != '/') 346 tr (result, '/', DIR_SEPARATOR); 347#endif 348 349 return result; 350} 351 352/* Reset the standard prefix. */ 353void 354set_std_prefix (const char *prefix, int len) 355{ 356 std_prefix = save_string (prefix, len); 357} 358