PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/library/PEAR/phing/types/Commandline.php

https://github.com/vinnivinsachi/Vincent-DR
PHP | 467 lines | 378 code | 16 blank | 73 comment | 9 complexity | 965d0536f36374e9e3a170440453f7e6 MD5 | raw file
  1. <?php
  2. /*
  3. * $Id: Commandline.php,v 1.11 2005/05/26 13:10:53 mrook Exp $
  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. */
  43. class Commandline {
  44. /**
  45. * @var array CommandlineArguments[]
  46. */
  47. public $arguments = array(); // public so "inner" class can access
  48. /**
  49. * Full path (if not on %PATH% env var) to executable program.
  50. * @var string
  51. */
  52. public $executable; // public so "inner" class can access
  53. const DISCLAIMER = "The ' characters around the executable and arguments are not part of the command.";
  54. public function __construct($to_process = null) {
  55. if ($to_process !== null) {
  56. $tmp = $this->translateCommandline($to_process);
  57. if ($tmp) {
  58. $this->setExecutable(array_shift($tmp)); // removes first el
  59. foreach($tmp as $arg) { // iterate through remaining elements
  60. $this->createArgument()->setValue($arg);
  61. }
  62. }
  63. }
  64. }
  65. /**
  66. * Creates an argument object and adds it to our list of args.
  67. *
  68. * <p>Each commandline object has at most one instance of the
  69. * argument class.</p>
  70. *
  71. * @param boolean $insertAtStart if true, the argument is inserted at the
  72. * beginning of the list of args, otherwise it is appended.
  73. * @return CommandlineArgument
  74. */
  75. public function createArgument($insertAtStart = false) {
  76. $argument = new CommandlineArgument($this);
  77. if ($insertAtStart) {
  78. array_unshift($this->arguments, $argument);
  79. } else {
  80. array_push($this->arguments, $argument);
  81. }
  82. return $argument;
  83. }
  84. /**
  85. * Sets the executable to run.
  86. */
  87. public function setExecutable($executable) {
  88. if (!$executable) {
  89. return;
  90. }
  91. $this->executable = $executable;
  92. $this->executable = strtr($this->executable, '/', DIRECTORY_SEPARATOR);
  93. $this->executable = strtr($this->executable, '\\', DIRECTORY_SEPARATOR);
  94. }
  95. public function getExecutable() {
  96. return $this->executable;
  97. }
  98. public function addArguments($line) {
  99. foreach($line as $arg) {
  100. $this->createArgument()->setValue($arg);
  101. }
  102. }
  103. /**
  104. * Returns the executable and all defined arguments.
  105. * @return array
  106. */
  107. public function getCommandline() {
  108. $args = $this->getArguments();
  109. if ($this->executable === null) {
  110. return $args;
  111. }
  112. return array_merge(array($this->executable), $args);
  113. }
  114. /**
  115. * Returns all arguments defined by <code>addLine</code>,
  116. * <code>addValue</code> or the argument object.
  117. */
  118. public function getArguments() {
  119. $result = array();
  120. foreach($this->arguments as $arg) {
  121. $parts = $arg->getParts();
  122. if ($parts !== null) {
  123. foreach($parts as $part) {
  124. $result[] = $part;
  125. }
  126. }
  127. }
  128. return $result;
  129. }
  130. public function __toString() {
  131. return self::toString($this->getCommandline());
  132. }
  133. /**
  134. * Put quotes around the given String if necessary.
  135. *
  136. * <p>If the argument doesn't include spaces or quotes, return it
  137. * as is. If it contains double quotes, use single quotes - else
  138. * surround the argument by double quotes.</p>
  139. *
  140. * @exception BuildException if the argument contains both, single
  141. * and double quotes.
  142. */
  143. public static function quoteArgument($argument) {
  144. if (strpos($argument, "\"") !== false) {
  145. if (strpos($argument, "'") !== false) {
  146. throw new BuildException("Can't handle single and double quotes in same argument");
  147. } else {
  148. return escapeshellarg($argument);
  149. }
  150. } elseif (strpos($argument, "'") !== false || strpos($argument, " ") !== false) {
  151. return escapeshellarg($argument);
  152. //return '\"' . $argument . '\"';
  153. } else {
  154. return $argument;
  155. }
  156. }
  157. /**
  158. * Quotes the parts of the given array in way that makes them
  159. * usable as command line arguments.
  160. */
  161. public static function toString($lines) {
  162. // empty path return empty string
  163. if (!$lines) {
  164. return "";
  165. }
  166. // path containing one or more elements
  167. $result = "";
  168. for ($i = 0, $len=count($lines); $i < $len; $i++) {
  169. if ($i > 0) {
  170. $result .= ' ';
  171. }
  172. $result .= self::quoteArgument($lines[$i]);
  173. }
  174. return $result;
  175. }
  176. /**
  177. *
  178. * @param string $to_process
  179. * @return array
  180. */
  181. public static function translateCommandline($to_process) {
  182. if (!$to_process) {
  183. return array();
  184. }
  185. // parse with a simple finite state machine
  186. $normal = 0;
  187. $inQuote = 1;
  188. $inDoubleQuote = 2;
  189. $state = $normal;
  190. $args = array();
  191. $current = "";
  192. $lastTokenHasBeenQuoted = false;
  193. $tok = strtok($to_process, "");
  194. $tokens = preg_split('/(["\' ])/', $to_process, -1, PREG_SPLIT_DELIM_CAPTURE);
  195. while(($nextTok = array_shift($tokens)) !== null) {
  196. switch ($state) {
  197. case $inQuote:
  198. if ("'" == $nextTok) {
  199. $lastTokenHasBeenQuoted = true;
  200. $state = $normal;
  201. } else {
  202. $current .= $nextTok;
  203. }
  204. break;
  205. case $inDoubleQuote:
  206. if ("\"" == $nextTok) {
  207. $lastTokenHasBeenQuoted = true;
  208. $state = $normal;
  209. } else {
  210. $current .= $nextTok;
  211. }
  212. break;
  213. default:
  214. if ("'" == $nextTok) {
  215. $state = $inQuote;
  216. } elseif ("\"" == $nextTok) {
  217. $state = $inDoubleQuote;
  218. } elseif (" " == $nextTok) {
  219. if ($lastTokenHasBeenQuoted || strlen($current) != 0) {
  220. $args[] = $current;
  221. $current = "";
  222. }
  223. } else {
  224. $current .= $nextTok;
  225. }
  226. $lastTokenHasBeenQuoted = false;
  227. break;
  228. }
  229. }
  230. if ($lastTokenHasBeenQuoted || strlen($current) != 0) {
  231. $args[] = $current;
  232. }
  233. if ($state == $inQuote || $state == $inDoubleQuote) {
  234. throw new BuildException("unbalanced quotes in " . $to_process);
  235. }
  236. return $args;
  237. }
  238. /**
  239. * @return int Number of components in current commandline.
  240. */
  241. public function size() {
  242. return count($this->getCommandline());
  243. }
  244. public function __copy() {
  245. $c = new Commandline();
  246. $c->setExecutable($this->executable);
  247. $c->addArguments($this->getArguments());
  248. return $c;
  249. }
  250. /**
  251. * Clear out the whole command line. */
  252. public function clear() {
  253. $this->executable = null;
  254. $this->arguments->removeAllElements();
  255. }
  256. /**
  257. * Clear out the arguments but leave the executable in place for
  258. * another operation.
  259. */
  260. public function clearArgs() {
  261. $this->arguments = array();
  262. }
  263. /**
  264. * Return a marker.
  265. *
  266. * <p>This marker can be used to locate a position on the
  267. * commandline - to insert something for example - when all
  268. * parameters have been set.</p>
  269. * @return CommandlineMarker
  270. */
  271. public function createMarker() {
  272. return new CommandlineMarker($this, count($this->arguments));
  273. }
  274. /**
  275. * Returns a String that describes the command and arguments
  276. * suitable for verbose output before a call to
  277. * <code>Runtime.exec(String[])<code>.
  278. *
  279. * <p>This method assumes that the first entry in the array is the
  280. * executable to run.</p>
  281. * @param array $args CommandlineArgument[] to use
  282. * @return string
  283. */
  284. public function describeCommand($args = null) {
  285. if ($args === null) {
  286. $args = $this->getCommandline();
  287. }
  288. if (!$args) {
  289. return "";
  290. }
  291. $buf = "Executing '";
  292. $buf .= $args[0];
  293. $buf .= "'";
  294. if (count($args) > 0) {
  295. $buf .= " with ";
  296. $buf .= $this->describeArguments($args, 1);
  297. } else {
  298. $buf .= self::DISCLAIMER;
  299. }
  300. return $buf;
  301. }
  302. /**
  303. * Returns a String that describes the arguments suitable for
  304. * verbose output before a call to
  305. * <code>Runtime.exec(String[])<code>
  306. * @param $args arguments to use (default is to use current class args)
  307. * @param $offset ignore entries before this index
  308. * @return string
  309. */
  310. protected function describeArguments($args = null, $offset = 0) {
  311. if ($args === null) {
  312. $args = $this->getArguments();
  313. }
  314. if ($args === null || count($args) <= $offset) {
  315. return "";
  316. }
  317. $buf = "argument";
  318. if (count($args) > $offset) {
  319. $buf .= "s";
  320. }
  321. $buf .= ":" . Phing::getProperty("line.separator");
  322. for ($i = $offset, $alen=count($args); $i < $alen; $i++) {
  323. $buf .= "'" . $args[$i] . "'" . Phing::getProperty("line.separator");
  324. }
  325. $buf .= self::DISCLAIMER;
  326. return $buf;
  327. }
  328. }
  329. /**
  330. * "Inner" class used for nested xml command line definitions.
  331. */
  332. class CommandlineArgument {
  333. private $parts = array();
  334. private $outer;
  335. public function __construct(Commandline $outer) {
  336. $this->outer = $outer;
  337. }
  338. /**
  339. * Sets a single commandline argument.
  340. *
  341. * @param string $value a single commandline argument.
  342. */
  343. public function setValue($value) {
  344. $this->parts = array($value);
  345. }
  346. /**
  347. * Line to split into several commandline arguments.
  348. *
  349. * @param line line to split into several commandline arguments
  350. */
  351. public function setLine($line) {
  352. if ($line === null) {
  353. return;
  354. }
  355. $this->parts = $this->outer->translateCommandline($line);
  356. }
  357. /**
  358. * Sets a single commandline argument and treats it like a
  359. * PATH - ensures the right separator for the local platform
  360. * is used.
  361. *
  362. * @param value a single commandline argument.
  363. */
  364. public function setPath($value) {
  365. $this->parts = array( (string) $value );
  366. }
  367. /**
  368. * Sets a single commandline argument to the absolute filename
  369. * of the given file.
  370. *
  371. * @param value a single commandline argument.
  372. */
  373. public function setFile(PhingFile $value) {
  374. $this->parts = array($value->getAbsolutePath());
  375. }
  376. /**
  377. * Returns the parts this Argument consists of.
  378. * @return array string[]
  379. */
  380. public function getParts() {
  381. return $this->parts;
  382. }
  383. }
  384. /**
  385. * Class to keep track of the position of an Argument.
  386. */
  387. // <p>This class is there to support the srcfile and targetfile
  388. // elements of &lt;execon&gt; and &lt;transform&gt; - don't know
  389. // whether there might be additional use cases.</p> --SB
  390. class CommandlineMarker {
  391. private $position;
  392. private $realPos = -1;
  393. private $outer;
  394. public function __construct(Comandline $outer, $position) {
  395. $this->outer = $outer;
  396. $this->position = $position;
  397. }
  398. /**
  399. * Return the number of arguments that preceeded this marker.
  400. *
  401. * <p>The name of the executable - if set - is counted as the
  402. * very first argument.</p>
  403. */
  404. public function getPosition() {
  405. if ($this->realPos == -1) {
  406. $realPos = ($this->outer->executable === null ? 0 : 1);
  407. for ($i = 0; $i < $position; $i++) {
  408. $arg = $this->arguments[$i];
  409. $realPos += count($arg->getParts());
  410. }
  411. }
  412. return $this->realPos;
  413. }
  414. }