| /******************************************************************************* |
| * Copyright (c) 2007 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.remote.internal.jsch.core; |
| |
| import java.text.CharacterIterator; |
| import java.text.StringCharacterIterator; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| /** |
| * Utilitiy class for managing command line arguments. |
| * |
| */ |
| public class ArgumentParser { |
| private static List<String> parseCommandline(String commandline) { |
| ArrayList<String> result = new ArrayList<>(); |
| StringCharacterIterator iterator = new StringCharacterIterator(commandline); |
| |
| for (iterator.first(); iterator.current() != CharacterIterator.DONE; iterator.next()) { |
| |
| // Restart to skip white space |
| if (Character.isWhitespace(iterator.current())) { |
| continue; |
| } |
| |
| // Read token |
| StringBuffer buffer = new StringBuffer(); |
| token_reader: for (; iterator.current() != CharacterIterator.DONE; iterator.next()) { |
| char tokenChar = iterator.current(); |
| |
| // A white space terminates the token |
| if (Character.isWhitespace(tokenChar)) { |
| break token_reader; |
| } |
| |
| // Handle character that composes the token |
| switch (tokenChar) { |
| case '"': { |
| /* |
| * Read all text within double quotes or until end of |
| * string. Allows escaping. |
| */ |
| iterator.next(); // Skip quote |
| quoted_reader: while ((iterator.current() != CharacterIterator.DONE) |
| && (iterator.current() != '"')) { |
| char innerChar = iterator.current(); |
| switch (innerChar) { |
| case '\\': |
| char nextChar = iterator.next(); |
| switch (nextChar) { |
| case CharacterIterator.DONE: |
| break quoted_reader; |
| case '"': |
| // Add the character, but remove the escape |
| buffer.append(nextChar); |
| iterator.next(); |
| continue quoted_reader; |
| default: |
| // Add the character and keep escape |
| buffer.append(innerChar); |
| buffer.append(nextChar); |
| iterator.next(); |
| continue quoted_reader; |
| } |
| default: |
| buffer.append(innerChar); |
| iterator.next(); |
| continue quoted_reader; |
| } |
| } |
| continue token_reader; |
| } |
| case '\'': { |
| /* |
| * Read all text within single quotes or until end of |
| * string. No escaping. |
| */ |
| iterator.next(); // Skip the quote |
| while ((iterator.current() != CharacterIterator.DONE) && (iterator.current() != '\'')) { |
| buffer.append(iterator.current()); |
| iterator.next(); |
| } |
| continue token_reader; |
| } |
| case '\\': { |
| /* |
| * Read escaped char. |
| */ |
| char nextChar = iterator.next(); |
| switch (nextChar) { |
| case CharacterIterator.DONE: |
| break token_reader; |
| case '\n': |
| // Ignore newline. Both lines are concatenated. |
| continue token_reader; |
| default: |
| // Add the character, but remove the escape |
| buffer.append(nextChar); |
| continue token_reader; |
| } |
| } |
| default: |
| /* |
| * Any other char, add to the buffer. |
| */ |
| buffer.append(tokenChar); |
| continue token_reader; |
| } |
| } |
| result.add(buffer.toString()); |
| } |
| |
| return result; |
| } |
| |
| private final List<String> tokens; |
| |
| /** |
| * Create a command line representation from an array of strings. The first |
| * element of the list is assumed to be the command, the remaining, the |
| * arguments. The elements are not parsed not (un)escaped., but taked as the |
| * are. |
| * |
| */ |
| public ArgumentParser(List<String> tokenList) { |
| this.tokens = new ArrayList<>(tokenList); |
| } |
| |
| /** |
| * Create a command line representation from the string with a shell command |
| * line. The command line is parsed and split on spaces. Quoted or escaped |
| * spaces are preserved.. |
| * |
| */ |
| public ArgumentParser(String commandline) { |
| this.tokens = parseCommandline(commandline); |
| } |
| |
| /** |
| * Create a command line representation from an array of strings. The first |
| * element of the array is assumed to be the command, the remaining, the |
| * arguments. The elements are not parsed not (un)escaped., but taked as the |
| * are. |
| * |
| */ |
| public ArgumentParser(String tokenArray[]) { |
| this(Arrays.asList(tokenArray)); |
| } |
| |
| /** |
| * Create a command line representation from the command and an list of |
| * parameters. The elements are not parsed not (un)escaped., but taked as |
| * the are. |
| * |
| */ |
| public ArgumentParser(String command, List<String> parameterList) { |
| this.tokens = new ArrayList<>(); |
| this.tokens.add(command); |
| this.tokens.addAll(parameterList); |
| } |
| |
| /** |
| * Create a command line representation from the command and an array of |
| * parameters. The elements are not parsed not (un)escaped., but taked as |
| * the are. |
| * |
| */ |
| public ArgumentParser(String command, String parameterArray[]) { |
| this(command, Arrays.asList(parameterArray)); |
| } |
| |
| private StringBuffer escapeToken(String token, boolean fullEscape) { |
| StringBuffer buffer = new StringBuffer(); |
| StringCharacterIterator iter = new StringCharacterIterator(token); |
| for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) { |
| if (Character.isWhitespace(c)) { |
| buffer.append('\\'); |
| buffer.append(c); |
| continue; |
| } |
| switch (c) { |
| case '(': |
| case ')': |
| case '[': |
| case ']': |
| case '{': |
| case '}': |
| case '|': |
| case '\\': |
| case '*': |
| case '&': |
| case '^': |
| case '%': |
| case '$': |
| case '#': |
| case '@': |
| case '!': |
| case '~': |
| case '`': |
| case '\'': |
| case '"': |
| case ':': |
| case ';': |
| case '?': |
| case '>': |
| case '<': |
| case '\n': |
| if (fullEscape) { |
| buffer.append('\\'); |
| } |
| buffer.append(c); |
| continue; |
| case ' ': |
| buffer.append('\\'); |
| buffer.append(c); |
| continue; |
| default: |
| buffer.append(c); |
| continue; |
| } |
| } |
| return buffer; |
| } |
| |
| /** |
| * Returns the command of the command line, assuming that the first entry is |
| * always the command. |
| * |
| * @return The command or null if the command lines has no command nor |
| * arguments. |
| */ |
| public String getCommand() { |
| if (this.tokens.size() == 0) { |
| return null; |
| } |
| return this.tokens.get(0); |
| } |
| |
| /** |
| * Convert all tokens in a full command line that can be executed in a |
| * shell. |
| * |
| * @param fullEscape |
| * If every special character shall be escaped. If false, only |
| * white spaces are escaped and the shell will interpret the |
| * special chars. If true, then all special chars are quoted. |
| */ |
| public String getCommandLine(boolean fullEscape) { |
| StringBuffer buffer = new StringBuffer(); |
| Iterator<String> iterator = this.tokens.iterator(); |
| boolean first = true; |
| while (iterator.hasNext()) { |
| String token = iterator.next(); |
| if (!first) { |
| buffer.append(' '); |
| } else { |
| first = false; |
| } |
| buffer.append(escapeToken(token, fullEscape)); |
| } |
| return buffer.toString(); |
| } |
| |
| /** |
| * Returns the command of the command line, assuming that the first entry is |
| * always the command. |
| * |
| * @return The command or null if the command lines has no command nor |
| * arguments. |
| * @param fullEscape |
| * If every special character shall be escaped. If false, only |
| * white spaces are escaped and the shell will interpret the |
| * special chars. If true, then all special chars are quoted. |
| */ |
| public String getEscapedCommand(boolean fullEscalpe) { |
| if (this.tokens.size() == 0) { |
| return null; |
| } |
| return escapeToken(this.tokens.get(0), fullEscalpe).toString(); |
| } |
| |
| /** |
| * Returns a list of all arguments, assuming that the first entry is the |
| * command name. |
| * |
| * @return The Array or null if the command lines has no command nor |
| * arguments. |
| */ |
| public String[] getParameterArray() { |
| if (this.tokens.size() == 0) { |
| return null; |
| } |
| return this.tokens.subList(1, this.tokens.size()).toArray(new String[this.tokens.size() - 1]); |
| } |
| |
| /** |
| * Returns a list of all arguments, assuming that the first entry is the |
| * command name. |
| * |
| * @return The List or null if the command lines has no command nor |
| * arguments. |
| */ |
| public List<String> getParameterList() { |
| if (this.tokens.size() == 0) { |
| return null; |
| } |
| return new ArrayList<>(this.tokens.subList(1, this.tokens.size())); |
| } |
| |
| /** |
| * Returns the total number of entries. |
| * |
| * @return |
| */ |
| public int getSize() { |
| return this.tokens.size(); |
| } |
| |
| /** |
| * Returns a List of all entries of the command line. |
| * |
| * @return The Array |
| */ |
| public String[] getTokenArray() { |
| return this.tokens.toArray(new String[this.tokens.size()]); |
| } |
| |
| /** |
| * Returns a List of all entries of the command line. |
| * |
| * @return The List |
| */ |
| public List<String> getTokenList() { |
| return new ArrayList<>(this.tokens); |
| } |
| |
| /** |
| * Returns a representation of the command line for debug purposes. |
| */ |
| @Override |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append("<"); //$NON-NLS-1$ |
| Iterator<String> iterator = this.tokens.iterator(); |
| boolean first = true; |
| while (iterator.hasNext()) { |
| String token = iterator.next(); |
| if (!first) { |
| buffer.append('\n'); |
| } else { |
| first = false; |
| } |
| buffer.append(token); |
| } |
| buffer.append(">"); //$NON-NLS-1$ |
| return buffer.toString(); |
| } |
| } |