public static String[] tokenizeForExec(String inputString)
            throws IOException {
        // The java.lang.Runtime.exec(String command) call uses
        // java.util.StringTokenizer() to parse the command string.
        // Unfortunately, this means that double quotes are not handled
        // in the same way that the shell handles them in that 'ls "foo
        // 'bar"' will interpreted as three tokens 'ls', '"foo' and
        // 'bar"'.  In the shell, the string would be two tokens 'ls' and
        // '"foo bar"'.  What is worse is that the exec() behaviour is
        // slightly different under Windows and Unix.  To solve this
        // problem, we preprocess the command argument using
        // java.io.StreamTokenizer, which converts quoted substrings into
        // single tokens.  We then call java.lang.Runtime.exec(String []
        // commands);
        // Parse the command into tokens
        List commandList = new LinkedList();

        StreamTokenizer streamTokenizer = new StreamTokenizer(new StringReader(
                inputString));

        // We reset the syntax so that we don't convert to numbers,
        // otherwise, if PTII is "d:\\tmp\\ptII\ 2.0", then
        // we have no end of problems.
        streamTokenizer.resetSyntax();
        streamTokenizer.whitespaceChars(0, 32);
        streamTokenizer.wordChars(33, 127);

        // We can't use quoteChar here because it does backslash
        // substitution, so "c:\ptII" ends up as "c:ptII"
        // Substituting forward slashes for backward slashes seems like
        // overkill.
        // streamTokenizer.quoteChar('"');
        streamTokenizer.ordinaryChar('"');

        streamTokenizer.eolIsSignificant(true);

        streamTokenizer.commentChar('#');

        // Current token
        String token = "";

        // Single character token, usually a -
        String singleToken = "";

        // Set to true if we are inside a double quoted String.
        boolean inDoubleQuotedString = false;

        while (streamTokenizer.nextToken() != StreamTokenizer.TT_EOF) {
            switch (streamTokenizer.ttype) {
            case StreamTokenizer.TT_WORD:

                if (inDoubleQuotedString) {
                    if (token.length() > 0) {
                        // FIXME: multiple spaces will get compacted here
                        token += " ";
                    }

                    token += (singleToken + streamTokenizer.sval);
                } else {
                    token = singleToken + streamTokenizer.sval;
                    commandList.add(token);
                }

                singleToken = "";
                break;

            case StreamTokenizer.TT_NUMBER:
                throw new RuntimeException("Internal error: Found TT_NUMBER: '"
                        + streamTokenizer.nval + "'.  We should not be "
                        + "tokenizing numbers");

                //break;
            case StreamTokenizer.TT_EOL:
            case StreamTokenizer.TT_EOF:
                break;

            default:
                singleToken = Character.toString((char) streamTokenizer.ttype);

                if (singleToken.equals("\"")) {
                    if (inDoubleQuotedString) {
                        commandList.add(token);
                    }

                    inDoubleQuotedString = !inDoubleQuotedString;
                    singleToken = "";
                    token = "";
                }

                break;
            }
        }

        return (String[]) commandList.toArray(new String[commandList.size()]);
    }