123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- <?php
- /*
- * $Id: Win32FileSystem.php 905 2010-10-05 16:28:03Z mrook $
- *
- * 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;
- }
-
- }
|