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 JNIEXPORT void JNICALL
83 JLI_InitArgProcessing(jboolean hasJavaArgs, jboolean disableArgFile) {
84 // No expansion for relaunch
85 if (argsCount != 1) {
86 relaunch = JNI_TRUE;
87 stopExpansion = JNI_TRUE;
88 argsCount = 1;
89 } else {
90 stopExpansion = disableArgFile;
91 }
92
93 expectingNoDashArg = JNI_FALSE;
94
95 // for tools, this value remains 0 all the time.
96 firstAppArgIndex = hasJavaArgs ? 0: NOT_FOUND;
97 }
98
99 JNIEXPORT int JNICALL
100 JLI_GetAppArgIndex() {
101 // Will be 0 for tools
359
360 fptr = fopen(arg, "r");
361 /* arg file cannot be openned */
362 if (fptr == NULL) {
363 JLI_ReportMessage(CFG_ERROR6, arg);
364 exit(1);
365 }
366
367 rv = readArgFile(fptr);
368 fclose(fptr);
369
370 /* error occurred reading the file */
371 if (rv == NULL) {
372 JLI_ReportMessage(DLL_ERROR4, arg);
373 exit(1);
374 }
375
376 return rv;
377 }
378
379 JNIEXPORT JLI_List JNICALL
380 JLI_PreprocessArg(const char *arg)
381 {
382 JLI_List rv;
383
384 if (firstAppArgIndex > 0) {
385 // In user application arg, no more work.
386 return NULL;
387 }
388
389 if (stopExpansion) {
390 // still looking for user application arg
391 checkArg(arg);
392 return NULL;
393 }
394
395 if (arg[0] != '@') {
396 checkArg(arg);
397 return NULL;
398 }
399
400 if (arg[1] == '\0') {
401 // @ by itself is an argument
402 checkArg(arg);
403 return NULL;
404 }
405
406 arg++;
407 if (arg[0] == '@') {
408 // escaped @argument
409 rv = JLI_List_new(1);
410 checkArg(arg);
411 JLI_List_add(rv, JLI_StringDup(arg));
412 } else {
413 rv = expandArgFile(arg);
414 }
418 int isTerminalOpt(char *arg) {
419 return JLI_StrCmp(arg, "-jar") == 0 ||
420 JLI_StrCmp(arg, "-m") == 0 ||
421 JLI_StrCmp(arg, "--module") == 0 ||
422 JLI_StrCmp(arg, "--dry-run") == 0 ||
423 JLI_StrCmp(arg, "-h") == 0 ||
424 JLI_StrCmp(arg, "-?") == 0 ||
425 JLI_StrCmp(arg, "-help") == 0 ||
426 JLI_StrCmp(arg, "--help") == 0 ||
427 JLI_StrCmp(arg, "-X") == 0 ||
428 JLI_StrCmp(arg, "--help-extra") == 0 ||
429 JLI_StrCmp(arg, "-version") == 0 ||
430 JLI_StrCmp(arg, "--version") == 0 ||
431 JLI_StrCmp(arg, "-fullversion") == 0 ||
432 JLI_StrCmp(arg, "--full-version") == 0;
433 }
434
435 JNIEXPORT jboolean JNICALL
436 JLI_AddArgsFromEnvVar(JLI_List args, const char *var_name) {
437 char *env = getenv(var_name);
438 char *p, *arg;
439 char quote;
440 JLI_List argsInFile;
441
442 if (firstAppArgIndex == 0) {
443 // Not 'java', return
444 return JNI_FALSE;
445 }
446
447 if (relaunch) {
448 return JNI_FALSE;
449 }
450
451 if (NULL == env) {
452 return JNI_FALSE;
453 }
454
455 JLI_ReportMessage(ARG_INFO_ENVVAR, var_name, env);
456
457 // This is retained until the process terminates as it is saved as the args
458 p = JLI_MemAlloc(JLI_StrLen(env) + 1);
459 while (*env != '\0') {
460 while (*env != '\0' && isspace(*env)) {
461 env++;
462 }
463
464 // Trailing space
465 if (*env == '\0') {
466 break;
467 }
468
469 arg = p;
470 while (*env != '\0' && !isspace(*env)) {
471 if (*env == '"' || *env == '\'') {
472 quote = *env++;
473 while (*env != quote && *env != '\0') {
474 *p++ = *env++;
475 }
476
477 if (*env == '\0') {
478 JLI_ReportMessage(ARG_ERROR8, var_name);
479 exit(1);
480 }
481 env++;
482 } else {
483 *p++ = *env++;
484 }
485 }
486
487 *p++ = '\0';
488
489 argsInFile = JLI_PreprocessArg(arg);
490
491 if (NULL == argsInFile) {
492 if (isTerminalOpt(arg)) {
493 JLI_ReportMessage(ARG_ERROR9, arg, var_name);
494 exit(1);
495 }
496 JLI_List_add(args, arg);
497 } else {
498 size_t cnt, idx;
499 char *argFile = arg;
500 cnt = argsInFile->size;
501 for (idx = 0; idx < cnt; idx++) {
502 arg = argsInFile->elements[idx];
503 if (isTerminalOpt(arg)) {
504 JLI_ReportMessage(ARG_ERROR10, arg, argFile, var_name);
505 exit(1);
506 }
507 JLI_List_add(args, arg);
508 }
509 // Shallow free, we reuse the string to avoid copy
510 JLI_MemFree(argsInFile->elements);
511 JLI_MemFree(argsInFile);
512 }
513 /*
514 * Check if main-class is specified after argument being checked. It
515 * must always appear after expansion, as a main-class could be specified
516 * indirectly into environment variable via an @argfile, and it must be
517 * caught now.
518 */
519 if (firstAppArgIndex != NOT_FOUND) {
520 JLI_ReportMessage(ARG_ERROR11, var_name);
521 exit(1);
522 }
523
524 assert (*env == '\0' || isspace(*env));
525 }
526
527 return JNI_TRUE;
528 }
529
530 #ifdef DEBUG_ARGFILE
531 /*
532 * Stand-alone sanity test, build with following command line
533 * $ CC -DDEBUG_ARGFILE -DNO_JNI -g args.c jli_util.c
534 */
535
536 void fail(char *expected, char *actual, size_t idx) {
537 printf("FAILED: Token[%lu] expected to be <%s>, got <%s>\n", idx, expected, actual);
538 exit(1);
539 }
540
541 void test_case(char *case_data, char **tokens, size_t cnt_tokens) {
542 size_t actual_cnt;
543 char *token;
544 __ctx_args ctx;
625 DO_CASE(open_quote);
626
627 char* escape_in_open_quote[] = { "Try \"this \\\\\\\\ escape\\n double quote \\\" in open quote",
628 "Try", "this \\\\ escape\n double quote \" in open quote" };
629 DO_CASE(escape_in_open_quote);
630
631 char* quote[] = { "'-Dmy.quote.single'='Property in single quote. Here a double quote\" Add some slashes \\\\/'",
632 "-Dmy.quote.single=Property in single quote. Here a double quote\" Add some slashes \\/" };
633 DO_CASE(quote);
634
635 char* multi[] = { "\"Open quote to \n new \"line \\\n\r third\\\n\r\\\tand\ffourth\"",
636 "Open quote to ", "new", "line third\tand\ffourth" };
637 DO_CASE(multi);
638
639 char* escape_quote[] = { "c:\\\"partial quote\"\\lib",
640 "c:\\partial quote\\lib" };
641 DO_CASE(escape_quote);
642
643 if (argc > 1) {
644 for (i = 0; i < argc; i++) {
645 JLI_List tokens = JLI_PreprocessArg(argv[i]);
646 if (NULL != tokens) {
647 for (j = 0; j < tokens->size; j++) {
648 printf("Token[%lu]: <%s>\n", (unsigned long) j, tokens->elements[j]);
649 }
650 }
651 }
652 }
653 }
654
655 #endif // DEBUG_ARGFILE
|
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
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
105 JLI_GetAppArgIndex() {
106 // Will be 0 for tools
364
365 fptr = fopen(arg, "r");
366 /* arg file cannot be openned */
367 if (fptr == NULL) {
368 JLI_ReportMessage(CFG_ERROR6, arg);
369 exit(1);
370 }
371
372 rv = readArgFile(fptr);
373 fclose(fptr);
374
375 /* error occurred reading the file */
376 if (rv == NULL) {
377 JLI_ReportMessage(DLL_ERROR4, arg);
378 exit(1);
379 }
380
381 return rv;
382 }
383
384 /*
385 * expand a string into a list of words separated by whitespace.
386 */
387 static JLI_List expandArg(const char *arg) {
388 JLI_List rv;
389
390 /* arbitrarily pick 8, seems to be a reasonable number of arguments */
391 rv = JLI_List_new(8);
392
393 expand(rv, arg, NULL);
394
395 return rv;
396 }
397
398 JNIEXPORT JLI_List JNICALL
399 JLI_PreprocessArg(const char *arg, jboolean expandSourceOpt) {
400 JLI_List rv;
401
402 if (firstAppArgIndex > 0) {
403 // In user application arg, no more work.
404 return NULL;
405 }
406
407 if (stopExpansion) {
408 // still looking for user application arg
409 checkArg(arg);
410 return NULL;
411 }
412
413 if (expandSourceOpt
414 && JLI_StrCCmp(arg, "--source") == 0
415 && JLI_StrChr(arg, ' ') != NULL) {
416 return expandArg(arg);
417 }
418
419 if (arg[0] != '@') {
420 checkArg(arg);
421 return NULL;
422 }
423
424 if (arg[1] == '\0') {
425 // @ by itself is an argument
426 checkArg(arg);
427 return NULL;
428 }
429
430 arg++;
431 if (arg[0] == '@') {
432 // escaped @argument
433 rv = JLI_List_new(1);
434 checkArg(arg);
435 JLI_List_add(rv, JLI_StringDup(arg));
436 } else {
437 rv = expandArgFile(arg);
438 }
442 int isTerminalOpt(char *arg) {
443 return JLI_StrCmp(arg, "-jar") == 0 ||
444 JLI_StrCmp(arg, "-m") == 0 ||
445 JLI_StrCmp(arg, "--module") == 0 ||
446 JLI_StrCmp(arg, "--dry-run") == 0 ||
447 JLI_StrCmp(arg, "-h") == 0 ||
448 JLI_StrCmp(arg, "-?") == 0 ||
449 JLI_StrCmp(arg, "-help") == 0 ||
450 JLI_StrCmp(arg, "--help") == 0 ||
451 JLI_StrCmp(arg, "-X") == 0 ||
452 JLI_StrCmp(arg, "--help-extra") == 0 ||
453 JLI_StrCmp(arg, "-version") == 0 ||
454 JLI_StrCmp(arg, "--version") == 0 ||
455 JLI_StrCmp(arg, "-fullversion") == 0 ||
456 JLI_StrCmp(arg, "--full-version") == 0;
457 }
458
459 JNIEXPORT jboolean JNICALL
460 JLI_AddArgsFromEnvVar(JLI_List args, const char *var_name) {
461 char *env = getenv(var_name);
462
463 if (firstAppArgIndex == 0) {
464 // Not 'java', return
465 return JNI_FALSE;
466 }
467
468 if (relaunch) {
469 return JNI_FALSE;
470 }
471
472 if (NULL == env) {
473 return JNI_FALSE;
474 }
475
476 JLI_ReportMessage(ARG_INFO_ENVVAR, var_name, env);
477 return expand(args, env, var_name);
478 }
479
480 /*
481 * Expand a string into a list of args.
482 * If the string is the result of looking up an environment variable,
483 * var_name should be set to the name of that environment variable,
484 * for use if needed in error messages.
485 */
486
487 static jboolean expand(JLI_List args, const char *str, const char *var_name) {
488 jboolean inEnvVar = (var_name != NULL);
489
490 char *p, *arg;
491 char quote;
492 JLI_List argsInFile;
493
494 // This is retained until the process terminates as it is saved as the args
495 p = JLI_MemAlloc(JLI_StrLen(str) + 1);
496 while (*str != '\0') {
497 while (*str != '\0' && isspace(*str)) {
498 str++;
499 }
500
501 // Trailing space
502 if (*str == '\0') {
503 break;
504 }
505
506 arg = p;
507 while (*str != '\0' && !isspace(*str)) {
508 if (inEnvVar && (*str == '"' || *str == '\'')) {
509 quote = *str++;
510 while (*str != quote && *str != '\0') {
511 *p++ = *str++;
512 }
513
514 if (*str == '\0') {
515 JLI_ReportMessage(ARG_ERROR8, var_name);
516 exit(1);
517 }
518 str++;
519 } else {
520 *p++ = *str++;
521 }
522 }
523
524 *p++ = '\0';
525
526 argsInFile = JLI_PreprocessArg(arg, JNI_FALSE);
527
528 if (NULL == argsInFile) {
529 if (isTerminalOpt(arg)) {
530 if (inEnvVar) {
531 JLI_ReportMessage(ARG_ERROR9, arg, var_name);
532 } else {
533 JLI_ReportMessage(ARG_ERROR15, arg);
534 }
535 exit(1);
536 }
537 JLI_List_add(args, arg);
538 } else {
539 size_t cnt, idx;
540 char *argFile = arg;
541 cnt = argsInFile->size;
542 for (idx = 0; idx < cnt; idx++) {
543 arg = argsInFile->elements[idx];
544 if (isTerminalOpt(arg)) {
545 if (inEnvVar) {
546 JLI_ReportMessage(ARG_ERROR10, arg, argFile, var_name);
547 } else {
548 JLI_ReportMessage(ARG_ERROR16, arg, argFile);
549 }
550 exit(1);
551 }
552 JLI_List_add(args, arg);
553 }
554 // Shallow free, we reuse the string to avoid copy
555 JLI_MemFree(argsInFile->elements);
556 JLI_MemFree(argsInFile);
557 }
558 /*
559 * Check if main-class is specified after argument being checked. It
560 * must always appear after expansion, as a main-class could be specified
561 * indirectly into environment variable via an @argfile, and it must be
562 * caught now.
563 */
564 if (firstAppArgIndex != NOT_FOUND) {
565 if (inEnvVar) {
566 JLI_ReportMessage(ARG_ERROR11, var_name);
567 } else {
568 JLI_ReportMessage(ARG_ERROR17);
569 }
570 exit(1);
571 }
572
573 assert (*str == '\0' || isspace(*str));
574 }
575
576 return JNI_TRUE;
577 }
578
579 #ifdef DEBUG_ARGFILE
580 /*
581 * Stand-alone sanity test, build with following command line
582 * $ CC -DDEBUG_ARGFILE -DNO_JNI -g args.c jli_util.c
583 */
584
585 void fail(char *expected, char *actual, size_t idx) {
586 printf("FAILED: Token[%lu] expected to be <%s>, got <%s>\n", idx, expected, actual);
587 exit(1);
588 }
589
590 void test_case(char *case_data, char **tokens, size_t cnt_tokens) {
591 size_t actual_cnt;
592 char *token;
593 __ctx_args ctx;
674 DO_CASE(open_quote);
675
676 char* escape_in_open_quote[] = { "Try \"this \\\\\\\\ escape\\n double quote \\\" in open quote",
677 "Try", "this \\\\ escape\n double quote \" in open quote" };
678 DO_CASE(escape_in_open_quote);
679
680 char* quote[] = { "'-Dmy.quote.single'='Property in single quote. Here a double quote\" Add some slashes \\\\/'",
681 "-Dmy.quote.single=Property in single quote. Here a double quote\" Add some slashes \\/" };
682 DO_CASE(quote);
683
684 char* multi[] = { "\"Open quote to \n new \"line \\\n\r third\\\n\r\\\tand\ffourth\"",
685 "Open quote to ", "new", "line third\tand\ffourth" };
686 DO_CASE(multi);
687
688 char* escape_quote[] = { "c:\\\"partial quote\"\\lib",
689 "c:\\partial quote\\lib" };
690 DO_CASE(escape_quote);
691
692 if (argc > 1) {
693 for (i = 0; i < argc; i++) {
694 JLI_List tokens = JLI_PreprocessArg(argv[i], JNI_FALSE);
695 if (NULL != tokens) {
696 for (j = 0; j < tokens->size; j++) {
697 printf("Token[%lu]: <%s>\n", (unsigned long) j, tokens->elements[j]);
698 }
699 }
700 }
701 }
702 }
703
704 #endif // DEBUG_ARGFILE
|