8027634: Support @argfiles for java command-line tool
authorhenryjen
Wed, 08 Jul 2015 23:26:48 -0700
changeset 32267 4e96a9ee01b1
parent 32266 e0a235a11254
child 32268 9fe7ee60d49d
8027634: Support @argfiles for java command-line tool Reviewed-by: ksrini, mchung
jdk/make/launcher/Launcher-java.base.gmk
jdk/make/lib/CoreLibraries.gmk
jdk/make/mapfiles/libjli/mapfile-vers
jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties
jdk/src/java.base/share/native/launcher/defines.h
jdk/src/java.base/share/native/launcher/main.c
jdk/src/java.base/share/native/libjli/args.c
jdk/src/java.base/share/native/libjli/emessages.h
jdk/src/java.base/share/native/libjli/java.c
jdk/src/java.base/share/native/libjli/jli_util.c
jdk/src/java.base/share/native/libjli/jli_util.h
jdk/src/java.base/share/native/libjli/wildcard.c
jdk/src/java.base/windows/native/libjli/cmdtoargs.c
jdk/test/tools/launcher/ArgFileSyntax.java
jdk/test/tools/launcher/ArgsFileTest.java
jdk/test/tools/launcher/TestHelper.java
--- a/jdk/make/launcher/Launcher-java.base.gmk	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/make/launcher/Launcher-java.base.gmk	Wed Jul 08 23:26:48 2015 -0700
@@ -31,7 +31,7 @@
 # into another dir and copy selectively so debuginfo for java.dll isn't
 # overwritten.
 $(eval $(call SetupLauncher,java, \
-    -DEXPAND_CLASSPATH_WILDCARDS,,,user32.lib comctl32.lib, \
+    -DEXPAND_CLASSPATH_WILDCARDS -DENABLE_ARG_FILES,,,user32.lib comctl32.lib, \
     $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jli_static.lib, $(JAVA_RC_FLAGS), \
     $(JAVA_VERSION_INFO_RESOURCE), $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/java_objs,true))
 
@@ -44,7 +44,7 @@
 
 ifeq ($(OPENJDK_TARGET_OS), windows)
   $(eval $(call SetupLauncher,javaw, \
-      -DJAVAW -DEXPAND_CLASSPATH_WILDCARDS,,,user32.lib comctl32.lib, \
+      -DJAVAW -DEXPAND_CLASSPATH_WILDCARDS -DENABLE_ARG_FILES,,,user32.lib comctl32.lib, \
       $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jli_static.lib, $(JAVA_RC_FLAGS), \
       $(JAVA_VERSION_INFO_RESOURCE),,true))
 endif
--- a/jdk/make/lib/CoreLibraries.gmk	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/make/lib/CoreLibraries.gmk	Wed Jul 08 23:26:48 2015 -0700
@@ -330,6 +330,13 @@
         -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_InitArgProcessing \
+        -export:JLI_PreprocessArg \
+        -export:JLI_GetAppArgIndex \
         advapi32.lib \
         comctl32.lib \
         user32.lib, \
--- a/jdk/make/mapfiles/libjli/mapfile-vers	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/make/mapfiles/libjli/mapfile-vers	Wed Jul 08 23:26:48 2015 -0700
@@ -36,6 +36,13 @@
 		JLI_ReportExceptionDescription;
 		JLI_GetStdArgs;
 		JLI_GetStdArgc;
+		JLI_List_new;
+		JLI_List_add;
+		JLI_StringDup;
+		JLI_MemFree;
+		JLI_InitArgProcessing;
+		JLI_PreprocessArg;
+		JLI_GetAppArgIndex;
 
 	local:
 		*;
--- a/jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties	Wed Jul 08 23:26:48 2015 -0700
@@ -24,8 +24,8 @@
 #
 
 # Translators please note do not translate the options themselves
-java.launcher.opt.header  =   Usage: {0} [-options] class [args...]\n\
-\           (to execute a class)\n   or  {0} [-options] -jar jarfile [args...]\n\
+java.launcher.opt.header  =   Usage: {0} [options] class [args...]\n\
+\           (to execute a class)\n   or  {0} [options] -jar jarfile [args...]\n\
 \           (to execute a jar file)\n\
 where options include:\n
 
@@ -68,6 +68,8 @@
 \                  load Java programming language agent, see java.lang.instrument\n\
 \    -splash:<imagepath>\n\
 \                  show splash screen with specified image\n\
+\    @<filepath>   read options from the specified file\n\
+
 See http://www.oracle.com/technetwork/java/javase/documentation/index.html for more details.
 
 # Translators please note do not translate the options themselves
@@ -102,7 +104,8 @@
 \    -XshowSettings:properties\n\
 \                      show all property settings and continue\n\
 \    -XshowSettings:locale\n\
-\                      show all locale related settings and continue\n\n\
+\                      show all locale related settings and continue\n\
+\    -Xdisable-@files  disable further argument file expansion\n\n\
 The -X options are non-standard and subject to change without notice.\n
 
 # Translators please note do not translate the options themselves
--- a/jdk/src/java.base/share/native/launcher/defines.h	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/src/java.base/share/native/launcher/defines.h	Wed Jul 08 23:26:48 2015 -0700
@@ -89,4 +89,9 @@
 static const jint const_ergo_class = DEFAULT_POLICY;
 #endif /* NEVER_ACT_AS_SERVER_CLASS_MACHINE */
 
+#ifdef ENABLE_ARG_FILES
+static const jboolean const_disable_argfile = JNI_FALSE;
+#else
+static const jboolean const_disable_argfile = JNI_TRUE;
+#endif
 #endif /*_DEFINES_H */
--- a/jdk/src/java.base/share/native/launcher/main.c	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/src/java.base/share/native/launcher/main.c	Wed Jul 08 23:26:48 2015 -0700
@@ -31,6 +31,7 @@
  */
 
 #include "defines.h"
+#include "jli_util.h"
 
 #ifdef _MSC_VER
 #if _MSC_VER > 1400 && _MSC_VER < 1600
@@ -96,6 +97,9 @@
     char** margv;
     const jboolean const_javaw = JNI_FALSE;
 #endif /* JAVAW */
+
+    JLI_InitArgProcessing(!HAS_JAVA_ARGS, const_disable_argfile);
+
 #ifdef _WIN32
     {
         int i = 0;
@@ -119,8 +123,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_PreprocessArg(argv[i]);
+            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,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/native/libjli/args.c	Wed Jul 08 23:26:48 2015 -0700
@@ -0,0 +1,532 @@
+/*
+ * 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 <stdio.h>
+#include <assert.h>
+#include <sys/stat.h>
+
+#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"
+
+#define MAX_ARGF_SIZE 0x7fffffffL
+
+static char* clone_substring(const char *begin, size_t len) {
+    char *rv = (char *) JLI_MemAlloc(len + 1);
+    memcpy(rv, begin, len);
+    rv[len] = '\0';
+    return rv;
+}
+
+enum STATE {
+    FIND_NEXT,
+    IN_COMMENT,
+    IN_QUOTE,
+    IN_ESCAPE,
+    SKIP_LEAD_WS,
+    IN_TOKEN
+};
+
+typedef struct {
+    enum STATE state;
+    const char* cptr;
+    const char* eob;
+    char quote_char;
+    JLI_List parts;
+} __ctx_args;
+
+#define NOT_FOUND -1
+static int firstAppArgIndex = NOT_FOUND;
+
+static jboolean expectingNoDashArg = JNI_FALSE;
+static size_t argsCount = 0;
+static jboolean stopExpansion = JNI_FALSE;
+
+void JLI_InitArgProcessing(jboolean isJava, jboolean disableArgFile) {
+    // No expansion for relaunch
+    if (argsCount != 0) {
+        stopExpansion = JNI_TRUE;
+        argsCount = 0;
+    } else {
+        stopExpansion = disableArgFile;
+    }
+
+    expectingNoDashArg = JNI_FALSE;
+
+    // for tools, this value remains 0 all the time.
+    firstAppArgIndex = isJava ? NOT_FOUND : 0;
+}
+
+int JLI_GetAppArgIndex() {
+    // Will be 0 for tools
+    return firstAppArgIndex;
+}
+
+static void checkArg(const char *arg) {
+    size_t idx = 0;
+    argsCount++;
+    if (argsCount == 1) {
+        // ignore first argument, the application name
+        return;
+    }
+
+    // All arguments arrive here must be a launcher argument,
+    // ie. by now, all argfile expansions must have been performed.
+    if (*arg++ == '-') {
+        expectingNoDashArg = JNI_FALSE;
+        if (JLI_StrCmp(arg, "cp") == 0 ||
+            JLI_StrCmp(arg, "classpath") == 0) {
+            expectingNoDashArg = JNI_TRUE;
+        } else if (JLI_StrCmp(arg, "jar") == 0) {
+            // This is tricky, we do expect NoDashArg
+            // But that is considered main class to stop expansion
+            expectingNoDashArg = JNI_FALSE;
+            // We can not just update the idx here because if -jar @file
+            // still need expansion of @file to get the argument for -jar
+        } else if (JLI_StrCmp(arg, "Xdisable-@files") == 0) {
+            stopExpansion = JNI_TRUE;
+        }
+    } else {
+        if (!expectingNoDashArg) {
+            // this is main class, argsCount is index to next arg
+            idx = argsCount;
+        }
+        expectingNoDashArg = JNI_FALSE;
+    }
+    // only update on java mode and not yet found main class
+    if (firstAppArgIndex == -1 && idx != 0) {
+        firstAppArgIndex = (int) idx;
+    }
+}
+
+/*
+       [\n\r]   +------------+                        +------------+ [\n\r]
+      +---------+ IN_COMMENT +<------+                | IN_ESCAPE  +---------+
+      |         +------------+       |                +------------+         |
+      |    [#]       ^               |[#]                 ^     |            |
+      |   +----------+               |                [\\]|     |[^\n\r]     |
+      v   |                          |                    |     v            |
++------------+ [^ \t\n\r\f]  +------------+['"]>      +------------+         |
+| FIND_NEXT  +-------------->+ IN_TOKEN   +-----------+ IN_QUOTE   +         |
++------------+               +------------+   <[quote]+------------+         |
+  |   ^                          |                       |  ^   ^            |
+  |   |               [ \t\n\r\f]|                 [\n\r]|  |   |[^ \t\n\r\f]v
+  |   +--------------------------+-----------------------+  |  +--------------+
+  |                       ['"]                              |  | SKIP_LEAD_WS |
+  +---------------------------------------------------------+  +--------------+
+*/
+static char* nextToken(__ctx_args *pctx) {
+    const char* nextc = pctx->cptr;
+    const char* const eob = pctx->eob;
+    const char* anchor = nextc;
+    char *token;
+
+    for (; nextc < eob; nextc++) {
+        register char ch = *nextc;
+
+        // Skip white space characters
+        if (pctx->state == FIND_NEXT || pctx->state == SKIP_LEAD_WS) {
+            while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f') {
+                nextc++;
+                if (nextc >= eob) {
+                    return NULL;
+                }
+                ch = *nextc;
+            }
+            pctx->state = (pctx->state == FIND_NEXT) ? IN_TOKEN : IN_QUOTE;
+            anchor = nextc;
+        // Deal with escape sequences
+        } else if (pctx->state == IN_ESCAPE) {
+            // concatenation directive
+            if (ch == '\n' || ch == '\r') {
+                pctx->state = SKIP_LEAD_WS;
+            } else {
+            // escaped character
+                char* escaped = (char*) JLI_MemAlloc(2 * sizeof(char));
+                escaped[1] = '\0';
+                switch (ch) {
+                    case 'n':
+                        escaped[0] = '\n';
+                        break;
+                    case 'r':
+                        escaped[0] = '\r';
+                        break;
+                    case 't':
+                        escaped[0] = '\t';
+                        break;
+                    case 'f':
+                        escaped[0] = '\f';
+                        break;
+                    default:
+                        escaped[0] = ch;
+                        break;
+                }
+                JLI_List_add(pctx->parts, escaped);
+                pctx->state = IN_QUOTE;
+            }
+            // anchor to next character
+            anchor = nextc + 1;
+            continue;
+        // ignore comment to EOL
+        } else if (pctx->state == IN_COMMENT) {
+            while (ch != '\n' && ch != '\r') {
+                nextc++;
+                if (nextc > eob) {
+                    return NULL;
+                }
+                ch = *nextc;
+            }
+            pctx->state = FIND_NEXT;
+            continue;
+        }
+
+        assert(pctx->state != IN_ESCAPE);
+        assert(pctx->state != FIND_NEXT);
+        assert(pctx->state != SKIP_LEAD_WS);
+        assert(pctx->state != IN_COMMENT);
+
+        switch(ch) {
+            case ' ':
+            case '\t':
+            case '\f':
+                if (pctx->state == IN_QUOTE) {
+                    continue;
+                }
+                // fall through
+            case '\n':
+            case '\r':
+                if (pctx->parts->size == 0) {
+                    token = clone_substring(anchor, nextc - anchor);
+                } else {
+                    JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
+                    token = JLI_List_combine(pctx->parts);
+                    JLI_List_free(pctx->parts);
+                    pctx->parts = JLI_List_new(4);
+                }
+                pctx->cptr = nextc + 1;
+                pctx->state = FIND_NEXT;
+                return token;
+            case '#':
+                if (pctx->state == IN_QUOTE) {
+                    continue;
+                }
+                pctx->state = IN_COMMENT;
+                break;
+            case '\\':
+                if (pctx->state != IN_QUOTE) {
+                    continue;
+                }
+                JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
+                pctx->state = IN_ESCAPE;
+                break;
+            case '\'':
+            case '"':
+                if (pctx->state == IN_QUOTE && pctx->quote_char != ch) {
+                    // not matching quote
+                    continue;
+                }
+                // partial before quote
+                if (anchor != nextc) {
+                    JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
+                }
+                // anchor after quote character
+                anchor = nextc + 1;
+                if (pctx->state == IN_TOKEN) {
+                    pctx->quote_char = ch;
+                    pctx->state = IN_QUOTE;
+                } else {
+                    pctx->state = IN_TOKEN;
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    assert(nextc == eob);
+    if (anchor != nextc) {
+        // not yet return until end of stream, we have part of a token.
+        JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
+    }
+    return NULL;
+}
+
+static JLI_List readArgFile(FILE *file) {
+    char buf[4096];
+    JLI_List rv;
+    __ctx_args ctx;
+    size_t size;
+    char *token;
+
+    ctx.state = FIND_NEXT;
+    ctx.parts = JLI_List_new(4);
+
+    /* arbitrarily pick 8, seems to be a reasonable number of arguments */
+    rv = JLI_List_new(8);
+
+    while (!feof(file)) {
+        size = fread(buf, sizeof(char), sizeof(buf), file);
+        if (ferror(file)) {
+            JLI_List_free(rv);
+            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
+         */
+        ctx.cptr = buf;
+        ctx.eob = buf + size;
+        token = nextToken(&ctx);
+        while (token != NULL) {
+            checkArg(token);
+            JLI_List_add(rv, token);
+            token = nextToken(&ctx);
+        }
+    }
+
+    // remaining partial token
+    if (ctx.state == IN_TOKEN || ctx.state == IN_QUOTE) {
+        if (ctx.parts->size != 0) {
+            JLI_List_add(rv, JLI_List_combine(ctx.parts));
+        }
+    }
+    JLI_List_free(ctx.parts);
+
+    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.
+ */
+static JLI_List expandArgFile(const char *arg) {
+    FILE *fptr;
+    struct stat st;
+    JLI_List rv;
+
+    /* failed to access the file */
+    if (stat(arg, &st) != 0) {
+        JLI_ReportMessage(CFG_ERROR6, arg);
+        exit(1);
+    }
+
+    if (st.st_size > MAX_ARGF_SIZE) {
+        JLI_ReportMessage(CFG_ERROR10, MAX_ARGF_SIZE);
+        exit(1);
+    }
+
+    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);
+        exit(1);
+    }
+
+    return rv;
+}
+
+JLI_List JLI_PreprocessArg(const char *arg)
+{
+    JLI_List rv;
+
+    if (firstAppArgIndex > 0) {
+        // In user application arg, no more work.
+        return NULL;
+    }
+
+    if (stopExpansion) {
+        // still looking for user application arg
+        checkArg(arg);
+        return NULL;
+    }
+
+    if (arg[0] != '@') {
+        checkArg(arg);
+        return NULL;
+    }
+
+    if (arg[1] == '\0') {
+        // @ by itself is an argument
+        checkArg(arg);
+        return NULL;
+    }
+
+    arg++;
+    if (arg[0] == '@') {
+        // escaped @argument
+        rv = JLI_List_new(1);
+        checkArg(arg);
+        JLI_List_add(rv, JLI_StringDup(arg));
+    } else {
+        rv = expandArgFile(arg);
+    }
+    return rv;
+}
+
+#ifdef DEBUG_ARGFILE
+/*
+ * Stand-alone sanity test, build with following command line
+ * $ CC -DDEBUG_ARGFILE -DNO_JNI -g args.c jli_util.c
+ */
+
+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) {
+    size_t actual_cnt;
+    char *token;
+    __ctx_args ctx;
+
+    actual_cnt = 0;
+
+    ctx.state = FIND_NEXT;
+    ctx.parts = JLI_List_new(4);
+    ctx.cptr = case_data;
+    ctx.eob = case_data + strlen(case_data);
+
+    printf("Test case: <%s>, expected %lu tokens.\n", case_data, cnt_tokens);
+
+    for (token = nextToken(&ctx); token != NULL; token = nextToken(&ctx)) {
+        // 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++;
+    }
+
+    char* last = NULL;
+    if (ctx.parts->size != 0) {
+        last = JLI_List_combine(ctx.parts);
+    }
+    JLI_List_free(ctx.parts);
+
+    if (actual_cnt >= cnt_tokens) {
+        // same number of tokens, should have nothing left to parse
+        if (last != NULL) {
+            if (*last != '#') {
+                printf("Leftover detected: %s", last);
+                exit(2);
+            }
+        }
+    } else {
+        if (JLI_StrCmp(last, tokens[actual_cnt]) != 0) {
+            fail(tokens[actual_cnt], last, 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) {
+    size_t 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\"\fand\t'Shi Fu' \"escape\tprison",
+        "com.foo.Panda", "Furious 5", "and", "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", "abc\f\n\r\tv96238228377477278287"};
+    DO_CASE(escaped_chars);
+
+    char* mixed_quote[]  = { "\"mix 'single quote' in double\" 'mix \"double quote\" in single' partial\"quote me\"this",
+        "mix 'single quote' in double", "mix \"double quote\" in single", "partialquote methis"};
+    DO_CASE(mixed_quote);
+
+    char* comments[]  = { "line one #comment\n'line #2' #rest are comment\r\n#comment on line 3\nline 4 #comment to eof",
+        "line", "one", "line #2", "line", "4"};
+    DO_CASE(comments);
+
+    char* open_quote[] = { "This is an \"open quote \n    across line\n\t, note for WS.",
+        "This", "is", "an", "open quote ", "across", "line", ",", "note", "for", "WS." };
+    DO_CASE(open_quote);
+
+    char* escape_in_open_quote[] = { "Try \"this \\\\\\\\ escape\\n double quote \\\" in open quote",
+        "Try", "this \\\\ escape\n double quote \" in open quote" };
+    DO_CASE(escape_in_open_quote);
+
+    char* quote[] = { "'-Dmy.quote.single'='Property in single quote. Here a double quote\" Add some slashes \\\\/'",
+        "-Dmy.quote.single=Property in single quote. Here a double quote\" Add some slashes \\/" };
+    DO_CASE(quote);
+
+    char* multi[] = { "\"Open quote to \n  new \"line \\\n\r   third\\\n\r\\\tand\ffourth\"",
+        "Open quote to ", "new", "line third\tand\ffourth" };
+    DO_CASE(multi);
+
+    char* escape_quote[] = { "c:\\\"partial quote\"\\lib",
+        "c:\\partial quote\\lib" };
+    DO_CASE(escape_quote);
+
+    if (argc > 1) {
+        for (i = 0; i < argc; i++) {
+            JLI_List tokens = JLI_PreprocessArg(argv[i]);
+            if (NULL != tokens) {
+                for (j = 0; j < tokens->size; j++) {
+                    printf("Token[%lu]: <%s>\n", (unsigned long) j, tokens->elements[j]);
+                }
+            }
+        }
+    }
+}
+
+#endif // DEBUG_ARGFILE
--- a/jdk/src/java.base/share/native/libjli/emessages.h	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/src/java.base/share/native/libjli/emessages.h	Wed Jul 08 23:26:48 2015 -0700
@@ -71,6 +71,7 @@
 #define CFG_ERROR7      "Error: no known VMs. (check for corrupt jvm.cfg file)"
 #define CFG_ERROR8      "Error: missing `%s' JVM at `%s'.\nPlease install or use the JRE or JDK that contains these missing components."
 #define CFG_ERROR9      "Error: could not determine JVM type."
+#define CFG_ERROR10     "Error: Argument file size should not be larger than %lu."
 
 #define JRE_ERROR1      "Error: Could not find Java SE Runtime Environment."
 #define JRE_ERROR2      "Error: This Java instance does not support a %d-bit JVM.\nPlease install the desired version."
--- a/jdk/src/java.base/share/native/libjli/java.c	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/src/java.base/share/native/libjli/java.c	Wed Jul 08 23:26:48 2015 -0700
@@ -1963,6 +1963,7 @@
 {
     if (!JLI_IsTraceLauncher()) return ;
     printf("Launcher state:\n");
+    printf("\tFirst application arg index: %d\n", JLI_GetAppArgIndex());
     printf("\tdebug:%s\n", (JLI_IsTraceLauncher() == JNI_TRUE) ? "on" : "off");
     printf("\tjavargs:%s\n", (_is_java_args == JNI_TRUE) ? "on" : "off");
     printf("\tprogram name:%s\n", GetProgramName());
--- a/jdk/src/java.base/share/native/libjli/jli_util.c	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/src/java.base/share/native/libjli/jli_util.c	Wed Jul 08 23:26:48 2015 -0700
@@ -25,8 +25,7 @@
 
 #include <stdio.h>
 #include <string.h>
-#include <jni.h>
-
+#include <stdarg.h>
 #include "jli_util.h"
 
 /*
@@ -97,6 +96,7 @@
     va_start(vl, fmt);
     vprintf(fmt,vl);
     va_end(vl);
+    fflush(stdout);
 }
 
 void
@@ -119,3 +119,122 @@
 {
    return JLI_StrNCmp(s1, s2, JLI_StrLen(s2));
 }
+
+JLI_List
+JLI_List_new(size_t 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) {
+            size_t 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, size_t 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)
+{
+    size_t i;
+    size_t size;
+    char *str;
+    char *p;
+    for (i = 0, size = 1; i < sl->size; i++)
+        size += JLI_StrLen(sl->elements[i]);
+
+    str = JLI_MemAlloc(size);
+
+    for (i = 0, p = str; i < sl->size; i++) {
+        size_t len = 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)
+{
+    size_t i;
+    size_t size;
+    char *str;
+    char *p;
+    for (i = 0, size = 1; i < sl->size; i++)
+        size += JLI_StrLen(sl->elements[i]) + 1;
+
+    str = JLI_MemAlloc(size);
+
+    for (i = 0, p = str; i < sl->size; i++) {
+        size_t len = 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;
+            }
+        }
+    }
+}
--- a/jdk/src/java.base/share/native/libjli/jli_util.h	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/src/java.base/share/native/libjli/jli_util.h	Wed Jul 08 23:26:48 2015 -0700
@@ -29,7 +29,15 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
-#include <jni.h>
+
+#ifndef NO_JNI
+  #include <jni.h>
+#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);
@@ -45,6 +53,7 @@
 
 StdArg *JLI_GetStdArgs();
 int     JLI_GetStdArgc();
+int     JLI_GetAppArgIndex();
 
 #define JLI_StrLen(p1)          strlen((p1))
 #define JLI_StrChr(p1, p2)      strchr((p1), (p2))
@@ -102,4 +111,29 @@
 void     JLI_SetTraceLauncher();
 jboolean JLI_IsTraceLauncher();
 
+/*
+ * JLI_List - a dynamic list of char*
+ */
+struct JLI_List_
+{
+    char **elements;
+    size_t size;
+    size_t capacity;
+};
+typedef struct JLI_List_ *JLI_List;
+
+JLI_List JLI_List_new(size_t capacity);
+void JLI_List_free(JLI_List l);
+void JLI_List_ensureCapacity(JLI_List l, size_t 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);
+
+void JLI_InitArgProcessing(jboolean isJava, jboolean disableArgFile);
+JLI_List JLI_PreprocessArg(const char *arg);
+
 #endif  /* _JLI_UTIL_H */
--- a/jdk/src/java.base/share/native/libjli/wildcard.c	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/src/java.base/share/native/libjli/wildcard.c	Wed Jul 08 23:26:48 2015 -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;
+    size_t 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;
+    size_t i;
     putchar('[');
     for (i = 0; i < fl->size; i++) {
         if (i > 0) printf(", ");
-        printf("\"%s\"",fl->files[i]);
+        printf("\"%s\"",fl->elements[i]);
     }
     putchar(']');
 }
--- a/jdk/src/java.base/windows/native/libjli/cmdtoargs.c	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/src/java.base/windows/native/libjli/cmdtoargs.c	Wed Jul 08 23:26:48 2015 -0700
@@ -198,18 +198,37 @@
     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_PreprocessArg(arg);
+        if (argsInFile != NULL) {
+            size_t 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);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/launcher/ArgFileSyntax.java	Wed Jul 08 23:26:48 2015 -0700
@@ -0,0 +1,271 @@
+/*
+ * 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 Verify syntax of argument file
+ * @build TestHelper
+ * @run main ArgFileSyntax
+ */
+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 ArgFileSyntax extends TestHelper {
+    private File createArgFile(List<String> lines) throws IOException {
+        File argFile = new File("argfile");
+        argFile.delete();
+        createAFile(argFile, lines);
+        return argFile;
+    }
+
+    private void verifyOutput(List<String> args, TestResult tr) {
+        if (args.isEmpty()) {
+            return;
+        }
+
+        int i = 1;
+        for (String x : args) {
+            tr.matches(".*argv\\[" + i + "\\] = " + Pattern.quote(x) + ".*");
+            i++;
+        }
+        if (! tr.testStatus) {
+            System.out.println(tr);
+            throw new RuntimeException("test fails");
+        }
+    }
+
+    // arg file content,  expected options
+    static String[] testCases[][] = {
+        { // empty file
+            {}, {}
+        },
+        { // comments and # inside quote
+            { "# a couple of -X flags",
+              "-Xmx32m",
+              "-XshowSettings #inline comment",
+              "-Dpound.in.quote=\"This property contains #.\"",
+              "# add -version",
+              "-version",
+              "# trail comment"
+            },
+            { "-Xmx32m",
+              "-XshowSettings",
+              "-Dpound.in.quote=This property contains #.",
+              "-version"
+            }
+        },
+        { // open quote with continuation directive
+          // multiple options in a line
+            { "-cp \"c:\\\\java lib\\\\all;\\",
+              "     c:\\\\lib\"",
+              "-Xmx32m -XshowSettings",
+              "-version"
+            },
+            { "-cp",
+              "c:\\java lib\\all;c:\\lib",
+              "-Xmx32m",
+              "-XshowSettings",
+              "-version"
+            }
+        },
+        { // no continuation on open quote
+          // multiple lines in a property
+            { "-cp \"c:\\\\open quote\\\\all;",
+              "     # c:\\\\lib\"",
+              "-Dmultiple.lines=\"line 1\\nline 2\\n\\rline 3\"",
+              "-Dopen.quote=\"Open quote to EOL",
+              "-Dcontinue.with.leadingWS=\"Continue with\\",
+              "  \\ leading WS.",
+              "-Dcontinue.without.leadingWS=\"Continue without \\",
+              "   leading WS.",
+              "-Descape.seq=\"escaped chars: \\\"\\a\\b\\c\\f\\t\\v\\9\\6\\23\\82\\28\\377\\477\\278\\287\\n\"",
+              "-version"
+            },
+            { "-cp",
+              "c:\\open quote\\all;",
+              "-Dmultiple.lines=line 1",
+              // line 2 and line 3 shoule be in output, but not as arg[x]=
+              "-Dopen.quote=Open quote to EOL",
+              "-Dcontinue.with.leadingWS=Continue with leading WS.",
+              "-Dcontinue.without.leadingWS=Continue without leading WS.",
+              // cannot verify \n and \r as that break output lines
+              "-Descape.seq=escaped chars: \"abc\f\tv96238228377477278287",
+              "-version"
+            }
+        },
+        { // No need to escape if not in quote
+          // also quote part of a token
+            { "-cp c:\\\"partial quote\"\\all",
+              "-Xmx32m -XshowSettings",
+              "-version"
+            },
+            { "-cp",
+              "c:\\partial quote\\all",
+              "-Xmx32m",
+              "-XshowSettings",
+              "-version"
+            }
+        },
+        { // No recursive expansion
+            { "-Xmx32m",
+              "-cp",
+              " # @cpfile should remains @cpfile",
+              "@cpfile",
+              "-version"
+            },
+            { "-Xmx32m",
+              "-cp",
+              "@cpfile",
+              "-version"
+            }
+        },
+        { // Mix quotation
+            { "-Dsingle.in.double=\"Mix 'single' in double\"",
+              "-Ddouble.in.single='Mix \"double\" in single'",
+              "-Dsingle.in.single='Escape \\\'single\\\' in single'",
+              "-Ddouble.in.double=\"Escape \\\"double\\\" in double\""
+            },
+            { "-Dsingle.in.double=Mix 'single' in double",
+              "-Ddouble.in.single=Mix \"double\" in single",
+              "-Dsingle.in.single=Escape 'single' in single",
+              "-Ddouble.in.double=Escape \"double\" in double"
+            },
+        },
+        { // \t\f as whitespace and in escape
+            { "-Xmx32m\t-Xint\f-version",
+              "-Dcontinue.with.leadingws=\"Line1\\",
+              " \t\fcontinue with \\f<ff> and \\t<tab>"
+            },
+            { "-Xmx32m",
+              "-Xint",
+              "-version",
+              "-Dcontinue.with.leadingws=Line1continue with \f<ff> and \t<tab>"
+            }
+        }
+    };
+
+    public List<List<List<String>>> loadCases() {
+        List<List<List<String>>> rv = new ArrayList<>();
+        for (String[][] testCaseArray: testCases) {
+            List<List<String>> testCase = new ArrayList<>(2);
+            testCase.add(Arrays.asList(testCaseArray[0]));
+            testCase.add(Arrays.asList(testCaseArray[1]));
+            rv.add(testCase);
+        }
+
+        // long lines
+        String bag = "-Dgarbage=";
+        String ver = "-version";
+        // a token 8192 long
+        char[] data = new char[8192 - bag.length()];
+        Arrays.fill(data, 'O');
+        List<String> scratch = new ArrayList<>();
+        scratch.add("-Xmx32m");
+        scratch.add(bag + String.valueOf(data));
+        scratch.add(ver);
+        rv.add(Collections.nCopies(2, scratch));
+
+        data = new char[8192 + 1024];
+        Arrays.fill(data, 'O');
+        scratch = new ArrayList<>();
+        scratch.add(bag + String.valueOf(data));
+        scratch.add(ver);
+        rv.add(Collections.nCopies(2, scratch));
+
+        return rv;
+    }
+
+    // ensure the arguments in the file are read in correctly
+    private void verifyParsing(List<String> lines, List<String> args) throws IOException {
+        File argFile = createArgFile(lines);
+        String fname = "@" + argFile.getName();
+        Map<String, String> env = new HashMap<>();
+        env.put(JLDEBUG_KEY, "true");
+
+        TestResult tr;
+        if (args.contains("-version")) {
+            tr = doExec(env, javaCmd, fname);
+        } else {
+            tr = doExec(env, javaCmd, fname, "-version");
+        }
+        tr.checkPositive();
+        verifyOutput(args, tr);
+
+        String lastArg = args.contains("-version") ? "-Dlast.arg" : "-version";
+        tr = doExec(env, javaCmd, "-Xint", fname, lastArg);
+        List<String> scratch = new ArrayList<>();
+        scratch.add("-Xint");
+        scratch.addAll(args);
+        scratch.add(lastArg);
+        verifyOutput(scratch, tr);
+
+        argFile.delete();
+    }
+
+    @Test
+    public void testSyntax() throws IOException {
+        List<List<List<String>>> allcases = loadCases();
+        for (List<List<String>> test: allcases) {
+            verifyParsing(test.get(0), test.get(1));
+        }
+    }
+
+    @Test
+    public void badCases() throws IOException {
+        List<String> lines = Arrays.asList(
+            "-Dno.escape=\"Forgot to escape backslash\\\" -version");
+        File argFile = createArgFile(lines);
+        String fname = "@" + argFile.getName();
+        Map<String, String> env = new HashMap<>();
+        env.put(JLDEBUG_KEY, "true");
+
+        TestResult tr = doExec(env, javaCmd, fname);
+        tr.contains("argv[1] = -Dno.escape=Forgot to escape backslash\" -version");
+        tr.checkNegative();
+        if (!tr.testStatus) {
+            System.out.println(tr);
+            throw new RuntimeException("test fails");
+        }
+        argFile.delete();
+    }
+
+    public static void main(String... args) throws Exception {
+        ArgFileSyntax a = new ArgFileSyntax();
+        a.run(args);
+        if (testExitValue > 0) {
+            System.out.println("Total of " + testExitValue + " failed");
+            System.exit(1);
+        } else {
+            System.out.println("All tests pass");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/launcher/ArgsFileTest.java	Wed Jul 08 23:26:48 2015 -0700
@@ -0,0 +1,290 @@
+/*
+ * 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 static File testJar = null;
+    private static Map<String, String> env = new HashMap<>();
+
+    static void init() throws IOException {
+        if  (testJar != null) {
+            return;
+        }
+        testJar = new File("test.jar");
+        StringBuilder tsrc = new StringBuilder();
+        tsrc.append("public static void main(String... args) {\n");
+        tsrc.append("   for (String x : args) {\n");
+        tsrc.append("        System.out.println(x);\n");
+        tsrc.append("   }\n");
+        tsrc.append("}\n");
+        createJar(testJar, new File("Foo"), tsrc.toString());
+
+        env.put(JLDEBUG_KEY, "true");
+    }
+
+    private File createArgFile(String fname, List<String> lines) throws IOException {
+        File argFile = new File(fname);
+        argFile.delete();
+        createAFile(argFile, lines);
+        return argFile;
+    }
+
+    private void verifyOptions(List<String> args, TestResult tr) {
+        if (args.isEmpty()) {
+            return;
+        }
+
+        int i = 1;
+        for (String x : args) {
+            tr.matches(".*argv\\[" + i + "\\] = " + Pattern.quote(x) + ".*");
+            i++;
+        }
+        if (! tr.testStatus) {
+            System.out.println(tr);
+            throw new RuntimeException("test fails");
+        }
+    }
+
+    private void verifyUserArgs(List<String> args, TestResult tr, int index) {
+        if (javaCmd != TestHelper.javaCmd) {
+            tr.contains("\tFirst application arg index: 1");
+        } else {
+            tr.contains("\tFirst application arg index: " + index);
+
+            for (String arg: args) {
+                tr.matches("^" + Pattern.quote(arg) + "$");
+            }
+        }
+
+        if (! tr.testStatus) {
+            System.out.println(tr);
+            throw new RuntimeException("test fails");
+        }
+    }
+
+    @Test
+    public void expandAll() throws IOException {
+        List<String> lines = new ArrayList<>();
+        lines.add("-Xmx32m");
+        lines.add("-Xint");
+        File argFile1 = createArgFile("argFile1", lines);
+        lines = new ArrayList<>();
+        lines.add("-jar");
+        lines.add("test.jar");
+        lines.add("uarg1 @uarg2 @@uarg3 -uarg4 uarg5");
+        File argFile2 = createArgFile("argFile2", lines);
+
+        TestResult tr = doExec(env, javaCmd, "@argFile1", "@argFile2");
+
+        List<String> appArgs = new ArrayList<>();
+        appArgs.add("uarg1");
+        appArgs.add("@uarg2");
+        appArgs.add("@@uarg3");
+        appArgs.add("-uarg4");
+        appArgs.add("uarg5");
+
+        List<String> options = new ArrayList<>();
+        options.add("-Xmx32m");
+        options.add("-Xint");
+        options.add("-jar");
+        options.add("test.jar");
+        options.addAll(appArgs);
+
+        verifyOptions(options, tr);
+        verifyUserArgs(appArgs, tr, 5);
+        argFile1.delete();
+        argFile2.delete();
+
+        File cpFile = createArgFile("cpFile", Arrays.asList("-cp", "test.jar"));
+        List<String> appCmd = new ArrayList<>();
+        appCmd.add("Foo");
+        appCmd.addAll(appArgs);
+        File appFile = createArgFile("appFile", appCmd);
+
+        tr = doExec(env, javaCmd, "@cpFile", "@appFile");
+        verifyOptions(Arrays.asList("-cp", "test.jar", "Foo",
+                "uarg1", "@uarg2", "@@uarg3", "-uarg4", "uarg5"), tr);
+        verifyUserArgs(appArgs, tr, 4);
+        cpFile.delete();
+        appFile.delete();
+    }
+
+    @Test
+    public void escapeArg() throws IOException {
+        List<String> lines = new ArrayList<>();
+        lines.add("-Xmx32m");
+        lines.add("-Xint");
+        File argFile1 = createArgFile("argFile1", lines);
+
+        TestResult tr = doExec(env, javaCmd, "-cp", "@@arg", "-cp", "@",
+                "-cp", "@@@cp", "@argFile1", "@@@@Main@@@@", "-version");
+        List<String> options = new ArrayList<>();
+        options.add("-cp");
+        options.add("@arg");
+        options.add("-cp");
+        options.add("@");
+        options.add("-cp");
+        options.add("@@cp");
+        options.add("-Xmx32m");
+        options.add("-Xint");
+        options.add("@@@Main@@@@");
+        options.add("-version");
+        verifyOptions(options, tr);
+        verifyUserArgs(Collections.emptyList(), tr, options.size());
+        argFile1.delete();
+    }
+
+    @Test
+    public void killSwitch() throws IOException {
+        List<String> lines = new ArrayList<>();
+        lines.add("-Xmx32m");
+        lines.add("-Xint");
+        File argFile1 = createArgFile("argFile1", lines);
+        lines = new ArrayList<>();
+        lines.add("-jar");
+        lines.add("test.jar");
+        lines.add("uarg1 @uarg2 @@uarg3 -uarg4 uarg5");
+        File argFile2 = createArgFile("argFile2", lines);
+        File argKill = createArgFile("argKill",
+            Collections.singletonList("-Xdisable-@files"));
+
+        TestResult tr = doExec(env, javaCmd, "@argFile1", "-Xdisable-@files", "@argFile2");
+        List<String> options = new ArrayList<>();
+        options.add("-Xmx32m");
+        options.add("-Xint");
+        options.add("-Xdisable-@files");
+        options.add("@argFile2");
+        verifyOptions(options, tr);
+        // Main class is @argFile2
+        verifyUserArgs(Collections.emptyList(), tr, 5);
+
+        // Specify in file is same as specify inline
+        tr = doExec(env, javaCmd, "@argFile1", "@argKill", "@argFile2");
+        verifyOptions(options, tr);
+        // Main class is @argFile2
+        verifyUserArgs(Collections.emptyList(), tr, 5);
+
+        // multiple is fine, once on is on.
+        tr = doExec(env, javaCmd, "@argKill", "@argFile1", "-Xdisable-@files", "@argFile2");
+        options = Arrays.asList("-Xdisable-@files", "@argFile1",
+                "-Xdisable-@files", "@argFile2");
+        verifyOptions(options, tr);
+        verifyUserArgs(Collections.emptyList(), tr, 3);
+
+        // after main class, becoming an user application argument
+        tr = doExec(env, javaCmd, "@argFile2", "@argKill");
+        options = Arrays.asList("-jar", "test.jar", "uarg1", "@uarg2", "@@uarg3",
+                "-uarg4", "uarg5", "@argKill");
+        verifyOptions(options, tr);
+        verifyUserArgs(Arrays.asList("uarg1", "@uarg2", "@@uarg3",
+                "-uarg4", "uarg5", "@argKill"), tr, 3);
+
+        argFile1.delete();
+        argFile2.delete();
+        argKill.delete();
+    }
+
+    @Test
+    public void userApplication() throws IOException {
+        List<String> lines = new ArrayList<>();
+        lines.add("-Xmx32m");
+        lines.add("-Xint");
+        File vmArgs = createArgFile("vmArgs", lines);
+        File jarOpt = createArgFile("jarOpt", Arrays.asList("-jar"));
+        File cpOpt = createArgFile("cpOpt", Arrays.asList("-cp"));
+        File jarArg = createArgFile("jarArg", Arrays.asList("test.jar"));
+        File userArgs = createArgFile("userArgs", Arrays.asList("-opt", "arg", "--longopt"));
+
+        TestResult tr = doExec(env, javaCmd,
+                "@vmArgs", "@jarOpt", "test.jar", "-opt", "arg", "--longopt");
+        verifyOptions(Arrays.asList(
+                "-Xmx32m", "-Xint", "-jar", "test.jar", "-opt", "arg", "--longopt"), tr);
+        verifyUserArgs(Arrays.asList("-opt", "arg", "--longopt"), tr, 5);
+
+        tr = doExec(env, javaCmd, "@jarOpt", "@jarArg", "@vmArgs");
+        verifyOptions(Arrays.asList("-jar", "test.jar", "@vmArgs"), tr);
+        verifyUserArgs(Arrays.asList("@vmArgs"), tr, 3);
+
+        tr = doExec(env, javaCmd, "-cp", "@jarArg", "@vmArgs", "Foo", "@userArgs");
+        verifyOptions(Arrays.asList("-cp", "test.jar", "-Xmx32m", "-Xint",
+                "Foo", "@userArgs"), tr);
+        verifyUserArgs(Arrays.asList("@userArgs"), tr, 6);
+
+        tr = doExec(env, javaCmd, "@cpOpt", "@jarArg", "@vmArgs", "Foo", "@userArgs");
+        verifyOptions(Arrays.asList("-cp", "test.jar", "-Xmx32m", "-Xint",
+                "Foo", "@userArgs"), tr);
+        verifyUserArgs(Arrays.asList("@userArgs"), tr, 6);
+
+        tr = doExec(env, javaCmd, "@cpOpt", "test.jar", "@vmArgs", "Foo", "@userArgs");
+        verifyOptions(Arrays.asList("-cp", "test.jar", "-Xmx32m", "-Xint",
+                "Foo", "@userArgs"), tr);
+        verifyUserArgs(Arrays.asList("@userArgs"), tr, 6);
+
+        vmArgs.delete();
+        jarOpt.delete();
+        cpOpt.delete();
+        jarArg.delete();
+        userArgs.delete();
+    }
+
+    // test with missing file
+    @Test
+    public 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 {
+        init();
+        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");
+        }
+    }
+}
--- a/jdk/test/tools/launcher/TestHelper.java	Fri Aug 21 20:41:21 2015 +0100
+++ b/jdk/test/tools/launcher/TestHelper.java	Wed Jul 08 23:26:48 2015 -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;
                 }