source: trunk/icedtea-web/launcher/wildcard.c@ 401

Last change on this file since 401 was 348, checked in by dmik, 13 years ago

vendor: Add icedtea-web v1.1.2 to current.

File size: 13.3 KB
Line 
1/*
2 * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * Class-Path Wildcards
28 *
29 * The syntax for wildcards is a single asterisk. The class path
30 * foo/"*", e.g., loads all jar files in the directory named foo.
31 * (This requires careful quotation when used in shell scripts.)
32 *
33 * Only files whose names end in .jar or .JAR are matched.
34 * Files whose names end in .zip, or which have a particular
35 * magic number, regardless of filename extension, are not
36 * matched.
37 *
38 * Files are considered regardless of whether or not they are
39 * "hidden" in the UNIX sense, i.e., have names beginning with '.'.
40 *
41 * A wildcard only matches jar files, not class files in the same
42 * directory. If you want to load both class files and jar files from
43 * a single directory foo then you can say foo:foo/"*", or foo/"*":foo
44 * if you want the jar files to take precedence.
45 *
46 * Subdirectories are not searched recursively, i.e., foo/"*" only
47 * looks for jar files in foo, not in foo/bar, foo/baz, etc.
48 *
49 * Expansion of wildcards is done early, prior to the invocation of a
50 * program's main method, rather than late, during the class-loading
51 * process itself. Each element of the input class path containing a
52 * wildcard is replaced by the (possibly empty) sequence of elements
53 * generated by enumerating the jar files in the named directory. If
54 * the directory foo contains a.jar, b.jar, and c.jar,
55 * e.g., then the class path foo/"*" is expanded into
56 * foo/a.jar:foo/b.jar:foo/c.jar, and that string would be the value
57 * of the system property java.class.path.
58 *
59 * The order in which the jar files in a directory are enumerated in
60 * the expanded class path is not specified and may vary from platform
61 * to platform and even from moment to moment on the same machine. A
62 * well-constructed application should not depend upon any particular
63 * order. If a specific order is required then the jar files can be
64 * enumerated explicitly in the class path.
65 *
66 * The CLASSPATH environment variable is not treated any differently
67 * from the -classpath (equiv. -cp) command-line option,
68 * i.e. wildcards are honored in all these cases.
69 *
70 * Class-path wildcards are not honored in the Class-Path jar-manifest
71 * header.
72 *
73 * Class-path wildcards are honored not only by the Java launcher but
74 * also by most other command-line tools that accept class paths, and
75 * in particular by javac and javadoc.
76 *
77 * Class-path wildcards are not honored in any other kind of path, and
78 * especially not in the bootstrap class path, which is a mere
79 * artifact of our implementation and not something that developers
80 * should use.
81 *
82 * Classpath wildcards are only expanded in the Java launcher code,
83 * supporting the use of wildcards on the command line and in the
84 * CLASSPATH environment variable. We do not support the use of
85 * wildcards by applications that embed the JVM.
86 */
87
88#include <stddef.h>
89#include <stdio.h>
90#include <stdlib.h>
91#include <string.h>
92#include <sys/types.h>
93#include "java.h" /* Strictly for PATH_SEPARATOR/FILE_SEPARATOR */
94#include "jli_util.h"
95
96#ifdef _WIN32
97#include <windows.h>
98#else /* Unix */
99#include <unistd.h>
100#include <dirent.h>
101#endif /* Unix */
102
103static int
104exists(const char* filename)
105{
106#ifdef _WIN32
107 return _access(filename, 0) == 0;
108#else
109 return access(filename, F_OK) == 0;
110#endif
111}
112
113#define NEW_(TYPE) ((TYPE) JLI_MemAlloc(sizeof(struct TYPE##_)))
114
115/*
116 * Wildcard directory iteration.
117 * WildcardIterator_for(wildcard) returns an iterator.
118 * Each call to that iterator's next() method returns the basename
119 * of an entry in the wildcard's directory. The basename's memory
120 * belongs to the iterator. The caller is responsible for prepending
121 * the directory name and file separator, if necessary.
122 * When done with the iterator, call the close method to clean up.
123 */
124typedef struct WildcardIterator_* WildcardIterator;
125
126#ifdef _WIN32
127struct WildcardIterator_
128{
129 HANDLE handle;
130 char *firstFile; /* Stupid FindFirstFile...FindNextFile */
131};
132
133static WildcardIterator
134WildcardIterator_for(const char *wildcard)
135{
136 WIN32_FIND_DATA find_data;
137 WildcardIterator it = NEW_(WildcardIterator);
138 HANDLE handle = FindFirstFile(wildcard, &find_data);
139 if (handle == INVALID_HANDLE_VALUE)
140 return NULL;
141 it->handle = handle;
142 it->firstFile = find_data.cFileName;
143 return it;
144}
145
146static char *
147WildcardIterator_next(WildcardIterator it)
148{
149 WIN32_FIND_DATA find_data;
150 if (it->firstFile != NULL) {
151 char *firstFile = it->firstFile;
152 it->firstFile = NULL;
153 return firstFile;
154 }
155 return FindNextFile(it->handle, &find_data)
156 ? find_data.cFileName : NULL;
157}
158
159static void
160WildcardIterator_close(WildcardIterator it)
161{
162 if (it) {
163 FindClose(it->handle);
164 JLI_MemFree(it->firstFile);
165 JLI_MemFree(it);
166 }
167}
168
169#else /* Unix */
170struct WildcardIterator_
171{
172 DIR *dir;
173};
174
175static WildcardIterator
176WildcardIterator_for(const char *wildcard)
177{
178 DIR *dir;
179 int wildlen = strlen(wildcard);
180 if (wildlen < 2) {
181 dir = opendir(".");
182 } else {
183 char *dirname = JLI_StringDup(wildcard);
184 dirname[wildlen - 1] = '\0';
185 dir = opendir(dirname);
186 JLI_MemFree(dirname);
187 }
188 if (dir == NULL)
189 return NULL;
190 else {
191 WildcardIterator it = NEW_(WildcardIterator);
192 it->dir = dir;
193 return it;
194 }
195}
196
197static char *
198WildcardIterator_next(WildcardIterator it)
199{
200 struct dirent* dirp = readdir(it->dir);
201 return dirp ? dirp->d_name : NULL;
202}
203
204static void
205WildcardIterator_close(WildcardIterator it)
206{
207 if (it) {
208 closedir(it->dir);
209 JLI_MemFree(it);
210 }
211}
212#endif /* Unix */
213
214static int
215equal(const char *s1, const char *s2)
216{
217 return strcmp(s1, s2) == 0;
218}
219
220/*
221 * FileList ADT - a dynamic list of C filenames
222 */
223struct FileList_
224{
225 char **files;
226 int size;
227 int capacity;
228};
229typedef struct FileList_ *FileList;
230
231static FileList
232FileList_new(int capacity)
233{
234 FileList fl = NEW_(FileList);
235 fl->capacity = capacity;
236 fl->files = (char **) JLI_MemAlloc(capacity * sizeof(fl->files[0]));
237 fl->size = 0;
238 return fl;
239}
240
241#ifdef DEBUG_WILDCARD
242static void
243FileList_print(FileList fl)
244{
245 int i;
246 putchar('[');
247 for (i = 0; i < fl->size; i++) {
248 if (i > 0) printf(", ");
249 printf("\"%s\"",fl->files[i]);
250 }
251 putchar(']');
252}
253#endif
254
255static void
256FileList_free(FileList fl)
257{
258 if (fl) {
259 if (fl->files) {
260 int i;
261 for (i = 0; i < fl->size; i++)
262 JLI_MemFree(fl->files[i]);
263 JLI_MemFree(fl->files);
264 }
265 JLI_MemFree(fl);
266 }
267}
268
269static void
270FileList_ensureCapacity(FileList fl, int capacity)
271{
272 if (fl->capacity < capacity) {
273 while (fl->capacity < capacity)
274 fl->capacity *= 2;
275 fl->files = JLI_MemRealloc(fl->files,
276 fl->capacity * sizeof(fl->files[0]));
277 }
278}
279
280static void
281FileList_add(FileList fl, char *file)
282{
283 FileList_ensureCapacity(fl, fl->size+1);
284 fl->files[fl->size++] = file;
285}
286
287static void
288FileList_addSubstring(FileList fl, const char *beg, int len)
289{
290 char *filename = (char *) JLI_MemAlloc(len+1);
291 memcpy(filename, beg, len);
292 filename[len] = '\0';
293 FileList_ensureCapacity(fl, fl->size+1);
294 fl->files[fl->size++] = filename;
295}
296
297static char *
298FileList_join(FileList fl, char sep)
299{
300 int i;
301 int size;
302 char *path;
303 char *p;
304 for (i = 0, size = 1; i < fl->size; i++)
305 size += strlen(fl->files[i]) + 1;
306
307 path = JLI_MemAlloc(size);
308
309 for (i = 0, p = path; i < fl->size; i++) {
310 int len = strlen(fl->files[i]);
311 if (i > 0) *p++ = sep;
312 memcpy(p, fl->files[i], len);
313 p += len;
314 }
315 *p = '\0';
316
317 return path;
318}
319
320static FileList
321FileList_split(const char *path, char sep)
322{
323 const char *p, *q;
324 int len = strlen(path);
325 int count;
326 FileList fl;
327 for (count = 1, p = path; p < path + len; p++)
328 count += (*p == sep);
329 fl = FileList_new(count);
330 for (p = path;;) {
331 for (q = p; q <= path + len; q++) {
332 if (*q == sep || *q == '\0') {
333 FileList_addSubstring(fl, p, q - p);
334 if (*q == '\0')
335 return fl;
336 p = q + 1;
337 }
338 }
339 }
340}
341
342static int
343isJarFileName(const char *filename)
344{
345 int len = strlen(filename);
346 return (len >= 4) &&
347 (filename[len - 4] == '.') &&
348 (equal(filename + len - 3, "jar") ||
349 equal(filename + len - 3, "JAR")) &&
350 /* Paranoia: Maybe filename is "DIR:foo.jar" */
351 (strchr(filename, PATH_SEPARATOR) == NULL);
352}
353
354static char *
355wildcardConcat(const char *wildcard, const char *basename)
356{
357 int wildlen = strlen(wildcard);
358 int baselen = strlen(basename);
359 char *filename = (char *) JLI_MemAlloc(wildlen + baselen);
360 /* Replace the trailing '*' with basename */
361 memcpy(filename, wildcard, wildlen-1);
362 memcpy(filename+wildlen-1, basename, baselen+1);
363 return filename;
364}
365
366static FileList
367wildcardFileList(const char *wildcard)
368{
369 const char *basename;
370 FileList fl = FileList_new(16);
371 WildcardIterator it = WildcardIterator_for(wildcard);
372 if (it == NULL)
373 return NULL;
374 while ((basename = WildcardIterator_next(it)) != NULL)
375 if (isJarFileName(basename))
376 FileList_add(fl, wildcardConcat(wildcard, basename));
377 WildcardIterator_close(it);
378 return fl;
379}
380
381static int
382isWildcard(const char *filename)
383{
384 int len = strlen(filename);
385 return (len > 0) &&
386 (filename[len - 1] == '*') &&
387 (len == 1 || IS_FILE_SEPARATOR(filename[len - 2])) &&
388 (! exists(filename));
389}
390
391static void
392FileList_expandWildcards(FileList fl)
393{
394 int i, j;
395 for (i = 0; i < fl->size; i++) {
396 if (isWildcard(fl->files[i])) {
397 FileList expanded = wildcardFileList(fl->files[i]);
398 if (expanded != NULL && expanded->size > 0) {
399 JLI_MemFree(fl->files[i]);
400 FileList_ensureCapacity(fl, fl->size + expanded->size);
401 for (j = fl->size - 1; j >= i+1; j--)
402 fl->files[j+expanded->size-1] = fl->files[j];
403 for (j = 0; j < expanded->size; j++)
404 fl->files[i+j] = expanded->files[j];
405 i += expanded->size - 1;
406 fl->size += expanded->size - 1;
407 /* fl expropriates expanded's elements. */
408 expanded->size = 0;
409 }
410 FileList_free(expanded);
411 }
412 }
413}
414
415const char *
416JLI_WildcardExpandClasspath(const char *classpath)
417{
418 char *expanded;
419 FileList fl;
420
421 if (strchr(classpath, '*') == NULL)
422 return classpath;
423 fl = FileList_split(classpath, PATH_SEPARATOR);
424 FileList_expandWildcards(fl);
425 expanded = FileList_join(fl, PATH_SEPARATOR);
426 FileList_free(fl);
427 if (getenv("_JAVA_LAUNCHER_DEBUG") != 0)
428 printf("Expanded wildcards:\n"
429 " before: \"%s\"\n"
430 " after : \"%s\"\n",
431 classpath, expanded);
432 return expanded;
433}
434
435#ifdef DEBUG_WILDCARD
436static void
437wildcardExpandArgv(const char ***argv)
438{
439 int i;
440 for (i = 0; (*argv)[i]; i++) {
441 if (equal((*argv)[i], "-cp") ||
442 equal((*argv)[i], "-classpath")) {
443 i++;
444 (*argv)[i] = wildcardExpandClasspath((*argv)[i]);
445 }
446 }
447}
448
449static void
450debugPrintArgv(char *argv[])
451{
452 int i;
453 putchar('[');
454 for (i = 0; argv[i]; i++) {
455 if (i > 0) printf(", ");
456 printf("\"%s\"", argv[i]);
457 }
458 printf("]\n");
459}
460
461int
462main(int argc, char *argv[])
463{
464 argv[0] = "java";
465 wildcardExpandArgv((const char***)&argv);
466 debugPrintArgv(argv);
467 /* execvp("java", argv); */
468 return 0;
469}
470#endif /* DEBUG_WILDCARD */
471
472/* Cute little perl prototype implementation....
473
474my $sep = ($^O =~ /^(Windows|cygwin)/) ? ";" : ":";
475
476sub expand($) {
477 opendir DIR, $_[0] or return $_[0];
478 join $sep, map {"$_[0]/$_"} grep {/\.(jar|JAR)$/} readdir DIR;
479}
480
481sub munge($) {
482 join $sep,
483 map {(! -r $_ and s/[\/\\]+\*$//) ? expand $_ : $_} split $sep, $_[0];
484}
485
486for (my $i = 0; $i < @ARGV - 1; $i++) {
487 $ARGV[$i+1] = munge $ARGV[$i+1] if $ARGV[$i] =~ /^-c(p|lasspath)$/;
488}
489
490$ENV{CLASSPATH} = munge $ENV{CLASSPATH} if exists $ENV{CLASSPATH};
491@ARGV = ("java", @ARGV);
492print "@ARGV\n";
493exec @ARGV;
494
495*/
Note: See TracBrowser for help on using the repository browser.