PageRenderTime 54ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/buildscripts/phing/classes/phing/types/Commandline.php

http://prado3.googlecode.com/
PHP | 475 lines | 386 code | 16 blank | 73 comment | 9 complexity | 1ae080b189b647629a1a73e6b774f78a MD5 | raw file
Possible License(s): Apache-2.0, IPL-1.0, LGPL-3.0, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. /*
  3. * $Id: 891b349dcd88b825ae99edf53ed4c7ac2c1c2467 $
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information please see
  19. * <http://phing.info>.
  20. */
  21. /**
  22. * Commandline objects help handling command lines specifying processes to
  23. * execute.
  24. *
  25. * The class can be used to define a command line as nested elements or as a
  26. * helper to define a command line by an application.
  27. * <p>
  28. * <code>
  29. * &lt;someelement&gt;<br>
  30. * &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
  31. * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
  32. * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
  33. * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
  34. * &nbsp;&nbsp;&lt;/acommandline&gt;<br>
  35. * &lt;/someelement&gt;<br>
  36. * </code>
  37. * The element <code>someelement</code> must provide a method
  38. * <code>createAcommandline</code> which returns an instance of this class.
  39. *
  40. * @author thomas.haas@softwired-inc.com
  41. * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
  42. * @package phing.types
  43. */
  44. class Commandline {
  45. /**
  46. * @var array CommandlineArguments[]
  47. */
  48. public $arguments = array(); // public so "inner" class can access
  49. /**
  50. * Full path (if not on %PATH% env var) to executable program.
  51. * @var string
  52. */
  53. public $executable; // public so "inner" class can access
  54. const DISCLAIMER = "The ' characters around the executable and arguments are not part of the command.";
  55. public function __construct($to_process = null) {
  56. if ($to_process !== null) {
  57. $tmp = $this->translateCommandline($to_process);
  58. if ($tmp) {
  59. $this->setExecutable(array_shift($tmp)); // removes first el
  60. foreach($tmp as $arg) { // iterate through remaining elements
  61. $this->createArgument()->setValue($arg);
  62. }
  63. }
  64. }
  65. }
  66. /**
  67. * Creates an argument object and adds it to our list of args.
  68. *
  69. * <p>Each commandline object has at most one instance of the
  70. * argument class.</p>
  71. *
  72. * @param boolean $insertAtStart if true, the argument is inserted at the
  73. * beginning of the list of args, otherwise it is appended.
  74. * @return CommandlineArgument
  75. */
  76. public function createArgument($insertAtStart = false) {
  77. $argument = new CommandlineArgument($this);
  78. if ($insertAtStart) {
  79. array_unshift($this->arguments, $argument);
  80. } else {
  81. array_push($this->arguments, $argument);
  82. }
  83. return $argument;
  84. }
  85. /**
  86. * Sets the executable to run.
  87. */
  88. public function setExecutable($executable) {
  89. if (!$executable) {
  90. return;
  91. }
  92. $this->executable = $executable;
  93. $this->executable = strtr($this->executable, '/', DIRECTORY_SEPARATOR);
  94. $this->executable = strtr($this->executable, '\\', DIRECTORY_SEPARATOR);
  95. }
  96. public function getExecutable() {
  97. return $this->executable;
  98. }
  99. public function addArguments($line) {
  100. foreach($line as $arg) {
  101. $this->createArgument()->setValue($arg);
  102. }
  103. }
  104. /**
  105. * Returns the executable and all defined arguments.
  106. * @return array
  107. */
  108. public function getCommandline() {
  109. $args = $this->getArguments();
  110. if ($this->executable === null) {
  111. return $args;
  112. }
  113. return array_merge(array($this->executable), $args);
  114. }
  115. /**
  116. * Returns all arguments defined by <code>addLine</code>,
  117. * <code>addValue</code> or the argument object.
  118. */
  119. public function getArguments() {
  120. $result = array();
  121. foreach($this->arguments as $arg) {
  122. $parts = $arg->getParts();
  123. if ($parts !== null) {
  124. foreach($parts as $part) {
  125. $result[] = $part;
  126. }
  127. }
  128. }
  129. return $result;
  130. }
  131. public function __toString() {
  132. return self::toString($this->getCommandline());
  133. }
  134. /**
  135. * Put quotes around the given String if necessary.
  136. *
  137. * <p>If the argument doesn't include spaces or quotes, return it
  138. * as is. If it contains double quotes, use single quotes - else
  139. * surround the argument by double quotes.</p>
  140. *
  141. * @exception BuildException if the argument contains both, single
  142. * and double quotes.
  143. */
  144. public static function quoteArgument($argument, $escape = false) {
  145. if ($escape) {
  146. return escapeshellarg($argument);
  147. } elseif (strpos($argument, "\"") !== false) {
  148. if (strpos($argument, "'") !== false) {
  149. throw new BuildException("Can't handle single and double quotes in same argument");
  150. } else {
  151. return escapeshellarg($argument);
  152. }
  153. } elseif (strpos($argument, "'") !== false || strpos($argument, " ") !== false) {
  154. return escapeshellarg($argument);
  155. //return '\"' . $argument . '\"';
  156. } else {
  157. return $argument;
  158. }
  159. }
  160. /**
  161. * Quotes the parts of the given array in way that makes them
  162. * usable as command line arguments.
  163. */
  164. public static function toString($lines, $escape = false) {
  165. // empty path return empty string
  166. if (!$lines) {
  167. return "";
  168. }
  169. // path containing one or more elements
  170. $result = "";
  171. for ($i = 0, $len=count($lines); $i < $len; $i++) {
  172. if ($i > 0) {
  173. $result .= ' ';
  174. }
  175. $result .= self::quoteArgument($lines[$i], $escape);
  176. }
  177. return $result;
  178. }
  179. /**
  180. *
  181. * @param string $to_process
  182. * @return array
  183. */
  184. public static function translateCommandline($to_process) {
  185. if (!$to_process) {
  186. return array();
  187. }
  188. // parse with a simple finite state machine
  189. $normal = 0;
  190. $inQuote = 1;
  191. $inDoubleQuote = 2;
  192. $state = $normal;
  193. $args = array();
  194. $current = "";
  195. $lastTokenHasBeenQuoted = false;
  196. $tok = strtok($to_process, "");
  197. $tokens = preg_split('/(["\' ])/', $to_process, -1, PREG_SPLIT_DELIM_CAPTURE);
  198. while(($nextTok = array_shift($tokens)) !== null) {
  199. switch ($state) {
  200. case $inQuote:
  201. if ("'" == $nextTok) {
  202. $lastTokenHasBeenQuoted = true;
  203. $state = $normal;
  204. } else {
  205. $current .= $nextTok;
  206. }
  207. break;
  208. case $inDoubleQuote:
  209. if ("\"" == $nextTok) {
  210. $lastTokenHasBeenQuoted = true;
  211. $state = $normal;
  212. } else {
  213. $current .= $nextTok;
  214. }
  215. break;
  216. default:
  217. if ("'" == $nextTok) {
  218. $state = $inQuote;
  219. } elseif ("\"" == $nextTok) {
  220. $state = $inDoubleQuote;
  221. } elseif (" " == $nextTok) {
  222. if ($lastTokenHasBeenQuoted || strlen($current) != 0) {
  223. $args[] = $current;
  224. $current = "";
  225. }
  226. } else {
  227. $current .= $nextTok;
  228. }
  229. $lastTokenHasBeenQuoted = false;
  230. break;
  231. }
  232. }
  233. if ($lastTokenHasBeenQuoted || strlen($current) != 0) {
  234. $args[] = $current;
  235. }
  236. if ($state == $inQuote || $state == $inDoubleQuote) {
  237. throw new BuildException("unbalanced quotes in " . $to_process);
  238. }
  239. return $args;
  240. }
  241. /**
  242. * @return int Number of components in current commandline.
  243. */
  244. public function size() {
  245. return count($this->getCommandline());
  246. }
  247. public function __copy() {
  248. $c = new Commandline();
  249. $c->setExecutable($this->executable);
  250. $c->addArguments($this->getArguments());
  251. return $c;
  252. }
  253. /**
  254. * Clear out the whole command line. */
  255. public function clear() {
  256. $this->executable = null;
  257. $this->arguments->removeAllElements();
  258. }
  259. /**
  260. * Clear out the arguments but leave the executable in place for
  261. * another operation.
  262. */
  263. public function clearArgs() {
  264. $this->arguments = array();
  265. }
  266. /**
  267. * Return a marker.
  268. *
  269. * <p>This marker can be used to locate a position on the
  270. * commandline - to insert something for example - when all
  271. * parameters have been set.</p>
  272. * @return CommandlineMarker
  273. */
  274. public function createMarker() {
  275. return new CommandlineMarker($this, count($this->arguments));
  276. }
  277. /**
  278. * Returns a String that describes the command and arguments
  279. * suitable for verbose output before a call to
  280. * <code>Runtime.exec(String[])</code>.
  281. *
  282. * <p>This method assumes that the first entry in the array is the
  283. * executable to run.</p>
  284. * @param array $args CommandlineArgument[] to use
  285. * @return string
  286. */
  287. public function describeCommand($args = null) {
  288. if ($args === null) {
  289. $args = $this->getCommandline();
  290. }
  291. if (!$args) {
  292. return "";
  293. }
  294. $buf = "Executing '";
  295. $buf .= $args[0];
  296. $buf .= "'";
  297. if (count($args) > 0) {
  298. $buf .= " with ";
  299. $buf .= $this->describeArguments($args, 1);
  300. } else {
  301. $buf .= self::DISCLAIMER;
  302. }
  303. return $buf;
  304. }
  305. /**
  306. * Returns a String that describes the arguments suitable for
  307. * verbose output before a call to
  308. * <code>Runtime.exec(String[])</code>
  309. * @param $args arguments to use (default is to use current class args)
  310. * @param $offset ignore entries before this index
  311. * @return string
  312. */
  313. protected function describeArguments($args = null, $offset = 0) {
  314. if ($args === null) {
  315. $args = $this->getArguments();
  316. }
  317. if ($args === null || count($args) <= $offset) {
  318. return "";
  319. }
  320. $buf = "argument";
  321. if (count($args) > $offset) {
  322. $buf .= "s";
  323. }
  324. $buf .= ":" . PHP_EOL;
  325. for ($i = $offset, $alen=count($args); $i < $alen; $i++) {
  326. $buf .= "'" . $args[$i] . "'" . PHP_EOL;
  327. }
  328. $buf .= self::DISCLAIMER;
  329. return $buf;
  330. }
  331. }
  332. /**
  333. * "Inner" class used for nested xml command line definitions.
  334. *
  335. * @package phing.types
  336. */
  337. class CommandlineArgument {
  338. private $parts = array();
  339. private $outer;
  340. public function __construct(Commandline $outer) {
  341. $this->outer = $outer;
  342. }
  343. /**
  344. * Sets a single commandline argument.
  345. *
  346. * @param string $value a single commandline argument.
  347. */
  348. public function setValue($value) {
  349. $this->parts = array($value);
  350. }
  351. /**
  352. * Line to split into several commandline arguments.
  353. *
  354. * @param line line to split into several commandline arguments
  355. */
  356. public function setLine($line) {
  357. if ($line === null) {
  358. return;
  359. }
  360. $this->parts = $this->outer->translateCommandline($line);
  361. }
  362. /**
  363. * Sets a single commandline argument and treats it like a
  364. * PATH - ensures the right separator for the local platform
  365. * is used.
  366. *
  367. * @param value a single commandline argument.
  368. */
  369. public function setPath($value) {
  370. $this->parts = array( (string) $value );
  371. }
  372. /**
  373. * Sets a single commandline argument to the absolute filename
  374. * of the given file.
  375. *
  376. * @param value a single commandline argument.
  377. */
  378. public function setFile(PhingFile $value) {
  379. $this->parts = array($value->getAbsolutePath());
  380. }
  381. /**
  382. * Returns the parts this Argument consists of.
  383. * @return array string[]
  384. */
  385. public function getParts() {
  386. return $this->parts;
  387. }
  388. }
  389. /**
  390. * Class to keep track of the position of an Argument.
  391. *
  392. * <p>This class is there to support the srcfile and targetfile
  393. * elements of &lt;execon&gt; and &lt;transform&gt; - don't know
  394. * whether there might be additional use cases.</p> --SB
  395. *
  396. * @package phing.types
  397. */
  398. class CommandlineMarker {
  399. private $position;
  400. private $realPos = -1;
  401. private $outer;
  402. public function __construct(Comandline $outer, $position) {
  403. $this->outer = $outer;
  404. $this->position = $position;
  405. }
  406. /**
  407. * Return the number of arguments that preceeded this marker.
  408. *
  409. * <p>The name of the executable - if set - is counted as the
  410. * very first argument.</p>
  411. */
  412. public function getPosition() {
  413. if ($this->realPos == -1) {
  414. $realPos = ($this->outer->executable === null ? 0 : 1);
  415. for ($i = 0; $i < $position; $i++) {
  416. $arg = $this->arguments[$i];
  417. $realPos += count($arg->getParts());
  418. }
  419. }
  420. return $this->realPos;
  421. }
  422. }