jdk/src/java.base/share/native/libjli/args.c
changeset 32267 4e96a9ee01b1
child 36511 9d0388c6b336
equal deleted inserted replaced
32266:e0a235a11254 32267:4e96a9ee01b1
       
     1 /*
       
     2  * Copyright (c) 2015, 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 <stdio.h>
       
    27 #include <assert.h>
       
    28 #include <sys/stat.h>
       
    29 
       
    30 #ifdef DEBUG_ARGFILE
       
    31   #ifndef NO_JNI
       
    32     #define NO_JNI
       
    33   #endif
       
    34   #define JLI_ReportMessage(p1, p2) printf((p1), (p2))
       
    35 #else
       
    36   #include "java.h"
       
    37 #endif
       
    38 
       
    39 #include "jli_util.h"
       
    40 #include "emessages.h"
       
    41 
       
    42 #define MAX_ARGF_SIZE 0x7fffffffL
       
    43 
       
    44 static char* clone_substring(const char *begin, size_t len) {
       
    45     char *rv = (char *) JLI_MemAlloc(len + 1);
       
    46     memcpy(rv, begin, len);
       
    47     rv[len] = '\0';
       
    48     return rv;
       
    49 }
       
    50 
       
    51 enum STATE {
       
    52     FIND_NEXT,
       
    53     IN_COMMENT,
       
    54     IN_QUOTE,
       
    55     IN_ESCAPE,
       
    56     SKIP_LEAD_WS,
       
    57     IN_TOKEN
       
    58 };
       
    59 
       
    60 typedef struct {
       
    61     enum STATE state;
       
    62     const char* cptr;
       
    63     const char* eob;
       
    64     char quote_char;
       
    65     JLI_List parts;
       
    66 } __ctx_args;
       
    67 
       
    68 #define NOT_FOUND -1
       
    69 static int firstAppArgIndex = NOT_FOUND;
       
    70 
       
    71 static jboolean expectingNoDashArg = JNI_FALSE;
       
    72 static size_t argsCount = 0;
       
    73 static jboolean stopExpansion = JNI_FALSE;
       
    74 
       
    75 void JLI_InitArgProcessing(jboolean isJava, jboolean disableArgFile) {
       
    76     // No expansion for relaunch
       
    77     if (argsCount != 0) {
       
    78         stopExpansion = JNI_TRUE;
       
    79         argsCount = 0;
       
    80     } else {
       
    81         stopExpansion = disableArgFile;
       
    82     }
       
    83 
       
    84     expectingNoDashArg = JNI_FALSE;
       
    85 
       
    86     // for tools, this value remains 0 all the time.
       
    87     firstAppArgIndex = isJava ? NOT_FOUND : 0;
       
    88 }
       
    89 
       
    90 int JLI_GetAppArgIndex() {
       
    91     // Will be 0 for tools
       
    92     return firstAppArgIndex;
       
    93 }
       
    94 
       
    95 static void checkArg(const char *arg) {
       
    96     size_t idx = 0;
       
    97     argsCount++;
       
    98     if (argsCount == 1) {
       
    99         // ignore first argument, the application name
       
   100         return;
       
   101     }
       
   102 
       
   103     // All arguments arrive here must be a launcher argument,
       
   104     // ie. by now, all argfile expansions must have been performed.
       
   105     if (*arg++ == '-') {
       
   106         expectingNoDashArg = JNI_FALSE;
       
   107         if (JLI_StrCmp(arg, "cp") == 0 ||
       
   108             JLI_StrCmp(arg, "classpath") == 0) {
       
   109             expectingNoDashArg = JNI_TRUE;
       
   110         } else if (JLI_StrCmp(arg, "jar") == 0) {
       
   111             // This is tricky, we do expect NoDashArg
       
   112             // But that is considered main class to stop expansion
       
   113             expectingNoDashArg = JNI_FALSE;
       
   114             // We can not just update the idx here because if -jar @file
       
   115             // still need expansion of @file to get the argument for -jar
       
   116         } else if (JLI_StrCmp(arg, "Xdisable-@files") == 0) {
       
   117             stopExpansion = JNI_TRUE;
       
   118         }
       
   119     } else {
       
   120         if (!expectingNoDashArg) {
       
   121             // this is main class, argsCount is index to next arg
       
   122             idx = argsCount;
       
   123         }
       
   124         expectingNoDashArg = JNI_FALSE;
       
   125     }
       
   126     // only update on java mode and not yet found main class
       
   127     if (firstAppArgIndex == -1 && idx != 0) {
       
   128         firstAppArgIndex = (int) idx;
       
   129     }
       
   130 }
       
   131 
       
   132 /*
       
   133        [\n\r]   +------------+                        +------------+ [\n\r]
       
   134       +---------+ IN_COMMENT +<------+                | IN_ESCAPE  +---------+
       
   135       |         +------------+       |                +------------+         |
       
   136       |    [#]       ^               |[#]                 ^     |            |
       
   137       |   +----------+               |                [\\]|     |[^\n\r]     |
       
   138       v   |                          |                    |     v            |
       
   139 +------------+ [^ \t\n\r\f]  +------------+['"]>      +------------+         |
       
   140 | FIND_NEXT  +-------------->+ IN_TOKEN   +-----------+ IN_QUOTE   +         |
       
   141 +------------+               +------------+   <[quote]+------------+         |
       
   142   |   ^                          |                       |  ^   ^            |
       
   143   |   |               [ \t\n\r\f]|                 [\n\r]|  |   |[^ \t\n\r\f]v
       
   144   |   +--------------------------+-----------------------+  |  +--------------+
       
   145   |                       ['"]                              |  | SKIP_LEAD_WS |
       
   146   +---------------------------------------------------------+  +--------------+
       
   147 */
       
   148 static char* nextToken(__ctx_args *pctx) {
       
   149     const char* nextc = pctx->cptr;
       
   150     const char* const eob = pctx->eob;
       
   151     const char* anchor = nextc;
       
   152     char *token;
       
   153 
       
   154     for (; nextc < eob; nextc++) {
       
   155         register char ch = *nextc;
       
   156 
       
   157         // Skip white space characters
       
   158         if (pctx->state == FIND_NEXT || pctx->state == SKIP_LEAD_WS) {
       
   159             while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f') {
       
   160                 nextc++;
       
   161                 if (nextc >= eob) {
       
   162                     return NULL;
       
   163                 }
       
   164                 ch = *nextc;
       
   165             }
       
   166             pctx->state = (pctx->state == FIND_NEXT) ? IN_TOKEN : IN_QUOTE;
       
   167             anchor = nextc;
       
   168         // Deal with escape sequences
       
   169         } else if (pctx->state == IN_ESCAPE) {
       
   170             // concatenation directive
       
   171             if (ch == '\n' || ch == '\r') {
       
   172                 pctx->state = SKIP_LEAD_WS;
       
   173             } else {
       
   174             // escaped character
       
   175                 char* escaped = (char*) JLI_MemAlloc(2 * sizeof(char));
       
   176                 escaped[1] = '\0';
       
   177                 switch (ch) {
       
   178                     case 'n':
       
   179                         escaped[0] = '\n';
       
   180                         break;
       
   181                     case 'r':
       
   182                         escaped[0] = '\r';
       
   183                         break;
       
   184                     case 't':
       
   185                         escaped[0] = '\t';
       
   186                         break;
       
   187                     case 'f':
       
   188                         escaped[0] = '\f';
       
   189                         break;
       
   190                     default:
       
   191                         escaped[0] = ch;
       
   192                         break;
       
   193                 }
       
   194                 JLI_List_add(pctx->parts, escaped);
       
   195                 pctx->state = IN_QUOTE;
       
   196             }
       
   197             // anchor to next character
       
   198             anchor = nextc + 1;
       
   199             continue;
       
   200         // ignore comment to EOL
       
   201         } else if (pctx->state == IN_COMMENT) {
       
   202             while (ch != '\n' && ch != '\r') {
       
   203                 nextc++;
       
   204                 if (nextc > eob) {
       
   205                     return NULL;
       
   206                 }
       
   207                 ch = *nextc;
       
   208             }
       
   209             pctx->state = FIND_NEXT;
       
   210             continue;
       
   211         }
       
   212 
       
   213         assert(pctx->state != IN_ESCAPE);
       
   214         assert(pctx->state != FIND_NEXT);
       
   215         assert(pctx->state != SKIP_LEAD_WS);
       
   216         assert(pctx->state != IN_COMMENT);
       
   217 
       
   218         switch(ch) {
       
   219             case ' ':
       
   220             case '\t':
       
   221             case '\f':
       
   222                 if (pctx->state == IN_QUOTE) {
       
   223                     continue;
       
   224                 }
       
   225                 // fall through
       
   226             case '\n':
       
   227             case '\r':
       
   228                 if (pctx->parts->size == 0) {
       
   229                     token = clone_substring(anchor, nextc - anchor);
       
   230                 } else {
       
   231                     JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
       
   232                     token = JLI_List_combine(pctx->parts);
       
   233                     JLI_List_free(pctx->parts);
       
   234                     pctx->parts = JLI_List_new(4);
       
   235                 }
       
   236                 pctx->cptr = nextc + 1;
       
   237                 pctx->state = FIND_NEXT;
       
   238                 return token;
       
   239             case '#':
       
   240                 if (pctx->state == IN_QUOTE) {
       
   241                     continue;
       
   242                 }
       
   243                 pctx->state = IN_COMMENT;
       
   244                 break;
       
   245             case '\\':
       
   246                 if (pctx->state != IN_QUOTE) {
       
   247                     continue;
       
   248                 }
       
   249                 JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
       
   250                 pctx->state = IN_ESCAPE;
       
   251                 break;
       
   252             case '\'':
       
   253             case '"':
       
   254                 if (pctx->state == IN_QUOTE && pctx->quote_char != ch) {
       
   255                     // not matching quote
       
   256                     continue;
       
   257                 }
       
   258                 // partial before quote
       
   259                 if (anchor != nextc) {
       
   260                     JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
       
   261                 }
       
   262                 // anchor after quote character
       
   263                 anchor = nextc + 1;
       
   264                 if (pctx->state == IN_TOKEN) {
       
   265                     pctx->quote_char = ch;
       
   266                     pctx->state = IN_QUOTE;
       
   267                 } else {
       
   268                     pctx->state = IN_TOKEN;
       
   269                 }
       
   270                 break;
       
   271             default:
       
   272                 break;
       
   273         }
       
   274     }
       
   275 
       
   276     assert(nextc == eob);
       
   277     if (anchor != nextc) {
       
   278         // not yet return until end of stream, we have part of a token.
       
   279         JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
       
   280     }
       
   281     return NULL;
       
   282 }
       
   283 
       
   284 static JLI_List readArgFile(FILE *file) {
       
   285     char buf[4096];
       
   286     JLI_List rv;
       
   287     __ctx_args ctx;
       
   288     size_t size;
       
   289     char *token;
       
   290 
       
   291     ctx.state = FIND_NEXT;
       
   292     ctx.parts = JLI_List_new(4);
       
   293 
       
   294     /* arbitrarily pick 8, seems to be a reasonable number of arguments */
       
   295     rv = JLI_List_new(8);
       
   296 
       
   297     while (!feof(file)) {
       
   298         size = fread(buf, sizeof(char), sizeof(buf), file);
       
   299         if (ferror(file)) {
       
   300             JLI_List_free(rv);
       
   301             return NULL;
       
   302         }
       
   303 
       
   304         /* nextc is next character to read from the buffer
       
   305          * eob is the end of input
       
   306          * token is the copied token value, NULL if no a complete token
       
   307          */
       
   308         ctx.cptr = buf;
       
   309         ctx.eob = buf + size;
       
   310         token = nextToken(&ctx);
       
   311         while (token != NULL) {
       
   312             checkArg(token);
       
   313             JLI_List_add(rv, token);
       
   314             token = nextToken(&ctx);
       
   315         }
       
   316     }
       
   317 
       
   318     // remaining partial token
       
   319     if (ctx.state == IN_TOKEN || ctx.state == IN_QUOTE) {
       
   320         if (ctx.parts->size != 0) {
       
   321             JLI_List_add(rv, JLI_List_combine(ctx.parts));
       
   322         }
       
   323     }
       
   324     JLI_List_free(ctx.parts);
       
   325 
       
   326     return rv;
       
   327 }
       
   328 
       
   329 /*
       
   330  * if the arg represent a file, that is, prefix with a single '@',
       
   331  * return a list of arguments from the file.
       
   332  * otherwise, return NULL.
       
   333  */
       
   334 static JLI_List expandArgFile(const char *arg) {
       
   335     FILE *fptr;
       
   336     struct stat st;
       
   337     JLI_List rv;
       
   338 
       
   339     /* failed to access the file */
       
   340     if (stat(arg, &st) != 0) {
       
   341         JLI_ReportMessage(CFG_ERROR6, arg);
       
   342         exit(1);
       
   343     }
       
   344 
       
   345     if (st.st_size > MAX_ARGF_SIZE) {
       
   346         JLI_ReportMessage(CFG_ERROR10, MAX_ARGF_SIZE);
       
   347         exit(1);
       
   348     }
       
   349 
       
   350     fptr = fopen(arg, "r");
       
   351     /* arg file cannot be openned */
       
   352     if (fptr == NULL) {
       
   353         JLI_ReportMessage(CFG_ERROR6, arg);
       
   354         exit(1);
       
   355     }
       
   356 
       
   357     rv = readArgFile(fptr);
       
   358     fclose(fptr);
       
   359 
       
   360     /* error occurred reading the file */
       
   361     if (rv == NULL) {
       
   362         JLI_ReportMessage(DLL_ERROR4, arg);
       
   363         exit(1);
       
   364     }
       
   365 
       
   366     return rv;
       
   367 }
       
   368 
       
   369 JLI_List JLI_PreprocessArg(const char *arg)
       
   370 {
       
   371     JLI_List rv;
       
   372 
       
   373     if (firstAppArgIndex > 0) {
       
   374         // In user application arg, no more work.
       
   375         return NULL;
       
   376     }
       
   377 
       
   378     if (stopExpansion) {
       
   379         // still looking for user application arg
       
   380         checkArg(arg);
       
   381         return NULL;
       
   382     }
       
   383 
       
   384     if (arg[0] != '@') {
       
   385         checkArg(arg);
       
   386         return NULL;
       
   387     }
       
   388 
       
   389     if (arg[1] == '\0') {
       
   390         // @ by itself is an argument
       
   391         checkArg(arg);
       
   392         return NULL;
       
   393     }
       
   394 
       
   395     arg++;
       
   396     if (arg[0] == '@') {
       
   397         // escaped @argument
       
   398         rv = JLI_List_new(1);
       
   399         checkArg(arg);
       
   400         JLI_List_add(rv, JLI_StringDup(arg));
       
   401     } else {
       
   402         rv = expandArgFile(arg);
       
   403     }
       
   404     return rv;
       
   405 }
       
   406 
       
   407 #ifdef DEBUG_ARGFILE
       
   408 /*
       
   409  * Stand-alone sanity test, build with following command line
       
   410  * $ CC -DDEBUG_ARGFILE -DNO_JNI -g args.c jli_util.c
       
   411  */
       
   412 
       
   413 void fail(char *expected, char *actual, size_t idx) {
       
   414     printf("FAILED: Token[%lu] expected to be <%s>, got <%s>\n", idx, expected, actual);
       
   415     exit(1);
       
   416 }
       
   417 
       
   418 void test_case(char *case_data, char **tokens, size_t cnt_tokens) {
       
   419     size_t actual_cnt;
       
   420     char *token;
       
   421     __ctx_args ctx;
       
   422 
       
   423     actual_cnt = 0;
       
   424 
       
   425     ctx.state = FIND_NEXT;
       
   426     ctx.parts = JLI_List_new(4);
       
   427     ctx.cptr = case_data;
       
   428     ctx.eob = case_data + strlen(case_data);
       
   429 
       
   430     printf("Test case: <%s>, expected %lu tokens.\n", case_data, cnt_tokens);
       
   431 
       
   432     for (token = nextToken(&ctx); token != NULL; token = nextToken(&ctx)) {
       
   433         // should not have more tokens than expected
       
   434         if (actual_cnt >= cnt_tokens) {
       
   435             printf("FAILED: Extra token detected: <%s>\n", token);
       
   436             exit(2);
       
   437         }
       
   438         if (JLI_StrCmp(token, tokens[actual_cnt]) != 0) {
       
   439             fail(tokens[actual_cnt], token, actual_cnt);
       
   440         }
       
   441         actual_cnt++;
       
   442     }
       
   443 
       
   444     char* last = NULL;
       
   445     if (ctx.parts->size != 0) {
       
   446         last = JLI_List_combine(ctx.parts);
       
   447     }
       
   448     JLI_List_free(ctx.parts);
       
   449 
       
   450     if (actual_cnt >= cnt_tokens) {
       
   451         // same number of tokens, should have nothing left to parse
       
   452         if (last != NULL) {
       
   453             if (*last != '#') {
       
   454                 printf("Leftover detected: %s", last);
       
   455                 exit(2);
       
   456             }
       
   457         }
       
   458     } else {
       
   459         if (JLI_StrCmp(last, tokens[actual_cnt]) != 0) {
       
   460             fail(tokens[actual_cnt], last, actual_cnt);
       
   461         }
       
   462         actual_cnt++;
       
   463     }
       
   464     if (actual_cnt != cnt_tokens) {
       
   465         printf("FAILED: Number of tokens not match, expected %lu, got %lu\n",
       
   466             cnt_tokens, actual_cnt);
       
   467         exit(3);
       
   468     }
       
   469 
       
   470     printf("PASS\n");
       
   471 }
       
   472 
       
   473 #define DO_CASE(name) \
       
   474     test_case(name[0], name + 1, sizeof(name)/sizeof(char*) - 1)
       
   475 
       
   476 int main(int argc, char** argv) {
       
   477     size_t i, j;
       
   478 
       
   479     char* case1[] = { "-version -cp \"c:\\\\java libs\\\\one.jar\" \n",
       
   480         "-version", "-cp", "c:\\java libs\\one.jar" };
       
   481     DO_CASE(case1);
       
   482 
       
   483     // note the open quote at the end
       
   484     char* case2[] = { "com.foo.Panda \"Furious 5\"\fand\t'Shi Fu' \"escape\tprison",
       
   485         "com.foo.Panda", "Furious 5", "and", "Shi Fu", "escape\tprison"};
       
   486     DO_CASE(case2);
       
   487 
       
   488     char* escaped_chars[] = { "escaped chars testing \"\\a\\b\\c\\f\\n\\r\\t\\v\\9\\6\\23\\82\\28\\377\\477\\278\\287\"",
       
   489         "escaped", "chars", "testing", "abc\f\n\r\tv96238228377477278287"};
       
   490     DO_CASE(escaped_chars);
       
   491 
       
   492     char* mixed_quote[]  = { "\"mix 'single quote' in double\" 'mix \"double quote\" in single' partial\"quote me\"this",
       
   493         "mix 'single quote' in double", "mix \"double quote\" in single", "partialquote methis"};
       
   494     DO_CASE(mixed_quote);
       
   495 
       
   496     char* comments[]  = { "line one #comment\n'line #2' #rest are comment\r\n#comment on line 3\nline 4 #comment to eof",
       
   497         "line", "one", "line #2", "line", "4"};
       
   498     DO_CASE(comments);
       
   499 
       
   500     char* open_quote[] = { "This is an \"open quote \n    across line\n\t, note for WS.",
       
   501         "This", "is", "an", "open quote ", "across", "line", ",", "note", "for", "WS." };
       
   502     DO_CASE(open_quote);
       
   503 
       
   504     char* escape_in_open_quote[] = { "Try \"this \\\\\\\\ escape\\n double quote \\\" in open quote",
       
   505         "Try", "this \\\\ escape\n double quote \" in open quote" };
       
   506     DO_CASE(escape_in_open_quote);
       
   507 
       
   508     char* quote[] = { "'-Dmy.quote.single'='Property in single quote. Here a double quote\" Add some slashes \\\\/'",
       
   509         "-Dmy.quote.single=Property in single quote. Here a double quote\" Add some slashes \\/" };
       
   510     DO_CASE(quote);
       
   511 
       
   512     char* multi[] = { "\"Open quote to \n  new \"line \\\n\r   third\\\n\r\\\tand\ffourth\"",
       
   513         "Open quote to ", "new", "line third\tand\ffourth" };
       
   514     DO_CASE(multi);
       
   515 
       
   516     char* escape_quote[] = { "c:\\\"partial quote\"\\lib",
       
   517         "c:\\partial quote\\lib" };
       
   518     DO_CASE(escape_quote);
       
   519 
       
   520     if (argc > 1) {
       
   521         for (i = 0; i < argc; i++) {
       
   522             JLI_List tokens = JLI_PreprocessArg(argv[i]);
       
   523             if (NULL != tokens) {
       
   524                 for (j = 0; j < tokens->size; j++) {
       
   525                     printf("Token[%lu]: <%s>\n", (unsigned long) j, tokens->elements[j]);
       
   526                 }
       
   527             }
       
   528         }
       
   529     }
       
   530 }
       
   531 
       
   532 #endif // DEBUG_ARGFILE