. */ /** * 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 thomas.haas@softwired-inc.com * @author Stefan Bodewig */ class Commandline { /** * @var array CommandlineArguments[] */ public $arguments = array(); // public so "inner" class can access /** * Full path (if not on %PATH% env var) to executable program. * @var string */ public $executable; // public so "inner" class can access const DISCLAIMER = "The ' characters around the executable and arguments are not part of the command."; public function __construct($to_process = null) { if ($to_process !== null) { $tmp = $this->translateCommandline($to_process); if ($tmp) { $this->setExecutable(array_shift($tmp)); // removes first el foreach($tmp as $arg) { // iterate through remaining elements $this->createArgument()->setValue($arg); } } } } /** * 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 boolean $insertAtStart if true, the argument is inserted at the * beginning of the list of args, otherwise it is appended. * @return CommandlineArgument */ public function createArgument($insertAtStart = false) { $argument = new CommandlineArgument($this); if ($insertAtStart) { array_unshift($this->arguments, $argument); } else { array_push($this->arguments, $argument); } return $argument; } /** * Sets the executable to run. */ public function setExecutable($executable) { if (!$executable) { return; } $this->executable = $executable; $this->executable = strtr($this->executable, '/', DIRECTORY_SEPARATOR); $this->executable = strtr($this->executable, '\\', DIRECTORY_SEPARATOR); } public function getExecutable() { return $this->executable; } public function addArguments($line) { foreach($line as $arg) { $this->createArgument()->setValue($arg); } } /** * Returns the executable and all defined arguments. * @return array */ public function getCommandline() { $args = $this->getArguments(); if ($this->executable === null) { return $args; } return array_merge(array($this->executable), $args); } /** * Returns all arguments defined by addLine, * addValue or the argument object. */ public function getArguments() { $result = array(); foreach($this->arguments as $arg) { $parts = $arg->getParts(); if ($parts !== null) { foreach($parts as $part) { $result[] = $part; } } } return $result; } public function __toString() { return self::toString($this->getCommandline()); } /** * Put quotes around the given String if necessary. * *

If the argument doesn't include spaces or quotes, return it * as is. If it contains double quotes, use single quotes - else * surround the argument by double quotes.

* * @exception BuildException if the argument contains both, single * and double quotes. */ public static function quoteArgument($argument) { if (strpos($argument, "\"") !== false) { if (strpos($argument, "'") !== false) { throw new BuildException("Can't handle single and double quotes in same argument"); } else { return escapeshellarg($argument); } } elseif (strpos($argument, "'") !== false || strpos($argument, " ") !== false) { return escapeshellarg($argument); //return '\"' . $argument . '\"'; } else { return $argument; } } /** * Quotes the parts of the given array in way that makes them * usable as command line arguments. */ public static function toString($lines) { // empty path return empty string if (!$lines) { return ""; } // path containing one or more elements $result = ""; for ($i = 0, $len=count($lines); $i < $len; $i++) { if ($i > 0) { $result .= ' '; } $result .= self::quoteArgument($lines[$i]); } return $result; } /** * * @param string $to_process * @return array */ public static function translateCommandline($to_process) { if (!$to_process) { return array(); } // parse with a simple finite state machine $normal = 0; $inQuote = 1; $inDoubleQuote = 2; $state = $normal; $args = array(); $current = ""; $lastTokenHasBeenQuoted = false; $tok = strtok($to_process, ""); $tokens = preg_split('/(["\' ])/', $to_process, -1, PREG_SPLIT_DELIM_CAPTURE); while(($nextTok = array_shift($tokens)) !== null) { switch ($state) { case $inQuote: if ("'" == $nextTok) { $lastTokenHasBeenQuoted = true; $state = $normal; } else { $current .= $nextTok; } break; case $inDoubleQuote: if ("\"" == $nextTok) { $lastTokenHasBeenQuoted = true; $state = $normal; } else { $current .= $nextTok; } break; default: if ("'" == $nextTok) { $state = $inQuote; } elseif ("\"" == $nextTok) { $state = $inDoubleQuote; } elseif (" " == $nextTok) { if ($lastTokenHasBeenQuoted || strlen($current) != 0) { $args[] = $current; $current = ""; } } else { $current .= $nextTok; } $lastTokenHasBeenQuoted = false; break; } } if ($lastTokenHasBeenQuoted || strlen($current) != 0) { $args[] = $current; } if ($state == $inQuote || $state == $inDoubleQuote) { throw new BuildException("unbalanced quotes in " . $to_process); } return $args; } /** * @return int Number of components in current commandline. */ public function size() { return count($this->getCommandline()); } public function __copy() { $c = new Commandline(); $c->setExecutable($this->executable); $c->addArguments($this->getArguments()); return $c; } /** * Clear out the whole command line. */ public function clear() { $this->executable = null; $this->arguments->removeAllElements(); } /** * Clear out the arguments but leave the executable in place for * another operation. */ public function clearArgs() { $this->arguments = array(); } /** * Return a marker. * *

This marker can be used to locate a position on the * commandline - to insert something for example - when all * parameters have been set.

* @return CommandlineMarker */ public function createMarker() { return new CommandlineMarker($this, count($this->arguments)); } /** * Returns a String that describes the command and arguments * suitable for verbose output before a call to * Runtime.exec(String[]). * *

This method assumes that the first entry in the array is the * executable to run.

* @param array $args CommandlineArgument[] to use * @return string */ public function describeCommand($args = null) { if ($args === null) { $args = $this->getCommandline(); } if (!$args) { return ""; } $buf = "Executing '"; $buf .= $args[0]; $buf .= "'"; if (count($args) > 0) { $buf .= " with "; $buf .= $this->describeArguments($args, 1); } else { $buf .= self::DISCLAIMER; } return $buf; } /** * Returns a String that describes the arguments suitable for * verbose output before a call to * Runtime.exec(String[]) * @param $args arguments to use (default is to use current class args) * @param $offset ignore entries before this index * @return string */ protected function describeArguments($args = null, $offset = 0) { if ($args === null) { $args = $this->getArguments(); } if ($args === null || count($args) <= $offset) { return ""; } $buf = "argument"; if (count($args) > $offset) { $buf .= "s"; } $buf .= ":" . Phing::getProperty("line.separator"); for ($i = $offset, $alen=count($args); $i < $alen; $i++) { $buf .= "'" . $args[$i] . "'" . Phing::getProperty("line.separator"); } $buf .= self::DISCLAIMER; return $buf; } } /** * "Inner" class used for nested xml command line definitions. */ class CommandlineArgument { private $parts = array(); private $outer; public function __construct(Commandline $outer) { $this->outer = $outer; } /** * Sets a single commandline argument. * * @param string $value a single commandline argument. */ public function setValue($value) { $this->parts = array($value); } /** * Line to split into several commandline arguments. * * @param line line to split into several commandline arguments */ public function setLine($line) { if ($line === null) { return; } $this->parts = $this->outer->translateCommandline($line); } /** * Sets a single commandline argument and treats it like a * PATH - ensures the right separator for the local platform * is used. * * @param value a single commandline argument. */ public function setPath($value) { $this->parts = array( (string) $value ); } /** * Sets a single commandline argument to the absolute filename * of the given file. * * @param value a single commandline argument. */ public function setFile(PhingFile $value) { $this->parts = array($value->getAbsolutePath()); } /** * Returns the parts this Argument consists of. * @return array string[] */ public function getParts() { return $this->parts; } } /** * 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 class CommandlineMarker { private $position; private $realPos = -1; private $outer; public function __construct(Comandline $outer, $position) { $this->outer = $outer; $this->position = $position; } /** * Return the number of arguments that preceeded this marker. * *

The name of the executable - if set - is counted as the * very first argument.

*/ public function getPosition() { if ($this->realPos == -1) { $realPos = ($this->outer->executable === null ? 0 : 1); for ($i = 0; $i < $position; $i++) { $arg = $this->arguments[$i]; $realPos += count($arg->getParts()); } } return $this->realPos; } }