diff options
Diffstat (limited to 'buildscripts/phing/classes/phing/system/io/Win32FileSystem.php')
-rw-r--r-- | buildscripts/phing/classes/phing/system/io/Win32FileSystem.php | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/buildscripts/phing/classes/phing/system/io/Win32FileSystem.php b/buildscripts/phing/classes/phing/system/io/Win32FileSystem.php new file mode 100644 index 00000000..c32c21ff --- /dev/null +++ b/buildscripts/phing/classes/phing/system/io/Win32FileSystem.php @@ -0,0 +1,477 @@ +<?php +/* + * $Id: Win32FileSystem.php,v 1.10 2005/05/26 13:10:52 mrook Exp $ + * + * 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/io/FileSystem.php'; + +/** + * @package phing.system.io + */ +class Win32FileSystem extends FileSystem { + + protected $slash; + protected $altSlash; + protected $semicolon; + + private static $driveDirCache = array(); + + function __construct() { + $this->slash = self::getSeparator(); + $this->semicolon = self::getPathSeparator(); + $this->altSlash = ($this->slash === '\\') ? '/' : '\\'; + } + + function isSlash($c) { + return ($c == '\\') || ($c == '/'); + } + + function isLetter($c) { + return ((ord($c) >= ord('a')) && (ord($c) <= ord('z'))) + || ((ord($c) >= ord('A')) && (ord($c) <= ord('Z'))); + } + + function slashify($p) { + if ((strlen($p) > 0) && ($p{0} != $this->slash)) { + return $this->slash.$p; + } + else { + return $p; + } + } + + /* -- Normalization and construction -- */ + + function getSeparator() { + // the ascii value of is the \ + return chr(92); + } + + function getPathSeparator() { + return ';'; + } + + /** + * A normal Win32 pathname contains no duplicate slashes, except possibly + * for a UNC prefix, and does not end with a slash. It may be the empty + * string. Normalized Win32 pathnames have the convenient property that + * the length of the prefix almost uniquely identifies the type of the path + * and whether it is absolute or relative: + * + * 0 relative to both drive and directory + * 1 drive-relative (begins with '\\') + * 2 absolute UNC (if first char is '\\'), else directory-relative (has form "z:foo") + * 3 absolute local pathname (begins with "z:\\") + */ + function normalizePrefix($strPath, $len, $sb) { + $src = 0; + while (($src < $len) && $this->isSlash($strPath{$src})) { + $src++; + } + $c = ""; + if (($len - $src >= 2) + && $this->isLetter($c = $strPath{$src}) + && $strPath{$src + 1} === ':') { + /* Remove leading slashes if followed by drive specifier. + * This hack is necessary to support file URLs containing drive + * specifiers (e.g., "file://c:/path"). As a side effect, + * "/c:/path" can be used as an alternative to "c:/path". */ + $sb .= $c; + $sb .= ':'; + $src += 2; + } + else { + $src = 0; + if (($len >= 2) + && $this->isSlash($strPath{0}) + && $this->isSlash($strPath{1})) { + /* UNC pathname: Retain first slash; leave src pointed at + * second slash so that further slashes will be collapsed + * into the second slash. The result will be a pathname + * beginning with "\\\\" followed (most likely) by a host + * name. */ + $src = 1; + $sb.=$this->slash; + } + } + return $src; + } + + /** Normalize the given pathname, whose length is len, starting at the given + offset; everything before this offset is already normal. */ + protected function normalizer($strPath, $len, $offset) { + if ($len == 0) { + return $strPath; + } + if ($offset < 3) { + $offset = 0; //Avoid fencepost cases with UNC pathnames + } + $src = 0; + $slash = $this->slash; + $sb = ""; + + if ($offset == 0) { + // Complete normalization, including prefix + $src = $this->normalizePrefix($strPath, $len, $sb); + } else { + // Partial normalization + $src = $offset; + $sb .= substr($strPath, 0, $offset); + } + + // Remove redundant slashes from the remainder of the path, forcing all + // slashes into the preferred slash + while ($src < $len) { + $c = $strPath{$src++}; + if ($this->isSlash($c)) { + while (($src < $len) && $this->isSlash($strPath{$src})) { + $src++; + } + if ($src === $len) { + /* Check for trailing separator */ + $sn = (int) strlen($sb); + if (($sn == 2) && ($sb{1} === ':')) { + // "z:\\" + $sb .= $slash; + break; + } + if ($sn === 0) { + // "\\" + $sb .= $slash; + break; + } + if (($sn === 1) && ($this->isSlash($sb{0}))) { + /* "\\\\" is not collapsed to "\\" because "\\\\" marks + the beginning of a UNC pathname. Even though it is + not, by itself, a valid UNC pathname, we leave it as + is in order to be consistent with the win32 APIs, + which treat this case as an invalid UNC pathname + rather than as an alias for the root directory of + the current drive. */ + $sb .= $slash; + break; + } + // Path does not denote a root directory, so do not append + // trailing slash + break; + } else { + $sb .= $slash; + } + } else { + $sb.=$c; + } + } + $rv = (string) $sb; + return $rv; + } + + /** + * Check that the given pathname is normal. If not, invoke the real + * normalizer on the part of the pathname that requires normalization. + * This way we iterate through the whole pathname string only once. + * @param string $strPath + * @return string + */ + function normalize($strPath) { + $n = strlen($strPath); + $slash = $this->slash; + $altSlash = $this->altSlash; + $prev = 0; + for ($i = 0; $i < $n; $i++) { + $c = $strPath{$i}; + if ($c === $altSlash) { + return $this->normalizer($strPath, $n, ($prev === $slash) ? $i - 1 : $i); + } + if (($c === $slash) && ($prev === $slash) && ($i > 1)) { + return $this->normalizer($strPath, $n, $i - 1); + } + if (($c === ':') && ($i > 1)) { + return $this->normalizer($strPath, $n, 0); + } + $prev = $c; + } + if ($prev === $slash) { + return $this->normalizer($strPath, $n, $n - 1); + } + return $strPath; + } + + function prefixLength($strPath) { + $path = (string) $strPath; + $slash = (string) $this->slash; + $n = (int) strlen($path); + if ($n === 0) { + return 0; + } + $c0 = $path{0}; + $c1 = ($n > 1) ? $path{1} : + 0; + if ($c0 === $slash) { + if ($c1 === $slash) { + return 2; // absolute UNC pathname "\\\\foo" + } + return 1; // drive-relative "\\foo" + } + + if ($this->isLetter($c0) && ($c1 === ':')) { + if (($n > 2) && ($path{2}) === $slash) { + return 3; // Absolute local pathname "z:\\foo" */ + } + return 2; // Directory-relative "z:foo" + } + return 0; // Completely relative + } + + function resolve($parent, $child) { + $parent = (string) $parent; + $child = (string) $child; + $slash = (string) $this->slash; + + $pn = (int) strlen($parent); + if ($pn === 0) { + return $child; + } + $cn = (int) strlen($child); + if ($cn === 0) { + return $parent; + } + + $c = $child; + if (($cn > 1) && ($c{0} === $slash)) { + if ($c{1} === $slash) { + // drop prefix when child is a UNC pathname + $c = substr($c, 2); + } + else { + //Drop prefix when child is drive-relative */ + $c = substr($c, 1); + } + } + + $p = $parent; + if ($p{$pn - 1} === $slash) { + $p = substr($p, 0, $pn - 1); + } + return $p.$this->slashify($c); + } + + function getDefaultParent() { + return (string) ("".$this->slash); + } + + function fromURIPath($strPath) { + $p = (string) $strPath; + if ((strlen($p) > 2) && ($p{2} === ':')) { + + // "/c:/foo" --> "c:/foo" + $p = substr($p,1); + + // "c:/foo/" --> "c:/foo", but "c:/" --> "c:/" + if ((strlen($p) > 3) && StringHelper::endsWith('/', $p)) { + $p = substr($p, 0, strlen($p) - 1); + } + } elseif ((strlen($p) > 1) && StringHelper::endsWith('/', $p)) { + // "/foo/" --> "/foo" + $p = substr($p, 0, strlen($p) - 1); + } + return (string) $p; + } + + + /* -- Path operations -- */ + + function isAbsolute(PhingFile $f) { + $pl = (int) $f->getPrefixLength(); + $p = (string) $f->getPath(); + return ((($pl === 2) && ($p{0} === $this->slash)) || ($pl === 3) || ($pl === 1 && $p{0} === $this->slash)); + } + + /** private */ + function _driveIndex($d) { + $d = (string) $d{0}; + if ((ord($d) >= ord('a')) && (ord($d) <= ord('z'))) { + return ord($d) - ord('a'); + } + if ((ord($d) >= ord('A')) && (ord($d) <= ord('Z'))) { + return ord($d) - ord('A'); + } + return -1; + } + + /** private */ + function _getDriveDirectory($drive) { + $drive = (string) $drive{0}; + $i = (int) $this->_driveIndex($drive); + if ($i < 0) { + return null; + } + + $s = (isset(self::$driveDirCache[$i]) ? self::$driveDirCache[$i] : null); + + if ($s !== null) { + return $s; + } + + $s = $this->_getDriveDirectory($i + 1); + self::$driveDirCache[$i] = $s; + return $s; + } + + function _getUserPath() { + //For both compatibility and security, we must look this up every time + return (string) $this->normalize(Phing::getProperty("user.dir")); + } + + function _getDrive($path) { + $path = (string) $path; + $pl = $this->prefixLength($path); + return ($pl === 3) ? substr($path, 0, 2) : null; + } + + function resolveFile(PhingFile $f) { + $path = $f->getPath(); + $pl = (int) $f->getPrefixLength(); + + if (($pl === 2) && ($path{0} === $this->slash)) { + return path; // UNC + } + + if ($pl === 3) { + return $path; // Absolute local + } + + if ($pl === 0) { + return (string) ($this->_getUserPath().$this->slashify($path)); //Completely relative + } + + if ($pl === 1) { // Drive-relative + $up = (string) $this->_getUserPath(); + $ud = (string) $this->_getDrive($up); + if ($ud !== null) { + return (string) $ud.$path; + } + return (string) $up.$path; //User dir is a UNC path + } + + if ($pl === 2) { // Directory-relative + $up = (string) $this->_getUserPath(); + $ud = (string) $this->_getDrive($up); + if (($ud !== null) && StringHelper::startsWith($ud, $path)) { + return (string) ($up . $this->slashify(substr($path,2))); + } + $drive = (string) $path{0}; + $dir = (string) $this->_getDriveDirectory($drive); + + $np = (string) ""; + if ($dir !== null) { + /* When resolving a directory-relative path that refers to a + drive other than the current drive, insist that the caller + have read permission on the result */ + $p = (string) $drive . (':'.$dir.$this->slashify(substr($path,2))); + + if (!$this->checkAccess($p, false)) { + // FIXME + // throw security error + die("Can't resolve path $p"); + } + return $p; + } + return (string) $drive.':'.$this->slashify(substr($path,2)); //fake it + } + + throw new Exception("Unresolvable path: " . $path); + } + + /* -- most of the following is mapped to the functions mapped th php natives in FileSystem */ + + /* -- Attribute accessors -- */ + + function setReadOnly($f) { + // dunno how to do this on win + throw new Exception("WIN32FileSystem doesn't support read-only yet."); + } + + /* -- Filesystem interface -- */ + + protected function _access($path) { + if (!$this->checkAccess($path, false)) { + throw new Exception("Can't resolve path $p"); + } + return true; + } + + function _nativeListRoots() { + // FIXME + } + + function listRoots() { + $ds = _nativeListRoots(); + $n = 0; + for ($i = 0; $i < 26; $i++) { + if ((($ds >> $i) & 1) !== 0) { + if (!$this->access((string)( chr(ord('A') + $i) . ':' . $this->slash))) { + $ds &= ~(1 << $i); + } else { + $n++; + } + } + } + $fs = array(); + $j = (int) 0; + $slash = (string) $this->slash; + for ($i = 0; $i < 26; $i++) { + if ((($ds >> $i) & 1) !== 0) { + $fs[$j++] = new PhingFile(chr(ord('A') + $i) . ':' . $this->slash); + } + } + return $fs; + } + + /* -- Basic infrastructure -- */ + + /** compares file paths lexicographically */ + function compare(PhingFile $f1, PhingFile $f2) { + $f1Path = $f1->getPath(); + $f2Path = $f2->getPath(); + return (boolean) strcasecmp((string) $f1Path, (string) $f2Path); + } + + + /** + * returns the contents of a directory in an array + */ + function lister($f) { + $dir = @opendir($f->getAbsolutePath()); + if (!$dir) { + throw new Exception("Can't open directory " . $f->__toString()); + } + $vv = array(); + while (($file = @readdir($dir)) !== false) { + if ($file == "." || $file == "..") { + continue; + } + $vv[] = (string) $file; + } + @closedir($dir); + return $vv; + } + +} + +?> |