prefix.c revision 169690
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, &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
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