< prev index next >

src/java.base/share/native/libjli/args.c

Print this page




  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
< prev index next >