1 /*
2 * Copyright (c) 2015, 2020, 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 #include <stdlib.h>
27 #include <stdio.h>
28 #include <assert.h>
29 #include <sys/stat.h>
30 #include <ctype.h>
31
32 #ifdef DEBUG_ARGFILE
33 #ifndef NO_JNI
34 #define NO_JNI
35 #endif
36 #define JLI_ReportMessage(...) printf(__VA_ARGS__)
37 #define JDK_JAVA_OPTIONS "JDK_JAVA_OPTIONS"
IsWhiteSpaceOption(const char * name)38 int IsWhiteSpaceOption(const char* name) { return 1; }
39 #else
40 #include "java.h"
41 #include "jni.h"
42 #endif
43
44 #include "jli_util.h"
45 #include "emessages.h"
46
47 #define MAX_ARGF_SIZE 0x7fffffffL
48
clone_substring(const char * begin,size_t len)49 static char* clone_substring(const char *begin, size_t len) {
50 char *rv = (char *) JLI_MemAlloc(len + 1);
51 memcpy(rv, begin, len);
52 rv[len] = '\0';
53 return rv;
54 }
55
56 enum STATE {
57 FIND_NEXT,
58 IN_COMMENT,
59 IN_QUOTE,
60 IN_ESCAPE,
61 SKIP_LEAD_WS,
62 IN_TOKEN
63 };
64
65 typedef struct {
66 enum STATE state;
67 const char* cptr;
68 const char* eob;
69 char quote_char;
70 JLI_List parts;
71 } __ctx_args;
72
73 #define NOT_FOUND -1
74 static int firstAppArgIndex = NOT_FOUND;
75
76 static jboolean expectingNoDashArg = JNI_FALSE;
77 // Initialize to 1, as the first argument is the app name and not preprocessed
78 static size_t argsCount = 1;
79 static jboolean stopExpansion = JNI_FALSE;
80 static jboolean relaunch = JNI_FALSE;
81
82 /*
83 * Prototypes for internal functions.
84 */
85 static jboolean expand(JLI_List args, const char *str, const char *var_name);
86
87 JNIEXPORT void JNICALL
JLI_InitArgProcessing(jboolean hasJavaArgs,jboolean disableArgFile)88 JLI_InitArgProcessing(jboolean hasJavaArgs, jboolean disableArgFile) {
89 // No expansion for relaunch
90 if (argsCount != 1) {
91 relaunch = JNI_TRUE;
92 stopExpansion = JNI_TRUE;
93 argsCount = 1;
94 } else {
95 stopExpansion = disableArgFile;
96 }
97
98 expectingNoDashArg = JNI_FALSE;
99
100 // for tools, this value remains 0 all the time.
101 firstAppArgIndex = hasJavaArgs ? 0: NOT_FOUND;
102 }
103
104 JNIEXPORT int JNICALL
JLI_GetAppArgIndex()105 JLI_GetAppArgIndex() {
106 // Will be 0 for tools
107 return firstAppArgIndex;
108 }
109
checkArg(const char * arg)110 static void checkArg(const char *arg) {
111 size_t idx = 0;
112 argsCount++;
113
114 // All arguments arrive here must be a launcher argument,
115 // ie. by now, all argfile expansions must have been performed.
116 if (*arg == '-') {
117 expectingNoDashArg = JNI_FALSE;
118 if (IsWhiteSpaceOption(arg)) {
119 // expect an argument
120 expectingNoDashArg = JNI_TRUE;
121
122 if (JLI_StrCmp(arg, "-jar") == 0 ||
123 JLI_StrCmp(arg, "--module") == 0 ||
124 JLI_StrCmp(arg, "-m") == 0) {
125 // This is tricky, we do expect NoDashArg
126 // But that is considered main class to stop expansion
127 expectingNoDashArg = JNI_FALSE;
128 // We can not just update the idx here because if -jar @file
129 // still need expansion of @file to get the argument for -jar
130 }
131 } else if (JLI_StrCmp(arg, "--disable-@files") == 0) {
132 stopExpansion = JNI_TRUE;
133 } else if (JLI_StrCCmp(arg, "--module=") == 0) {
134 idx = argsCount;
135 }
136 } else {
137 if (!expectingNoDashArg) {
138 // this is main class, argsCount is index to next arg
139 idx = argsCount;
140 }
141 expectingNoDashArg = JNI_FALSE;
142 }
143 // only update on java mode and not yet found main class
144 if (firstAppArgIndex == NOT_FOUND && idx != 0) {
145 firstAppArgIndex = (int) idx;
146 }
147 }
148
149 /*
150 [\n\r] +------------+ +------------+ [\n\r]
151 +---------+ IN_COMMENT +<------+ | IN_ESCAPE +---------+
152 | +------------+ | +------------+ |
153 | [#] ^ |[#] ^ | |
154 | +----------+ | [\\]| |[^\n\r] |
155 v | | | v |
156 +------------+ [^ \t\n\r\f] +------------+['"]> +------------+ |
157 | FIND_NEXT +-------------->+ IN_TOKEN +-----------+ IN_QUOTE + |
158 +------------+ +------------+ <[quote]+------------+ |
159 | ^ | | ^ ^ |
160 | | [ \t\n\r\f]| [\n\r]| | |[^ \t\n\r\f]v
161 | +--------------------------+-----------------------+ | +--------------+
162 | ['"] | | SKIP_LEAD_WS |
163 +---------------------------------------------------------+ +--------------+
164 */
nextToken(__ctx_args * pctx)165 static char* nextToken(__ctx_args *pctx) {
166 const char* nextc = pctx->cptr;
167 const char* const eob = pctx->eob;
168 const char* anchor = nextc;
169 char *token;
170
171 for (; nextc < eob; nextc++) {
172 register char ch = *nextc;
173
174 // Skip white space characters
175 if (pctx->state == FIND_NEXT || pctx->state == SKIP_LEAD_WS) {
176 while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f') {
177 nextc++;
178 if (nextc >= eob) {
179 return NULL;
180 }
181 ch = *nextc;
182 }
183 pctx->state = (pctx->state == FIND_NEXT) ? IN_TOKEN : IN_QUOTE;
184 anchor = nextc;
185 // Deal with escape sequences
186 } else if (pctx->state == IN_ESCAPE) {
187 // concatenation directive
188 if (ch == '\n' || ch == '\r') {
189 pctx->state = SKIP_LEAD_WS;
190 } else {
191 // escaped character
192 char* escaped = (char*) JLI_MemAlloc(2 * sizeof(char));
193 escaped[1] = '\0';
194 switch (ch) {
195 case 'n':
196 escaped[0] = '\n';
197 break;
198 case 'r':
199 escaped[0] = '\r';
200 break;
201 case 't':
202 escaped[0] = '\t';
203 break;
204 case 'f':
205 escaped[0] = '\f';
206 break;
207 default:
208 escaped[0] = ch;
209 break;
210 }
211 JLI_List_add(pctx->parts, escaped);
212 pctx->state = IN_QUOTE;
213 }
214 // anchor to next character
215 anchor = nextc + 1;
216 continue;
217 // ignore comment to EOL
218 } else if (pctx->state == IN_COMMENT) {
219 while (ch != '\n' && ch != '\r') {
220 nextc++;
221 if (nextc >= eob) {
222 return NULL;
223 }
224 ch = *nextc;
225 }
226 anchor = nextc + 1;
227 pctx->state = FIND_NEXT;
228 continue;
229 }
230
231 assert(pctx->state != IN_ESCAPE);
232 assert(pctx->state != FIND_NEXT);
233 assert(pctx->state != SKIP_LEAD_WS);
234 assert(pctx->state != IN_COMMENT);
235
236 switch(ch) {
237 case ' ':
238 case '\t':
239 case '\f':
240 if (pctx->state == IN_QUOTE) {
241 continue;
242 }
243 // fall through
244 case '\n':
245 case '\r':
246 if (pctx->parts->size == 0) {
247 token = clone_substring(anchor, nextc - anchor);
248 } else {
249 JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
250 token = JLI_List_combine(pctx->parts);
251 JLI_List_free(pctx->parts);
252 pctx->parts = JLI_List_new(4);
253 }
254 pctx->cptr = nextc + 1;
255 pctx->state = FIND_NEXT;
256 return token;
257 case '#':
258 if (pctx->state == IN_QUOTE) {
259 continue;
260 }
261 pctx->state = IN_COMMENT;
262 anchor = nextc + 1;
263 break;
264 case '\\':
265 if (pctx->state != IN_QUOTE) {
266 continue;
267 }
268 JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
269 pctx->state = IN_ESCAPE;
270 // anchor after backslash character
271 anchor = nextc + 1;
272 break;
273 case '\'':
274 case '"':
275 if (pctx->state == IN_QUOTE && pctx->quote_char != ch) {
276 // not matching quote
277 continue;
278 }
279 // partial before quote
280 if (anchor != nextc) {
281 JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
282 }
283 // anchor after quote character
284 anchor = nextc + 1;
285 if (pctx->state == IN_TOKEN) {
286 pctx->quote_char = ch;
287 pctx->state = IN_QUOTE;
288 } else {
289 pctx->state = IN_TOKEN;
290 }
291 break;
292 default:
293 break;
294 }
295 }
296
297 assert(nextc == eob);
298 // Only need partial token, not comment or whitespaces
299 if (pctx->state == IN_TOKEN || pctx->state == IN_QUOTE) {
300 if (anchor < nextc) {
301 // not yet return until end of stream, we have part of a token.
302 JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
303 }
304 }
305 return NULL;
306 }
307
readArgFile(FILE * file)308 static JLI_List readArgFile(FILE *file) {
309 char buf[4096];
310 JLI_List rv;
311 __ctx_args ctx;
312 size_t size;
313 char *token;
314
315 ctx.state = FIND_NEXT;
316 ctx.parts = JLI_List_new(4);
317 // initialize to avoid -Werror=maybe-uninitialized issues from gcc 7.3 onwards.
318 ctx.quote_char = '"';
319
320 /* arbitrarily pick 8, seems to be a reasonable number of arguments */
321 rv = JLI_List_new(8);
322
323 while (!feof(file)) {
324 size = fread(buf, sizeof(char), sizeof(buf), file);
325 if (ferror(file)) {
326 JLI_List_free(rv);
327 return NULL;
328 }
329
330 /* nextc is next character to read from the buffer
331 * eob is the end of input
332 * token is the copied token value, NULL if no a complete token
333 */
334 ctx.cptr = buf;
335 ctx.eob = buf + size;
336 token = nextToken(&ctx);
337 while (token != NULL) {
338 checkArg(token);
339 JLI_List_add(rv, token);
340 token = nextToken(&ctx);
341 }
342 }
343
344 // remaining partial token
345 if (ctx.state == IN_TOKEN || ctx.state == IN_QUOTE) {
346 if (ctx.parts->size != 0) {
347 token = JLI_List_combine(ctx.parts);
348 checkArg(token);
349 JLI_List_add(rv, token);
350 }
351 }
352 JLI_List_free(ctx.parts);
353
354 return rv;
355 }
356
357 /*
358 * if the arg represent a file, that is, prefix with a single '@',
359 * return a list of arguments from the file.
360 * otherwise, return NULL.
361 */
expandArgFile(const char * arg)362 static JLI_List expandArgFile(const char *arg) {
363 FILE *fptr;
364 struct stat st;
365 JLI_List rv;
366
367 /* failed to access the file */
368 if (stat(arg, &st) != 0) {
369 JLI_ReportMessage(CFG_ERROR6, arg);
370 exit(1);
371 }
372
373 if (st.st_size > MAX_ARGF_SIZE) {
374 JLI_ReportMessage(CFG_ERROR10, MAX_ARGF_SIZE);
375 exit(1);
376 }
377
378 fptr = fopen(arg, "r");
379 /* arg file cannot be openned */
380 if (fptr == NULL) {
381 JLI_ReportMessage(CFG_ERROR6, arg);
382 exit(1);
383 }
384
385 rv = readArgFile(fptr);
386 fclose(fptr);
387
388 /* error occurred reading the file */
389 if (rv == NULL) {
390 JLI_ReportMessage(DLL_ERROR4, arg);
391 exit(1);
392 }
393
394 return rv;
395 }
396
397 /*
398 * expand a string into a list of words separated by whitespace.
399 */
expandArg(const char * arg)400 static JLI_List expandArg(const char *arg) {
401 JLI_List rv;
402
403 /* arbitrarily pick 8, seems to be a reasonable number of arguments */
404 rv = JLI_List_new(8);
405
406 expand(rv, arg, NULL);
407
408 return rv;
409 }
410
411 JNIEXPORT JLI_List JNICALL
JLI_PreprocessArg(const char * arg,jboolean expandSourceOpt)412 JLI_PreprocessArg(const char *arg, jboolean expandSourceOpt) {
413 JLI_List rv;
414
415 if (firstAppArgIndex > 0) {
416 // In user application arg, no more work.
417 return NULL;
418 }
419
420 if (stopExpansion) {
421 // still looking for user application arg
422 checkArg(arg);
423 return NULL;
424 }
425
426 if (expandSourceOpt
427 && JLI_StrCCmp(arg, "--source") == 0
428 && JLI_StrChr(arg, ' ') != NULL) {
429 return expandArg(arg);
430 }
431
432 if (arg[0] != '@') {
433 checkArg(arg);
434 return NULL;
435 }
436
437 if (arg[1] == '\0') {
438 // @ by itself is an argument
439 checkArg(arg);
440 return NULL;
441 }
442
443 arg++;
444 if (arg[0] == '@') {
445 // escaped @argument
446 rv = JLI_List_new(1);
447 checkArg(arg);
448 JLI_List_add(rv, JLI_StringDup(arg));
449 } else {
450 rv = expandArgFile(arg);
451 }
452 return rv;
453 }
454
isTerminalOpt(char * arg)455 int isTerminalOpt(char *arg) {
456 return JLI_StrCmp(arg, "-jar") == 0 ||
457 JLI_StrCmp(arg, "-m") == 0 ||
458 JLI_StrCmp(arg, "--module") == 0 ||
459 JLI_StrCCmp(arg, "--module=") == 0 ||
460 JLI_StrCmp(arg, "--dry-run") == 0 ||
461 JLI_StrCmp(arg, "-h") == 0 ||
462 JLI_StrCmp(arg, "-?") == 0 ||
463 JLI_StrCmp(arg, "-help") == 0 ||
464 JLI_StrCmp(arg, "--help") == 0 ||
465 JLI_StrCmp(arg, "-X") == 0 ||
466 JLI_StrCmp(arg, "--help-extra") == 0 ||
467 JLI_StrCmp(arg, "-version") == 0 ||
468 JLI_StrCmp(arg, "--version") == 0 ||
469 JLI_StrCmp(arg, "-fullversion") == 0 ||
470 JLI_StrCmp(arg, "--full-version") == 0;
471 }
472
473 JNIEXPORT jboolean JNICALL
JLI_AddArgsFromEnvVar(JLI_List args,const char * var_name)474 JLI_AddArgsFromEnvVar(JLI_List args, const char *var_name) {
475 char *env = getenv(var_name);
476
477 if (firstAppArgIndex == 0) {
478 // Not 'java', return
479 return JNI_FALSE;
480 }
481
482 if (relaunch) {
483 return JNI_FALSE;
484 }
485
486 if (NULL == env) {
487 return JNI_FALSE;
488 }
489
490 JLI_ReportMessage(ARG_INFO_ENVVAR, var_name, env);
491 return expand(args, env, var_name);
492 }
493
494 /*
495 * Expand a string into a list of args.
496 * If the string is the result of looking up an environment variable,
497 * var_name should be set to the name of that environment variable,
498 * for use if needed in error messages.
499 */
500
expand(JLI_List args,const char * str,const char * var_name)501 static jboolean expand(JLI_List args, const char *str, const char *var_name) {
502 jboolean inEnvVar = (var_name != NULL);
503
504 char *p, *arg;
505 char quote;
506 JLI_List argsInFile;
507
508 // This is retained until the process terminates as it is saved as the args
509 p = JLI_MemAlloc(JLI_StrLen(str) + 1);
510 while (*str != '\0') {
511 while (*str != '\0' && isspace(*str)) {
512 str++;
513 }
514
515 // Trailing space
516 if (*str == '\0') {
517 break;
518 }
519
520 arg = p;
521 while (*str != '\0' && !isspace(*str)) {
522 if (inEnvVar && (*str == '"' || *str == '\'')) {
523 quote = *str++;
524 while (*str != quote && *str != '\0') {
525 *p++ = *str++;
526 }
527
528 if (*str == '\0') {
529 JLI_ReportMessage(ARG_ERROR8, var_name);
530 exit(1);
531 }
532 str++;
533 } else {
534 *p++ = *str++;
535 }
536 }
537
538 *p++ = '\0';
539
540 argsInFile = JLI_PreprocessArg(arg, JNI_FALSE);
541
542 if (NULL == argsInFile) {
543 if (isTerminalOpt(arg)) {
544 if (inEnvVar) {
545 JLI_ReportMessage(ARG_ERROR9, arg, var_name);
546 } else {
547 JLI_ReportMessage(ARG_ERROR15, arg);
548 }
549 exit(1);
550 }
551 JLI_List_add(args, arg);
552 } else {
553 size_t cnt, idx;
554 char *argFile = arg;
555 cnt = argsInFile->size;
556 for (idx = 0; idx < cnt; idx++) {
557 arg = argsInFile->elements[idx];
558 if (isTerminalOpt(arg)) {
559 if (inEnvVar) {
560 JLI_ReportMessage(ARG_ERROR10, arg, argFile, var_name);
561 } else {
562 JLI_ReportMessage(ARG_ERROR16, arg, argFile);
563 }
564 exit(1);
565 }
566 JLI_List_add(args, arg);
567 }
568 // Shallow free, we reuse the string to avoid copy
569 JLI_MemFree(argsInFile->elements);
570 JLI_MemFree(argsInFile);
571 }
572 /*
573 * Check if main-class is specified after argument being checked. It
574 * must always appear after expansion, as a main-class could be specified
575 * indirectly into environment variable via an @argfile, and it must be
576 * caught now.
577 */
578 if (firstAppArgIndex != NOT_FOUND) {
579 if (inEnvVar) {
580 JLI_ReportMessage(ARG_ERROR11, var_name);
581 } else {
582 JLI_ReportMessage(ARG_ERROR17);
583 }
584 exit(1);
585 }
586
587 assert (*str == '\0' || isspace(*str));
588 }
589
590 return JNI_TRUE;
591 }
592
593 #ifdef DEBUG_ARGFILE
594 /*
595 * Stand-alone sanity test, build with following command line
596 * $ CC -DDEBUG_ARGFILE -DNO_JNI -g args.c jli_util.c
597 */
598
fail(char * expected,char * actual,size_t idx)599 void fail(char *expected, char *actual, size_t idx) {
600 printf("FAILED: Token[%lu] expected to be <%s>, got <%s>\n", idx, expected, actual);
601 exit(1);
602 }
603
test_case(char * case_data,char ** tokens,size_t cnt_tokens)604 void test_case(char *case_data, char **tokens, size_t cnt_tokens) {
605 size_t actual_cnt;
606 char *token;
607 __ctx_args ctx;
608
609 actual_cnt = 0;
610
611 ctx.state = FIND_NEXT;
612 ctx.parts = JLI_List_new(4);
613 ctx.cptr = case_data;
614 ctx.eob = case_data + strlen(case_data);
615
616 printf("Test case: <%s>, expected %lu tokens.\n", case_data, cnt_tokens);
617
618 for (token = nextToken(&ctx); token != NULL; token = nextToken(&ctx)) {
619 // should not have more tokens than expected
620 if (actual_cnt >= cnt_tokens) {
621 printf("FAILED: Extra token detected: <%s>\n", token);
622 exit(2);
623 }
624 if (JLI_StrCmp(token, tokens[actual_cnt]) != 0) {
625 fail(tokens[actual_cnt], token, actual_cnt);
626 }
627 actual_cnt++;
628 }
629
630 char* last = NULL;
631 if (ctx.parts->size != 0) {
632 last = JLI_List_combine(ctx.parts);
633 }
634 JLI_List_free(ctx.parts);
635
636 if (actual_cnt >= cnt_tokens) {
637 // same number of tokens, should have nothing left to parse
638 if (last != NULL) {
639 if (*last != '#') {
640 printf("Leftover detected: %s", last);
641 exit(2);
642 }
643 }
644 } else {
645 if (JLI_StrCmp(last, tokens[actual_cnt]) != 0) {
646 fail(tokens[actual_cnt], last, actual_cnt);
647 }
648 actual_cnt++;
649 }
650 if (actual_cnt != cnt_tokens) {
651 printf("FAILED: Number of tokens not match, expected %lu, got %lu\n",
652 cnt_tokens, actual_cnt);
653 exit(3);
654 }
655
656 printf("PASS\n");
657 }
658
659 #define DO_CASE(name) \
660 test_case(name[0], name + 1, sizeof(name)/sizeof(char*) - 1)
661
main(int argc,char ** argv)662 int main(int argc, char** argv) {
663 size_t i, j;
664
665 char* case1[] = { "-version -cp \"c:\\\\java libs\\\\one.jar\" \n",
666 "-version", "-cp", "c:\\java libs\\one.jar" };
667 DO_CASE(case1);
668
669 // note the open quote at the end
670 char* case2[] = { "com.foo.Panda \"Furious 5\"\fand\t'Shi Fu' \"escape\tprison",
671 "com.foo.Panda", "Furious 5", "and", "Shi Fu", "escape\tprison"};
672 DO_CASE(case2);
673
674 char* escaped_chars[] = { "escaped chars testing \"\\a\\b\\c\\f\\n\\r\\t\\v\\9\\6\\23\\82\\28\\377\\477\\278\\287\"",
675 "escaped", "chars", "testing", "abc\f\n\r\tv96238228377477278287"};
676 DO_CASE(escaped_chars);
677
678 char* mixed_quote[] = { "\"mix 'single quote' in double\" 'mix \"double quote\" in single' partial\"quote me\"this",
679 "mix 'single quote' in double", "mix \"double quote\" in single", "partialquote methis"};
680 DO_CASE(mixed_quote);
681
682 char* comments[] = { "line one #comment\n'line #2' #rest are comment\r\n#comment on line 3\nline 4 #comment to eof",
683 "line", "one", "line #2", "line", "4"};
684 DO_CASE(comments);
685
686 char* open_quote[] = { "This is an \"open quote \n across line\n\t, note for WS.",
687 "This", "is", "an", "open quote ", "across", "line", ",", "note", "for", "WS." };
688 DO_CASE(open_quote);
689
690 char* escape_in_open_quote[] = { "Try \"this \\\\\\\\ escape\\n double quote \\\" in open quote",
691 "Try", "this \\\\ escape\n double quote \" in open quote" };
692 DO_CASE(escape_in_open_quote);
693
694 char* quote[] = { "'-Dmy.quote.single'='Property in single quote. Here a double quote\" Add some slashes \\\\/'",
695 "-Dmy.quote.single=Property in single quote. Here a double quote\" Add some slashes \\/" };
696 DO_CASE(quote);
697
698 char* multi[] = { "\"Open quote to \n new \"line \\\n\r third\\\n\r\\\tand\ffourth\"",
699 "Open quote to ", "new", "line third\tand\ffourth" };
700 DO_CASE(multi);
701
702 char* escape_quote[] = { "c:\\\"partial quote\"\\lib",
703 "c:\\partial quote\\lib" };
704 DO_CASE(escape_quote);
705
706 if (argc > 1) {
707 for (i = 0; i < argc; i++) {
708 JLI_List tokens = JLI_PreprocessArg(argv[i], JNI_FALSE);
709 if (NULL != tokens) {
710 for (j = 0; j < tokens->size; j++) {
711 printf("Token[%lu]: <%s>\n", (unsigned long) j, tokens->elements[j]);
712 }
713 }
714 }
715 }
716 }
717
718 #endif // DEBUG_ARGFILE
719