prefix.c revision 117395
1/* Utility to update paths from internal to external forms.
2   Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002
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., 59 Temple Place - Suite 330,
20Boston, MA 02111-1307, 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#if defined(_WIN32) && defined(ENABLE_WIN32_REGISTRY)
71#include <windows.h>
72#endif
73#include "prefix.h"
74
75static const char *std_prefix = PREFIX;
76
77static const char *get_key_value	PARAMS ((char *));
78static char *translate_name		PARAMS ((char *));
79static char *save_string		PARAMS ((const char *, int));
80static void tr				PARAMS ((char *, int, int));
81
82#if defined(_WIN32) && defined(ENABLE_WIN32_REGISTRY)
83static char *lookup_key		PARAMS ((char *));
84static HKEY reg_key = (HKEY) INVALID_HANDLE_VALUE;
85#endif
86
87/* Given KEY, as above, return its value.  */
88
89static const char *
90get_key_value (key)
91     char *key;
92{
93  const char *prefix = 0;
94  char *temp = 0;
95
96#if defined(_WIN32) && defined(ENABLE_WIN32_REGISTRY)
97  prefix = lookup_key (key);
98#endif
99
100  if (prefix == 0)
101    prefix = getenv (temp = concat (key, "_ROOT", NULL));
102
103  if (prefix == 0)
104    prefix = std_prefix;
105
106  if (temp)
107    free (temp);
108
109  return prefix;
110}
111
112/* Return a copy of a string that has been placed in the heap.  */
113
114static char *
115save_string (s, len)
116  const char *s;
117  int len;
118{
119  char *result = xmalloc (len + 1);
120
121  memcpy (result, s, len);
122  result[len] = 0;
123  return result;
124}
125
126#if defined(_WIN32) && defined(ENABLE_WIN32_REGISTRY)
127
128/* Look up "key" in the registry, as above.  */
129
130static char *
131lookup_key (key)
132     char *key;
133{
134  char *dst;
135  DWORD size;
136  DWORD type;
137  LONG res;
138
139  if (reg_key == (HKEY) INVALID_HANDLE_VALUE)
140    {
141      res = RegOpenKeyExA (HKEY_LOCAL_MACHINE, "SOFTWARE", 0,
142			   KEY_READ, &reg_key);
143
144      if (res == ERROR_SUCCESS)
145	res = RegOpenKeyExA (reg_key, "Free Software Foundation", 0,
146			     KEY_READ, &reg_key);
147
148      if (res == ERROR_SUCCESS)
149	res = RegOpenKeyExA (reg_key, WIN32_REGISTRY_KEY, 0,
150			     KEY_READ, &reg_key);
151
152      if (res != ERROR_SUCCESS)
153	{
154	  reg_key = (HKEY) INVALID_HANDLE_VALUE;
155	  return 0;
156	}
157    }
158
159  size = 32;
160  dst = (char *) xmalloc (size);
161
162  res = RegQueryValueExA (reg_key, key, 0, &type, dst, &size);
163  if (res == ERROR_MORE_DATA && type == REG_SZ)
164    {
165      dst = (char *) xrealloc (dst, size);
166      res = RegQueryValueExA (reg_key, key, 0, &type, dst, &size);
167    }
168
169  if (type != REG_SZ || res != ERROR_SUCCESS)
170    {
171      free (dst);
172      dst = 0;
173    }
174
175  return dst;
176}
177#endif
178
179/* If NAME, a malloc-ed string, starts with a '@' or '$', apply the
180   translation rules above and return a newly malloc-ed name.
181   Otherwise, return the given name.  */
182
183static char *
184translate_name (name)
185     char *name;
186{
187  char code;
188  char *key, *old_name;
189  const char *prefix;
190  int keylen;
191
192  for (;;)
193    {
194      code = name[0];
195      if (code != '@' && code != '$')
196	break;
197
198      for (keylen = 0;
199	   (name[keylen + 1] != 0 && !IS_DIR_SEPARATOR (name[keylen + 1]));
200	   keylen++)
201	;
202
203      key = (char *) alloca (keylen + 1);
204      strncpy (key, &name[1], keylen);
205      key[keylen] = 0;
206
207      if (code == '@')
208	{
209	  prefix = get_key_value (key);
210	  if (prefix == 0)
211	    prefix = std_prefix;
212	}
213      else
214	prefix = getenv (key);
215
216      if (prefix == 0)
217	prefix = PREFIX;
218
219      /* We used to strip trailing DIR_SEPARATORs here, but that can
220	 sometimes yield a result with no separator when one was coded
221	 and intended by the user, causing two path components to run
222	 together.  */
223
224      old_name = name;
225      name = concat (prefix, &name[keylen + 1], NULL);
226      free (old_name);
227    }
228
229  return name;
230}
231
232/* In a NUL-terminated STRING, replace character C1 with C2 in-place.  */
233static void
234tr (string, c1, c2)
235     char *string;
236     int c1, c2;
237{
238  do
239    {
240      if (*string == c1)
241	*string = c2;
242    }
243  while (*string++);
244}
245
246/* Update PATH using KEY if PATH starts with PREFIX.  The returned
247   string is always malloc-ed, and the caller is responsible for
248   freeing it.  */
249
250char *
251update_path (path, key)
252  const char *path;
253  const char *key;
254{
255  char *result, *p;
256
257  if (! strncmp (path, std_prefix, strlen (std_prefix)) && key != 0)
258    {
259      bool free_key = false;
260
261      if (key[0] != '$')
262	{
263	  key = concat ("@", key, NULL);
264	  free_key = true;
265	}
266
267      result = concat (key, &path[strlen (std_prefix)], NULL);
268      if (free_key)
269	free ((char *) key);
270      result = translate_name (result);
271    }
272  else
273    result = xstrdup (path);
274
275#ifndef ALWAYS_STRIP_DOTDOT
276#define ALWAYS_STRIP_DOTDOT 0
277#endif
278
279  p = result;
280  while (1)
281    {
282      char *src, *dest;
283
284      p = strchr (p, '.');
285      if (p == NULL)
286	break;
287      /* Look for `/../'  */
288      if (p[1] == '.'
289	  && IS_DIR_SEPARATOR (p[2])
290	  && (p != result && IS_DIR_SEPARATOR (p[-1])))
291	{
292	  *p = 0;
293	  if (!ALWAYS_STRIP_DOTDOT && access (result, X_OK) == 0)
294	    {
295	      *p = '.';
296	      break;
297	    }
298	  else
299	    {
300	      /* We can't access the dir, so we won't be able to
301		 access dir/.. either.  Strip out `dir/../'.  If `dir'
302		 turns out to be `.', strip one more path component.  */
303	      dest = p;
304	      do
305		{
306		  --dest;
307		  while (dest != result && IS_DIR_SEPARATOR (*dest))
308		    --dest;
309		  while (dest != result && !IS_DIR_SEPARATOR (dest[-1]))
310		    --dest;
311		}
312	      while (dest != result && *dest == '.');
313	      /* If we have something like `./..' or `/..', don't
314		 strip anything more.  */
315	      if (*dest == '.' || IS_DIR_SEPARATOR (*dest))
316		{
317		  *p = '.';
318		  break;
319		}
320	      src = p + 3;
321	      while (IS_DIR_SEPARATOR (*src))
322		++src;
323	      p = dest;
324	      while ((*dest++ = *src++) != 0)
325		;
326	    }
327	}
328      else
329	++p;
330    }
331
332#ifdef UPDATE_PATH_HOST_CANONICALIZE
333  /* Perform host dependent canonicalization when needed.  */
334  UPDATE_PATH_HOST_CANONICALIZE (result);
335#endif
336
337#ifdef DIR_SEPARATOR_2
338  /* Convert DIR_SEPARATOR_2 to DIR_SEPARATOR.  */
339  if (DIR_SEPARATOR_2 != DIR_SEPARATOR)
340    tr (result, DIR_SEPARATOR_2, DIR_SEPARATOR);
341#endif
342
343#if defined (DIR_SEPARATOR) && !defined (DIR_SEPARATOR_2)
344  if (DIR_SEPARATOR != '/')
345    tr (result, '/', DIR_SEPARATOR);
346#endif
347
348  return result;
349}
350
351/* Reset the standard prefix */
352void
353set_std_prefix (prefix, len)
354  const char *prefix;
355  int len;
356{
357  std_prefix = save_string (prefix, len);
358}
359