OSDN Git Service

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