Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/*
3
 *  $Id: Win32FileSystem.php 325 2007-12-20 15:44:58Z hans $
4
 *
5
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
 *
17
 * This software consists of voluntary contributions made by many individuals
18
 * and is licensed under the LGPL. For more information please see
19
 * <http://phing.info>.
20
 */
21
 
22
include_once 'phing/system/io/FileSystem.php';
23
 
24
/**
25
 *  @package   phing.system.io
26
 */
27
class Win32FileSystem extends FileSystem {
28
 
29
    protected $slash;
30
    protected $altSlash;
31
    protected $semicolon;
32
 
33
    private static $driveDirCache = array();
34
 
35
    function __construct() {
36
        $this->slash = self::getSeparator();
37
        $this->semicolon = self::getPathSeparator();
38
        $this->altSlash = ($this->slash === '\\') ? '/' : '\\';
39
    }
40
 
41
    function isSlash($c) {
42
        return ($c == '\\') || ($c == '/');
43
    }
44
 
45
    function isLetter($c) {
46
        return ((ord($c) >= ord('a')) && (ord($c) <= ord('z')))
47
               || ((ord($c) >= ord('A')) && (ord($c) <= ord('Z')));
48
    }
49
 
50
    function slashify($p) {
51
        if ((strlen($p) > 0) && ($p{0} != $this->slash)) {
52
            return $this->slash.$p;
53
        }
54
        else {
55
            return $p;
56
        }
57
    }
58
 
59
    /* -- Normalization and construction -- */
60
 
61
    function getSeparator() {
62
        // the ascii value of is the \
63
        return chr(92);
64
    }
65
 
66
    function getPathSeparator() {
67
        return ';';
68
    }
69
 
70
    /**
71
     * A normal Win32 pathname contains no duplicate slashes, except possibly
72
     * for a UNC prefix, and does not end with a slash.  It may be the empty
73
     * string.  Normalized Win32 pathnames have the convenient property that
74
     * the length of the prefix almost uniquely identifies the type of the path
75
     * and whether it is absolute or relative:
76
     *
77
     *    0  relative to both drive and directory
78
     *    1  drive-relative (begins with '\\')
79
     *    2  absolute UNC (if first char is '\\'), else directory-relative (has form "z:foo")
80
     *    3  absolute local pathname (begins with "z:\\")
81
     */
82
    function normalizePrefix($strPath, $len, &$sb) {
83
        $src = 0;
84
        while (($src < $len) && $this->isSlash($strPath{$src})) {
85
            $src++;
86
        }
87
        $c = "";
88
        if (($len - $src >= 2)
89
                && $this->isLetter($c = $strPath{$src})
90
                && $strPath{$src + 1} === ':') {
91
            /* Remove leading slashes if followed by drive specifier.
92
             * This hack is necessary to support file URLs containing drive
93
             * specifiers (e.g., "file://c:/path").  As a side effect,
94
             * "/c:/path" can be used as an alternative to "c:/path". */
95
            $sb .= $c;
96
            $sb .= ':';
97
            $src += 2;
98
        }
99
        else {
100
            $src = 0;
101
            if (($len >= 2)
102
                    && $this->isSlash($strPath{0})
103
                    && $this->isSlash($strPath{1})) {
104
                /* UNC pathname: Retain first slash; leave src pointed at
105
                 * second slash so that further slashes will be collapsed
106
                 * into the second slash.  The result will be a pathname
107
                 * beginning with "\\\\" followed (most likely) by a host
108
                 * name. */
109
                $src = 1;
110
                $sb.=$this->slash;
111
            }
112
        }
113
        return $src;
114
    }
115
 
116
    /** Normalize the given pathname, whose length is len, starting at the given
117
       offset; everything before this offset is already normal. */
118
    protected function normalizer($strPath, $len, $offset) {
119
        if ($len == 0) {
120
            return $strPath;
121
        }
122
        if ($offset < 3) {
123
            $offset = 0;    //Avoid fencepost cases with UNC pathnames
124
        }
125
        $src = 0;
126
        $slash = $this->slash;
127
        $sb = "";
128
 
129
        if ($offset == 0) {
130
            // Complete normalization, including prefix
131
            $src = $this->normalizePrefix($strPath, $len, $sb);
132
        } else {
133
            // Partial normalization
134
            $src = $offset;
135
            $sb .= substr($strPath, 0, $offset);
136
        }
137
 
138
        // Remove redundant slashes from the remainder of the path, forcing all
139
        // slashes into the preferred slash
140
        while ($src < $len) {
141
            $c = $strPath{$src++};
142
            if ($this->isSlash($c)) {
143
                while (($src < $len) && $this->isSlash($strPath{$src})) {
144
                    $src++;
145
                }
146
                if ($src === $len) {
147
                    /* Check for trailing separator */
148
                    $sn = (int) strlen($sb);
149
                    if (($sn == 2) && ($sb{1} === ':')) {
150
                        // "z:\\"
151
                        $sb .= $slash;
152
                        break;
153
                    }
154
                    if ($sn === 0) {
155
                        // "\\"
156
                        $sb .= $slash;
157
                        break;
158
                    }
159
                    if (($sn === 1) && ($this->isSlash($sb{0}))) {
160
                        /* "\\\\" is not collapsed to "\\" because "\\\\" marks
161
                        the beginning of a UNC pathname.  Even though it is
162
                        not, by itself, a valid UNC pathname, we leave it as
163
                        is in order to be consistent with the win32 APIs,
164
                        which treat this case as an invalid UNC pathname
165
                        rather than as an alias for the root directory of
166
                        the current drive. */
167
                        $sb .= $slash;
168
                        break;
169
                    }
170
                    // Path does not denote a root directory, so do not append
171
                    // trailing slash
172
                    break;
173
                } else {
174
                    $sb .= $slash;
175
                }
176
            } else {
177
                $sb.=$c;
178
            }
179
        }
180
        $rv = (string) $sb;
181
        return $rv;
182
    }
183
 
184
    /**
185
     * Check that the given pathname is normal.  If not, invoke the real
186
     * normalizer on the part of the pathname that requires normalization.
187
     * This way we iterate through the whole pathname string only once.
188
     * @param string $strPath
189
     * @return string
190
     */
191
    function normalize($strPath) {
192
        $n = strlen($strPath);
193
        $slash    = $this->slash;
194
        $altSlash = $this->altSlash;
195
        $prev = 0;
196
        for ($i = 0; $i < $n; $i++) {
197
            $c = $strPath{$i};
198
            if ($c === $altSlash) {
199
                return $this->normalizer($strPath, $n, ($prev === $slash) ? $i - 1 : $i);
200
            }
201
            if (($c === $slash) && ($prev === $slash) && ($i > 1)) {
202
                return $this->normalizer($strPath, $n, $i - 1);
203
            }
204
            if (($c === ':') && ($i > 1)) {
205
                return $this->normalizer($strPath, $n, 0);
206
            }
207
            $prev = $c;
208
        }
209
        if ($prev === $slash) {
210
            return $this->normalizer($strPath, $n, $n - 1);
211
        }
212
        return $strPath;
213
    }
214
 
215
    function prefixLength($strPath) {
216
        $path  = (string) $strPath;
217
        $slash = (string) $this->slash;
218
        $n = (int) strlen($path);
219
        if ($n === 0) {
220
            return 0;
221
        }
222
        $c0 = $path{0};
223
        $c1 = ($n > 1) ? $path{1} :
224
              0;
225
        if ($c0 === $slash) {
226
            if ($c1 === $slash) {
227
                return 2;            // absolute UNC pathname "\\\\foo"
228
            }
229
            return 1;                // drive-relative "\\foo"
230
        }
231
 
232
        if ($this->isLetter($c0) && ($c1 === ':')) {
233
            if (($n > 2) && ($path{2}) === $slash) {
234
                return 3;            // Absolute local pathname "z:\\foo" */
235
            }
236
            return 2;                // Directory-relative "z:foo"
237
        }
238
        return 0;                    // Completely relative
239
    }
240
 
241
    function resolve($parent, $child) {
242
        $parent = (string) $parent;
243
        $child  = (string) $child;
244
        $slash  = (string) $this->slash;
245
 
246
        $pn = (int) strlen($parent);
247
        if ($pn === 0) {
248
            return $child;
249
        }
250
        $cn = (int) strlen($child);
251
        if ($cn === 0) {
252
            return $parent;
253
        }
254
 
255
        $c = $child;
256
        if (($cn > 1) && ($c{0} === $slash)) {
257
            if ($c{1} === $slash) {
258
                // drop prefix when child is a UNC pathname
259
                $c = substr($c, 2);
260
            }
261
            else {
262
                //Drop prefix when child is drive-relative */
263
                $c = substr($c, 1);
264
            }
265
        }
266
 
267
        $p = $parent;
268
        if ($p{$pn - 1} === $slash) {
269
            $p = substr($p, 0, $pn - 1);
270
        }
271
        return $p.$this->slashify($c);
272
    }
273
 
274
    function getDefaultParent() {
275
        return (string) ("".$this->slash);
276
    }
277
 
278
    function fromURIPath($strPath) {
279
        $p = (string) $strPath;
280
        if ((strlen($p) > 2) && ($p{2} === ':')) {
281
 
282
            // "/c:/foo" --> "c:/foo"
283
            $p = substr($p,1);
284
 
285
            // "c:/foo/" --> "c:/foo", but "c:/" --> "c:/"
286
            if ((strlen($p) > 3) && StringHelper::endsWith('/', $p)) {
287
                $p = substr($p, 0, strlen($p) - 1);
288
            }
289
        } elseif ((strlen($p) > 1) && StringHelper::endsWith('/', $p)) {
290
            // "/foo/" --> "/foo"
291
            $p = substr($p, 0, strlen($p) - 1);
292
        }
293
        return (string) $p;
294
    }
295
 
296
 
297
    /* -- Path operations -- */
298
 
299
    function isAbsolute(PhingFile $f) {
300
        $pl = (int) $f->getPrefixLength();
301
        $p  = (string) $f->getPath();
302
        return ((($pl === 2) && ($p{0} === $this->slash)) || ($pl === 3) || ($pl === 1 && $p{0} === $this->slash));
303
    }
304
 
305
    /** private */
306
    function _driveIndex($d) {
307
        $d = (string) $d{0};
308
        if ((ord($d) >= ord('a')) && (ord($d) <= ord('z'))) {
309
            return ord($d) - ord('a');
310
        }
311
        if ((ord($d) >= ord('A')) && (ord($d) <= ord('Z'))) {
312
            return ord($d) - ord('A');
313
        }
314
        return -1;
315
    }
316
 
317
    /** private */
318
    function _getDriveDirectory($drive) {
319
        $drive = (string) $drive{0};
320
        $i = (int) $this->_driveIndex($drive);
321
        if ($i < 0) {
322
            return null;
323
        }
324
 
325
        $s = (isset(self::$driveDirCache[$i]) ? self::$driveDirCache[$i] : null);
326
 
327
        if ($s !== null) {
328
            return $s;
329
        }
330
 
331
        $s = $this->_getDriveDirectory($i + 1);
332
        self::$driveDirCache[$i] = $s;
333
        return $s;
334
    }
335
 
336
    function _getUserPath() {
337
        //For both compatibility and security, we must look this up every time
338
        return (string) $this->normalize(Phing::getProperty("user.dir"));
339
    }
340
 
341
    function _getDrive($path) {
342
        $path = (string) $path;
343
        $pl   = $this->prefixLength($path);
344
        return ($pl === 3) ? substr($path, 0, 2) : null;
345
    }
346
 
347
    function resolveFile(PhingFile $f) {
348
        $path = $f->getPath();
349
        $pl   = (int) $f->getPrefixLength();
350
 
351
        if (($pl === 2) && ($path{0} === $this->slash)) {
352
            return $path;            // UNC
353
        }
354
 
355
        if ($pl === 3) {
356
            return $path;            // Absolute local
357
        }
358
 
359
        if ($pl === 0) {
360
            return (string) ($this->_getUserPath().$this->slashify($path)); //Completely relative
361
        }
362
 
363
        if ($pl === 1) {            // Drive-relative
364
            $up = (string) $this->_getUserPath();
365
            $ud = (string) $this->_getDrive($up);
366
            if ($ud !== null) {
367
                return (string) $ud.$path;
368
            }
369
            return (string) $up.$path;            //User dir is a UNC path
370
        }
371
 
372
        if ($pl === 2) {                // Directory-relative
373
            $up = (string) $this->_getUserPath();
374
            $ud = (string) $this->_getDrive($up);
375
            if (($ud !== null) && StringHelper::startsWith($ud, $path)) {
376
                return (string) ($up . $this->slashify(substr($path,2)));
377
            }
378
            $drive = (string) $path{0};
379
            $dir   = (string) $this->_getDriveDirectory($drive);
380
 
381
            $np = (string) "";
382
            if ($dir !== null) {
383
                /* When resolving a directory-relative path that refers to a
384
                drive other than the current drive, insist that the caller
385
                have read permission on the result */
386
                $p = (string) $drive . (':'.$dir.$this->slashify(substr($path,2)));
387
 
388
                if (!$this->checkAccess($p, false)) {
389
                    // FIXME
390
                    // throw security error
391
                    die("Can't resolve path $p");
392
                }
393
                return $p;
394
            }
395
            return (string) $drive.':'.$this->slashify(substr($path,2)); //fake it
396
        }
397
 
398
        throw new Exception("Unresolvable path: " . $path);
399
    }
400
 
401
    /* -- most of the following is mapped to the functions mapped th php natives in FileSystem */
402
 
403
    /* -- Attribute accessors -- */
404
 
405
    function setReadOnly($f) {
406
        // dunno how to do this on win
407
        throw new Exception("WIN32FileSystem doesn't support read-only yet.");
408
    }
409
 
410
    /* -- Filesystem interface -- */
411
 
412
    protected function _access($path) {
413
        if (!$this->checkAccess($path, false)) {
414
            throw new Exception("Can't resolve path $p");
415
        }
416
        return true;
417
    }
418
 
419
    function _nativeListRoots() {
420
        // FIXME
421
    }
422
 
423
    function listRoots() {
424
        $ds = _nativeListRoots();
425
        $n = 0;
426
        for ($i = 0; $i < 26; $i++) {
427
            if ((($ds >> $i) & 1) !== 0) {
428
                if (!$this->access((string)( chr(ord('A') + $i) . ':' . $this->slash))) {
429
                    $ds &= ~(1 << $i);
430
                } else {
431
                    $n++;
432
                }
433
            }
434
        }
435
        $fs = array();
436
        $j = (int) 0;
437
        $slash = (string) $this->slash;
438
        for ($i = 0; $i < 26; $i++) {
439
            if ((($ds >> $i) & 1) !== 0) {
440
                $fs[$j++] = new PhingFile(chr(ord('A') + $i) . ':' . $this->slash);
441
            }
442
        }
443
        return $fs;
444
    }
445
 
446
    /* -- Basic infrastructure -- */
447
 
448
    /** compares file paths lexicographically */
449
    function compare(PhingFile $f1, PhingFile $f2) {
450
        $f1Path = $f1->getPath();
451
        $f2Path = $f2->getPath();
452
        return (boolean) strcasecmp((string) $f1Path, (string) $f2Path);
453
    }
454
 
455
 
456
    /**
457
     * returns the contents of a directory in an array
458
     */
459
    function lister($f) {
460
        $dir = @opendir($f->getAbsolutePath());
461
        if (!$dir) {
462
            throw new Exception("Can't open directory " . $f->__toString());
463
        }
464
        $vv = array();
465
        while (($file = @readdir($dir)) !== false) {
466
            if ($file == "." || $file == "..") {
467
                continue;
468
            }
469
            $vv[] = (string) $file;
470
        }
471
        @closedir($dir);
472
        return $vv;
473
    }
474
 
475
}
476
 
477