OSDN Git Service

2007-04-16 H.J. Lu <hongjiu.lu@intel.com>
[pf3gnuchains/gcc-fork.git] / gcc / java / jcf-path.c
1 /* Handle CLASSPATH, -classpath, and path searching.
2    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006
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
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 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
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING.  If not, write to
19 the Free Software Foundation, 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.  
21
22 Java and all Java-based marks are trademarks or registered trademarks
23 of Sun Microsystems, Inc. in the United States and other countries.
24 The Free Software Foundation is independent of Sun Microsystems, Inc.  */
25
26 /* Written by Tom Tromey <tromey@cygnus.com>, October 1998.  */
27
28 #include "config.h"
29 #include "system.h"
30 #include "coretypes.h"
31 #include "tm.h"
32
33 #include <dirent.h>
34
35 #include "jcf.h"
36
37 #ifndef DIR_UP
38 #define DIR_UP ".."
39 #endif
40
41 \f
42
43 /* Possible flag values.  */
44 #define FLAG_SYSTEM 1
45 #define FLAG_ZIP    2
46
47 /* We keep linked lists of directory names.  A ``directory'' can be
48    either an ordinary directory or a .zip file.  */
49 struct entry
50 {
51   char *name;
52   int flags;
53   struct entry *next;
54 };
55
56 static void free_entry (struct entry **);
57 static void append_entry (struct entry **, struct entry *);
58 static void add_entry (struct entry **, const char *, int);
59 static void add_path (struct entry **, const char *, int);
60
61 /* We support several different ways to set the class path.
62
63    built-in system directory (only libgcj.jar)
64    CLASSPATH environment variable
65    -classpath option overrides $CLASSPATH
66    -CLASSPATH option is a synonym for -classpath (for compatibility)
67    -bootclasspath overrides built-in
68    -extdirs sets the extensions directory path (overrides built-in)
69    -I prepends path to list
70
71    We implement this by keeping several path lists, and then simply
72    ignoring the ones which are not relevant.  */
73
74 /* This holds all the -I directories.  */
75 static struct entry *include_dirs;
76
77 /* This holds the CLASSPATH environment variable.  */
78 static struct entry *classpath_env;
79
80 /* This holds the -classpath command-line option.  */
81 static struct entry *classpath_user;
82
83 /* This holds the default directories.  Some of these will have the
84    "system" flag set.  */
85 static struct entry *sys_dirs;
86
87 /* This holds the extensions path entries.  */
88 static struct entry *extensions;
89
90 /* This is the sealed list.  It is just a combination of other lists.  */
91 static struct entry *sealed;
92
93 /* We keep track of the longest path we've seen.  */
94 static int longest_path = 0;
95
96 \f
97
98 static void
99 free_entry (struct entry **entp)
100 {
101   struct entry *e, *n;
102
103   for (e = *entp; e; e = n)
104     {
105       n = e->next;
106       free (e->name);
107       free (e);
108     }
109   *entp = NULL;
110 }
111
112 static void
113 append_entry (struct entry **entp, struct entry *ent)
114 {
115   /* It doesn't matter if this is slow, since it is run only at
116      startup, and then infrequently.  */
117   struct entry *e;
118
119   /* Find end of list.  */
120   for (e = *entp; e && e->next; e = e->next)
121     ;
122
123   if (e)
124     e->next = ent;
125   else
126     *entp = ent;
127 }
128
129 static void
130 add_entry (struct entry **entp, const char *filename, int is_system)
131 {
132   int len;
133   struct entry *n;
134
135   n = XNEW (struct entry);
136   n->flags = is_system ? FLAG_SYSTEM : 0;
137   n->next = NULL;
138
139   len = strlen (filename);
140
141   if (len > 4 && (FILENAME_CMP (filename + len - 4, ".zip") == 0
142                   || FILENAME_CMP (filename + len - 4, ".jar") == 0))
143     {
144       n->flags |= FLAG_ZIP;
145       /* If the user uses -classpath then he'll have to include
146          libgcj.jar in the value.  We check for this in a simplistic
147          way.  Symlinks will fool this test.  This is only used for
148          -MM and -MMD, so it probably isn't terribly important.  */
149       if (! FILENAME_CMP (filename, LIBGCJ_ZIP_FILE))
150         n->flags |= FLAG_SYSTEM;
151     }
152
153   /* Note that we add a trailing separator to `.zip' names as well.
154      This is a little hack that lets the searching code in jcf-io.c
155      work more easily.  Eww.  */
156   if (! IS_DIR_SEPARATOR (filename[len - 1]))
157     {
158       char *f2 = alloca (len + 2);
159       strcpy (f2, filename);
160       f2[len] = DIR_SEPARATOR;
161       f2[len + 1] = '\0';
162       n->name = xstrdup (f2);
163       ++len;
164     }
165   else
166     n->name = xstrdup (filename);
167
168   if (len > longest_path)
169     longest_path = len;
170
171   append_entry (entp, n);
172 }
173
174 static void
175 add_path (struct entry **entp, const char *cp, int is_system)
176 {
177   const char *startp, *endp;
178
179   if (cp)
180     {
181       char *buf = alloca (strlen (cp) + 3);
182       startp = endp = cp;
183       while (1)
184         {
185           if (! *endp || *endp == PATH_SEPARATOR)
186             {
187               if (endp == startp)
188                 {
189                   buf[0] = '.';
190                   buf[1] = DIR_SEPARATOR;
191                   buf[2] = '\0';
192                 }
193               else
194                 {
195                   strncpy (buf, startp, endp - startp);
196                   buf[endp - startp] = '\0';
197                 }
198               add_entry (entp, buf, is_system);
199               if (! *endp)
200                 break;
201               ++endp;
202               startp = endp;
203             }
204           else
205             ++endp;
206         }
207     }
208 }
209
210 static int init_done = 0;
211
212 /* Initialize the path module.  */
213 void
214 jcf_path_init (void)
215 {
216   char *cp;
217   char *try, sep[2];
218   struct stat stat_b;
219   int found = 0, len;
220
221   if (init_done)
222     return;
223   init_done = 1;
224
225   sep[0] = DIR_SEPARATOR;
226   sep[1] = '\0';
227
228   GET_ENVIRONMENT (cp, "GCC_EXEC_PREFIX");
229   if (cp)
230     {
231       try = alloca (strlen (cp) + 50);
232       /* The exec prefix can be something like
233          /usr/local/bin/../lib/gcc-lib/.  We want to change this
234          into a pointer to the share/java directory.  We support two
235          configurations: one where prefix and exec-prefix are the
236          same, and one where exec-prefix is `prefix/SOMETHING'.  */
237       strcpy (try, cp);
238       strcat (try, DIR_UP);
239       strcat (try, sep);
240       strcat (try, DIR_UP);
241       strcat (try, sep);
242       len = strlen (try);
243
244       strcpy (try + len, "share");
245       strcat (try, sep);
246       strcat (try, "java");
247       strcat (try, sep);
248       strcat (try, "libgcj-" DEFAULT_TARGET_VERSION ".jar");
249       if (! stat (try, &stat_b))
250         {
251           add_entry (&sys_dirs, try, 1);
252           found = 1;
253           strcpy (&try[strlen (try)
254                       - strlen ("libgcj-" DEFAULT_TARGET_VERSION ".jar")],
255                   sep);
256           strcat (try, "ext");
257           strcat (try, sep);
258           if (! stat (try, &stat_b))
259             jcf_path_extdirs_arg (try);
260         }
261       else
262         {
263           strcpy (try + len, DIR_UP);
264           strcat (try, sep);
265           strcat (try, "share");
266           strcat (try, sep);
267           strcat (try, "java");
268           strcat (try, sep);
269           strcat (try, "libgcj-" DEFAULT_TARGET_VERSION ".jar");
270           if (! stat (try, &stat_b))
271             {
272               add_entry (&sys_dirs, try, 1);
273               found = 1;
274               strcpy (&try[strlen (try)
275                           - strlen ("libgcj-" DEFAULT_TARGET_VERSION ".jar")],
276                       sep);
277               strcat (try, "ext");
278               strcat (try, sep);
279               if (! stat (try, &stat_b))
280                 jcf_path_extdirs_arg (try);
281             }
282         }
283     }
284   if (! found)
285     {
286       /* Desperation: use the installed one.  */
287       char *extdirs;
288       add_entry (&sys_dirs, LIBGCJ_ZIP_FILE, 1);
289       extdirs = alloca (strlen (LIBGCJ_ZIP_FILE) + 1);
290       strcpy (extdirs, LIBGCJ_ZIP_FILE);
291       strcpy (&extdirs[strlen (LIBGCJ_ZIP_FILE)
292                       - strlen ("libgcj-" DEFAULT_TARGET_VERSION ".jar")],
293               "ext");
294       strcat (extdirs, sep);
295       if (! stat (extdirs, &stat_b))
296         jcf_path_extdirs_arg (extdirs);
297     }
298
299   GET_ENVIRONMENT (cp, "CLASSPATH");
300   add_path (&classpath_env, cp, 0);
301 }
302
303 /* Call this when -classpath is seen on the command line.
304    This overrides only the $CLASSPATH environment variable.
305  */
306 void
307 jcf_path_classpath_arg (const char *path)
308 {
309   free_entry (&classpath_user);
310   add_path (&classpath_user, path, 0);
311 }
312
313 /* Call this when -bootclasspath is seen on the command line.
314  */
315 void
316 jcf_path_bootclasspath_arg (const char *path)
317 {
318   free_entry (&sys_dirs);
319   add_path (&sys_dirs, path, 1);
320 }
321
322 /* Call this when -extdirs is seen on the command line.
323  */
324 void
325 jcf_path_extdirs_arg (const char *cp)
326 {
327   const char *startp, *endp;
328
329   free_entry (&extensions);
330
331   if (cp)
332     {
333       char *buf = alloca (strlen (cp) + 3);
334       startp = endp = cp;
335       while (1)
336         {
337           if (! *endp || *endp == PATH_SEPARATOR)
338             {
339               if (endp == startp)
340                 return;
341
342               strncpy (buf, startp, endp - startp);
343               buf[endp - startp] = '\0';
344
345               {  
346                 DIR *dirp = NULL;
347                 int dirname_length = strlen (buf);
348                 
349                 dirp = opendir (buf);
350                 if (dirp == NULL)
351                   return;
352                 
353                 for (;;)
354                   {
355                     struct dirent *direntp = readdir (dirp);
356                     
357                     if (!direntp)
358                       break;
359                     
360                     if (direntp->d_name[0] != '.')
361                       {
362                         char *name = alloca (dirname_length
363                                              + strlen (direntp->d_name) + 2);
364                         strcpy (name, buf);
365                         if (! IS_DIR_SEPARATOR (name[dirname_length-1]))
366                           {
367                             name[dirname_length] = DIR_SEPARATOR;
368                             name[dirname_length+1] = 0;
369                           }
370                         strcat (name, direntp->d_name);
371                         add_entry (&extensions, name, 0);
372                       }
373                   }
374                 if (dirp)
375                   closedir (dirp);
376               }
377
378               if (! *endp)
379                 break;
380               ++endp;
381               startp = endp;
382             }
383           else
384             ++endp;
385         }
386     }
387 }
388
389 /* Call this when -I is seen on the command line.  */
390 void
391 jcf_path_include_arg (const char *path)
392 {
393   add_entry (&include_dirs, path, 0);
394 }
395
396 /* We `seal' the path by linking everything into one big list.  Then
397    we provide a way to iterate through the sealed list.  If PRINT is
398    true then we print the final class path to stderr.  */
399 void
400 jcf_path_seal (int print)
401 {
402   struct entry *secondary;
403
404   sealed = include_dirs;
405   include_dirs = NULL;
406
407   if (classpath_user)
408     {
409       secondary = classpath_user;
410       classpath_user = NULL;
411     }
412   else
413     {
414       if (! classpath_env)
415         add_entry (&classpath_env, ".", 0);
416
417       secondary = classpath_env;
418       classpath_env = NULL;
419     }
420
421
422   free_entry (&classpath_user);
423   free_entry (&classpath_env);
424
425   append_entry (&sealed, secondary);
426   append_entry (&sealed, sys_dirs);
427   append_entry (&sealed, extensions);
428   sys_dirs = NULL;
429   extensions = NULL;
430
431   if (print)
432     {
433       struct entry *ent;
434       fprintf (stderr, "Class path starts here:\n");
435       for (ent = sealed; ent; ent = ent->next)
436         {
437           fprintf (stderr, "    %s", ent->name);
438           if ((ent->flags & FLAG_SYSTEM))
439             fprintf (stderr, " (system)");
440           if ((ent->flags & FLAG_ZIP))
441             fprintf (stderr, " (zip)");
442           fprintf (stderr, "\n");
443         }
444     }
445 }
446
447 void *
448 jcf_path_start (void)
449 {
450   return (void *) sealed;
451 }
452
453 void *
454 jcf_path_next (void *x)
455 {
456   struct entry *ent = (struct entry *) x;
457   return (void *) ent->next;
458 }
459
460 static const char
461 PATH_SEPARATOR_STR[] = {PATH_SEPARATOR, '\0'};
462
463 char *
464 jcf_path_compute (const char *prefix)
465 {
466   struct entry *iter;
467   char *result;
468   int length = strlen (prefix) + 1;
469   int first;
470
471   for (iter = sealed; iter != NULL; iter = iter->next)
472     length += strlen (iter->name) + 1;
473
474   result = (char *) xmalloc (length);
475   strcpy (result, prefix);
476   first = 1;
477   for (iter = sealed; iter != NULL; iter = iter->next)
478     {
479       if (! first)
480         strcat (result, PATH_SEPARATOR_STR);
481       first = 0;
482       strcat (result, iter->name);
483       /* Ugly: we want to strip the '/' from zip entries when
484          computing a string classpath.  */
485       if ((iter->flags & FLAG_ZIP) != 0)
486         result[strlen (result) - 1] = '\0';
487     }
488
489   return result;
490 }
491
492 /* We guarantee that the return path will either be a zip file, or it
493    will end with a directory separator.  */
494 char *
495 jcf_path_name (void *x)
496 {
497   struct entry *ent = (struct entry *) x;
498   return ent->name;
499 }
500
501 int
502 jcf_path_is_zipfile (void *x)
503 {
504   struct entry *ent = (struct entry *) x;
505   return (ent->flags & FLAG_ZIP);
506 }
507
508 int
509 jcf_path_is_system (void *x)
510 {
511   struct entry *ent = (struct entry *) x;
512   return (ent->flags & FLAG_SYSTEM);
513 }
514
515 int
516 jcf_path_max_len (void)
517 {
518   return longest_path;
519 }