--- old/make/lib/CoreLibraries.gmk 2015-07-15 12:24:22.000000000 -0700 +++ new/make/lib/CoreLibraries.gmk 2015-07-15 12:24:22.000000000 -0700 @@ -330,6 +330,11 @@ -export:JLI_CmdToArgs \ -export:JLI_GetStdArgc \ -export:JLI_GetStdArgs \ + -export:JLI_List_new \ + -export:JLI_List_add \ + -export:JLI_StringDup \ + -export:JLI_MemFree \ + -export:JLI_ExpandArgFile \ advapi32.lib \ comctl32.lib \ user32.lib, \ --- old/make/mapfiles/libjli/mapfile-vers 2015-07-15 12:24:23.000000000 -0700 +++ new/make/mapfiles/libjli/mapfile-vers 2015-07-15 12:24:23.000000000 -0700 @@ -36,6 +36,11 @@ JLI_ReportExceptionDescription; JLI_GetStdArgs; JLI_GetStdArgc; + JLI_List_new; + JLI_List_add; + JLI_StringDup; + JLI_MemFree; + JLI_ExpandArgFile; local: *; --- old/src/java.base/share/native/launcher/main.c 2015-07-15 12:24:24.000000000 -0700 +++ new/src/java.base/share/native/launcher/main.c 2015-07-15 12:24:24.000000000 -0700 @@ -31,6 +31,7 @@ */ #include "defines.h" +#include "jli_util.h" #ifdef _MSC_VER #if _MSC_VER > 1400 && _MSC_VER < 1600 @@ -106,7 +107,7 @@ } } } - JLI_CmdToArgs(GetCommandLine()); + JLI_CmdToArgs(GetCommandLine(), HAS_JAVA_ARGS); margc = JLI_GetStdArgc(); // add one more to mark the end margv = (char **)JLI_MemAlloc((margc + 1) * (sizeof(char *))); @@ -119,8 +120,30 @@ margv[i] = NULL; } #else /* *NIXES */ - margc = argc; - margv = argv; + // accommodate the NULL at the end + JLI_List args = JLI_List_new(argc + 1); + { + int i = 0; + for (i = 0; i < argc; i++) { + JLI_List argsInFile = JLI_ExpandArgFile(argv[i], HAS_JAVA_ARGS); + if (NULL == argsInFile) { + JLI_List_add(args, JLI_StringDup(argv[i])); + } else { + int cnt, idx; + cnt = argsInFile->size; + for (idx = 0; idx < cnt; idx++) { + JLI_List_add(args, argsInFile->elements[idx]); + } + // Shallow free, we reuse the string to avoid copy + JLI_MemFree(argsInFile->elements); + JLI_MemFree(argsInFile); + } + } + } + margc = args->size; + // add the NULL pointer at argv[argc] + JLI_List_add(args, NULL); + margv = args->elements; #endif /* WIN32 */ return JLI_Launch(margc, margv, sizeof(const_jargs) / sizeof(char *), const_jargs, --- old/src/java.base/share/native/libjli/jli_util.c 2015-07-15 12:24:25.000000000 -0700 +++ new/src/java.base/share/native/libjli/jli_util.c 2015-07-15 12:24:25.000000000 -0700 @@ -25,8 +25,7 @@ #include #include -#include - +#include #include "jli_util.h" /* @@ -119,3 +118,122 @@ { return JLI_StrNCmp(s1, s2, JLI_StrLen(s2)); } + +JLI_List +JLI_List_new(int capacity) +{ + JLI_List l = (JLI_List) JLI_MemAlloc(sizeof(struct JLI_List_)); + l->capacity = capacity; + l->elements = (char **) JLI_MemAlloc(capacity * sizeof(l->elements[0])); + l->size = 0; + return l; +} + +void +JLI_List_free(JLI_List sl) +{ + if (sl) { + if (sl->elements) { + int i; + for (i = 0; i < sl->size; i++) + JLI_MemFree(sl->elements[i]); + JLI_MemFree(sl->elements); + } + JLI_MemFree(sl); + } +} + +void +JLI_List_ensureCapacity(JLI_List sl, int capacity) +{ + if (sl->capacity < capacity) { + while (sl->capacity < capacity) + sl->capacity *= 2; + sl->elements = JLI_MemRealloc(sl->elements, + sl->capacity * sizeof(sl->elements[0])); + } +} + +void +JLI_List_add(JLI_List sl, char *str) +{ + JLI_List_ensureCapacity(sl, sl->size+1); + sl->elements[sl->size++] = str; +} + +void +JLI_List_addSubstring(JLI_List sl, const char *beg, size_t len) +{ + char *str = (char *) JLI_MemAlloc(len+1); + memcpy(str, beg, len); + str[len] = '\0'; + JLI_List_ensureCapacity(sl, sl->size+1); + sl->elements[sl->size++] = str; +} + +char * +JLI_List_combine(JLI_List sl) +{ + int i; + int size; + char *str; + char *p; + for (i = 0, size = 1; i < sl->size; i++) + size += (int)JLI_StrLen(sl->elements[i]); + + str = JLI_MemAlloc(size); + + for (i = 0, p = str; i < sl->size; i++) { + int len = (int)JLI_StrLen(sl->elements[i]); + memcpy(p, sl->elements[i], len); + p += len; + } + *p = '\0'; + + return str; +} + +char * +JLI_List_join(JLI_List sl, char sep) +{ + int i; + int size; + char *str; + char *p; + for (i = 0, size = 1; i < sl->size; i++) + size += (int)JLI_StrLen(sl->elements[i]) + 1; + + str = JLI_MemAlloc(size); + + for (i = 0, p = str; i < sl->size; i++) { + int len = (int)JLI_StrLen(sl->elements[i]); + if (i > 0) *p++ = sep; + memcpy(p, sl->elements[i], len); + p += len; + } + *p = '\0'; + + return str; +} + +JLI_List +JLI_List_split(const char *str, char sep) +{ + const char *p, *q; + size_t len = JLI_StrLen(str); + int count; + JLI_List sl; + for (count = 1, p = str; p < str + len; p++) + count += (*p == sep); + sl = JLI_List_new(count); + for (p = str;;) { + for (q = p; q <= str + len; q++) { + if (*q == sep || *q == '\0') { + JLI_List_addSubstring(sl, p, q - p); + if (*q == '\0') + return sl; + p = q + 1; + } + } + } +} --- old/src/java.base/share/native/libjli/jli_util.h 2015-07-15 12:24:26.000000000 -0700 +++ new/src/java.base/share/native/libjli/jli_util.h 2015-07-15 12:24:26.000000000 -0700 @@ -29,7 +29,15 @@ #include #include #include -#include + +#ifndef NO_JNI + #include +#else + #define jboolean int + #define JNI_TRUE 1 + #define JNI_FALSE 0 +#endif + #define JLDEBUG_ENV_ENTRY "_JAVA_LAUNCHER_DEBUG" void *JLI_MemAlloc(size_t size); @@ -68,7 +76,7 @@ #define JLI_StrCaseCmp(p1, p2) stricmp((p1), (p2)) #define JLI_StrNCaseCmp(p1, p2, p3) strnicmp((p1), (p2), (p3)) int JLI_Snprintf(char *buffer, size_t size, const char *format, ...); -void JLI_CmdToArgs(char *cmdline); +void JLI_CmdToArgs(char *cmdline, jboolean not_java); #define JLI_Lseek _lseeki64 #define JLI_PutEnv _putenv #define JLI_GetPid _getpid @@ -102,4 +110,28 @@ void JLI_SetTraceLauncher(); jboolean JLI_IsTraceLauncher(); +/* + * JLI_List - a dynamic list of char* + */ +struct JLI_List_ +{ + char **elements; + int size; + int capacity; +}; +typedef struct JLI_List_ *JLI_List; + +JLI_List JLI_List_new(int capacity); +void JLI_List_free(JLI_List l); +void JLI_List_ensureCapacity(JLI_List l, int capacity); +/* e must be JLI_MemFree-able */ +void JLI_List_add(JLI_List l, char *e); +/* a copy is made out of beg */ +void JLI_List_addSubstring(JLI_List l, const char *beg, size_t len); +char *JLI_List_combine(JLI_List sl); +char *JLI_List_join(JLI_List l, char sep); +JLI_List JLI_List_split(const char *str, char sep); + +JLI_List JLI_ExpandArgFile(const char *arg, jboolean not_java); + #endif /* _JLI_UTIL_H */ --- old/src/java.base/share/native/libjli/wildcard.c 2015-07-15 12:24:27.000000000 -0700 +++ new/src/java.base/share/native/libjli/wildcard.c 2015-07-15 12:24:27.000000000 -0700 @@ -218,116 +218,6 @@ return JLI_StrCmp(s1, s2) == 0; } -/* - * FileList ADT - a dynamic list of C filenames - */ -struct FileList_ -{ - char **files; - int size; - int capacity; -}; -typedef struct FileList_ *FileList; - -static FileList -FileList_new(int capacity) -{ - FileList fl = NEW_(FileList); - fl->capacity = capacity; - fl->files = (char **) JLI_MemAlloc(capacity * sizeof(fl->files[0])); - fl->size = 0; - return fl; -} - - - -static void -FileList_free(FileList fl) -{ - if (fl) { - if (fl->files) { - int i; - for (i = 0; i < fl->size; i++) - JLI_MemFree(fl->files[i]); - JLI_MemFree(fl->files); - } - JLI_MemFree(fl); - } -} - -static void -FileList_ensureCapacity(FileList fl, int capacity) -{ - if (fl->capacity < capacity) { - while (fl->capacity < capacity) - fl->capacity *= 2; - fl->files = JLI_MemRealloc(fl->files, - fl->capacity * sizeof(fl->files[0])); - } -} - -static void -FileList_add(FileList fl, char *file) -{ - FileList_ensureCapacity(fl, fl->size+1); - fl->files[fl->size++] = file; -} - -static void -FileList_addSubstring(FileList fl, const char *beg, size_t len) -{ - char *filename = (char *) JLI_MemAlloc(len+1); - memcpy(filename, beg, len); - filename[len] = '\0'; - FileList_ensureCapacity(fl, fl->size+1); - fl->files[fl->size++] = filename; -} - -static char * -FileList_join(FileList fl, char sep) -{ - int i; - int size; - char *path; - char *p; - for (i = 0, size = 1; i < fl->size; i++) - size += (int)JLI_StrLen(fl->files[i]) + 1; - - path = JLI_MemAlloc(size); - - for (i = 0, p = path; i < fl->size; i++) { - int len = (int)JLI_StrLen(fl->files[i]); - if (i > 0) *p++ = sep; - memcpy(p, fl->files[i], len); - p += len; - } - *p = '\0'; - - return path; -} - -static FileList -FileList_split(const char *path, char sep) -{ - const char *p, *q; - size_t len = JLI_StrLen(path); - int count; - FileList fl; - for (count = 1, p = path; p < path + len; p++) - count += (*p == sep); - fl = FileList_new(count); - for (p = path;;) { - for (q = p; q <= path + len; q++) { - if (*q == sep || *q == '\0') { - FileList_addSubstring(fl, p, q - p); - if (*q == '\0') - return fl; - p = q + 1; - } - } - } -} - static int isJarFileName(const char *filename) { @@ -352,22 +242,22 @@ return filename; } -static FileList +static JLI_List wildcardFileList(const char *wildcard) { const char *basename; - FileList fl = FileList_new(16); + JLI_List fl = JLI_List_new(16); WildcardIterator it = WildcardIterator_for(wildcard); if (it == NULL) { - FileList_free(fl); + JLI_List_free(fl); return NULL; } while ((basename = WildcardIterator_next(it)) != NULL) if (isJarFileName(basename)) - FileList_add(fl, wildcardConcat(wildcard, basename)); + JLI_List_add(fl, wildcardConcat(wildcard, basename)); WildcardIterator_close(it); return fl; } @@ -383,25 +273,25 @@ } static void -FileList_expandWildcards(FileList fl) +FileList_expandWildcards(JLI_List fl) { int i, j; for (i = 0; i < fl->size; i++) { - if (isWildcard(fl->files[i])) { - FileList expanded = wildcardFileList(fl->files[i]); + if (isWildcard(fl->elements[i])) { + JLI_List expanded = wildcardFileList(fl->elements[i]); if (expanded != NULL && expanded->size > 0) { - JLI_MemFree(fl->files[i]); - FileList_ensureCapacity(fl, fl->size + expanded->size); + JLI_MemFree(fl->elements[i]); + JLI_List_ensureCapacity(fl, fl->size + expanded->size); for (j = fl->size - 1; j >= i+1; j--) - fl->files[j+expanded->size-1] = fl->files[j]; + fl->elements[j+expanded->size-1] = fl->elements[j]; for (j = 0; j < expanded->size; j++) - fl->files[i+j] = expanded->files[j]; + fl->elements[i+j] = expanded->elements[j]; i += expanded->size - 1; fl->size += expanded->size - 1; /* fl expropriates expanded's elements. */ expanded->size = 0; } - FileList_free(expanded); + JLI_List_free(expanded); } } } @@ -410,14 +300,14 @@ JLI_WildcardExpandClasspath(const char *classpath) { char *expanded; - FileList fl; + JLI_List fl; if (JLI_StrChr(classpath, '*') == NULL) return classpath; - fl = FileList_split(classpath, PATH_SEPARATOR); + fl = JLI_List_split(classpath, PATH_SEPARATOR); FileList_expandWildcards(fl); - expanded = FileList_join(fl, PATH_SEPARATOR); - FileList_free(fl); + expanded = JLI_List_join(fl, PATH_SEPARATOR); + JLI_List_free(fl); if (getenv(JLDEBUG_ENV_ENTRY) != 0) printf("Expanded wildcards:\n" " before: \"%s\"\n" @@ -428,13 +318,13 @@ #ifdef DEBUG_WILDCARD static void -FileList_print(FileList fl) +FileList_print(JLI_List fl) { int i; putchar('['); for (i = 0; i < fl->size; i++) { if (i > 0) printf(", "); - printf("\"%s\"",fl->files[i]); + printf("\"%s\"",fl->elements[i]); } putchar(']'); } --- old/src/java.base/windows/native/libjli/cmdtoargs.c 2015-07-15 12:24:28.000000000 -0700 +++ new/src/java.base/windows/native/libjli/cmdtoargs.c 2015-07-15 12:24:28.000000000 -0700 @@ -193,23 +193,42 @@ return stdargs; } -void JLI_CmdToArgs(char* cmdline) { +void JLI_CmdToArgs(char* cmdline, jboolean not_java) { int nargs = 0; StdArg* argv = NULL; jboolean wildcard = JNI_FALSE; char* src = cmdline; + JLI_List argsInFile; // allocate arg buffer with sufficient space to receive the largest arg char* arg = JLI_StringDup(cmdline); do { src = next_arg(src, arg, &wildcard); - // resize to accommodate another Arg - argv = (StdArg*) JLI_MemRealloc(argv, (nargs+1) * sizeof(StdArg)); - argv[nargs].arg = JLI_StringDup(arg); - argv[nargs].has_wildcard = wildcard; + argsInFile = JLI_ExpandArgFile(arg, not_java); + if (argsInFile != NULL) { + int cnt, i; + // resize to accommodate another Arg + cnt = argsInFile->size; + argv = (StdArg*) JLI_MemRealloc(argv, (nargs + cnt) * sizeof(StdArg)); + for (i = 0; i < cnt; i++) { + argv[nargs].arg = argsInFile->elements[i]; + // wildcard is not supported in argfile + argv[nargs].has_wildcard = JNI_FALSE; + nargs++; + } + // Shallow free, we reuse the string to avoid copy + JLI_MemFree(argsInFile->elements); + JLI_MemFree(argsInFile); + } else { + // resize to accommodate another Arg + argv = (StdArg*) JLI_MemRealloc(argv, (nargs+1) * sizeof(StdArg)); + argv[nargs].arg = JLI_StringDup(arg); + argv[nargs].has_wildcard = wildcard; + *arg = '\0'; + nargs++; + } *arg = '\0'; - nargs++; } while (src != NULL); JLI_MemFree(arg); --- old/test/tools/launcher/TestHelper.java 2015-07-15 12:24:29.000000000 -0700 +++ new/test/tools/launcher/TestHelper.java 2015-07-15 12:24:29.000000000 -0700 @@ -594,7 +594,7 @@ } boolean notContains(String str) { - for (String x : testOutput) { + for (String x : testOutput) { if (x.contains(str)) { appendError("string <" + str + "> found"); return false; @@ -604,7 +604,7 @@ } boolean matches(String stringToMatch) { - for (String x : testOutput) { + for (String x : testOutput) { if (x.matches(stringToMatch)) { return true; } @@ -622,6 +622,16 @@ appendError("string <" + stringToMatch + "> found"); return false; } + + String findInOutput(String pattern) { + for (String x : testOutput) { + if (x.matches(pattern)) { + return x; + } + } + appendError("string <" + pattern + "> not found"); + return null; + } } /** * Indicates that the annotated method is a test method. --- /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 --- /dev/null 2015-07-15 12:24:31.000000000 -0700 +++ new/test/tools/launcher/ArgsFileTest.java 2015-07-15 12:24:31.000000000 -0700 @@ -0,0 +1,210 @@ +/* + * 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. + * + * 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. + */ + +/** + * @test + * @bug 8027634 + * @summary Argument parsing from file + * @build TestHelper + * @run main ArgsFileTest + */ +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ArgsFileTest extends TestHelper { + private File createFile(List lines) throws IOException { + File argFile = new File("argfile"); + argFile.delete(); + createAFile(argFile, lines); + return argFile; + } + + private void verifyOutput(List args, TestResult tr, int start_idx) { + if (args.isEmpty()) { + return; + } + + if (start_idx <= 0) { + String arg = Pattern.quote(args.get(0)); + String line = tr.findInOutput(".*argv\\[(\\d+)\\] = " + arg + ".*"); + if (line == null) { + System.out.println(tr); + throw new RuntimeException("test fails"); + } + Matcher m = Pattern.compile(".*argv\\[(\\d+)\\] = " + arg + ".*").matcher(line); + if (!m.matches()) { + System.out.println(tr); + throw new RuntimeException("test fails"); + } + start_idx = Integer.parseInt(m.group(1)) + 1; + args = args.subList(1, args.size()); + } + + int i = start_idx; + for (String x : args) { + tr.matches(".*argv\\[" + i + "\\] = " + Pattern.quote(x) + ".*"); + i++; + } + if (! tr.testStatus) { + System.out.println(tr); + throw new RuntimeException("test fails"); + } + } + + static String[] cases[][] = { + { + { "# a couple of -X flags", + "-Xmx32m", + "-XshowSettings", + "# add -version", + "-version" + }, + { "-Xmx32m", + "-XshowSettings", + "-version" + } + }, + { + { "-cp \"c:\\\\java lib\\\\all", + "-Xmx32m -XshowSettings", + "-version" + }, + { "-cp", + "c:\\java lib\\all", + "-Xmx32m", + "-XshowSettings", + "-version" + } + } + }; + + public Object[][] loadCases() { + int cnt = cases.length; + + Object[][] rv = new Object[cnt + 2][2]; + for (int i = 0; i < cnt; i++) { + rv[i][0] = Arrays.asList(cases[i][0]); + rv[i][1] = Arrays.asList(cases[i][1]); + } + + // long lines + String bcp = "-Xbootclasspath/a:"; + String ver = "-version"; + // a token 8192 long + char[] data = new char[8192 - bcp.length()]; + Arrays.fill(data, 'O'); + List scratch = new ArrayList<>(); + scratch.add("-Xmx32m"); + scratch.add(bcp + String.valueOf(data)); + scratch.add(ver); + rv[cnt][0] = scratch; + rv[cnt++][1] = scratch; + + data = new char[8192 + 1024]; + Arrays.fill(data, 'O'); + scratch = new ArrayList<>(); + scratch.add(bcp + String.valueOf(data)); + scratch.add(ver); + rv[cnt][0] = scratch; + rv[cnt][1] = scratch; + + return rv; + } + + // ensure the arguments in the file are read in correctly + public void getArguments(List lines, List args) throws IOException { + File argFile = createFile(lines); + String fname = "@" + argFile.getName(); + Map env = new HashMap<>(); + env.put(JLDEBUG_KEY, "true"); + + TestResult tr = doExec(env, javaCmd, fname); + tr.checkPositive(); + verifyOutput(args, tr, 1); + argFile.delete(); + } + + @Test + public void runner() throws IOException { + Object[][] allcases = loadCases(); + for (Object[] test: allcases) { + getArguments((List) test[0], (List) test[1]); + } + } + + @Test + public void variousTools() throws IOException { + List lines = Arrays.asList(cases[0][0]); + List args = Arrays.asList(cases[0][1]); + + File argFile = createFile(lines); + String fname = "@" + argFile.getName(); + Map env = new HashMap<>(); + env.put(JLDEBUG_KEY, "true"); + + // Without -J, @argfile should not expand for javac + TestResult tr = doExec(env, javacCmd, fname); + verifyOutput(Collections.singletonList(fname), tr, -1); + // With -J@argfile, @argfile is expanded + tr = doExec(env, javacCmd, "-J" + fname); + verifyOutput(args, tr, -1); + + tr = doExec(env, javaCmd, "-Xint", fname, "-Dlast.arg"); + List scratch = new ArrayList<>(); + scratch.add("-Xint"); + scratch.addAll(args); + scratch.add("-Dlast.arg"); + verifyOutput(scratch, tr, 1); + } + + // test with missing file + @Test + void missingFileNegativeTest() throws IOException { + TestResult tr = doExec(javaCmd, "@" + "missing.cmd"); + tr.checkNegative(); + tr.contains("Error: could not open `missing.cmd'"); + if (!tr.testStatus) { + System.out.println(tr); + throw new RuntimeException("test fails"); + } + } + + public static void main(String... args) throws Exception { + ArgsFileTest a = new ArgsFileTest(); + a.run(args); + if (testExitValue > 0) { + System.out.println("Total of " + testExitValue + " failed"); + System.exit(1); + } else { + System.out.println("All tests pass"); + } + } +}