summaryrefslogtreecommitdiff
path: root/buildscripts/phing/classes/phing/util
diff options
context:
space:
mode:
Diffstat (limited to 'buildscripts/phing/classes/phing/util')
-rwxr-xr-xbuildscripts/phing/classes/phing/util/DataStore.php151
-rwxr-xr-xbuildscripts/phing/classes/phing/util/DirectoryScanner.php755
-rwxr-xr-xbuildscripts/phing/classes/phing/util/ExtendedFileStream.php129
-rwxr-xr-xbuildscripts/phing/classes/phing/util/FileUtils.php298
-rwxr-xr-xbuildscripts/phing/classes/phing/util/LogWriter.php95
-rw-r--r--buildscripts/phing/classes/phing/util/PathTokenizer.php245
-rw-r--r--buildscripts/phing/classes/phing/util/PearPackageScanner.php170
-rw-r--r--buildscripts/phing/classes/phing/util/SourceFileScanner.php159
-rw-r--r--buildscripts/phing/classes/phing/util/StringHelper.php208
-rw-r--r--buildscripts/phing/classes/phing/util/regexp/PregEngine.php167
-rwxr-xr-xbuildscripts/phing/classes/phing/util/regexp/Regexp.php203
-rwxr-xr-xbuildscripts/phing/classes/phing/util/regexp/RegexpEngine.php73
12 files changed, 2653 insertions, 0 deletions
diff --git a/buildscripts/phing/classes/phing/util/DataStore.php b/buildscripts/phing/classes/phing/util/DataStore.php
new file mode 100755
index 00000000..af696697
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/DataStore.php
@@ -0,0 +1,151 @@
+<?php
+
+/*
+ * $Id: 77f24d8b9d8082b4c23cb4cd5d23a06a3be88e2d $
+ *
+ * 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 COPYRIGHT
+ * OWNER 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+require_once 'phing/system/io/PhingFile.php';
+require_once 'phing/system/io/FileWriter.php';
+
+/**
+ * An abstract representation of file and directory pathnames.
+ *
+ * @package phing.util
+ * @author Michiel Rook <mrook@php.net>
+ * @version $Id$
+ */
+class DataStore
+{
+ private $data = array();
+ private $file = null;
+
+ /**
+ * Constructs a new data store
+ *
+ * @param PhingFile $file object pointing to the data store on disk
+ */
+ function __construct(PhingFile $file)
+ {
+ $this->file = $file;
+
+ if ($this->file->exists())
+ {
+ $this->read();
+ }
+ }
+
+ /**
+ * Destructor
+ */
+ function __destruct()
+ {
+ $this->commit();
+ }
+
+ /**
+ * Retrieves a value from the data store
+ *
+ * @param string $key the key
+ *
+ * @return mixed the value
+ */
+ public function get($key)
+ {
+ if (!isset($this->data[$key]))
+ {
+ return null;
+ }
+ else
+ {
+ return $this->data[$key];
+ }
+ }
+
+ /**
+ * Adds a value to the data store
+ *
+ * @param string $key the key
+ * @param mixed $value the value
+ * @param boolean $autocommit whether to auto-commit (write)
+ * the data store to disk
+ *
+ * @return none
+ */
+ public function put($key, $value, $autocommit = false)
+ {
+ $this->data[$key] = $value;
+
+ if ($autocommit)
+ {
+ $this->commit();
+ }
+ }
+
+ /**
+ * Commits data store to disk
+ *
+ * @return none
+ */
+ public function commit()
+ {
+ $this->write();
+ }
+
+ /**
+ * Internal function to read data store from file
+ *
+ * @return none
+ */
+ private function read()
+ {
+ if (!$this->file->canRead())
+ {
+ throw new BuildException("Can't read data store from '" .
+ $file->getPath() . "'");
+ }
+ else
+ {
+ $serializedData = $this->file->contents();
+
+ $this->data = unserialize($serializedData);
+ }
+ }
+
+ /**
+ * Internal function to write data store to file
+ *
+ * @return none
+ */
+ private function write()
+ {
+ if (!$this->file->canWrite())
+ {
+ throw new BuildException("Can't write data store to '" .
+ $file->getPath() . "'");
+ }
+ else
+ {
+ $serializedData = serialize($this->data);
+
+ $writer = new FileWriter($this->file);
+ $writer->write($serializedData);
+ $writer->close();
+ }
+ }
+};
diff --git a/buildscripts/phing/classes/phing/util/DirectoryScanner.php b/buildscripts/phing/classes/phing/util/DirectoryScanner.php
new file mode 100755
index 00000000..02d5be88
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/DirectoryScanner.php
@@ -0,0 +1,755 @@
+<?php
+/*
+ * $Id: 7aef4b4e372e89055248ab063660dbee92a98cc3 $
+ *
+ * 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 COPYRIGHT
+ * OWNER 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+require_once 'phing/types/selectors/SelectorScanner.php';
+include_once 'phing/util/StringHelper.php';
+include_once 'phing/types/selectors/SelectorUtils.php';
+
+/**
+ * Class for scanning a directory for files/directories that match a certain
+ * criteria.
+ *
+ * These criteria consist of a set of include and exclude patterns. With these
+ * patterns, you can select which files you want to have included, and which
+ * files you want to have excluded.
+ *
+ * The idea is simple. A given directory is recursively scanned for all files
+ * and directories. Each file/directory is matched against a set of include
+ * and exclude patterns. Only files/directories that match at least one
+ * pattern of the include pattern list, and don't match a pattern of the
+ * exclude pattern list will be placed in the list of files/directories found.
+ *
+ * When no list of include patterns is supplied, "**" will be used, which
+ * means that everything will be matched. When no list of exclude patterns is
+ * supplied, an empty list is used, such that nothing will be excluded.
+ *
+ * The pattern matching is done as follows:
+ * The name to be matched is split up in path segments. A path segment is the
+ * name of a directory or file, which is bounded by DIRECTORY_SEPARATOR
+ * ('/' under UNIX, '\' under Windows).
+ * E.g. "abc/def/ghi/xyz.php" is split up in the segments "abc", "def", "ghi"
+ * and "xyz.php".
+ * The same is done for the pattern against which should be matched.
+ *
+ * Then the segments of the name and the pattern will be matched against each
+ * other. When '**' is used for a path segment in the pattern, then it matches
+ * zero or more path segments of the name.
+ *
+ * There are special case regarding the use of DIRECTORY_SEPARATOR at
+ * the beginning of the pattern and the string to match:
+ * When a pattern starts with a DIRECTORY_SEPARATOR, the string
+ * to match must also start with a DIRECTORY_SEPARATOR.
+ * When a pattern does not start with a DIRECTORY_SEPARATOR, the
+ * string to match may not start with a DIRECTORY_SEPARATOR.
+ * When one of these rules is not obeyed, the string will not
+ * match.
+ *
+ * When a name path segment is matched against a pattern path segment, the
+ * following special characters can be used:
+ * '*' matches zero or more characters,
+ * '?' matches one character.
+ *
+ * Examples:
+ *
+ * "**\*.php" matches all .php files/dirs in a directory tree.
+ *
+ * "test\a??.php" matches all files/dirs which start with an 'a', then two
+ * more characters and then ".php", in a directory called test.
+ *
+ * "**" matches everything in a directory tree.
+ *
+ * "**\test\**\XYZ*" matches all files/dirs that start with "XYZ" and where
+ * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
+ *
+ * Case sensitivity may be turned off if necessary. By default, it is
+ * turned on.
+ *
+ * Example of usage:
+ * $ds = new DirectroyScanner();
+ * $includes = array("**\*.php");
+ * $excludes = array("modules\*\**");
+ * $ds->SetIncludes($includes);
+ * $ds->SetExcludes($excludes);
+ * $ds->SetBasedir("test");
+ * $ds->SetCaseSensitive(true);
+ * $ds->Scan();
+ *
+ * print("FILES:");
+ * $files = ds->GetIncludedFiles();
+ * for ($i = 0; $i < count($files);$i++) {
+ * println("$files[$i]\n");
+ * }
+ *
+ * This will scan a directory called test for .php files, but excludes all
+ * .php files in all directories under a directory called "modules"
+ *
+ * This class is complete preg/ereg free port of the Java class
+ * org.apache.tools.ant.DirectoryScanner. Even functions that use preg/ereg
+ * internally (like split()) are not used. Only the _fast_ string functions
+ * and comparison operators (=== !=== etc) are used for matching and tokenizing.
+ *
+ * @author Arnout J. Kuiper, ajkuiper@wxs.nl
+ * @author Magesh Umasankar, umagesh@rediffmail.com
+ * @author Andreas Aderhold, andi@binarycloud.com
+ *
+ * @version $Id: 7aef4b4e372e89055248ab063660dbee92a98cc3 $
+ * @package phing.util
+ */
+class DirectoryScanner implements SelectorScanner {
+
+ /** default set of excludes */
+ protected $DEFAULTEXCLUDES = array(
+ "**/*~",
+ "**/#*#",
+ "**/.#*",
+ "**/%*%",
+ "**/CVS",
+ "**/CVS/**",
+ "**/.cvsignore",
+ "**/SCCS",
+ "**/SCCS/**",
+ "**/vssver.scc",
+ "**/.svn",
+ "**/.svn/**",
+ "**/._*",
+ "**/.DS_Store",
+ "**/.darcs",
+ "**/.darcs/**",
+ "**/.git",
+ "**/.git/**",
+ "**/.gitattributes",
+ "**/.gitignore",
+ "**/.gitmodules",
+ );
+
+ /** The base directory which should be scanned. */
+ protected $basedir;
+
+ /** The patterns for the files that should be included. */
+ protected $includes = null;
+
+ /** The patterns for the files that should be excluded. */
+ protected $excludes = null;
+
+ /** Whether to expand/dereference symbolic links, default is false */
+ protected $expandSymbolicLinks = false;
+
+ /**
+ * The files that where found and matched at least one includes, and matched
+ * no excludes.
+ */
+ protected $filesIncluded;
+
+ /** The files that where found and did not match any includes. Trie */
+ protected $filesNotIncluded;
+
+ /**
+ * The files that where found and matched at least one includes, and also
+ * matched at least one excludes. Trie object.
+ */
+ protected $filesExcluded;
+
+ /**
+ * The directories that where found and matched at least one includes, and
+ * matched no excludes.
+ */
+ protected $dirsIncluded;
+
+ /** The directories that where found and did not match any includes. */
+ protected $dirsNotIncluded;
+
+ /**
+ * The files that where found and matched at least one includes, and also
+ * matched at least one excludes.
+ */
+ protected $dirsExcluded;
+
+ /** Have the vars holding our results been built by a slow scan? */
+ protected $haveSlowResults = false;
+
+ /** Should the file system be treated as a case sensitive one? */
+ protected $isCaseSensitive = true;
+
+ /** Selectors */
+ protected $selectors = null;
+
+ protected $filesDeselected;
+ protected $dirsDeselected;
+
+ /** if there are no deselected files */
+ protected $everythingIncluded = true;
+
+ /**
+ * Does the path match the start of this pattern up to the first "**".
+ * This is a static mehtod and should always be called static
+ *
+ * This is not a general purpose test and should only be used if you
+ * can live with false positives.
+ *
+ * pattern=**\a and str=b will yield true.
+ *
+ * @param pattern the (non-null) pattern to match against
+ * @param str the (non-null) string (path) to match
+ * @param isCaseSensitive must matches be case sensitive?
+ * @return boolean true if matches, otherwise false
+ */
+ function matchPatternStart($pattern, $str, $isCaseSensitive = true) {
+ return SelectorUtils::matchPatternStart($pattern, $str, $isCaseSensitive);
+ }
+
+ /**
+ * Matches a path against a pattern. Static
+ *
+ * @param pattern the (non-null) pattern to match against
+ * @param str the (non-null) string (path) to match
+ * @param isCaseSensitive must a case sensitive match be done?
+ *
+ * @return true when the pattern matches against the string.
+ * false otherwise.
+ */
+ function matchPath($pattern, $str, $isCaseSensitive = true) {
+ return SelectorUtils::matchPath($pattern, $str, $isCaseSensitive);
+ }
+
+ /**
+ * Matches a string against a pattern. The pattern contains two special
+ * characters:
+ * '*' which means zero or more characters,
+ * '?' which means one and only one character.
+ *
+ * @param pattern the (non-null) pattern to match against
+ * @param str the (non-null) string that must be matched against the
+ * pattern
+ *
+ * @return boolean true when the string matches against the pattern,
+ * false otherwise.
+ * @access public
+ */
+ function match($pattern, $str, $isCaseSensitive = true) {
+ return SelectorUtils::match($pattern, $str, $isCaseSensitive);
+ }
+
+ /**
+ * Sets the basedir for scanning. This is the directory that is scanned
+ * recursively. All '/' and '\' characters are replaced by
+ * DIRECTORY_SEPARATOR
+ *
+ * @param basedir the (non-null) basedir for scanning
+ */
+ function setBasedir($_basedir) {
+ $_basedir = str_replace('\\', DIRECTORY_SEPARATOR, $_basedir);
+ $_basedir = str_replace('/', DIRECTORY_SEPARATOR, $_basedir);
+ $this->basedir = $_basedir;
+ }
+
+ /**
+ * Gets the basedir that is used for scanning. This is the directory that
+ * is scanned recursively.
+ *
+ * @return the basedir that is used for scanning
+ */
+ function getBasedir() {
+ return $this->basedir;
+ }
+
+ /**
+ * Sets the case sensitivity of the file system
+ *
+ * @param specifies if the filesystem is case sensitive
+ */
+ function setCaseSensitive($_isCaseSensitive) {
+ $this->isCaseSensitive = ($_isCaseSensitive) ? true : false;
+ }
+
+ /**
+ * Sets the set of include patterns to use. All '/' and '\' characters are
+ * replaced by DIRECTORY_SEPARATOR. So the separator used need
+ * not match DIRECTORY_SEPARATOR.
+ *
+ * When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @param includes list of include patterns
+ */
+ function setIncludes($_includes = array()) {
+ if (empty($_includes) || is_null($_includes)) {
+ $this->includes = null;
+ } else {
+ for ($i = 0; $i < count($_includes); $i++) {
+ $pattern = null;
+ $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $_includes[$i]);
+ $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern);
+ if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) {
+ $pattern .= "**";
+ }
+ $this->includes[] = $pattern;
+ }
+ }
+ }
+
+ /**
+ * Sets the set of exclude patterns to use. All '/' and '\' characters are
+ * replaced by <code>File.separatorChar</code>. So the separator used need
+ * not match <code>File.separatorChar</code>.
+ *
+ * When a pattern ends with a '/' or '\', "**" is appended.
+ *
+ * @param excludes list of exclude patterns
+ */
+
+ function setExcludes($_excludes = array()) {
+ if (empty($_excludes) || is_null($_excludes)) {
+ $this->excludes = null;
+ } else {
+ for ($i = 0; $i < count($_excludes); $i++) {
+ $pattern = null;
+ $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $_excludes[$i]);
+ $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern);
+ if (StringHelper::endsWith(DIRECTORY_SEPARATOR, $pattern)) {
+ $pattern .= "**";
+ }
+ $this->excludes[] = $pattern;
+ }
+ }
+ }
+
+ /**
+ * Sets whether to expand/dereference symbolic links
+ *
+ * @param expandSymbolicLinks boolean value
+ */
+ function setExpandSymbolicLinks($expandSymbolicLinks)
+ {
+ $this->expandSymbolicLinks = $expandSymbolicLinks;
+ }
+
+ /**
+ * Scans the base directory for files that match at least one include
+ * pattern, and don't match any exclude patterns.
+ *
+ */
+ function scan() {
+
+ if ((empty($this->basedir)) || (!@is_dir($this->basedir))) {
+ return false;
+ }
+
+ if ($this->includes === null) {
+ // No includes supplied, so set it to 'matches all'
+ $this->includes = array("**");
+ }
+ if (is_null($this->excludes)) {
+ $this->excludes = array();
+ }
+
+ $this->filesIncluded = array();
+ $this->filesNotIncluded = array();
+ $this->filesExcluded = array();
+ $this->dirsIncluded = array();
+ $this->dirsNotIncluded = array();
+ $this->dirsExcluded = array();
+ $this->dirsDeselected = array();
+ $this->filesDeselected = array();
+
+ if ($this->isIncluded("")) {
+ if (!$this->isExcluded("")) {
+ if ($this->isSelected("", $this->basedir)) {
+ $this->dirsIncluded[] = "";
+ } else {
+ $this->dirsDeselected[] = "";
+ }
+ } else {
+ $this->dirsExcluded[] = "";
+ }
+ } else {
+ $this->dirsNotIncluded[] = "";
+ }
+
+ $this->scandir($this->basedir, "", true);
+ return true;
+ }
+
+ /**
+ * Toplevel invocation for the scan.
+ *
+ * Returns immediately if a slow scan has already been requested.
+ */
+ protected function slowScan() {
+
+ if ($this->haveSlowResults) {
+ return;
+ }
+
+ // copy trie object add CopyInto() method
+ $excl = $this->dirsExcluded;
+ $notIncl = $this->dirsNotIncluded;
+
+ for ($i=0, $_i=count($excl); $i < $_i; $i++) {
+ if (!$this->couldHoldIncluded($excl[$i])) {
+ $this->scandir($this->basedir.$excl[$i], $excl[$i].DIRECTORY_SEPARATOR, false);
+ }
+ }
+
+ for ($i=0, $_i=count($notIncl); $i < $_i; $i++) {
+ if (!$this->couldHoldIncluded($notIncl[$i])) {
+ $this->scandir($this->basedir.$notIncl[$i], $notIncl[$i].DIRECTORY_SEPARATOR, false);
+ }
+ }
+
+ $this->haveSlowResults = true;
+ }
+
+ /**
+ * Lists contens of a given directory and returns array with entries
+ *
+ * @param src String. Source path and name file to copy.
+ *
+ * @access public
+ * @return array directory entries
+ * @author Albert Lash, alash@plateauinnovation.com
+ */
+
+ function listDir($_dir) {
+ $d = dir($_dir);
+ $list = array();
+ while(($entry = $d->read()) !== false) {
+ if ($entry != "." && $entry != "..") {
+ $list[] = $entry;
+ }
+ }
+ $d->close();
+ return $list;
+ }
+
+ /**
+ * Scans the passed dir for files and directories. Found files and
+ * directories are placed in their respective collections, based on the
+ * matching of includes and excludes. When a directory is found, it is
+ * scanned recursively.
+ *
+ * @param dir the directory to scan
+ * @param vpath the path relative to the basedir (needed to prevent
+ * problems with an absolute path when using dir)
+ *
+ * @access private
+ * @see #filesIncluded
+ * @see #filesNotIncluded
+ * @see #filesExcluded
+ * @see #dirsIncluded
+ * @see #dirsNotIncluded
+ * @see #dirsExcluded
+ */
+ private function scandir($_rootdir, $_vpath, $_fast) {
+
+ if (!is_readable($_rootdir)) {
+ return;
+ }
+
+ $newfiles = self::listDir($_rootdir);
+
+ for ($i=0,$_i=count($newfiles); $i < $_i; $i++) {
+
+ $file = $_rootdir . DIRECTORY_SEPARATOR . $newfiles[$i];
+ $name = $_vpath . $newfiles[$i];
+
+ if (@is_link($file) && !$this->expandSymbolicLinks)
+ {
+ if ($this->isIncluded($name)) {
+ if (!$this->isExcluded($name)) {
+ if ($this->isSelected($name, $file)) {
+ $this->filesIncluded[] = $name;
+ } else {
+ $this->everythingIncluded = false;
+ $this->filesDeselected[] = $name;
+ }
+ } else {
+ $this->everythingIncluded = false;
+ $this->filesExcluded[] = $name;
+ }
+ } else {
+ $this->everythingIncluded = false;
+ $this->filesNotIncluded[] = $name;
+ }
+ }
+ else
+ if (@is_dir($file)) {
+ if ($this->isIncluded($name)) {
+ if (!$this->isExcluded($name)) {
+ if ($this->isSelected($name, $file)) {
+ $this->dirsIncluded[] = $name;
+ if ($_fast) {
+ $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast);
+ }
+ } else {
+ $this->everythingIncluded = false;
+ $this->dirsDeselected[] = $name;
+ if ($_fast && $this->couldHoldIncluded($name)) {
+ $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast);
+ }
+ }
+ } else {
+ $this->everythingIncluded = false;
+ $this->dirsExcluded[] = $name;
+ if ($_fast && $this->couldHoldIncluded($name)) {
+ $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast);
+ }
+ }
+ } else {
+ $this->everythingIncluded = false;
+ $this->dirsNotIncluded[] = $name;
+ if ($_fast && $this->couldHoldIncluded($name)) {
+ $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast);
+ }
+ }
+
+ if (!$_fast) {
+ $this->scandir($file, $name.DIRECTORY_SEPARATOR, $_fast);
+ }
+
+ } elseif (@is_file($file)) {
+ if ($this->isIncluded($name)) {
+ if (!$this->isExcluded($name)) {
+ if ($this->isSelected($name, $file)) {
+ $this->filesIncluded[] = $name;
+ } else {
+ $this->everythingIncluded = false;
+ $this->filesDeselected[] = $name;
+ }
+ } else {
+ $this->everythingIncluded = false;
+ $this->filesExcluded[] = $name;
+ }
+ } else {
+ $this->everythingIncluded = false;
+ $this->filesNotIncluded[] = $name;
+ }
+ }
+ }
+ }
+
+ /**
+ * Tests whether a name matches against at least one include pattern.
+ *
+ * @param name the name to match
+ * @return <code>true</code> when the name matches against at least one
+ * include pattern, <code>false</code> otherwise.
+ */
+ protected function isIncluded($_name) {
+ for ($i=0, $_i=count($this->includes); $i < $_i; $i++) {
+ if (DirectoryScanner::matchPath($this->includes[$i], $_name, $this->isCaseSensitive)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether a name matches the start of at least one include pattern.
+ *
+ * @param name the name to match
+ * @return <code>true</code> when the name matches against at least one
+ * include pattern, <code>false</code> otherwise.
+ */
+ protected function couldHoldIncluded($_name) {
+ for ($i = 0; $i < count($this->includes); $i++) {
+ if (DirectoryScanner::matchPatternStart($this->includes[$i], $_name, $this->isCaseSensitive)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether a name matches against at least one exclude pattern.
+ *
+ * @param name the name to match
+ * @return <code>true</code> when the name matches against at least one
+ * exclude pattern, <code>false</code> otherwise.
+ */
+ protected function isExcluded($_name) {
+ for ($i = 0; $i < count($this->excludes); $i++) {
+ if (DirectoryScanner::matchPath($this->excludes[$i], $_name, $this->isCaseSensitive)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the names of the files that matched at least one of the include
+ * patterns, and matched none of the exclude patterns.
+ * The names are relative to the basedir.
+ *
+ * @return the names of the files
+ */
+ function getIncludedFiles() {
+ return $this->filesIncluded;
+ }
+
+ /**
+ * Get the names of the files that matched at none of the include patterns.
+ * The names are relative to the basedir.
+ *
+ * @return the names of the files
+ */
+ function getNotIncludedFiles() {
+ $this->slowScan();
+ return $this->filesNotIncluded;
+ }
+
+ /**
+ * Get the names of the files that matched at least one of the include
+ * patterns, an matched also at least one of the exclude patterns.
+ * The names are relative to the basedir.
+ *
+ * @return the names of the files
+ */
+
+ function getExcludedFiles() {
+ $this->slowScan();
+ return $this->filesExcluded;
+ }
+
+ /**
+ * <p>Returns the names of the files which were selected out and
+ * therefore not ultimately included.</p>
+ *
+ * <p>The names are relative to the base directory. This involves
+ * performing a slow scan if one has not already been completed.</p>
+ *
+ * @return the names of the files which were deselected.
+ *
+ * @see #slowScan
+ */
+ public function getDeselectedFiles() {
+ $this->slowScan();
+ return $this->filesDeselected;
+ }
+
+ /**
+ * Get the names of the directories that matched at least one of the include
+ * patterns, an matched none of the exclude patterns.
+ * The names are relative to the basedir.
+ *
+ * @return the names of the directories
+ */
+
+ function getIncludedDirectories() {
+ return $this->dirsIncluded;
+ }
+
+ /**
+ * Get the names of the directories that matched at none of the include
+ * patterns.
+ * The names are relative to the basedir.
+ *
+ * @return the names of the directories
+ */
+ function getNotIncludedDirectories() {
+ $this->slowScan();
+ return $this->dirsNotIncluded;
+ }
+
+ /**
+ * <p>Returns the names of the directories which were selected out and
+ * therefore not ultimately included.</p>
+ *
+ * <p>The names are relative to the base directory. This involves
+ * performing a slow scan if one has not already been completed.</p>
+ *
+ * @return the names of the directories which were deselected.
+ *
+ * @see #slowScan
+ */
+ public function getDeselectedDirectories() {
+ $this->slowScan();
+ return $this->dirsDeselected;
+ }
+
+ /**
+ * Get the names of the directories that matched at least one of the include
+ * patterns, an matched also at least one of the exclude patterns.
+ * The names are relative to the basedir.
+ *
+ * @return the names of the directories
+ */
+ function getExcludedDirectories() {
+ $this->slowScan();
+ return $this->dirsExcluded;
+ }
+
+ /**
+ * Adds the array with default exclusions to the current exclusions set.
+ *
+ */
+ function addDefaultExcludes() {
+ //$excludesLength = ($this->excludes == null) ? 0 : count($this->excludes);
+ foreach($this->DEFAULTEXCLUDES as $pattern) {
+ $pattern = str_replace('\\', DIRECTORY_SEPARATOR, $pattern);
+ $pattern = str_replace('/', DIRECTORY_SEPARATOR, $pattern);
+ $this->excludes[] = $pattern;
+ }
+ }
+
+ /**
+ * Sets the selectors that will select the filelist.
+ *
+ * @param selectors specifies the selectors to be invoked on a scan
+ */
+ public function setSelectors($selectors) {
+ $this->selectors = $selectors;
+ }
+
+ /**
+ * Returns whether or not the scanner has included all the files or
+ * directories it has come across so far.
+ *
+ * @return <code>true</code> if all files and directories which have
+ * been found so far have been included.
+ */
+ public function isEverythingIncluded() {
+ return $this->everythingIncluded;
+ }
+
+ /**
+ * Tests whether a name should be selected.
+ *
+ * @param string $name The filename to check for selecting.
+ * @param string $file The full file path.
+ * @return boolean False when the selectors says that the file
+ * should not be selected, True otherwise.
+ */
+ protected function isSelected($name, $file) {
+ if ($this->selectors !== null) {
+ $basedir = new PhingFile($this->basedir);
+ $file = new PhingFile($file);
+ if (!$file->canRead())
+ return false;
+
+ foreach($this->selectors as $selector) {
+ if (!$selector->isSelected($basedir, $name, $file)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/buildscripts/phing/classes/phing/util/ExtendedFileStream.php b/buildscripts/phing/classes/phing/util/ExtendedFileStream.php
new file mode 100755
index 00000000..9c09b56f
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/ExtendedFileStream.php
@@ -0,0 +1,129 @@
+<?php
+
+ include_once 'phing/system/io/PhingFile.php';
+
+ /**
+ * $Id: f7e4641c758d5e781ad209d3eaf653a20404bf56 $
+ *
+ * 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 COPYRIGHT
+ * OWNER 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+ /**
+ * Extended file stream wrapper class which auto-creates directories
+ *
+ * @author Michiel Rook <mrook@php.net>
+ * @version $Id: f7e4641c758d5e781ad209d3eaf653a20404bf56 $
+ * @package phing.util
+ */
+ class ExtendedFileStream
+ {
+ private $fp = NULL;
+
+ static function registerStream()
+ {
+ if (!in_array("efile", stream_get_wrappers()))
+ {
+ stream_wrapper_register("efile", "ExtendedFileStream");
+ }
+ }
+
+ static function unregisterStream()
+ {
+ stream_wrapper_unregister("efile");
+ }
+
+ private function createDirectories($path)
+ {
+ $f = new PhingFile($path);
+ if (!$f->exists()) {
+ $f->mkdirs();
+ }
+ }
+
+ function stream_open($path, $mode, $options, &$opened_path)
+ {
+ $filepath = substr($path, 8);
+
+ $this->createDirectories(dirname($filepath));
+
+ $this->fp = fopen($filepath, $mode);
+
+ return true;
+ }
+
+ function stream_close()
+ {
+ fclose($this->fp);
+ $this->fp = NULL;
+ }
+
+ function stream_read($count)
+ {
+ return fread($this->fp, $count);
+ }
+
+ function stream_write($data)
+ {
+ return fwrite($this->fp, $data);
+ }
+
+ function stream_eof()
+ {
+ return feof($this->fp);
+ }
+
+ function stream_tell()
+ {
+ return ftell($this->fp);
+ }
+
+ function stream_seek($offset, $whence)
+ {
+ return fseek($this->fp, $offset, $whence);
+ }
+
+ function stream_flush()
+ {
+ return fflush($this->fp);
+ }
+
+ function stream_stat()
+ {
+ return fstat($this->fp);
+ }
+
+ function unlink($path)
+ {
+ return FALSE;
+ }
+
+ function rename($path_from, $path_to)
+ {
+ return FALSE;
+ }
+
+ function mkdir($path, $mode, $options)
+ {
+ return FALSE;
+ }
+
+ function rmdir($path, $options)
+ {
+ return FALSE;
+ }
+ };
+
diff --git a/buildscripts/phing/classes/phing/util/FileUtils.php b/buildscripts/phing/classes/phing/util/FileUtils.php
new file mode 100755
index 00000000..c98bbdd3
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/FileUtils.php
@@ -0,0 +1,298 @@
+<?php
+/*
+ * $Id: fe077b4174763861e773f5e5e55bbfc5030cac4d $
+ *
+ * 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 COPYRIGHT
+ * OWNER 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+include_once 'phing/system/lang/Character.php';
+include_once 'phing/util/StringHelper.php';
+include_once 'phing/system/io/BufferedReader.php';
+include_once 'phing/system/io/BufferedWriter.php';
+include_once 'phing/filters/util/ChainReaderHelper.php';
+include_once 'phing/system/io/PhingFile.php';
+
+/**
+ * File utility class.
+ * - handles os independent stuff etc
+ * - mapper stuff
+ * - filter stuff
+ *
+ * @package phing.util
+ * @version $Id$
+ */
+class FileUtils {
+
+ /**
+ * Returns a new Reader with filterchains applied. If filterchains are empty,
+ * simply returns passed reader.
+ *
+ * @param Reader $in Reader to modify (if appropriate).
+ * @param array &$filterChains filter chains to apply.
+ * @param Project $project
+ * @return Reader Assembled Reader (w/ filter chains).
+ */
+ public static function getChainedReader(Reader $in, &$filterChains, Project $project) {
+ if (!empty($filterChains)) {
+ $crh = new ChainReaderHelper();
+ $crh->setBufferSize(65536); // 64k buffer, but isn't being used (yet?)
+ $crh->setPrimaryReader($in);
+ $crh->setFilterChains($filterChains);
+ $crh->setProject($project);
+ $rdr = $crh->getAssembledReader();
+ return $rdr;
+ } else {
+ return $in;
+ }
+ }
+
+ /**
+ * Copies a file using filter chains.
+ *
+ * @param PhingFile $sourceFile
+ * @param PhingFile $destFile
+ * @param boolean $overwrite
+ * @param boolean $preserveLastModified
+ * @param array $filterChains
+ * @param Project $project
+ * @param integer $mode
+ * @return void
+ */
+ function copyFile(PhingFile $sourceFile, PhingFile $destFile, $overwrite = false, $preserveLastModified = true, &$filterChains = null, Project $project, $mode = 0755) {
+
+ if ($overwrite || !$destFile->exists() || $destFile->lastModified() < $sourceFile->lastModified()) {
+ if ($destFile->exists() && $destFile->isFile()) {
+ $destFile->delete();
+ }
+
+ // ensure that parent dir of dest file exists!
+ $parent = $destFile->getParentFile();
+ if ($parent !== null && !$parent->exists()) {
+ $parent->mkdirs($mode);
+ }
+
+ if ((is_array($filterChains)) && (!empty($filterChains))) {
+
+ $in = self::getChainedReader(new BufferedReader(new FileReader($sourceFile)), $filterChains, $project);
+ $out = new BufferedWriter(new FileWriter($destFile));
+
+ // New read() methods returns a big buffer.
+ while(-1 !== ($buffer = $in->read())) { // -1 indicates EOF
+ $out->write($buffer);
+ }
+
+ if ( $in !== null )
+ $in->close();
+ if ( $out !== null )
+ $out->close();
+
+ $destFile->setMode($sourceFile->getMode());
+
+ } else {
+ // simple copy (no filtering)
+ $sourceFile->copyTo($destFile);
+ }
+
+ if ($preserveLastModified) {
+ $destFile->setLastModified($sourceFile->lastModified());
+ }
+
+ }
+ }
+
+ /**
+ * Interpret the filename as a file relative to the given file -
+ * unless the filename already represents an absolute filename.
+ *
+ * @param $file the "reference" file for relative paths. This
+ * instance must be an absolute file and must not contain
+ * ./ or ../ sequences (same for \ instead of /).
+ * @param $filename a file name
+ *
+ * @return PhingFile A PhingFile object pointing to an absolute file that doesn't contain ./ or ../ sequences
+ * and uses the correct separator for the current platform.
+ */
+ function resolveFile($file, $filename) {
+ // remove this and use the static class constant File::seperator
+ // as soon as ZE2 is ready
+ $fs = FileSystem::getFileSystem();
+
+ $filename = str_replace('/', $fs->getSeparator(), str_replace('\\', $fs->getSeparator(), $filename));
+
+ // deal with absolute files
+ if (StringHelper::startsWith($fs->getSeparator(), $filename) ||
+ (strlen($filename) >= 2 && Character::isLetter($filename{0}) && $filename{1} === ':')) {
+ return new PhingFile($this->normalize($filename));
+ }
+
+ if (strlen($filename) >= 2 && Character::isLetter($filename{0}) && $filename{1} === ':') {
+ return new PhingFile($this->normalize($filename));
+ }
+
+ $helpFile = new PhingFile($file->getAbsolutePath());
+
+ $tok = strtok($filename, $fs->getSeparator());
+ while ($tok !== false) {
+ $part = $tok;
+ if ($part === '..') {
+ $parentFile = $helpFile->getParent();
+ if ($parentFile === null) {
+ $msg = "The file or path you specified ($filename) is invalid relative to ".$file->getPath();
+ throw new IOException($msg);
+ }
+ $helpFile = new PhingFile($parentFile);
+ } else if ($part === '.') {
+ // Do nothing here
+ } else {
+ $helpFile = new PhingFile($helpFile, $part);
+ }
+ $tok = strtok($fs->getSeparator());
+ }
+ return new PhingFile($helpFile->getAbsolutePath());
+ }
+
+ /**
+ * Normalize the given absolute path.
+ *
+ * This includes:
+ * - Uppercase the drive letter if there is one.
+ * - Remove redundant slashes after the drive spec.
+ * - resolve all ./, .\, ../ and ..\ sequences.
+ * - DOS style paths that start with a drive letter will have
+ * \ as the separator.
+ * @param string $path Path to normalize.
+ * @return string
+ */
+ function normalize($path) {
+
+ $path = (string) $path;
+ $orig = $path;
+
+ $path = str_replace('/', DIRECTORY_SEPARATOR, str_replace('\\', DIRECTORY_SEPARATOR, $path));
+
+ // make sure we are dealing with an absolute path
+ if (!StringHelper::startsWith(DIRECTORY_SEPARATOR, $path)
+ && !(strlen($path) >= 2 && Character::isLetter($path{0}) && $path{1} === ':')) {
+ throw new IOException("$path is not an absolute path");
+ }
+
+ $dosWithDrive = false;
+ $root = null;
+
+ // Eliminate consecutive slashes after the drive spec
+
+ if (strlen($path) >= 2 && Character::isLetter($path{0}) && $path{1} === ':') {
+ $dosWithDrive = true;
+
+ $ca = str_replace('/', '\\', $path);
+ $ca = StringHelper::toCharArray($ca);
+
+ $path = strtoupper($ca[0]).':';
+
+ for ($i=2, $_i=count($ca); $i < $_i; $i++) {
+ if (($ca[$i] !== '\\') ||
+ ($ca[$i] === '\\' && $ca[$i - 1] !== '\\')
+ ) {
+ $path .= $ca[$i];
+ }
+ }
+
+ $path = str_replace('\\', DIRECTORY_SEPARATOR, $path);
+
+ if (strlen($path) == 2) {
+ $root = $path;
+ $path = "";
+ } else {
+ $root = substr($path, 0, 3);
+ $path = substr($path, 3);
+ }
+
+ } else {
+ if (strlen($path) == 1) {
+ $root = DIRECTORY_SEPARATOR;
+ $path = "";
+ } else if ($path{1} == DIRECTORY_SEPARATOR) {
+ // UNC drive
+ $root = DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR;
+ $path = substr($path, 2);
+ }
+ else {
+ $root = DIRECTORY_SEPARATOR;
+ $path = substr($path, 1);
+ }
+ }
+
+ $s = array();
+ array_push($s, $root);
+ $tok = strtok($path, DIRECTORY_SEPARATOR);
+ while ($tok !== false) {
+ $thisToken = $tok;
+ if ("." === $thisToken) {
+ $tok = strtok(DIRECTORY_SEPARATOR);
+ continue;
+ } elseif (".." === $thisToken) {
+ if (count($s) < 2) {
+ // using '..' in path that is too short
+ throw new IOException("Cannot resolve path: $orig");
+ } else {
+ array_pop($s);
+ }
+ } else { // plain component
+ array_push($s, $thisToken);
+ }
+ $tok = strtok(DIRECTORY_SEPARATOR);
+ }
+
+ $sb = "";
+ for ($i=0,$_i=count($s); $i < $_i; $i++) {
+ if ($i > 1) {
+ // not before the filesystem root and not after it, since root
+ // already contains one
+ $sb .= DIRECTORY_SEPARATOR;
+ }
+ $sb .= (string) $s[$i];
+ }
+
+
+ $path = (string) $sb;
+ if ($dosWithDrive === true) {
+ $path = str_replace('/', '\\', $path);
+ }
+ return $path;
+ }
+
+ /**
+ * @return boolean Whether contents of two files is the same.
+ */
+ public function contentEquals(PhingFile $file1, PhingFile $file2) {
+
+ if (!($file1->exists() || $file2->exists())) {
+ return false;
+ }
+
+ if (!($file1->canRead() || $file2->canRead())) {
+ return false;
+ }
+
+ $c1 = file_get_contents($file1->getAbsolutePath());
+ $c2 = file_get_contents($file2->getAbsolutePath());
+
+ return trim($c1) == trim($c2);
+ }
+
+}
+
diff --git a/buildscripts/phing/classes/phing/util/LogWriter.php b/buildscripts/phing/classes/phing/util/LogWriter.php
new file mode 100755
index 00000000..ae97705e
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/LogWriter.php
@@ -0,0 +1,95 @@
+<?php
+
+ /**
+ * $Id: ccb6c8b930bb3f293074969323467edeaf40bd02 $
+ *
+ * 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 COPYRIGHT
+ * OWNER 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+ require_once 'phing/system/io/Writer.php';
+ require_once 'phing/Task.php';
+
+ /**
+ * Extends the Writer class to output messages to Phing's log
+ *
+ * @author Michiel Rook <mrook@php.net>
+ * @version $Id: ccb6c8b930bb3f293074969323467edeaf40bd02 $
+ * @package phing.util
+ */
+ class LogWriter extends Writer
+ {
+ private $task = NULL;
+
+ private $level = NULL;
+
+ /**
+ * Constructs a new LogWriter object
+ */
+ function __construct(Task $task, $level = Project::MSG_INFO)
+ {
+ $this->task = $task;
+ $this->level = $level;
+ }
+
+ /**
+ * @see Writer::write()
+ */
+ function write($buf, $off = null, $len = null)
+ {
+ $lines = explode("\n", $buf);
+
+ foreach ($lines as $line)
+ {
+ if ($line == "")
+ {
+ continue;
+ }
+
+ $this->task->log($line, $this->level);
+ }
+ }
+
+ /**
+ * @see Writer::reset()
+ */
+ function reset()
+ {
+ }
+
+ /**
+ * @see Writer::close()
+ */
+ function close()
+ {
+ }
+
+ /**
+ * @see Writer::open()
+ */
+ function open()
+ {
+ }
+
+ /**
+ * @see Writer::getResource()
+ */
+ function getResource()
+ {
+ return $this->task;
+ }
+ }
+
diff --git a/buildscripts/phing/classes/phing/util/PathTokenizer.php b/buildscripts/phing/classes/phing/util/PathTokenizer.php
new file mode 100644
index 00000000..0a469acf
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/PathTokenizer.php
@@ -0,0 +1,245 @@
+<?php
+/*
+ * $Id: 0ca7f2a419b5cc4285a8f84695c4b680b0aa190b $
+ *
+ * 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 COPYRIGHT
+ * OWNER 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+
+
+include_once 'phing/util/StringHelper.php';
+
+
+
+/**
+
+ * A Path tokenizer takes a path and returns the components that make up
+
+ * that path.
+
+ *
+
+ * The path can use path separators of either ':' or ';' and file separators
+
+ * of either '/' or '\'.
+
+ *
+
+ * @author Hans Lellelid <hans@xmpl.org> (Phing)
+
+ * @author Conor MacNeill (Ant)
+
+ * @author Jeff Tulley <jtulley@novell.com> (Ant)
+
+ * @package phing.util
+
+ */
+
+class PathTokenizer {
+
+
+
+ /**
+
+ * A array of tokens, created by preg_split().
+
+ */
+
+ private $tokens = array();
+
+
+
+ /**
+
+ * A string which stores any path components which have been read ahead
+
+ * due to DOS filesystem compensation.
+
+ * @var string
+
+ */
+
+ private $lookahead;
+
+
+
+ /**
+
+ * Flag to indicate whether or not we are running on a platform with a
+
+ * DOS style filesystem
+
+ * @var boolean
+
+ */
+
+ private $dosStyleFilesystem;
+
+
+
+ /**
+
+ * Constructs a path tokenizer for the specified path.
+
+ *
+
+ * @param path The path to tokenize. Must not be <code>null</code>.
+
+ */
+
+ public function __construct($path) {
+
+ // on Windows and Unix, we can ignore delimiters and still have
+
+ // enough information to tokenize correctly.
+
+ $this->tokens = preg_split("/[;:]/", $path, -1, PREG_SPLIT_NO_EMPTY);
+
+ $this->dosStyleFilesystem = ( PATH_SEPARATOR == ';');
+
+ }
+
+
+
+ /**
+
+ * Tests if there are more path elements available from this tokenizer's
+
+ * path. If this method returns <code>true</code>, then a subsequent call
+
+ * to nextToken will successfully return a token.
+
+ *
+
+ * @return <code>true</code> if and only if there is at least one token
+
+ * in the string after the current position; <code>false</code> otherwise.
+
+ */
+
+ public function hasMoreTokens() {
+
+ if ($this->lookahead !== null) {
+
+ return true;
+
+ }
+
+ return !empty($this->tokens);
+
+ }
+
+
+
+ /**
+
+ * Returns the next path element from this tokenizer.
+
+ *
+
+ * @return the next path element from this tokenizer.
+
+ *
+
+ * @throws Exception if there are no more elements in this tokenizer's path.
+
+ */
+
+ public function nextToken() {
+
+
+
+ if ($this->lookahead !== null) {
+
+ $token = $this->lookahead;
+
+ $this->lookahead = null;
+
+ } else {
+
+ $token = trim(array_shift($this->tokens));
+
+ }
+
+
+
+
+
+ if (strlen($token) === 1 && Character::isLetter($token{0})
+
+ && $this->dosStyleFilesystem
+
+ && !empty($this->tokens)) {
+
+ // we are on a dos style system so this path could be a drive
+
+ // spec. We look at the next token
+
+ $nextToken = trim(array_shift($this->tokens));
+
+ if (StringHelper::startsWith('\\', $nextToken) || StringHelper::startsWith('/', $nextToken)) {
+
+ // we know we are on a DOS style platform and the next path
+
+ // starts with a slash or backslash, so we know this is a
+
+ // drive spec
+
+ $token .= ':' . $nextToken;
+
+ } else {
+
+ // store the token just read for next time
+
+ $this->lookahead = $nextToken;
+
+ }
+
+ }
+
+
+
+ return $token;
+
+ }
+
+
+
+ /**
+
+ * Non StringTokenizer function, that indicates whether the specified path is contained in loaded tokens.
+
+ * We can do this easily because in PHP implimentation we're using arrays.
+
+ * @param string $path path to search for.
+
+ * @return boolean
+
+ */
+
+ public function contains($path) {
+
+ return in_array($path, $this->tokens, true);
+
+ }
+
+
+
+}
+
+
+
diff --git a/buildscripts/phing/classes/phing/util/PearPackageScanner.php b/buildscripts/phing/classes/phing/util/PearPackageScanner.php
new file mode 100644
index 00000000..9071ed37
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/PearPackageScanner.php
@@ -0,0 +1,170 @@
+<?php
+/**
+ * Part of phing, the PHP build tool
+ *
+ * PHP version 5
+ *
+ * @category Util
+ * @package phing.util
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
+ * @version SVN: $Id: e549026313edf53c67f495489f671cf0b71df80d $
+ * @link http://www.phing.info/
+ */
+require_once 'phing/util/DirectoryScanner.php';
+require_once 'PEAR/Config.php';
+
+/**
+ * Scans for files in a PEAR package.
+ *
+ * @category Util
+ * @package phing.util
+ * @author Christian Weiske <cweiske@cweiske.de>
+ * @license LGPL v3 or later http://www.gnu.org/licenses/lgpl.html
+ * @link http://www.phing.info/
+ */
+class PearPackageScanner extends DirectoryScanner
+{
+ protected $packageInfo;
+ protected $role = 'php';
+ protected $config;
+ protected $package;
+ protected $channel = 'pear.php.net';
+
+ /**
+ * Sets the name of the PEAR package to get the files from
+ *
+ * @param string $package Package name without channel
+ *
+ * @return void
+ */
+ public function setPackage($package)
+ {
+ $this->package = $package;
+ }
+
+ /**
+ * Sets the name of the package channel name
+ *
+ * @param string $channel package channel name or alias
+ *
+ * @return void
+ */
+ public function setChannel($channel)
+ {
+ $this->channel = $channel;
+ }
+
+ /**
+ * Sets the full path to the PEAR configuration file
+ *
+ * @param string $config Configuration file
+ *
+ * @return void
+ */
+ public function setConfig($config)
+ {
+ if ($config != '' && !file_exists($config)) {
+ throw new BuildException(
+ 'PEAR configuration file "' . $config . '" does not exist'
+ );
+ }
+
+ $this->config = $config;
+ }
+
+ /**
+ * Sets the role of files that should be included.
+ * Examples are php,doc,script
+ *
+ * @param string $role PEAR file role
+ *
+ * @return void
+ *
+ * @internal
+ * We do not verify the role against a hardcoded list since that
+ * would break packages with additional roles.
+ */
+ public function setRole($role)
+ {
+ if ($role == '') {
+ throw new BuildException('A non-empty role is required');
+ }
+
+ $this->role = $role;
+ }
+
+ /**
+ * Loads the package information.
+ *
+ * @return void
+ *
+ * @uses $packageInfo
+ */
+ protected function init()
+ {
+ if (!$this->packageInfo) {
+ $this->packageInfo = $this->loadPackageInfo();
+ }
+ }
+
+ /**
+ * Loads and returns the PEAR package information.
+ *
+ * @return PEAR_PackageFile_v2 Package information object
+ *
+ * @throws BuildException When the package does not exist
+ */
+ protected function loadPackageInfo()
+ {
+ $cfg = PEAR_Config::singleton($this->config);
+ $reg = $cfg->getRegistry();
+ if (!$reg->packageExists($this->package, $this->channel)) {
+ throw new BuildException(
+ sprintf(
+ 'PEAR package %s/%s does not exist',
+ $this->channel, $this->package
+ )
+ );
+ }
+
+ $packageInfo = $reg->getPackage($this->package, $this->channel);
+ return $packageInfo;
+ }
+
+ /**
+ * Generates the list of included files and directories
+ *
+ * @return boolean True if all went well, false if something was wrong
+ *
+ * @uses $filesIncluded
+ * @uses $filesDeselected
+ * @uses $filesNotIncluded
+ * @uses $filesExcluded
+ * @uses $everythingIncluded
+ * @uses $dirsIncluded
+ * @uses $dirsDeselected
+ * @uses $dirsNotIncluded
+ * @uses $dirsExcluded
+ */
+ public function scan()
+ {
+ $this->init();
+ $list = $this->packageInfo->getFilelist();
+ $found = null;
+ foreach ($list as $file => $att) {
+ if ($att['role'] != $this->role) {
+ continue;
+ }
+ $this->filesIncluded[] = $file;
+ $found = array($file, $att);
+ }
+ if ($found !== null) {
+ list($file, $att) = $found;
+ $this->setBaseDir(substr($att['installed_as'], 0, -strlen($file)));
+ }
+
+ return true;
+ }
+
+}
diff --git a/buildscripts/phing/classes/phing/util/SourceFileScanner.php b/buildscripts/phing/classes/phing/util/SourceFileScanner.php
new file mode 100644
index 00000000..d958a15f
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/SourceFileScanner.php
@@ -0,0 +1,159 @@
+<?php
+/*
+ * $Id: 0555ae8b4e7418c211bd69a066877ab8cf95fbb8 $
+ *
+ * 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 COPYRIGHT
+ * OWNER 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+/**
+ * Utility class that collects the functionality of the various
+ * scanDir methods that have been scattered in several tasks before.
+ *
+ * The only method returns an array of source files. The array is a
+ * subset of the files given as a parameter and holds only those that
+ * are newer than their corresponding target files.
+ * @package phing.util
+ */
+class SourceFileScanner {
+
+ /** Instance of FileUtils */
+ private $fileUtils;
+
+ /** Task this class is working for -- for logging purposes. */
+ private $task;
+
+ /**
+ * @param task The task we should log messages through
+ */
+ function __construct($task) {
+ $this->task = $task;
+ $this->fileUtils = new FileUtils();
+ }
+
+ /**
+ * Restrict the given set of files to those that are newer than
+ * their corresponding target files.
+ *
+ * @param files the original set of files
+ * @param srcDir all files are relative to this directory
+ * @param destDir target files live here. if null file names
+ * returned by the mapper are assumed to be absolute.
+ * @param FilenameMapper knows how to construct a target file names from
+ * source file names.
+ * @param force Boolean that determines if the files should be
+ * forced to be copied.
+ */
+ function restrict(&$files, $srcDir, $destDir, $mapper, $force = false) {
+ $now = time();
+ $targetList = "";
+
+ /*
+ If we're on Windows, we have to munge the time up to 2 secs to
+ be able to check file modification times.
+ (Windows has a max resolution of two secs for modification times)
+ */
+ $osname = strtolower(Phing::getProperty('os.name'));
+
+ // indexOf()
+ $index = ((($res = strpos($osname, 'win')) === false) ? -1 : $res);
+ if ($index >= 0 ) {
+ $now += 2000;
+ }
+
+ $v = array();
+
+ for ($i=0, $size=count($files); $i < $size; $i++) {
+
+ $targets = $mapper->main($files[$i]);
+ if (empty($targets)) {
+ $this->task->log($files[$i]." skipped - don't know how to handle it", Project::MSG_VERBOSE);
+ continue;
+ }
+
+ $src = null;
+ try {
+ if ($srcDir === null) {
+ $src = new PhingFile($files[$i]);
+ } else {
+ $src = $this->fileUtils->resolveFile($srcDir, $files[$i]);
+ }
+
+ if ($src->lastModified() > $now) {
+ $this->task->log("Warning: ".$files[$i]." modified in the future (".$src->lastModified()." > ".$now.")", Project::MSG_WARN);
+ }
+ } catch (IOException $ioe) {
+ $this->task->log("Unable to read file ".$files[$i]." (skipping): " . $ioe->getMessage());
+ continue;
+ }
+
+ $added = false;
+ $targetList = "";
+
+ for ($j=0,$_j=count($targets); (!$added && $j < $_j); $j++) {
+
+ $dest = null;
+ if ($destDir === null) {
+ $dest = new PhingFile($targets[$j]);
+ } else {
+ $dest = $this->fileUtils->resolveFile($destDir, $targets[$j]);
+ }
+
+ if (!$dest->exists()) {
+ $this->task->log($files[$i]." added as " . $dest->__toString() . " doesn't exist.", Project::MSG_VERBOSE);
+ $v[] =$files[$i];
+ $added = true;
+ } elseif ($src->lastModified() > $dest->lastModified()) {
+ $this->task->log($files[$i]." added as " . $dest->__toString() . " is outdated.", Project::MSG_VERBOSE );
+ $v[]=$files[$i];
+ $added = true;
+ } elseif ($force === true) {
+ $this->task->log($files[$i]." added as " . $dest->__toString() . " is forced to be overwritten.", Project::MSG_VERBOSE );
+ $v[]=$files[$i];
+ $added = true;
+ } else {
+ if (strlen($targetList) > 0) {
+ $targetList .= ", ";
+ }
+ $targetList .= $dest->getAbsolutePath();
+ }
+ }
+
+ if (!$added) {
+ $this->task->log($files[$i]." omitted as ".$targetList." ".(count($targets) === 1 ? " is " : " are ")."up to date.", Project::MSG_VERBOSE);
+ }
+
+ }
+ $result = array();
+ $result = $v;
+ return $result;
+ }
+
+ /**
+ * Convenience layer on top of restrict that returns the source
+ * files as PhingFile objects (containing absolute paths if srcDir is
+ * absolute).
+ */
+ function restrictAsFiles(&$files, &$srcDir, &$destDir, &$mapper) {
+ $res = $this->restrict($files, $srcDir, $destDir, $mapper);
+ $result = array();
+ for ($i=0; $i<count($res); $i++) {
+ $result[$i] = new PhingFile($srcDir, $res[$i]);
+ }
+ return $result;
+ }
+}
+
diff --git a/buildscripts/phing/classes/phing/util/StringHelper.php b/buildscripts/phing/classes/phing/util/StringHelper.php
new file mode 100644
index 00000000..31d0b0e9
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/StringHelper.php
@@ -0,0 +1,208 @@
+<?php
+
+/**
+ * String helper utility class.
+ *
+ * This class includes some Java-like functions for parsing strings,
+ * as well as some functions for getting qualifiers / unqualifying phing-style
+ * classpaths. (e.g. "phing.util.StringHelper").
+ *
+ * @author Hans Lellelid <hans@xmpl.org>
+ * @package phing.system.util
+ */
+class StringHelper {
+
+ private static $TRUE_VALUES = array("on", "true", "t", "yes");
+ private static $FALSE_VALUES = array("off", "false", "f", "no");
+
+ /**
+ * Replaces identifier tokens with corresponding text values in passed string.
+ *
+ * @param array $strings Array of strings to multiply. (If string is passed, will convert to array)
+ * @param array $tokens The tokens to search for.
+ * @param array $replacements The values with which to replace found tokens.
+ * @return string
+ */
+ public static function multiply($strings, $tokens, $replacements) {
+ $strings = (array) $strings;
+ $results = array();
+ foreach ($strings as $string) {
+ $results[] = str_replace($tokens, $replacements, $string);
+ }
+ return $results;
+ }
+
+ /**
+ * Remove qualification to name.
+ * E.g. eg.Cat -> Cat
+ * @param string $qualifiedName
+ * @param string $separator Character used to separate.
+ */
+ public static function unqualify($qualifiedName, $separator = '.') {
+ // if false, then will be 0
+ $pos = strrpos($qualifiedName, $separator);
+ if ($pos === false) {
+ return $qualifiedName; // there is no '.' in the qualifed name
+ } else {
+ return substr($qualifiedName, $pos + 1); // start just after '.'
+ }
+ }
+
+ /**
+ * Converts a string to an indexed array of chars
+ * There's really no reason for this to be used in PHP, since strings
+ * are all accessible using the $string{0} notation.
+ * @param string $string
+ * @return array
+ * @deprecated
+ */
+ public static function toCharArray($str) {
+ $ret=array();
+ $len=strlen($str);
+ for ($i=0; $i < $len; $i++) {
+ $ret[] = $str{$i};
+ }
+ return $ret;
+ }
+
+ /**
+ * Get the qualifier part of a qualified name.
+ * E.g. eg.Cat -> eg
+ * @return string
+ */
+ public static function qualifier($qualifiedName, $seperator = '.') {
+ $pos = strrchr($qualifiedName, $seperator);
+ if ($pos === false) {
+ return '';
+ } else {
+ return substr($qualifiedName, 0, $pos);
+ }
+ }
+
+ /**
+ * @param array $columns String[]
+ * @param string $prefix
+ * @return array String[]
+ */
+ public static function prefix( $columns, $prefix) {
+ if ($prefix == null) return $columns;
+ $qualified = array();
+ foreach($columns as $key => $column) {
+ $qualified[$key] = $prefix . $column;
+ }
+ return $qualified;
+ }
+
+ /**
+ *
+ * @return string
+ */
+ public static function root($qualifiedName, $separator = '.') {
+ $loc = strpos($qualifiedName, $separator);
+ return ($loc === false) ? $qualifiedName : substr($qualifiedName, 0, $loc);
+ }
+
+ /**
+ * @return int
+ */
+ public static function hashCode($string) {
+ return crc32($string);
+ }
+
+ /**
+ * @return boolean
+ */
+ public static function booleanValue($s) {
+ if (is_bool($s)) {
+ return $s; // it's already boolean (not a string)
+ }
+ // otherwise assume it's something like "true" or "t"
+ $trimmed = strtolower(trim($s));
+ return (boolean) in_array($trimmed, self::$TRUE_VALUES);
+ }
+
+ /** tests if a string is a representative of a boolean */
+ public static function isBoolean($s) {
+
+ if (is_bool($s)) {
+ return true; // it already is boolean
+ }
+
+ if ($s === "" || $s === null || !is_string($s)) {
+ return false; // not a valid string for testing
+ }
+
+ $test = trim(strtolower($s));
+ return (boolean) in_array($test, array_merge(self::$FALSE_VALUES, self::$TRUE_VALUES));
+ }
+
+ /**
+ * Creates a key based on any number of passed params.
+ * @return string
+ */
+ public static function key() {
+ $args = func_get_args();
+ return serialize($args);
+ }
+
+ /** tests if a string starts with a given string */
+ public static function startsWith($check, $string) {
+ if ($check === "" || $check === $string) {
+ return true;
+ } else {
+ return (strpos($string, $check) === 0) ? true : false;
+ }
+ }
+
+ /** tests if a string ends with a given string */
+ public static function endsWith($check, $string) {
+ if ($check === "" || $check === $string) {
+ return true;
+ } else {
+ return (strpos(strrev($string), strrev($check)) === 0) ? true : false;
+ }
+ }
+
+ /**
+ * a natural way of getting a subtring, php's circular string buffer and strange
+ * return values suck if you want to program strict as of C or friends
+ */
+ public static function substring($string, $startpos, $endpos = -1) {
+ $len = strlen($string);
+ $endpos = (int) (($endpos === -1) ? $len-1 : $endpos);
+ if ($startpos > $len-1 || $startpos < 0) {
+ trigger_error("substring(), Startindex out of bounds must be 0<n<$len", E_USER_ERROR);
+ }
+ if ($endpos > $len-1 || $endpos < $startpos) {
+ trigger_error("substring(), Endindex out of bounds must be $startpos<n<".($len-1), E_USER_ERROR);
+ }
+ if ($startpos === $endpos) {
+ return (string) $string{$startpos};
+ } else {
+ $len = $endpos-$startpos;
+ }
+ return substr($string, $startpos, $len+1);
+ }
+
+ /**
+ * Does the value correspond to a slot variable?
+ * @param string $value
+ */
+ public static function isSlotVar($value) {
+ $value = trim($value);
+ if ($value === "") return false;
+ return preg_match('/^%\{([\w\.\-]+)\}$/', $value);
+ }
+
+ /**
+ * Extracts the variable name for a slot var in the format %{task.current_file}
+ * @param string $var The var from build file.
+ * @return string Extracted name part.
+ */
+ public static function slotVar($var) {
+ return trim($var, '%{} ');
+ }
+
+}
+
+
diff --git a/buildscripts/phing/classes/phing/util/regexp/PregEngine.php b/buildscripts/phing/classes/phing/util/regexp/PregEngine.php
new file mode 100644
index 00000000..76cf56b3
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/regexp/PregEngine.php
@@ -0,0 +1,167 @@
+<?php
+/*
+ * $Id: 94607411e16d4c9091369ff4a65ea8f44bde8781 $
+ *
+ * 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 COPYRIGHT
+ * OWNER 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+require_once 'phing/util/regexp/RegexpEngine.php';
+
+/**
+ * PREG Regexp Engine.
+ * Implements a regexp engine using PHP's preg_match(), preg_match_all(), and preg_replace() functions.
+ *
+ * @author hans lellelid, hans@velum.net
+ * @package phing.util.regexp
+ */
+class PregEngine implements RegexpEngine {
+
+ /**
+ * Set to null by default to distinguish between false and not set
+ * @var boolean
+ */
+ private $ignoreCase = null;
+
+ /**
+ * Set to null by default to distinguish between false and not set
+ * @var boolean
+ */
+ private $multiline = null;
+
+ /**
+ * Pattern modifiers
+ * @link http://php.net/manual/en/reference.pcre.pattern.modifiers.php
+ * @var string
+ */
+ private $modifiers = null;
+
+ /**
+ * Sets pattern modifiers for regex engine
+ *
+ * @param string $mods Modifiers to be applied to a given regex
+ * @return void
+ */
+ public function setModifiers($mods) {
+ $this->modifiers = (string)$mods;
+ }
+
+ /**
+ * Gets pattern modifiers.
+ * @return string
+ */
+ public function getModifiers() {
+ $mods = $this->modifiers;
+ if($this->getIgnoreCase()) {
+ $mods .= 'i';
+ } elseif($this->getIgnoreCase() === false) {
+ $mods = str_replace('i', '', $mods);
+ }
+ if($this->getMultiline()) {
+ $mods .= 's';
+ } elseif($this->getMultiline() === false) {
+ $mods = str_replace('s', '', $mods);
+ }
+ // filter out duplicates
+ $mods = preg_split('//', $mods, -1, PREG_SPLIT_NO_EMPTY);
+ $mods = implode('', array_unique($mods));
+ return $mods;
+ }
+
+ /**
+ * Sets whether or not regex operation is case sensitive.
+ * @param boolean $bit
+ * @return void
+ */
+ function setIgnoreCase($bit) {
+ $this->ignoreCase = (boolean) $bit;
+ }
+
+ /**
+ * Gets whether or not regex operation is case sensitive.
+ * @return boolean
+ */
+ function getIgnoreCase() {
+ return $this->ignoreCase;
+ }
+
+ /**
+ * Sets whether regexp should be applied in multiline mode.
+ * @param boolean $bit
+ */
+ function setMultiline($bit) {
+ $this->multiline = $bit;
+ }
+
+ /**
+ * Gets whether regexp is to be applied in multiline mode.
+ * @return boolean
+ */
+ function getMultiline() {
+ return $this->multiline;
+ }
+
+ /**
+ * The pattern needs to be converted into PREG style -- which includes adding expression delims & any flags, etc.
+ * @param string $pattern
+ * @return string prepared pattern.
+ */
+ private function preparePattern($pattern)
+ {
+ // Use backquotes since hardly ever found in a regexp pattern, avoids using preg_quote
+ return '`'.$pattern.'`' . $this->getModifiers();
+ }
+
+ /**
+ * Matches pattern against source string and sets the matches array.
+ * @param string $pattern The regex pattern to match.
+ * @param string $source The source string.
+ * @param array $matches The array in which to store matches.
+ * @return boolean Success of matching operation.
+ */
+ function match($pattern, $source, &$matches) {
+ return preg_match($this->preparePattern($pattern), $source, $matches);
+ }
+
+ /**
+ * Matches all patterns in source string and sets the matches array.
+ * @param string $pattern The regex pattern to match.
+ * @param string $source The source string.
+ * @param array $matches The array in which to store matches.
+ * @return boolean Success of matching operation.
+ */
+ function matchAll($pattern, $source, &$matches) {
+ return preg_match_all($this->preparePattern($pattern), $source, $matches);
+ }
+
+ /**
+ * Replaces $pattern with $replace in $source string.
+ * References to \1 group matches will be replaced with more preg-friendly
+ * $1.
+ * @param string $pattern The regex pattern to match.
+ * @param string $replace The string with which to replace matches.
+ * @param string $source The source string.
+ * @return string The replaced source string.
+ */
+ function replace($pattern, $replace, $source) {
+ // convert \1 -> $1, because we want to use the more generic \1 in the XML
+ // but PREG prefers $1 syntax.
+ $replace = preg_replace('/\\\(\d+)/', '\$$1', $replace);
+ return preg_replace($this->preparePattern($pattern), $replace, $source);
+ }
+
+}
+
diff --git a/buildscripts/phing/classes/phing/util/regexp/Regexp.php b/buildscripts/phing/classes/phing/util/regexp/Regexp.php
new file mode 100755
index 00000000..7188997e
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/regexp/Regexp.php
@@ -0,0 +1,203 @@
+<?php
+/*
+ * $Id: b669eb9f2dd8533cba67b2058b7cbc2f558bdeae $
+ *
+ * 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 COPYRIGHT
+ * OWNER 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+/**
+ * A factory class for regex functions.
+ * @author Hans Lellelid <hans@xmpl.org>
+ * @package phing.util.regexp
+ * @version $Id$
+ */
+class Regexp {
+
+ /**
+ * Matching groups found.
+ * @var array
+ */
+ private $groups = array();
+
+ /**
+ * Pattern to match.
+ * @var string
+ */
+ private $pattern;
+
+ /**
+ * Replacement pattern.
+ * @var string
+ */
+ private $replace;
+
+ /**
+ * The regex engine -- e.g. 'preg' or 'ereg';
+ * @var RegexpEngine
+ */
+ private $engine;
+
+ /**
+ * Constructor sets the regex engine to use (preg by default).
+ * @param string $_engineType The regex engine to use.
+ */
+ function __construct($engineType='preg') {
+ if ($engineType == 'preg') {
+ include_once 'phing/util/regexp/PregEngine.php';
+ $this->engine = new PregEngine();
+ } elseif ($engineType == 'ereg') {
+ include_once 'phing/util/regexp/EregEngine.php';
+ $this->engine = new EregEngine();
+ } else {
+ throw new BuildException("Invalid engine type for Regexp: " . $engineType);
+ }
+ }
+
+ /**
+ * Sets pattern to use for matching.
+ * @param string $pat The pattern to match on.
+ * @return void
+ */
+ public function setPattern($pat) {
+ $this->pattern = (string) $pat;
+ }
+
+
+ /**
+ * Gets pattern to use for matching.
+ * @return string The pattern to match on.
+ */
+ public function getPattern() {
+ return $this->pattern;
+ }
+
+ /**
+ * Sets replacement string.
+ * @param string $rep The pattern to replace matches with.
+ * @return void
+ */
+ public function setReplace($rep) {
+ $this->replace = (string) $rep;
+ }
+
+ /**
+ * Gets replacement string.
+ * @return string The pattern to replace matches with.
+ */
+ public function getReplace() {
+ return $this->replace;
+ }
+
+ /**
+ * Performs match of specified pattern against $subject.
+ * @param string $subject The subject, on which to perform matches.
+ * @return boolean Whether or not pattern matches subject string passed.
+ */
+ public function matches($subject) {
+ if($this->pattern === null) {
+ throw new Exception("No pattern specified for regexp match().");
+ }
+ return $this->engine->match($this->pattern, $subject, $this->groups);
+ }
+
+ /**
+ * Performs replacement of specified pattern and replacement strings.
+ * @param string $subject Text on which to perform replacement.
+ * @return string subject after replacement has been performed.
+ */
+ public function replace($subject) {
+ if ($this->pattern === null || $this->replace === null) {
+ throw new Exception("Missing pattern or replacement string regexp replace().");
+ }
+ return $this->engine->replace($this->pattern, $this->replace, $subject);
+ }
+
+ /**
+ * Get array of matched groups.
+ * @return array Matched groups
+ */
+ function getGroups() {
+ return $this->groups;
+ }
+
+ /**
+ * Get specific matched group.
+ * @param integer $idx
+ * @return string specified group or NULL if group is not set.
+ */
+ function getGroup($idx) {
+ if (!isset($this->groups[$idx])) {
+ return null;
+ }
+ return $this->groups[$idx];
+ }
+
+ /**
+ * Sets pattern modifiers for regex engine
+ *
+ * @param string $mods Modifiers to be applied to a given regex
+ * @return void
+ */
+ public function setModifiers($mods) {
+ $this->engine->setModifiers($mods);
+ }
+
+ /**
+ * Gets pattern modifiers.
+ * Subsequent call to engines getModifiers() filters out duplicates
+ * i.e. if i is provided in $mods, and setIgnoreCase(true), "i"
+ * modifier would be included only once
+ * @return string
+ */
+ public function getModifiers() {
+ return $this->engine->getModifiers();
+ }
+
+ /**
+ * Sets whether the regexp matching is case insensitive.
+ * (default is false -- i.e. case sensisitive)
+ * @param boolean $bit
+ */
+ function setIgnoreCase($bit) {
+ $this->engine->setIgnoreCase($bit);
+ }
+
+ /**
+ * Gets whether the regexp matching is case insensitive.
+ * @return boolean
+ */
+ function getIgnoreCase() {
+ return $this->engine->getIgnoreCase();
+ }
+
+ /**
+ * Sets whether regexp should be applied in multiline mode.
+ * @param boolean $bit
+ */
+ function setMultiline($bit) {
+ $this->engine->setMultiline($bit);
+ }
+
+ /**
+ * Gets whether regexp is to be applied in multiline mode.
+ * @return boolean
+ */
+ function getMultiline() {
+ return $this->engine->getMultiline();
+ }
+}
+
diff --git a/buildscripts/phing/classes/phing/util/regexp/RegexpEngine.php b/buildscripts/phing/classes/phing/util/regexp/RegexpEngine.php
new file mode 100755
index 00000000..3eb8c408
--- /dev/null
+++ b/buildscripts/phing/classes/phing/util/regexp/RegexpEngine.php
@@ -0,0 +1,73 @@
+<?php
+/*
+ * $Id: 5e2886f3fae60fff1fd142e79717a3a7a4555772 $
+ *
+ * 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 COPYRIGHT
+ * OWNER 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information please see
+ * <http://phing.info>.
+ */
+
+/**
+ * Contains some shared attributes and methods -- and some abstract methods with
+ * engine-specific implementations that sub-classes must override.
+ *
+ * @author Hans Lellelid <hans@velum.net>
+ * @package phing.util.regexp
+ * @version $Id$
+ */
+interface RegexpEngine {
+
+ /**
+ * Sets whether or not regex operation should ingore case.
+ * @param boolean $bit
+ * @return void
+ */
+ public function setIgnoreCase($bit);
+
+ /**
+ * Returns status of ignore case flag.
+ * @return boolean
+ */
+ public function getIgnoreCase();
+
+ /**
+ * Matches pattern against source string and sets the matches array.
+ * @param string $pattern The regex pattern to match.
+ * @param string $source The source string.
+ * @param array $matches The array in which to store matches.
+ * @return boolean Success of matching operation.
+ */
+ function match($pattern, $source, &$matches);
+
+ /**
+ * Matches all patterns in source string and sets the matches array.
+ * @param string $pattern The regex pattern to match.
+ * @param string $source The source string.
+ * @param array $matches The array in which to store matches.
+ * @return boolean Success of matching operation.
+ */
+ function matchAll($pattern, $source, &$matches);
+
+ /**
+ * Replaces $pattern with $replace in $source string.
+ * @param string $pattern The regex pattern to match.
+ * @param string $replace The string with which to replace matches.
+ * @param string $source The source string.
+ * @return string The replaced source string.
+ */
+ function replace($pattern, $replace, $source);
+
+}
+