Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
 
4
/**
5
 * Factory to access the most common File_Archive features
6
 * It uses lazy include, so you dont have to include the files from
7
 * File/Archive/* directories
8
 *
9
 * PHP versions 4 and 5
10
 *
11
 * This library is free software; you can redistribute it and/or
12
 * modify it under the terms of the GNU Lesser General Public
13
 * License as published by the Free Software Foundation; either
14
 * version 2.1 of the License, or (at your option) any later version.
15
 *
16
 * This library is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
 * Lesser General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Lesser General Public
22
 * License along with this library; if not, write to the Free Software
23
 * Foundation, Inc., 59 Temple Place, Suite 330,Boston,MA 02111-1307 USA
24
 *
25
 * @category   File Formats
26
 * @package    File_Archive
27
 * @author     Vincent Lascaux <vincentlascaux@php.net>
28
 * @copyright  1997-2005 The PHP Group
29
 * @license    http://www.gnu.org/copyleft/lesser.html  LGPL
30
 * @version    CVS: $Id: Archive.php,v 1.90 2008/05/28 19:58:07 cbrunet Exp $
31
 * @link       http://pear.php.net/package/File_Archive
32
 */
33
 
34
/**
35
 * To have access to PEAR::isError and PEAR::raiseError
36
 * We should probably use lazy include and remove this inclusion...
37
 */
38
require_once "PEAR.php";
39
 
40
function File_Archive_cleanCache($file, $group)
41
{
42
    $file = split('_', $file);
43
    if (count($file) != 3) {
44
        return false; //not a File_Archive file, keep it
45
    }
46
 
47
    $name = $file[2];
48
    $name = urldecode($name);
49
 
50
    $group = $file[1];
51
 
52
    //clean the cache only for files in File_Archive groups
53
    return substr($group, 0, 11) == 'FileArchive' &&
54
           !file_exists($name); //and only if the related file no longer exists
55
}
56
 
57
/**
58
 * Factory to access the most common File_Archive features
59
 * It uses lazy include, so you dont have to include the files from
60
 * File/Archive/* directories
61
 */
62
class File_Archive
63
{
64
    function& _option($name)
65
    {
66
        static $container = array(
67
            'zipCompressionLevel' => 9,
68
             'gzCompressionLevel' => 9,
69
            'tmpDirectory' => '.',
70
            'cache' => null,
71
            'appendRemoveDuplicates' => false,
72
            'blockSize' => 65536,
73
            'cacheCondition' => false
74
        );
75
        return $container[$name];
76
    }
77
    /**
78
     * Sets an option that will be used by default by all readers or writers
79
     * Option names are case sensitive
80
     * Currently, the following options are used:
81
     *
82
     * "cache"
83
     *      Instance of a Cache_Lite object used to cache some compressed
84
     *      data to speed up future compressions of files
85
     *      Default: null (no cache used)
86
     *
87
     * "zipCompressionLevel"
88
     *      Value between 0 and 9 specifying the default compression level used
89
     *      by Zip writers (0 no compression, 9 highest compression)
90
     *      Default: 9
91
     *
92
     * "gzCompressionLevel"
93
     *      Value between 0 and 9 specifying the default compression level used
94
     *      by Gz writers (0 no compression, 9 highest compression)
95
     *      Default: 9
96
     *
97
     * "tmpDirectory"
98
     *      Directory where the temporary files generated by File_Archive will
99
     *      be created
100
     *      Default: '.'
101
     *
102
     * "appendRemoveDuplicates"
103
     *      If set to true, the appender created will by default remove the
104
     *      file present in the archive when adding a new one. This will slow the
105
     *      appending of files to archives
106
     *      Default: false
107
     *
108
     * "blockSize"
109
     *      To transfer data from a reader to a writer, some chunks a read from the
110
     *      source and written to the writer. This parameter controls the size of the
111
     *      chunks
112
     *      Default: 64kB
113
     *
114
     * "cacheCondition"
115
     *      This parameter specifies when a cache should be used. When the cache is
116
     *      used, the data of the reader is saved in a temporary file for future access.
117
     *      The cached reader will be read only once, even if you read it several times.
118
     *      This can be usefull to read compressed files or downloaded files (from http or ftp)
119
     *      The possible values for this option are
120
     *       - false: never use cache
121
     *       - a regexp: A cache will be used if the specified URL matches the regexp
122
     *         preg_match is used
123
     *      Default: false
124
     *      Example: '/^(http|ftp):\/\//' will cache all files downloaded via http or ftp
125
     *
126
     */
127
    function setOption($name, $value)
128
    {
129
        $option =& File_Archive::_option($name);
130
        $option = $value;
131
        if ($name == 'cache' && $value !== null) {
132
            //TODO: ask to Cache_Lite to allow that
133
            $value->_fileNameProtection = false;
134
        }
135
    }
136
 
137
    /**
138
     * Retrieve the value of an option
139
     */
140
    function getOption($name)
141
    {
142
        return File_Archive::_option($name);
143
    }
144
 
145
    /**
146
     * Create a reader to read the URL $URL.
147
     * If the URL is a directory, it will recursively read that directory.
148
     * If $uncompressionLevel is not null, the archives (files with extension
149
     * tar, zip, gz or tgz) will be considered as directories (up to a depth of
150
     * $uncompressionLevel if $uncompressionLevel > 0). The reader will only
151
     * read files with a directory depth of $directoryDepth. It reader will
152
     * replace the given URL ($URL) with $symbolic in the public filenames
153
     * The default symbolic name is the last filename in the URL (or '' for
154
     * directories)
155
     *
156
     * Examples:
157
     * Considere the following file system
158
     * <pre>
159
     * a.txt
160
     * b.tar (archive that contains the following files)
161
     *     c.txt
162
     *     d.tgz (archive that contains the following files)
163
     *         e.txt
164
     *         dir1/
165
     *             f.txt
166
     * dir2/
167
     *     g.txt
168
     *     dir3/
169
     *         h.tar (archive that contains the following files)
170
     *             i.txt
171
     * </pre>
172
     *
173
     * read('.') will return a reader that gives access to following
174
     * files (recursively read current dir):
175
     * <pre>
176
     * a.txt
177
     * b.tar
178
     * dir2/g.txt
179
     * dir2/dir3/h.tar
180
     * </pre>
181
     *
182
     * read('.', 'myBaseDir') will return the following reader:
183
     * <pre>
184
     * myBaseDir/a.txt
185
     * myBaseDir/b.tar
186
     * myBaseDir/dir2/g.txt
187
     * myBaseDir/dir2/dir3/h.tar
188
     * </pre>
189
     *
190
     * read('.', '', -1) will return the following reader (uncompress
191
     * everything)
192
     * <pre>
193
     * a.txt
194
     * b.tar/c.txt
195
     * b.tar/d.tgz/e.txt
196
     * b.tar/d.tgz/dir1/f.txt
197
     * dir2/g.txt
198
     * dir2/dir3/h.tar/i.txt
199
     * </pre>
200
     *
201
     * read('.', '', 1) will uncompress only one level (so d.tgz will
202
     * not be uncompressed):
203
     * <pre>
204
     * a.txt
205
     * b.tar/c.txt
206
     * b.tar/d.tgz
207
     * dir2/g.txt
208
     * dir2/dir3/h.tar/i.txt
209
     * </pre>
210
     *
211
     * read('.', '', 0, 0) will not recurse into subdirectories
212
     * <pre>
213
     * a.txt
214
     * b.tar
215
     * </pre>
216
     *
217
     * read('.', '', 0, 1) will recurse only one level in
218
     * subdirectories
219
     * <pre>
220
     * a.txt
221
     * b.tar
222
     * dir2/g.txt
223
     * </pre>
224
     *
225
     * read('.', '', -1, 2) will uncompress everything and recurse in
226
     * only 2 levels in subdirectories or archives
227
     * <pre>
228
     * a.txt
229
     * b.tar/c.txt
230
     * b.tar/d.tgz/e.txt
231
     * dir2/g.txt
232
     * </pre>
233
     *
234
     * The recursion level is determined by the real path, not the symbolic one.
235
     * So read('.', 'myBaseDir', -1, 2) will result to the same files:
236
     * <pre>
237
     * myBaseDir/a.txt
238
     * myBaseDir/b.tar/c.txt
239
     * myBaseDir/b.tar/d.tgz/e.txt (accepted because the real depth is 2)
240
     * myBaseDir/dir2/g.txt
241
     * </pre>
242
     *
243
     * Use readSource to do the same thing, reading from a specified reader instead of
244
     * reading from the system files
245
     *
246
     * To read a single file, you can do read('a.txt', 'public_name.txt')
247
     * If no public name is provided, the default one is the name of the file
248
     * read('dir2/g.txt') contains the single file named 'g.txt'
249
     * read('b.tar/c.txt') contains the single file named 'c.txt'
250
     *
251
     * Note: This function uncompress files reading their extension
252
     *       The compressed files must have a tar, zip, gz or tgz extension
253
     *       Since it is impossible for some URLs to use is_dir or is_file, this
254
     *       function may not work with
255
     *       URLs containing folders which name ends with such an extension
256
     */
257
    function readSource(&$source, $URL, $symbolic = null,
258
                  $uncompression = 0, $directoryDepth = -1)
259
    {
260
        return File_Archive::_readSource($source, $URL, $reachable, $baseDir,
261
                  $symbolic, $uncompression, $directoryDepth);
262
    }
263
 
264
    /**
265
     * This function performs exactly as readSource, but with two additional parameters
266
     * ($reachable and $baseDir) that will be set so that $reachable."/".$baseDir == $URL
267
     * and $reachable can be reached (in case of error)
268
     *
269
     * @access private
270
     */
271
    function _readSource(&$toConvert, $URL, &$reachable, &$baseDir, $symbolic = null,
272
                  $uncompression = 0, $directoryDepth = -1)
273
    {
274
        $source =& File_Archive::_convertToReader($toConvert);
275
        if (PEAR::isError($source)) {
276
            return $source;
277
        }
278
        if (is_array($URL)) {
279
            $converted = array();
280
            foreach($URL as $key => $foo) {
281
                $converted[] =& File_Archive::_convertToReader($URL[$key]);
282
            }
283
            return File_Archive::readMulti($converted);
284
        }
285
 
286
        //No need to uncompress more than $directoryDepth
287
        //That's not perfect, and some archives will still be uncompressed just
288
        //to be filtered out :(
289
        if ($directoryDepth >= 0) {
290
            $uncompressionLevel = min($uncompression, $directoryDepth);
291
        } else {
292
            $uncompressionLevel = $uncompression;
293
        }
294
 
295
        require_once 'File/Archive/Reader.php';
296
        $std = File_Archive_Reader::getStandardURL($URL);
297
 
298
        //Modify the symbolic name if necessary
299
        $slashPos = strrpos($std, '/');
300
        if ($symbolic === null) {
301
            if ($slashPos === false) {
302
                $realSymbolic = $std;
303
            } else {
304
                $realSymbolic = substr($std, $slashPos+1);
305
            }
306
        } else {
307
            $realSymbolic = $symbolic;
308
        }
309
        if ($slashPos !== false) {
310
            $baseFile = substr($std, 0, $slashPos+1);
311
            $lastFile = substr($std, $slashPos+1);
312
        } else {
313
            $baseFile = '';
314
            $lastFile = $std;
315
        }
316
 
317
        if (strpos($lastFile, '*')!==false ||
318
            strpos($lastFile, '?')!==false) {
319
            //We have to build a regexp here
320
            $regexp = str_replace(
321
                array('\*', '\?'),
322
                array('[^/]*', '[^/]'),
323
                preg_quote($lastFile)
324
            );
325
            $result = File_Archive::_readSource($source, $baseFile,
326
                                                $reachable, $baseDir, null, 0, -1);
327
            return File_Archive::filter(
328
                    File_Archive::predEreg('^'.$regexp.'$'),
329
                    $result
330
                   );
331
        }
332
 
333
        //If the URL can be interpreted as a directory, and we are reading from the file system
334
        if ((empty($URL) || is_dir($URL)) && $source === null) {
335
            require_once "File/Archive/Reader/Directory.php";
336
 
337
            if ($uncompressionLevel != 0) {
338
                require_once "File/Archive/Reader/Uncompress.php";
339
                $result = new File_Archive_Reader_Uncompress(
340
                    new File_Archive_Reader_Directory($std, '', $directoryDepth),
341
                    $uncompressionLevel
342
                );
343
            } else {
344
                $result = new File_Archive_Reader_Directory($std, '', $directoryDepth);
345
            }
346
 
347
            if ($directoryDepth >= 0) {
348
                require_once 'File/Archive/Reader/Filter.php';
349
                require_once 'File/Archive/Predicate/MaxDepth.php';
350
 
351
                $tmp =& File_Archive::filter(
352
                    new File_Archive_Predicate_MaxDepth($directoryDepth),
353
                    $result
354
                );
355
                unset($result);
356
                $result =& $tmp;
357
            }
358
            if (!empty($realSymbolic)) {
359
                if ($symbolic === null) {
360
                    $realSymbolic = '';
361
                }
362
                require_once "File/Archive/Reader/ChangeName/AddDirectory.php";
363
                $tmp =& new File_Archive_Reader_ChangeName_AddDirectory(
364
                    $realSymbolic,
365
                    $result
366
                );
367
                unset($result);
368
                $result =& $tmp;
369
            }
370
 
371
        //If the URL can be interpreted as a file, and we are reading from the file system
372
        } else if (is_file($URL) && substr($URL, -1)!='/' && $source === null) {
373
            require_once "File/Archive/Reader/File.php";
374
            $result = new File_Archive_Reader_File($URL, $realSymbolic);
375
 
376
        //Else, we will have to build a complex reader
377
        } else {
378
            require_once "File/Archive/Reader/File.php";
379
 
380
            $realPath = $std;
381
 
382
            // Try to find a file with a known extension in the path (
383
            // (to manage URLs like archive.tar/directory/file)
384
            $pos = 0;
385
            do {
386
                if ($pos+1<strlen($realPath)) {
387
                    $pos = strpos($realPath, '/', $pos+1);
388
                } else {
389
                    $pos = false;
390
                }
391
                if ($pos === false) {
392
                    $pos = strlen($realPath);
393
                }
394
 
395
                $file = substr($realPath, 0, $pos);
396
                $baseDir = substr($realPath, $pos+1);
397
                $dotPos = strrpos($file, '.');
398
                $extension = '';
399
                if ($dotPos !== false) {
400
                    $extension = substr($file, $dotPos+1);
401
                }
402
            } while ($pos < strlen($realPath) &&
403
                (!File_Archive::isKnownExtension($extension) ||
404
                 (is_dir($file) && $source==null)));
405
 
406
            $reachable = $file;
407
 
408
            //If we are reading from the file system
409
            if ($source === null) {
410
                //Create a file reader
411
                $result = new File_Archive_Reader_File($file);
412
            } else {
413
                //Select in the source the file $file
414
 
415
                require_once "File/Archive/Reader/Select.php";
416
                $result = new File_Archive_Reader_Select($file, $source);
417
            }
418
 
419
            require_once "File/Archive/Reader/Uncompress.php";
420
            $tmp = new File_Archive_Reader_Uncompress($result, $uncompressionLevel);
421
            unset($result);
422
            $result = $tmp;
423
 
424
            //Select the requested folder in the uncompress reader
425
            $isDir = $result->setBaseDir($std);
426
            if (PEAR::isError($isDir)) {
427
                return $isDir;
428
            }
429
            if ($isDir && $symbolic==null) {
430
                //Default symbolic name for directories is empty
431
                $realSymbolic = '';
432
            }
433
 
434
            if ($directoryDepth >= 0) {
435
                //Limit the maximum depth if necessary
436
                require_once "File/Archive/Reader/Filter.php";
437
                require_once "File/Archive/Predicate/MaxDepth.php";
438
 
439
                $tmp = new File_Archive_Reader_Filter(
440
                    new File_Archive_Predicate(
441
                        $directoryDepth +
442
                        substr_count(substr($std, $pos+1), '/')
443
                    ),
444
                    $result
445
                );
446
                unset($result);
447
                $result =& $tmp;
448
            }
449
 
450
            if ($std != $realSymbolic) {
451
                require_once "File/Archive/Reader/ChangeName/Directory.php";
452
 
453
                //Change the base name to the symbolic one if necessary
454
                $tmp = new File_Archive_Reader_ChangeName_Directory(
455
                    $std,
456
                    $realSymbolic,
457
                    $result
458
                );
459
                unset($result);
460
                $result =& $tmp;
461
            }
462
        }
463
 
464
        $cacheCondition = File_Archive::getOption('cacheCondition');
465
        if ($cacheCondition !== false &&
466
            preg_match($cacheCondition, $URL)) {
467
            $tmp =& File_Archive::cache($result);
468
            unset($result);
469
            $result =& $tmp;
470
        }
471
 
472
        return $result;
473
    }
474
    function read($URL, $symbolic = null,
475
                  $uncompression = 0, $directoryDepth = -1)
476
    {
477
        $source = null;
478
        return File_Archive::readSource($source, $URL, $symbolic, $uncompression, $directoryDepth);
479
    }
480
 
481
    /**
482
     * Create a file reader on an uploaded file. The reader will read
483
     * $_FILES[$name]['tmp_name'] and will have $_FILES[$name]['name']
484
     * as a symbolic filename.
485
     *
486
     * A PEAR error is returned if one of the following happen
487
     *  - $_FILES[$name] is not set
488
     *  - $_FILES[$name]['error'] is not 0
489
     *  - is_uploaded_file returns false
490
     *
491
     * @param string $name Index of the file in the $_FILES array
492
     * @return File_Archive_Reader File reader on the uploaded file
493
     */
494
    function readUploadedFile($name)
495
    {
496
        if (!isset($_FILES[$name])) {
497
            return PEAR::raiseError("File $name has not been uploaded");
498
        }
499
        switch ($_FILES[$name]['error']) {
500
        case 0:
501
            //No error
502
            break;
503
        case 1:
504
            return PEAR::raiseError(
505
                        "The upload size limit didn't allow to upload file ".
506
                        $_FILES[$name]['name']
507
                    );
508
        case 2:
509
            return PEAR::raiseError(
510
                        "The form size limit didn't allow to upload file ".
511
                        $_FILES[$name]['name']
512
                   );
513
        case 3:
514
            return PEAR::raiseError(
515
                        "The file was not entirely uploaded"
516
                   );
517
        case 4:
518
            return PEAR::raiseError(
519
                        "The uploaded file is empty"
520
                   );
521
        default:
522
            return PEAR::raiseError(
523
                        "Unknown error ".$_FILES[$name]['error']." in file upload. ".
524
                        "Please, report a bug"
525
                   );
526
        }
527
        if (!is_uploaded_file($_FILES[$name]['tmp_name'])) {
528
            return PEAR::raiseError("The file is not an uploaded file");
529
        }
530
 
531
        require_once "File/Archive/Reader/File.php";
532
        return new File_Archive_Reader_File(
533
                    $_FILES[$name]['tmp_name'],
534
                    $_FILES[$name]['name'],
535
                    $_FILES[$name]['type']
536
               );
537
    }
538
 
539
    /**
540
     * Adds a cache layer above the specified reader
541
     * The data of the reader is saved in a temporary file for future access.
542
     * The cached reader will be read only once, even if you read it several times.
543
     * This can be usefull to read compressed files or downloaded files (from http or ftp)
544
     *
545
     * @param mixed $toConvert The reader to cache
546
     *        It can be a File_Archive_Reader or a string, which will be converted using the
547
     *        read function
548
     */
549
    function cache(&$toConvert)
550
    {
551
        $source =& File_Archive::_convertToReader($toConvert);
552
        if (PEAR::isError($source)) {
553
            return $source;
554
        }
555
 
556
        require_once 'File/Archive/Reader/Cache.php';
557
        return new File_Archive_Reader_Cache($source);
558
    }
559
 
560
    /**
561
     * Try to interpret the object as a reader
562
     * Strings are converted to readers using File_Archive::read
563
     * Arrays are converted to readers using File_Archive::readMulti
564
     *
565
     * @access private
566
     */
567
    function &_convertToReader(&$source)
568
    {
569
        if (is_string($source)) {
570
            $cacheCondition = File_Archive::getOption('cacheCondition');
571
            if ($cacheCondition !== false &&
572
                preg_match($cacheCondition, $source)) {
573
                $obj = File_Archive::cache(File_Archive::read($source));
574
                return $obj;
575
            } else {
576
                $obj = File_Archive::read($source);
577
                return $obj;
578
            }
579
        } else if (is_array($source)) {
580
            return File_Archive::readMulti($source);
581
        } else {
582
            return $source;
583
        }
584
     }
585
 
586
    /**
587
     * Try to interpret the object as a writer
588
     * Strings are converted to writers using File_Archive::appender
589
     * Arrays are converted to writers using a multi writer
590
     *
591
     * @access private
592
     */
593
    function &_convertToWriter(&$dest)
594
    {
595
        if (is_string($dest)) {
596
            $obj =& File_Archive::appender($dest);
597
            return $obj;
598
        } else if (is_array($dest)) {
599
            require_once 'File/Archive/Writer/Multi.php';
600
            $writer = new File_Archive_Writer_Multi();
601
            foreach($dest as $key => $foo) {
602
                $writer->addWriter($dest[$key]);
603
            }
604
            return $writer;
605
        } else {
606
            return $dest;
607
        }
608
    }
609
 
610
    /**
611
     * Check if a file with a specific extension can be read as an archive
612
     * with File_Archive::read*
613
     * This function is case sensitive.
614
     *
615
     * @param string $extension the checked extension
616
     * @return bool whether this file can be understood reading its extension
617
     *         Currently, supported extensions are tar, zip, jar, gz, tgz,
618
     *         tbz, bz2, bzip2, ar, deb
619
     */
620
    function isKnownExtension($extension)
621
    {
622
        return $extension == 'tar'   ||
623
               $extension == 'zip'   ||
624
               $extension == 'jar'   ||
625
               $extension == 'gz'    ||
626
               $extension == 'tgz'   ||
627
               $extension == 'tbz'   ||
628
               $extension == 'bz2'   ||
629
               $extension == 'bzip2' ||
630
               $extension == 'ar'    ||
631
               $extension == 'deb'   /* ||
632
               $extension == 'cab'   ||
633
               $extension == 'rar' */;
634
    }
635
 
636
    /**
637
     * Create a reader that will read the single file source $source as
638
     * a specific archive
639
     *
640
     * @param string $extension determines the kind of archive $source contains
641
     *        $extension is case sensitive
642
     * @param File_Archive_Reader $source stores the archive
643
     * @param bool $sourceOpened specifies if the archive is already opened
644
     *        if false, next will be called on source
645
     *        Closing the returned archive will close $source iif $sourceOpened
646
     *        is true
647
     * @return A File_Archive_Reader that uncompresses the archive contained in
648
     *         $source interpreting it as a $extension archive
649
     *         If $extension is not handled return false
650
     */
651
    function readArchive($extension, &$toConvert, $sourceOpened = false)
652
    {
653
        $source =& File_Archive::_convertToReader($toConvert);
654
        if (PEAR::isError($source)) {
655
            return $source;
656
        }
657
 
658
        switch($extension) {
659
        case 'tgz':
660
            return File_Archive::readArchive('tar',
661
                    File_Archive::readArchive('gz', $source, $sourceOpened)
662
                    );
663
        case 'tbz':
664
            return File_Archive::readArchive('tar',
665
                    File_Archive::readArchive('bz2', $source, $sourceOpened)
666
                    );
667
        case 'tar':
668
            require_once 'File/Archive/Reader/Tar.php';
669
            return new File_Archive_Reader_Tar($source, $sourceOpened);
670
 
671
        case 'gz':
672
        case 'gzip':
673
            require_once 'File/Archive/Reader/Gzip.php';
674
            return new File_Archive_Reader_Gzip($source, $sourceOpened);
675
 
676
        case 'zip':
677
        case 'jar':
678
            require_once 'File/Archive/Reader/Zip.php';
679
            return new File_Archive_Reader_Zip($source, $sourceOpened);
680
 
681
        case 'bz2':
682
        case 'bzip2':
683
            require_once 'File/Archive/Reader/Bzip2.php';
684
            return new File_Archive_Reader_Bzip2($source, $sourceOpened);
685
 
686
        case 'deb':
687
        case 'ar':
688
            require_once 'File/Archive/Reader/Ar.php';
689
            return new File_Archive_Reader_Ar($source, $sourceOpened);
690
 
691
/*        case 'cab':
692
            require_once 'File/Archive/Reader/Cab.php';
693
            return new File_Archive_Reader_Cab($source, $sourceOpened);
694
 
695
 
696
        case 'rar':
697
            require_once "File/Archive/Reader/Rar.php";
698
            return new File_Archive_Reader_Rar($source, $sourceOpened); */
699
 
700
        default:
701
            return false;
702
        }
703
    }
704
 
705
    /**
706
     * Contains only one file with data read from a memory buffer
707
     *
708
     * @param string $memory content of the file
709
     * @param string $filename public name of the file
710
     * @param array $stat statistics of the file. Index 7 (size) will be
711
     *        overwritten to match the size of $memory
712
     * @param string $mime mime type of the file. Default will determine the
713
     *        mime type thanks to the extension of $filename
714
     * @see File_Archive_Reader_Memory
715
     */
716
    function readMemory($memory, $filename, $stat=array(), $mime=null)
717
    {
718
        require_once "File/Archive/Reader/Memory.php";
719
        return new File_Archive_Reader_Memory($memory, $filename, $stat, $mime);
720
    }
721
 
722
    /**
723
     * Contains several other sources. Take care the sources don't have several
724
     * files with the same filename. The sources are given as a parameter, or
725
     * can be added thanks to the reader addSource method
726
     *
727
     * @param array $sources Array of strings or readers that will be added to
728
     *        the multi reader. If the parameter is a string, a reader will be
729
     *        built thanks to the read function
730
     * @see   File_Archive_Reader_Multi, File_Archive::read()
731
     */
732
    function readMulti($sources = array())
733
    {
734
        require_once "File/Archive/Reader/Multi.php";
735
        $result = new File_Archive_Reader_Multi();
736
        foreach ($sources as $index => $foo) {
737
            $s =& File_Archive::_convertToReader($sources[$index]);
738
            if (PEAR::isError($s)) {
739
                return $s;
740
            } else {
741
                $result->addSource($s);
742
            }
743
        }
744
        return $result;
745
    }
746
    /**
747
     * Make the files of a source appear as one large file whose content is the
748
     * concatenation of the content of all the files
749
     *
750
     * @param File_Archive_Reader $toConvert The source whose files must be
751
     *        concatened
752
     * @param string $filename name of the only file of the created reader
753
     * @param array $stat statistics of the file. Index 7 (size) will be
754
     *        overwritten to match the total size of the files
755
     * @param string $mime mime type of the file. Default will determine the
756
     *        mime type thanks to the extension of $filename
757
     * @see   File_Archive_Reader_Concat
758
     */
759
    function readConcat(&$toConvert, $filename, $stat=array(), $mime=null)
760
    {
761
        $source =& File_Archive::_convertToReader($toConvert);
762
        if (PEAR::isError($source)) {
763
            return $source;
764
        }
765
 
766
        require_once "File/Archive/Reader/Concat.php";
767
        return new File_Archive_Reader_Concat($source, $filename, $stat, $mime);
768
    }
769
 
770
    /**
771
     * Changes the name of each file in a reader by applying a custom function
772
     * The function must return false if the file is to be discarded, or the new
773
     * name of the file else
774
     *
775
     * @param Callable $function Function called to modify the name of the file
776
     *        $function takes the name of the file as a parameter and returns the
777
     *        new name, or false if the file must be discarded
778
     * @param File_Archive_Reader $toConvert The files of this source will be
779
     *        modified
780
     * @return File_Archive_Reader a new reader that contains the same files
781
     *        as $toConvert but with a different name
782
     */
783
    function changeName($function, &$toConvert)
784
    {
785
        $source =& File_Archive::_convertToReader($toConvert);
786
        if (PEAR::isError($source)) {
787
            return $source;
788
        }
789
 
790
        require_once "File/Archive/Reader/ChangeName.php";
791
        return new File_Archive_Reader_RemoveDirectory($source);
792
    }
793
 
794
    /**
795
     * Removes from a source the files that do not follow a given predicat
796
     *
797
     * @param File_Archive_Predicate $predicate Only the files for which
798
     *        $predicate->isTrue() will be kept
799
     * @param File_Archive_Reader $source Source that will be filtered
800
     * @see   File_Archive_Reader_Filter
801
     */
802
    function filter($predicate, &$toConvert)
803
    {
804
        $source =& File_Archive::_convertToReader($toConvert);
805
        if (PEAR::isError($source)) {
806
            return $source;
807
        }
808
 
809
        require_once "File/Archive/Reader/Filter.php";
810
        return new File_Archive_Reader_Filter($predicate, $source);
811
    }
812
    /**
813
     * Predicate that always evaluate to true
814
     *
815
     * @see File_Archive_Predicate_True
816
     */
817
    function predTrue()
818
    {
819
        require_once "File/Archive/Predicate/True.php";
820
        return new File_Archive_Predicate_True();
821
    }
822
    /**
823
     * Predicate that always evaluate to false
824
     *
825
     * @see File_Archive_Predicate_False
826
     */
827
    function predFalse()
828
    {
829
        require_once "File/Archive/Predicate/False.php";
830
        return new File_Archive_Predicate_False();
831
    }
832
    /**
833
     * Predicate that evaluates to the logical AND of the parameters
834
     * You can add other predicates thanks to the
835
     * File_Archive_Predicate_And::addPredicate() function
836
     *
837
     * @param File_Archive_Predicate (any number of them)
838
     * @see File_Archive_Predicate_And
839
     */
840
    function predAnd()
841
    {
842
        require_once "File/Archive/Predicate/And.php";
843
        $pred = new File_Archive_Predicate_And();
844
        $args = func_get_args();
845
        foreach ($args as $p) {
846
            $pred->addPredicate($p);
847
        }
848
        return $pred;
849
    }
850
    /**
851
     * Predicate that evaluates to the logical OR of the parameters
852
     * You can add other predicates thanks to the
853
     * File_Archive_Predicate_Or::addPredicate() function
854
     *
855
     * @param File_Archive_Predicate (any number of them)
856
     * @see File_Archive_Predicate_Or
857
     */
858
    function predOr()
859
    {
860
        require_once "File/Archive/Predicate/Or.php";
861
        $pred = new File_Archive_Predicate_Or();
862
        $args = func_get_args();
863
        foreach ($args as $p) {
864
            $pred->addPredicate($p);
865
        }
866
        return $pred;
867
    }
868
    /**
869
     * Negate a predicate
870
     *
871
     * @param File_Archive_Predicate $pred Predicate to negate
872
     * @see File_Archive_Predicate_Not
873
     */
874
    function predNot($pred)
875
    {
876
        require_once "File/Archive/Predicate/Not.php";
877
        return new File_Archive_Predicate_Not($pred);
878
    }
879
    /**
880
     * Evaluates to true iif the file is larger than a given size
881
     *
882
     * @param int $size the minimal size of the files (in Bytes)
883
     * @see File_Archive_Predicate_MinSize
884
     */
885
    function predMinSize($size)
886
    {
887
        require_once "File/Archive/Predicate/MinSize.php";
888
        return new File_Archive_Predicate_MinSize($size);
889
    }
890
    /**
891
     * Evaluates to true iif the file has been modified after a given time
892
     *
893
     * @param int $time Unix timestamp of the minimal modification time of the
894
     *        files
895
     * @see File_Archive_Predicate_MinTime
896
     */
897
    function predMinTime($time)
898
    {
899
        require_once "File/Archive/Predicate/MinTime.php";
900
        return new File_Archive_Predicate_MinTime($time);
901
    }
902
    /**
903
     * Evaluates to true iif the file has less that a given number of
904
     * directories in its path
905
     *
906
     * @param int $depth Maximal number of directories in path of the files
907
     * @see File_Archive_Predicate_MaxDepth
908
     */
909
    function predMaxDepth($depth)
910
    {
911
        require_once "File/Archive/Predicate/MaxDepth.php";
912
        return new File_Archive_Predicate_MaxDepth($depth);
913
    }
914
    /**
915
     * Evaluates to true iif the extension of the file is in a given list
916
     *
917
     * @param array or string $list List or comma separated string of possible
918
     * extension of the files
919
     * @see File_Archive_Predicate_Extension
920
     */
921
    function predExtension($list)
922
    {
923
        require_once "File/Archive/Predicate/Extension.php";
924
        return new File_Archive_Predicate_Extension($list);
925
    }
926
    /**
927
     * Evaluates to true iif the MIME type of the file is in a given list
928
     *
929
     * @param array or string $list List or comma separated string of possible
930
     *        MIME types of the files. You may enter wildcards like "image/*" to
931
     *        select all the MIME in class image
932
     * @see   File_Archive_Predicate_MIME, MIME_Type::isWildcard()
933
     */
934
    function predMIME($list)
935
    {
936
        require_once "File/Archive/Predicate/MIME.php";
937
        return new File_Archive_Predicate_MIME($list);
938
    }
939
    /**
940
     * Evaluates to true iif the name of the file follow a given regular
941
     * expression
942
     *
943
     * @param string $ereg regular expression that the filename must follow
944
     * @see File_Archive_Predicate_Ereg, ereg()
945
     */
946
    function predEreg($ereg)
947
    {
948
        require_once "File/Archive/Predicate/Ereg.php";
949
        return new File_Archive_Predicate_Ereg($ereg);
950
    }
951
    /**
952
     * Evaluates to true iif the name of the file follow a given regular
953
     * expression (case insensitive version)
954
     *
955
     * @param string $ereg regular expression that the filename must follow
956
     * @see File_Archive_Predicate_Eregi, eregi
957
     */
958
    function predEregi($ereg)
959
    {
960
        require_once "File/Archive/Predicate/Eregi.php";
961
        return new File_Archive_Predicate_Eregi($ereg);
962
    }
963
    /**
964
     * Evaluates to true only after a given number of evaluations
965
     * This can be used to select files by index since the evaluation is done
966
     * once per file
967
     *
968
     * @param array The indexes for which the returned predicate will return true
969
     *        are the keys of the array
970
     *        The predicate will return true if isset($indexes[$pos])
971
     */
972
    function predIndex($indexes)
973
    {
974
        require_once "File/Archive/Predicate/Index.php";
975
        return new File_Archive_Predicate_Index($indexes);
976
    }
977
    /**
978
     * Custom predicate built by supplying a string expression
979
     *
980
     * Here are different ways to create a predicate that keeps only files
981
     * with names shorter than 100 chars
982
     * <sample>
983
     *  File_Archive::predCustom("return strlen($name)<100;")
984
     *  File_Archive::predCustom("strlen($name)<100;")
985
     *  File_Archive::predCustom("strlen($name)<100")
986
     *  File_Archive::predCustom("strlen($source->getFilename())<100")
987
     * </sample>
988
     *
989
     * @param string $expression String containing an expression that evaluates
990
     *        to a boolean. If the expression doesn't contain a return
991
     *        statement, it will be added at the begining of the expression
992
     *        A ';' will be added at the end of the expression so that you don't
993
     *        have to write it. You may use the $name variable to refer to the
994
     *        current filename (with path...), $time for the modification time
995
     *        (unix timestamp), $size for the size of the file in bytes, $mime
996
     *        for the MIME type of the file
997
     * @see   File_Archive_Predicate_Custom
998
     */
999
    function predCustom($expression)
1000
    {
1001
        require_once "File/Archive/Predicate/Custom.php";
1002
        return new File_Archive_Predicate_Custom($expression);
1003
    }
1004
 
1005
    /**
1006
     * Send the files as a mail attachment
1007
     *
1008
     * @param Mail $mail Object used to send mail (see Mail::factory)
1009
     * @param array or String $to An array or a string with comma separated
1010
     *        recipients
1011
     * @param array $headers The headers that will be passed to the Mail_mime
1012
     *        object
1013
     * @param string $message Text body of the mail
1014
     * @see File_Archive_Writer_Mail
1015
     */
1016
    function toMail($to, $headers, $message, $mail = null)
1017
    {
1018
        require_once "File/Archive/Writer/Mail.php";
1019
        return new File_Archive_Writer_Mail($to, $headers, $message, $mail);
1020
    }
1021
    /**
1022
     * Write the files on the hard drive
1023
     *
1024
     * @param string $baseDir if specified, the files will be created in that
1025
     *        directory. If they don't exist, the directories will automatically
1026
     *        be created
1027
     * @see   File_Archive_Writer_Files
1028
     */
1029
    function toFiles($baseDir = "")
1030
    {
1031
        require_once "File/Archive/Writer/Files.php";
1032
        return new File_Archive_Writer_Files($baseDir);
1033
    }
1034
    /**
1035
     * Send the content of the files to a memory buffer
1036
     *
1037
     * toMemory returns a writer where the data will be written.
1038
     * In this case, the data is accessible using the getData member
1039
     *
1040
     * toVariable returns a writer that will write into the given
1041
     * variable
1042
     *
1043
     * @param out $data if specified, the data will be written to this buffer
1044
     *        Else, you can retrieve the buffer with the
1045
     *        File_Archive_Writer_Memory::getData() function
1046
     * @see   File_Archive_Writer_Memory
1047
     */
1048
    function toMemory()
1049
    {
1050
        $v = '';
1051
        return File_Archive::toVariable($v);
1052
    }
1053
    function toVariable(&$v)
1054
    {
1055
        require_once "File/Archive/Writer/Memory.php";
1056
        return new File_Archive_Writer_Memory($v);
1057
    }
1058
    /**
1059
     * Duplicate the writing operation on two writers
1060
     *
1061
     * @param File_Archive_Writer $a, $b writers where data will be duplicated
1062
     * @see File_Archive_Writer_Multi
1063
     */
1064
    function toMulti(&$aC, &$bC)
1065
    {
1066
        $a =& File_Archive::_convertToWriter($aC);
1067
        $b =& File_Archive::_convertToWriter($bC);
1068
 
1069
        if (PEAR::isError($a)) {
1070
            return $a;
1071
        }
1072
        if (PEAR::isError($b)) {
1073
            return $b;
1074
        }
1075
 
1076
        require_once "File/Archive/Writer/Multi.php";
1077
        $writer = new File_Archive_Writer_Multi();
1078
        $writer->addWriter($a);
1079
        $writer->addWriter($b);
1080
        return $writer;
1081
    }
1082
    /**
1083
     * Send the content of the files to the standard output (so to the client
1084
     * for a website)
1085
     *
1086
     * @param bool $sendHeaders If true some headers will be sent to force the
1087
     *        download of the file. Default value is true
1088
     * @see   File_Archive_Writer_Output
1089
     */
1090
    function toOutput($sendHeaders = true)
1091
    {
1092
        require_once "File/Archive/Writer/Output.php";
1093
        return new File_Archive_Writer_Output($sendHeaders);
1094
    }
1095
    /**
1096
     * Compress the data to a tar, gz, tar/gz or zip format
1097
     *
1098
     * @param string $filename name of the archive file
1099
     * @param File_Archive_Writer $innerWriter writer where the archive will be
1100
     *        written
1101
     * @param string $type can be one of tgz, tbz, tar, zip, gz, gzip, bz2,
1102
     *        bzip2 (default is the extension of $filename) or any composition
1103
     *        of them (for example tar.gz or tar.bz2). The case of this
1104
     *        parameter is not important.
1105
     * @param array $stat Statistics of the archive (see stat function)
1106
     * @param bool $autoClose If set to true, $innerWriter will be closed when
1107
     *        the returned archive is close. Default value is true.
1108
     */
1109
    function toArchive($filename, &$toConvert, $type = null,
1110
                       $stat = array(), $autoClose = true)
1111
    {
1112
        $innerWriter =& File_Archive::_convertToWriter($toConvert);
1113
        if (PEAR::isError($innerWriter)) {
1114
            return $innerWriter;
1115
        }
1116
        $shortcuts = array("tgz"   , "tbz"    );
1117
        $reals     = array("tar.gz", "tar.bz2");
1118
 
1119
        if ($type === null) {
1120
            $extensions = strtolower($filename);
1121
        } else {
1122
            $extensions = strtolower($type);
1123
        }
1124
        $extensions = explode('.', str_replace($shortcuts, $reals, $extensions));
1125
        if ($innerWriter !== null) {
1126
            $writer =& $innerWriter;
1127
        } else {
1128
            $writer = File_Archive::toFiles();
1129
        }
1130
        $nbCompressions = 0;
1131
        $currentFilename = $filename;
1132
        while (($extension = array_pop($extensions)) !== null) {
1133
            unset($next);
1134
            switch($extension) {
1135
            case "tar":
1136
                require_once "File/Archive/Writer/Tar.php";
1137
                $next = new File_Archive_Writer_Tar(
1138
                    $currentFilename, $writer, $stat, $autoClose
1139
                );
1140
                unset($writer); $writer =& $next;
1141
                break;
1142
            case "zip":
1143
                require_once "File/Archive/Writer/Zip.php";
1144
                $next = new File_Archive_Writer_Zip(
1145
                    $currentFilename, $writer, $stat, $autoClose
1146
                );
1147
                unset($writer); $writer =& $next;
1148
                break;
1149
            case "gz":
1150
            case "gzip":
1151
                require_once "File/Archive/Writer/Gzip.php";
1152
                $next = new File_Archive_Writer_Gzip(
1153
                    $currentFilename, $writer, $stat, $autoClose
1154
                );
1155
                unset($writer); $writer =& $next;
1156
                break;
1157
            case "bz2":
1158
            case "bzip2":
1159
                require_once "File/Archive/Writer/Bzip2.php";
1160
                $next = new File_Archive_Writer_Bzip2(
1161
                    $currentFilename, $writer, $stat, $autoClose
1162
                );
1163
                unset($writer); $writer =& $next;
1164
                break;
1165
            case "deb":
1166
            case "ar":
1167
                require_once "File/Archive/Writer/Ar.php";
1168
                $next = new File_Archive_Writer_Ar(
1169
                    $currentFilename, $writer, $stat, $autoClose
1170
                );
1171
                unset($writer); $writer =& $next;
1172
                break;
1173
            default:
1174
                if ($type !== null || $nbCompressions == 0) {
1175
                    return PEAR::raiseError("Archive $extension unknown");
1176
                }
1177
                break;
1178
            }
1179
            $nbCompressions ++;
1180
            $autoClose = true;
1181
            $currentFilename = implode(".", $extensions);
1182
        }
1183
        return $writer;
1184
    }
1185
 
1186
 
1187
    /**
1188
     * File_Archive::extract($source, $dest) is equivalent to $source->extract($dest)
1189
     * If $source is a PEAR error, the error will be returned
1190
     * It is thus easier to use this function than $source->extract, since it reduces the number of
1191
     * error checking and doesn't force you to define a variable $source
1192
     *
1193
     * You may use strings as source and dest. In that case the source is automatically
1194
     * converted to a reader using File_Archive::read and the dest is converted to a
1195
     * writer using File_Archive::appender
1196
     * Since PHP doesn't allow to pass literal strings by ref, you will have to use temporary
1197
     * variables.
1198
     * File_Archive::extract($src = 'archive.zip/', $dest = 'dir') will extract the archive to 'dir'
1199
     * It is the same as
1200
     * File_Archive::extract(
1201
     *    File_Archive::read('archive.zip/'),
1202
     *    File_Archive::appender('dir')
1203
     * );
1204
     * You may use any variable in the extract function ($from/$to, $a/$b...).
1205
     *
1206
     * @param File_Archive_Reader $source The source that will be read
1207
     * @param File_Archive_Writer $dest Where to copy $source files
1208
     * @param bool $autoClose if true (default), $dest will be closed after the extraction
1209
     * @param int $bufferSize Size of the buffer to use to move data from the reader to the buffer
1210
     *        If $bufferSize <= 0 (default), the blockSize option is used
1211
     *        You shouldn't need to change that
1212
     * @return null or a PEAR error if an error occured
1213
     */
1214
    function extract(&$sourceToConvert, &$destToConvert, $autoClose = true, $bufferSize = 0)
1215
    {
1216
        $source =& File_Archive::_convertToReader($sourceToConvert);
1217
        if (PEAR::isError($source)) {
1218
            return $source;
1219
        }
1220
        $dest =& File_Archive::_convertToWriter($destToConvert);
1221
        return $source->extract($dest, $autoClose, $bufferSize);
1222
    }
1223
 
1224
    /**
1225
     * Create a writer that can be used to append files to an archive inside a source
1226
     * If the archive can't be found in the source, it will be created
1227
     * If source is set to null, File_Archive::toFiles will be assumed
1228
     * If type is set to null, the type of the archive will be determined looking at
1229
     * the extension in the URL
1230
     * stat is the array of stat (returned by stat() PHP function of Reader getStat())
1231
     * to use if the archive must be created
1232
     *
1233
     * This function allows to create or append data to nested archives. Only one
1234
     * archive will be created and if your creation requires creating several nested
1235
     * archives, a PEAR error will be returned
1236
     *
1237
     * After this call, $source will be closed and should not be used until the
1238
     * returned writer is closed.
1239
     *
1240
     * @param File_Archive_Reader $source A reader where some files will be appended
1241
     * @param string $URL URL to reach the archive in the source.
1242
     *        if $URL is null, a writer to append files to the $source reader will
1243
     *        be returned
1244
     * @param bool $unique If true, the duplicate files will be deleted on close
1245
     *        Default is false (and setting it to true may have some performance
1246
     *        consequences)
1247
     * @param string $type Extension of the archive (or null to use the one in the URL)
1248
     * @param array $stat Used only if archive is created, array of stat as returned
1249
     *        by PHP stat function or Reader getStat function: stats of the archive)
1250
     *        Time (index 9) will be overwritten to current time
1251
     * @return File_Archive_Writer a writer that you can use to append files to the reader
1252
     */
1253
    function appenderFromSource(&$toConvert, $URL = null, $unique = null,
1254
                                 $type = null, $stat = array())
1255
    {
1256
        $source =& File_Archive::_convertToReader($toConvert);
1257
        if (PEAR::isError($source)) {
1258
            return $source;
1259
        }
1260
        if ($unique == null) {
1261
            $unique = File_Archive::getOption("appendRemoveDuplicates");
1262
        }
1263
 
1264
        //Do not report the fact that the archive does not exist as an error
1265
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1266
 
1267
        if ($URL === null) {
1268
            $result =& $source;
1269
        } else {
1270
            if ($type === null) {
1271
                $result = File_Archive::_readSource($source, $URL.'/', $reachable, $baseDir);
1272
            } else {
1273
                $result = File_Archive::readArchive(
1274
                            $type,
1275
                            File_Archive::_readSource($source, $URL, $reachable, $baseDir)
1276
                          );
1277
            }
1278
        }
1279
 
1280
        PEAR::popErrorHandling();
1281
 
1282
        if (!PEAR::isError($result)) {
1283
            if ($unique) {
1284
                require_once "File/Archive/Writer/UniqueAppender.php";
1285
                return new File_Archive_Writer_UniqueAppender($result);
1286
            } else {
1287
                return $result->makeAppendWriter();
1288
            }
1289
        }
1290
 
1291
        //The source can't be found and has to be created
1292
        $stat[9] = $stat['mtime'] = time();
1293
 
1294
        if (empty($baseDir)) {
1295
            if ($source !== null) {
1296
                $writer =& $source->makeWriter();
1297
            } else {
1298
                $writer =& File_Archive::toFiles();
1299
            }
1300
            if (PEAR::isError($writer)) {
1301
                return $writer;
1302
            }
1303
 
1304
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1305
            $result = File_Archive::toArchive($reachable, $writer, $type);
1306
            PEAR::popErrorHandling();
1307
 
1308
            if (PEAR::isError($result)) {
1309
                $result = File_Archive::toFiles($reachable);
1310
            }
1311
        } else {
1312
            $reachedSource = File_Archive::readSource($source, $reachable);
1313
            if (PEAR::isError($reachedSource)) {
1314
                return $reachedSource;
1315
            }
1316
            $writer = $reachedSource->makeWriter();
1317
            if (PEAR::isError($writer)) {
1318
                return $writer;
1319
            }
1320
 
1321
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1322
            $result = File_Archive::toArchive($baseDir, $writer, $type);
1323
            PEAR::popErrorHandling();
1324
 
1325
            if (PEAR::isError($result)) {
1326
                require_once "File/Archive/Writer/AddBaseName.php";
1327
                $result = new File_Archive_Writer_AddBaseName(
1328
                                       $baseDir, $writer);
1329
                if (PEAR::isError($result)) {
1330
                    return $result;
1331
                }
1332
            }
1333
        }
1334
        return $result;
1335
    }
1336
 
1337
    /**
1338
     * Create a writer that allows appending new files to an existing archive
1339
     * This function actes as appendToSource with source being the system files
1340
     * $URL can't be null here
1341
     *
1342
     * @param File_Archive_Reader $source A reader where some files will be appended
1343
     * @return File_Archive_Writer a writer that you can use to append files to the reader
1344
     */
1345
    function appender($URL, $unique = null, $type = null, $stat = array())
1346
    {
1347
        $source = null;
1348
        return File_Archive::appenderFromSource($source, $URL, $unique, $type, $stat);
1349
    }
1350
 
1351
    /**
1352
     * Remove the files that follow a given predicate from the source
1353
     * If URL is null, the files will be removed from the source directly
1354
     * Else, URL must link to a source from which the files will be removed
1355
     *
1356
     * @param File_Archive_Predicate $pred The files that follow the predicate
1357
     *        (for which $pred->isTrue($source) is true) will be erased
1358
     * @param File_Archive_Reader $source A reader that contains the files to remove
1359
     */
1360
    function removeFromSource(&$pred, &$toConvert, $URL = null)
1361
    {
1362
        $source =& File_Archive::_convertToReader($toConvert);
1363
        if (PEAR::isError($source)) {
1364
            return $source;
1365
        }
1366
        if ($URL === null) {
1367
            $result = &$source;
1368
        } else {
1369
            if (substr($URL, -1) !== '/') {
1370
                $URL .= '/';
1371
            }
1372
            $result = File_Archive::readSource($source, $URL);
1373
        }
1374
 
1375
        $writer = $result->makeWriterRemoveFiles($pred);
1376
        if (PEAR::isError($writer)) {
1377
            return $writer;
1378
        }
1379
        $writer->close();
1380
    }
1381
 
1382
    /**
1383
     * Remove the files that follow a given predicate from the archive specified
1384
     * in $URL
1385
     *
1386
     * @param $URL URL of the archive where some files must be removed
1387
     */
1388
    function remove($pred, $URL)
1389
    {
1390
        $source = null;
1391
        return File_Archive::removeFromSource($pred, $source, $URL);
1392
    }
1393
 
1394
    /**
1395
     * Remove duplicates from a source, keeping the most recent one (or the one that has highest pos in
1396
     * the archive if the files have same date or no date specified)
1397
     *
1398
     * @param File_Archive_Reader a reader that may contain duplicates
1399
     */
1400
    function removeDuplicatesFromSource(&$toConvert, $URL = null)
1401
    {
1402
        $source =& File_Archive::_convertToReader($toConvert);
1403
        if (PEAR::isError($source)) {
1404
            return $source;
1405
        }
1406
        if ($URL !== null && substr($URL, -1) != '/') {
1407
            $URL .= '/';
1408
        }
1409
 
1410
        if ($source === null) {
1411
            $source = File_Archive::read($URL);
1412
        }
1413
 
1414
        require_once "File/Archive/Predicate/Duplicate.php";
1415
        $pred = new File_Archive_Predicate_Duplicate($source);
1416
        $source->close();
1417
        return File_Archive::removeFromSource(
1418
            $pred,
1419
            $source,
1420
            null
1421
        );
1422
    }
1423
 
1424
    /**
1425
     * Remove duplicates from the archive specified in the URL
1426
     */
1427
    function removeDuplicates($URL)
1428
    {
1429
        $source = null;
1430
        return File_Archive::removeDuplicatesFromSource($source, $URL);
1431
    }
1432
}
1433
 
1434
?>