8049300: jjs scripting: need way to quote $EXEC command arguments to protect spaces

Fri, 15 May 2015 16:36:25 +0200

author
mhaupt
date
Fri, 15 May 2015 16:36:25 +0200
changeset 1370
a71a115c2dd5
parent 1369
50f858c7a76c
child 1371
a8c536d1d3e0

8049300: jjs scripting: need way to quote $EXEC command arguments to protect spaces
Summary: honor quoting with "" and '' as well as escaped spaces
Reviewed-by: hannesw, sundar

src/jdk/nashorn/internal/runtime/ScriptingFunctions.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java	Mon Apr 20 10:39:55 2015 +0200
     1.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java	Fri May 15 16:36:25 2015 +0200
     1.3 @@ -1,5 +1,5 @@
     1.4  /*
     1.5 - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     1.6 + * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
     1.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.8   *
     1.9   * This code is free software; you can redistribute it and/or modify it
    1.10 @@ -34,10 +34,13 @@
    1.11  import java.io.IOException;
    1.12  import java.io.InputStreamReader;
    1.13  import java.io.OutputStreamWriter;
    1.14 +import java.io.StreamTokenizer;
    1.15 +import java.io.StringReader;
    1.16  import java.lang.invoke.MethodHandle;
    1.17  import java.lang.invoke.MethodHandles;
    1.18 +import java.util.ArrayList;
    1.19 +import java.util.List;
    1.20  import java.util.Map;
    1.21 -import java.util.StringTokenizer;
    1.22  
    1.23  /**
    1.24   * Global functions supported only in scripting mode.
    1.25 @@ -133,15 +136,8 @@
    1.26          // Current global is need to fetch additional inputs and for additional results.
    1.27          final ScriptObject global = Context.getGlobal();
    1.28  
    1.29 -        // Break exec string into tokens.
    1.30 -        final StringTokenizer tokenizer = new StringTokenizer(JSType.toString(string));
    1.31 -        final String[] cmdArray = new String[tokenizer.countTokens()];
    1.32 -        for (int i = 0; tokenizer.hasMoreTokens(); i++) {
    1.33 -            cmdArray[i] = tokenizer.nextToken();
    1.34 -        }
    1.35 -
    1.36          // Set up initial process.
    1.37 -        final ProcessBuilder processBuilder = new ProcessBuilder(cmdArray);
    1.38 +        final ProcessBuilder processBuilder = new ProcessBuilder(tokenizeCommandLine(JSType.toString(string)));
    1.39  
    1.40          // Current ENV property state.
    1.41          final Object env = global.get(ENV_NAME);
    1.42 @@ -239,4 +235,43 @@
    1.43      private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
    1.44          return MH.findStatic(MethodHandles.lookup(), ScriptingFunctions.class, name, MH.type(rtype, types));
    1.45      }
    1.46 +
    1.47 +    /**
    1.48 +     * Break an exec string into tokens, honoring quoted arguments and escaped
    1.49 +     * spaces.
    1.50 +     *
    1.51 +     * @param execString a {@link String} with the command line to execute.
    1.52 +     * @return a {@link List} of {@link String}s representing the tokens that
    1.53 +     * constitute the command line.
    1.54 +     * @throws IOException in case {@link StreamTokenizer#nextToken()} raises it.
    1.55 +     */
    1.56 +    private static List<String> tokenizeCommandLine(final String execString) throws IOException {
    1.57 +        final StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(execString));
    1.58 +        tokenizer.resetSyntax();
    1.59 +        tokenizer.wordChars(0, 255);
    1.60 +        tokenizer.whitespaceChars(0, ' ');
    1.61 +        tokenizer.commentChar('#');
    1.62 +        tokenizer.quoteChar('"');
    1.63 +        tokenizer.quoteChar('\'');
    1.64 +        final List<String> cmdList = new ArrayList<>();
    1.65 +        final StringBuilder toAppend = new StringBuilder();
    1.66 +        while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
    1.67 +            final String s = tokenizer.sval;
    1.68 +            // The tokenizer understands about honoring quoted strings and recognizes
    1.69 +            // them as one token that possibly contains multiple space-separated words.
    1.70 +            // It does not recognize quoted spaces, though, and will split after the
    1.71 +            // escaping \ character. This is handled here.
    1.72 +            if (s.endsWith("\\")) {
    1.73 +                // omit trailing \, append space instead
    1.74 +                toAppend.append(s.substring(0, s.length() - 1)).append(' ');
    1.75 +            } else {
    1.76 +                cmdList.add(toAppend.append(s).toString());
    1.77 +                toAppend.setLength(0);
    1.78 +            }
    1.79 +        }
    1.80 +        if (toAppend.length() != 0) {
    1.81 +            cmdList.add(toAppend.toString());
    1.82 +        }
    1.83 +        return cmdList;
    1.84 +    }
    1.85  }

mercurial