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 
5 This file is part of GCC.
6 
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU Library General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at
10 your option) any later version.
11 
12 GCC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Library General Public License for more details.
16 
17 You should have received a copy of the GNU Library General Public
18 License along with GCC; see the file COPYING.  If not, write to the Free
19 Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, 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 
77 static const char *std_prefix = PREFIX;
78 
79 static const char *get_key_value (char *);
80 static char *translate_name (char *);
81 static char *save_string (const char *, int);
82 static void tr (char *, int, int);
83 
84 #if defined(_WIN32) && defined(ENABLE_WIN32_REGISTRY)
85 static char *lookup_key (char *);
86 static HKEY reg_key = (HKEY) INVALID_HANDLE_VALUE;
87 #endif
88 
89 /* Given KEY, as above, return its value.  */
90 
91 static const char *
get_key_value(char * key)92 get_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 
115 static char *
save_string(const char * s,int len)116 save_string (const char *s, int len)
117 {
118   char *result = xmalloc (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 
133 static char *
lookup_key(char * key)134 lookup_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, &reg_key);
145 
146       if (res == ERROR_SUCCESS)
147 	res = RegOpenKeyExA (reg_key, "Free Software Foundation", 0,
148 			     KEY_READ, &reg_key);
149 
150       if (res == ERROR_SUCCESS)
151 	res = RegOpenKeyExA (reg_key, WIN32_REGISTRY_KEY, 0,
152 			     KEY_READ, &reg_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 
185 static char *
translate_name(char * name)186 translate_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 = 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.  */
234 static void
tr(char * string,int c1,int c2)235 tr (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 
249 char *
update_path(const char * path,const char * key)250 update_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.  */
353 void
set_std_prefix(const char * prefix,int len)354 set_std_prefix (const char *prefix, int len)
355 {
356   std_prefix = save_string (prefix, len);
357 }
358