--- /dev/null 2015-07-15 12:24:30.000000000 -0700 +++ new/src/java.base/share/native/libjli/args.c 2015-07-15 12:24:30.000000000 -0700 @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +#ifdef DEBUG_ARGFILE + #ifndef NO_JNI + #define NO_JNI + #endif + #define JLI_ReportMessage(p1, p2) printf((p1), (p2)) +#else + #include "java.h" +#endif + +#include "jli_util.h" +#include "emessages.h" + +static char* clone_substring(char *begin, size_t len) { + char *rv; + + rv = (char *) JLI_MemAlloc(len + 1); + memcpy(rv, begin, len); + rv[len] = '\0'; + return rv; +} + +/* + * get quoted token, *cptr points to the start of the token, the beginning + * quote + */ +static char* dequote(char** cptr, const char * const eob) { + char *nextc = *cptr; + char quote_char = *nextc; + char *anchor = nextc + 1; + char *c; + char d, c2, first; + /* arbitarily pick 4, hopefully in most cases no more than 4 escaped chars */ + JLI_List escaped = JLI_List_new(4); + +#ifdef DQ_READ + #error "DQ_READ had been defined!" +#endif +#define DQ_READ(V) \ + nextc++; \ + if (nextc >= eob) { \ + JLI_List_free(escaped); \ + return NULL; \ + } \ + V = (*nextc) +/* end DQ_READ */ + + DQ_READ(d); + while (d != quote_char && d != '\n' && d != '\r') { + if (d == '\\') { + /* string before '\\' */ + JLI_List_addSubstring(escaped, anchor, nextc - anchor); + c = JLI_MemAlloc(2 * sizeof(char)); + c[1] = '\0'; + DQ_READ(c[0]); + first = c[0]; /* To allow \377, but not \477 */ + if (c[0] >= '0' && c[0] <= '7') { + c[0] = c[0] - '0'; + DQ_READ(c2); + if ('0' <= c2 && c2 <= '7') { + c[0] = (c[0] << 3) + (c2 - '0'); + DQ_READ(c2); + if ('0' <= c2 && c2 <= '7' && first <= '3') { + c[0] = (c[0] << 3) + (c2 - '0'); + DQ_READ(d); + } else { + d = c2; + } + } else { + d = c2; + } + } else { + switch (c[0]) { + case 'a': + c[0] = 0x7; + break; + case 'b': + c[0] = '\b'; + break; + case 'f': + c[0] = 0xC; + break; + case 'n': + c[0] = '\n'; + break; + case 'r': + c[0] = '\r'; + break; + case 't': + c[0] = '\t'; + break; + case 'v': + c[0] = 0xB; + break; + } + DQ_READ(d); + } + JLI_List_add(escaped, c); + anchor = nextc; + } else { + DQ_READ(d); + } + } +#undef DQ_READ + + /* nextc points to end quote or eol */ + // assert(nextc < eob); + *cptr = nextc + 1; + if (escaped->size == 0) { + return clone_substring(anchor, nextc - anchor); + } else { + JLI_List_addSubstring(escaped, anchor, nextc - anchor); + return JLI_List_combine(escaped); + } +} + +/* + * cptr: the output parameter of a char* points at next read location + * eob: the pointer points to end of buffer, one after the last character + * return the pointer to start of next token, NULL if the buffer contains no token. + * *cptr will point to next character after the token. + * if returns NULL and cptr != eob indicated partial token remains in the buffer + */ +static char* nextToken(char** cptr, const char * const eob) { + char *nextc = *cptr; + char *anchor; + size_t len; + char *rv; + + /* whitespaceChars(0, ' ') */ + while (nextc < eob && *nextc <= ' ') nextc++; + if (nextc == eob) { + *cptr = nextc; + return NULL; + } + + /* start with non-whitespace character */ + anchor = nextc; + switch (*nextc) { + case '#': /* commentChar('#') */ + /* ignore until eol */ + for(nextc++; nextc < eob && *nextc != '\n'; nextc++); + if (nextc < eob) { + /* skip comment, continue read for next token */ + *cptr = nextc++; + return nextToken(cptr, eob); + } + break; + case '"': /* quoteChar('"') */ + case '\'': /* quoteChar('\'') */ + *cptr = nextc; + return dequote(cptr, eob); + default: + for (nextc++; nextc < eob && *nextc > ' '; nextc++) { + /* comment or quote break a token just like whitespace */ + if (*nextc == '#' || *nextc == '"' || *nextc == '\'') { + break; + } + } + if (nextc < eob) { + len = nextc - anchor; + rv = clone_substring(anchor, len); + *cptr = nextc; + return rv; + } + } + + /* nextc == eob, partial of next token + * need to combine with next read + */ + *cptr = anchor; + return NULL; +} + +static JLI_List readArgFile(FILE *file) { + char buf[4096]; + JLI_List rv; + size_t size = 0; + size_t buf_size = sizeof(buf); + char *nextc = buf; + char *token = NULL; + char *eob = buf; + char *extra_buf = NULL; + size_t extra_size = sizeof(buf); + char *active_buf = buf; + + /* arbitrarily pick 8, seems to be a reasonable number of arguments */ + rv = JLI_List_new(8); + + while (!feof(file)) { + size = fread(eob, sizeof(char), buf_size - size, file); + if (ferror(file)) { + JLI_List_free(rv); + if (extra_buf != NULL) { + JLI_MemFree(extra_buf); + } + return NULL; + } + + /* nextc is next character to read from the buffer + * eob is the end of input + * token is the copied token value, NULL if no a complete token + */ + nextc = active_buf; + eob = eob + size; + token = nextToken(&nextc, eob); + while (token != NULL) { + JLI_List_add(rv, token); + token = nextToken(&nextc, eob); + } + + /* remaining characters of a non-complete token, + * need to combine with next read. + * if size = 0, means the input is complete read and no token + * available in this buffer + */ + size = eob - nextc; + if (size == 0) { + active_buf = buf; + buf_size = sizeof(buf); + } else { + if (size < sizeof(buf)) { + // use stack whenever possible + active_buf = buf; + buf_size = sizeof(buf); + memmove(active_buf, nextc, size); + } else { + if (size >= extra_size) { + // increase 4K + extra_size += sizeof(buf); + extra_buf = (char*) JLI_MemRealloc(extra_buf, extra_size); + if (active_buf == buf) { + memmove(extra_buf, nextc, size); + } // else realloc takes care of copy + } else { + // size fit in extra but still to large for buf + memmove(extra_buf, nextc, size); + } + active_buf = extra_buf; + buf_size = extra_size; + } + } + // assert(size < buf_size); + eob = active_buf + size; + } + + /* last token from remaining */ + if (active_buf < eob) { + if (*active_buf == '"' || *active_buf == '\'') { + nextc = active_buf + 1; + } else { + nextc = active_buf; + } + if (*nextc != '#') { + token = clone_substring(nextc, eob - nextc); + JLI_List_add(rv, token); + } + } + if (extra_buf != NULL) { + JLI_MemFree(extra_buf); + } + return rv; +} + +/* + * if the arg represent a file, that is, prefix with a single '@', + * return a list of arguments from the file. + * otherwise, return NULL. + */ +JLI_List JLI_ExpandArgFile(const char *arg, jboolean not_java) { + JLI_List rv; + FILE *fptr; + + if (not_java) { + if (JLI_StrNCmp("-J@", arg, 3) != 0) { + return NULL; + } else { + arg += 3; + } + } else if (*arg != '@') { + return NULL; + } else { + arg++; + } + + if (*arg == '@' || *arg == '\0') { + return NULL; + } + + fptr = fopen(arg, "r"); + /* arg file cannot be openned */ + if (fptr == NULL) { + JLI_ReportMessage(CFG_ERROR6, arg); + exit(1); + } + + rv = readArgFile(fptr); + fclose(fptr); + + /* error occurred reading the file */ + if (rv == NULL) { + JLI_ReportMessage(DLL_ERROR4, arg); + } + + return rv; +} + +#ifdef DEBUG_ARGFILE + +void fail(char *expected, char *actual, size_t idx) { + printf("FAILED: Token[%lu] expected to be <%s>, got <%s>\n", idx, expected, actual); + exit(1); +} + +void test_case(char *case_data, char **tokens, size_t cnt_tokens) { + char *b, *eob; + size_t actual_cnt; + char *token; + //char **tokens = case_data + 1; + + b = case_data; + eob = b + strlen(b); + actual_cnt = 0; + + printf("Test case: <%s>, expected %lu tokens.\n", b, cnt_tokens); + + for (token = nextToken(&b, eob); token != NULL; token = nextToken(&b, eob)) { + // should not have more tokens than expected + if (actual_cnt >= cnt_tokens) { + printf("FAILED: Extra token detected: <%s>\n", token); + exit(2); + } + if (JLI_StrCmp(token, tokens[actual_cnt]) != 0) { + fail(tokens[actual_cnt], token, actual_cnt); + } + actual_cnt++; + } + + if (actual_cnt >= cnt_tokens) { + // same number of tokens, should have nothing left to parse + if (b != eob) { + // remainder could be comment + if (*b != '#') { + printf("FAILED: Leftover detected: <%s>\n", b); + exit(2); + } + } + } else { + if (JLI_StrCmp(b, tokens[actual_cnt]) != 0) { + fail(tokens[actual_cnt], b, actual_cnt); + } + actual_cnt++; + } + if (actual_cnt != cnt_tokens) { + printf("FAILED: Number of tokens not match, expected %lu, got %lu\n", + cnt_tokens, actual_cnt); + exit(3); + } + + printf("PASS\n"); +} + +#define DO_CASE(name) \ + test_case(name[0], name + 1, sizeof(name)/sizeof(char*) - 1) + +int main(int argc, char** argv) { + int i, j; + + char* case1[] = { "-version -cp \"c:\\\\java libs\\\\one.jar\" \n", + "-version", "-cp", "c:\\java libs\\one.jar" }; + DO_CASE(case1); + + // note the open quote at the end + char* case2[] = { "com.foo.Panda \"Furious 5\" 'Shi Fu' \"escape\tprison", + "com.foo.Panda", "Furious 5", "Shi Fu", "\"escape\tprison"}; + DO_CASE(case2); + + char* escaped_chars[] = { "escaped chars testing \"\\a\\b\\c\\f\\n\\r\\t\\v\\9\\6\\23\\82\\28\\377\\477\\278\\287\"", + "escaped", "chars", "testing", "\a\bc\f\n\r\t\v9\006\02382\0028\377\0477\0278\00287"}; + DO_CASE(escaped_chars); + + char* mixed_quote[] = { "\"mix 'single quote' in double\" 'mix \"double quote\" in single'", + "mix 'single quote' in double", "mix \"double quote\" in single"}; + DO_CASE(mixed_quote); + + char* comments[] = { "line one #comment\n'line #2' #rest are comment\r#comment on line 3\nline 4 #comment to eof", + "line", "one", "line #2", "line", "4"}; + DO_CASE(comments); + + if (argc > 1) { + for (i = 0; i < argc; i++) { + JLI_List tokens = JLI_ExpandArgFile(argv[i], JNI_FALSE); + if (NULL != tokens) { + for (j = 0; j < tokens->size; j++) { + printf("Token[%d]: <%s>\n", j, tokens->elements[j]); + } + } + } + } +} + +#endif // DEBUG_ARGFILE