org.codehaus.plexus.util.cli.Commandline Maven / Gradle / Ivy
package org.codehaus.plexus.util.cli;
/*
* Copyright The Codehaus Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/***************************************************************************************************
* CruiseControl, a Continuous Integration Toolkit Copyright (c) 2001-2003, ThoughtWorks, Inc. 651 W
* Washington Ave. Suite 500 Chicago, IL 60661 USA All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met: + Redistributions of source code must retain the
* above copyright notice, this list of conditions and the following disclaimer. + Redistributions
* in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution. +
* Neither the name of ThoughtWorks, Inc., CruiseControl, nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**************************************************************************************************/
/*
* ====================================================================
* Copyright 2003-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import org.codehaus.plexus.util.Os;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.cli.shell.BourneShell;
import org.codehaus.plexus.util.cli.shell.CmdShell;
import org.codehaus.plexus.util.cli.shell.CommandShell;
import org.codehaus.plexus.util.cli.shell.Shell;
/**
* Commandline objects help handling command lines specifying processes to execute.
*
* The class can be used to define a command line as nested elements or as a helper to define a command line by an
* application.
*
*
* <someelement>
* <acommandline executable="/executable/to/run">
* <argument value="argument 1" />
* <argument line="argument_1 argument_2 argument_3" />
* <argument value="argument 4" />
* </acommandline>
* </someelement>
*
*
* The element someelement
must provide a method createAcommandline
which returns an instance
* of this class.
*
* @author [email protected]
* @author Stefan Bodewig
*/
public class Commandline implements Cloneable {
/**
* @deprecated Use {@link org.codehaus.plexus.util.Os} class instead.
*/
@Deprecated
protected static final String OS_NAME = "os.name";
/**
* @deprecated Use {@link org.codehaus.plexus.util.Os} class instead.
*/
@Deprecated
protected static final String WINDOWS = "Windows";
protected Vector arguments = new Vector<>();
// protected Vector envVars = new Vector();
// synchronized added to preserve synchronize of Vector class
protected Map envVars = Collections.synchronizedMap(new LinkedHashMap());
private long pid = -1;
private Shell shell;
/**
* @deprecated Use {@link Commandline#setExecutable(String)} instead.
*/
@Deprecated
protected String executable;
/**
* @deprecated Use {@link Commandline#setWorkingDirectory(File)} or {@link Commandline#setWorkingDirectory(String)}
* instead.
*/
@Deprecated
private File workingDir;
/**
* Create a new command line object. Shell is autodetected from operating system Shell usage is only desirable when
* generating code for remote execution.
*
* @param toProcess sh to process
* @param shell Shell to use
*/
public Commandline(String toProcess, Shell shell) {
this.shell = shell;
String[] tmp = new String[0];
try {
tmp = CommandLineUtils.translateCommandline(toProcess);
} catch (Exception e) {
System.err.println("Error translating Commandline.");
}
if ((tmp != null) && (tmp.length > 0)) {
setExecutable(tmp[0]);
for (int i = 1; i < tmp.length; i++) {
createArgument().setValue(tmp[i]);
}
}
}
/**
* Create a new command line object. Shell is autodetected from operating system Shell usage is only desirable when
* generating code for remote execution.
* @param shell the Shell
*/
public Commandline(Shell shell) {
this.shell = shell;
}
/**
* Create a new command line object, given a command following POSIX sh quoting rules
*
* @param toProcess the process
*/
public Commandline(String toProcess) {
setDefaultShell();
String[] tmp = new String[0];
try {
tmp = CommandLineUtils.translateCommandline(toProcess);
} catch (Exception e) {
System.err.println("Error translating Commandline.");
}
if ((tmp != null) && (tmp.length > 0)) {
setExecutable(tmp[0]);
for (int i = 1; i < tmp.length; i++) {
createArgument().setValue(tmp[i]);
}
}
}
/**
* Create a new command line object.
*/
public Commandline() {
setDefaultShell();
}
public long getPid() {
if (pid == -1) {
pid = Long.parseLong(String.valueOf(System.currentTimeMillis()));
}
return pid;
}
public void setPid(long pid) {
this.pid = pid;
}
/**
* Class to keep track of the position of an Argument.
*/
// This class is there to support the srcfile and targetfile
// elements of <execon> and <transform> - don't know
// whether there might be additional use cases.
--SB
public class Marker {
private int position;
private int realPos = -1;
Marker(int position) {
this.position = position;
}
/**
* @return the number of arguments that preceded this marker.
*
* The name of the executable - if set - is counted as the very first argument.
*/
public int getPosition() {
if (realPos == -1) {
realPos = (getLiteralExecutable() == null ? 0 : 1);
for (int i = 0; i < position; i++) {
Arg arg = arguments.elementAt(i);
realPos += arg.getParts().length;
}
}
return realPos;
}
}
/**
*
* Sets the shell or command-line interpreter for the detected operating system, and the shell arguments.
*
*/
private void setDefaultShell() {
// If this is windows set the shell to command.com or cmd.exe with correct arguments.
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
if (Os.isFamily(Os.FAMILY_WIN9X)) {
setShell(new CommandShell());
} else {
setShell(new CmdShell());
}
} else {
setShell(new BourneShell());
}
}
/**
* Creates an argument object.
*
* Each commandline object has at most one instance of the argument class. This method calls
* this.createArgument(false)
.
*
* @return the argument object.
* @see #createArgument(boolean)
* @deprecated Use {@link Commandline#createArg()} instead
*/
@Deprecated
public Argument createArgument() {
return this.createArgument(false);
}
/**
* Creates an argument object and adds it to our list of args.
*
* Each commandline object has at most one instance of the argument class.
*
* @param insertAtStart if true, the argument is inserted at the beginning of the list of args, otherwise it is
* appended.
* @deprecated Use {@link Commandline#createArg(boolean)} instead
* @return Argument the argument Object
*/
@Deprecated
public Argument createArgument(boolean insertAtStart) {
Argument argument = new Argument();
if (insertAtStart) {
arguments.insertElementAt(argument, 0);
} else {
arguments.addElement(argument);
}
return argument;
}
/**
* Creates an argument object.
*
* Each commandline object has at most one instance of the argument class. This method calls
* this.createArgument(false)
.
*
* @return the argument object.
* @see #createArgument(boolean)
*/
public Arg createArg() {
return this.createArg(false);
}
/**
* @return Creates an argument object and adds it to our list of args.
*
* Each commandline object has at most one instance of the argument class.
*
* @param insertAtStart if true, the argument is inserted at the beginning of the list of args, otherwise it is
* appended.
*/
public Arg createArg(boolean insertAtStart) {
Arg argument = new Argument();
if (insertAtStart) {
arguments.insertElementAt(argument, 0);
} else {
arguments.addElement(argument);
}
return argument;
}
/**
* @param argument the argument
* @see #addArg(Arg,boolean)
*/
public void addArg(Arg argument) {
this.addArg(argument, false);
}
/**
* Adds an argument object to our list of args.
* @param argument the argument
* @param insertAtStart if true, the argument is inserted at the beginning of the list of args, otherwise it is
* appended.
*/
public void addArg(Arg argument, boolean insertAtStart) {
if (insertAtStart) {
arguments.insertElementAt(argument, 0);
} else {
arguments.addElement(argument);
}
}
/**
* Sets the executable to run.
* @param executable the executable
*/
public void setExecutable(String executable) {
shell.setExecutable(executable);
this.executable = executable;
}
/**
* @return Executable to be run, as a literal string (no shell quoting/munging)
*/
public String getLiteralExecutable() {
return executable;
}
/**
* Return an executable name, quoted for shell use. Shell usage is only desirable when generating code for remote
* execution.
*
* @return Executable to be run, quoted for shell interpretation
*/
public String getExecutable() {
String exec = shell.getExecutable();
if (exec == null) {
exec = executable;
}
return exec;
}
public void addArguments(String[] line) {
for (String aLine : line) {
createArgument().setValue(aLine);
}
}
/**
* Add an environment variable
* @param name name
* @param value value
*/
public void addEnvironment(String name, String value) {
// envVars.add( name + "=" + value );
envVars.put(name, value);
}
/**
* Add system environment variables
* @throws Exception if error
*/
public void addSystemEnvironment() throws Exception {
Properties systemEnvVars = CommandLineUtils.getSystemEnvVars();
for (Object o : systemEnvVars.keySet()) {
String key = (String) o;
if (!envVars.containsKey(key)) {
addEnvironment(key, systemEnvVars.getProperty(key));
}
}
}
/**
* @return String[] Return the list of environment variables
* @throws CommandLineException if error
*/
public String[] getEnvironmentVariables() throws CommandLineException {
try {
addSystemEnvironment();
} catch (Exception e) {
throw new CommandLineException("Error setting up environmental variables", e);
}
String[] environmentVars = new String[envVars.size()];
int i = 0;
for (Object o : envVars.keySet()) {
String name = (String) o;
String value = envVars.get(name);
environmentVars[i] = name + "=" + value;
i++;
}
return environmentVars;
}
/**
* @return Returns the executable and all defined arguments.
* For Windows Family, {@link Commandline#getShellCommandline()} is returned
*/
public String[] getCommandline() {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
return getShellCommandline();
}
return getRawCommandline();
}
/**
* Returns the executable and all defined arguments.
* @return the command line as array not escaped neither quoted
*/
public String[] getRawCommandline() {
final String[] args = getArguments();
String executable = getLiteralExecutable();
if (executable == null) {
return args;
}
final String[] result = new String[args.length + 1];
result[0] = executable;
System.arraycopy(args, 0, result, 1, args.length);
return result;
}
/**
* Returns the shell, executable and all defined arguments. Shell usage is only desirable when generating code for
* remote execution.
* @return the command line as array
*/
public String[] getShellCommandline() {
// TODO: Provided only for backward compat. with <= 1.4
verifyShellState();
return getShell().getShellCommandLine(getArguments()).toArray(new String[0]);
}
/**
* @return Returns all arguments defined by addLine
, addValue
or the argument object.
*/
public String[] getArguments() {
Vector result = new Vector<>(arguments.size() * 2);
for (int i = 0; i < arguments.size(); i++) {
Arg arg = arguments.elementAt(i);
String[] s = arg.getParts();
if (s != null) {
for (String value : s) {
result.addElement(value);
}
}
}
String[] res = new String[result.size()];
result.copyInto(res);
return res;
}
@Override
public String toString() {
return StringUtils.join(getShellCommandline(), " ");
}
public int size() {
return getCommandline().length;
}
@Override
public Object clone() {
Commandline c = new Commandline((Shell) shell.clone());
c.executable = executable;
c.workingDir = workingDir;
c.addArguments(getArguments());
return c;
}
/**
* Clear out the whole command line.
*/
public void clear() {
executable = null;
workingDir = null;
shell.setExecutable(null);
shell.clearArguments();
arguments.removeAllElements();
}
/**
* Clear out the arguments but leave the executable in place for another operation.
*/
public void clearArgs() {
arguments.removeAllElements();
}
/**
*
* This marker can be used to locate a position on the commandline - to insert something for example - when all
* parameters have been set.
*
* @return Return a marker.
*/
public Marker createMarker() {
return new Marker(arguments.size());
}
/**
* Sets execution directory.
* @param path the working directory as String
*/
public void setWorkingDirectory(String path) {
shell.setWorkingDirectory(path);
workingDir = new File(path);
}
/**
* Sets execution directory.
* @param workingDirectory the File used as working directory
*/
public void setWorkingDirectory(File workingDirectory) {
shell.setWorkingDirectory(workingDirectory);
workingDir = workingDirectory;
}
public File getWorkingDirectory() {
File workDir = shell.getWorkingDirectory();
if (workDir == null) {
workDir = workingDir;
}
return workDir;
}
/**
* Executes the command.
* @return the Process
* @throws CommandLineException if error
*/
public Process execute() throws CommandLineException {
// TODO: Provided only for backward compat. with <= 1.4
verifyShellState();
Process process;
// addEnvironment( "MAVEN_TEST_ENVAR", "MAVEN_TEST_ENVAR_VALUE" );
String[] environment = getEnvironmentVariables();
File workingDir = shell.getWorkingDirectory();
try {
if (workingDir == null) {
process = Runtime.getRuntime().exec(getCommandline(), environment, workingDir);
} else {
if (!workingDir.exists()) {
throw new CommandLineException(
"Working directory \"" + workingDir.getPath() + "\" does not exist!");
} else if (!workingDir.isDirectory()) {
throw new CommandLineException(
"Path \"" + workingDir.getPath() + "\" does not specify a directory.");
}
process = Runtime.getRuntime().exec(getCommandline(), environment, workingDir);
}
} catch (IOException ex) {
throw new CommandLineException("Error while executing process.", ex);
}
return process;
}
/**
* @deprecated Remove once backward compat with plexus-utils <= 1.4 is no longer a consideration
*/
@Deprecated
private void verifyShellState() {
if (shell.getWorkingDirectory() == null) {
shell.setWorkingDirectory(workingDir);
}
if (shell.getOriginalExecutable() == null) {
shell.setExecutable(executable);
}
}
public Properties getSystemEnvVars() throws Exception {
return CommandLineUtils.getSystemEnvVars();
}
/**
* Allows to set the shell to be used in this command line. Shell usage is only desirable when generating code for
* remote execution.
*
* @param shell Shell to use
* @since 1.2
*/
public void setShell(Shell shell) {
this.shell = shell;
}
/**
* Get the shell to be used in this command line. Shell usage is only desirable when generating code for remote
* execution.
*
* @since 1.2
* @return the Shell
*/
public Shell getShell() {
return shell;
}
/**
* @param toProcess the process
* @return the command line arguments
* @throws Exception if error happen
* @deprecated Use {@link CommandLineUtils#translateCommandline(String)} instead.
*/
@Deprecated
public static String[] translateCommandline(String toProcess) throws Exception {
return CommandLineUtils.translateCommandline(toProcess);
}
/**
* @param argument the argument
* @return the quote arg
* @throws CommandLineException if error happen
* @deprecated Use {@link CommandLineUtils#quote(String)} instead.
*/
@Deprecated
public static String quoteArgument(String argument) throws CommandLineException {
return CommandLineUtils.quote(argument);
}
/**
* @deprecated Use {@link CommandLineUtils#toString(String[])} instead.
* @param line the lines
* @return lines as single String
*/
@Deprecated
public static String toString(String[] line) {
return CommandLineUtils.toString(line);
}
public static class Argument implements Arg {
private String[] parts;
/*
* (non-Javadoc)
* @see org.codehaus.plexus.util.cli.Argument#setValue(java.lang.String)
*/
@Override
public void setValue(String value) {
if (value != null) {
parts = new String[] {value};
}
}
/*
* (non-Javadoc)
* @see org.codehaus.plexus.util.cli.Argument#setLine(java.lang.String)
*/
@Override
public void setLine(String line) {
if (line == null) {
return;
}
try {
parts = CommandLineUtils.translateCommandline(line);
} catch (Exception e) {
System.err.println("Error translating Commandline.");
}
}
/*
* (non-Javadoc)
* @see org.codehaus.plexus.util.cli.Argument#setFile(java.io.File)
*/
@Override
public void setFile(File value) {
parts = new String[] {value.getAbsolutePath()};
}
/*
* (non-Javadoc)
* @see org.codehaus.plexus.util.cli.Argument#getParts()
*/
@Override
public String[] getParts() {
return parts;
}
}
}