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