source: temp/trunk/data/module/Tar.php @ 5451

Revision 5451, 57.7 KB checked in by kakinaka, 20 years ago (diff)

* empty log message *

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1<?php
2/* vim: set ts=4 sw=4: */
3// +----------------------------------------------------------------------+
4// | PHP Version 4                                                        |
5// +----------------------------------------------------------------------+
6// | Copyright (c) 1997-2003 The PHP Group                                |
7// +----------------------------------------------------------------------+
8// | This source file is subject to version 3.0 of the PHP license,       |
9// | that is bundled with this package in the file LICENSE, and is        |
10// | available through the world-wide-web at the following url:           |
11// | http://www.php.net/license/3_0.txt.                                  |
12// | If you did not receive a copy of the PHP license and are unable to   |
13// | obtain it through the world-wide-web, please send a note to          |
14// | [email protected] so we can mail you a copy immediately.               |
15// +----------------------------------------------------------------------+
16// | Author: Vincent Blavet <[email protected]>                      |
17// +----------------------------------------------------------------------+
18//
19// $Id$
20
21require_once 'PEAR.php';
22
23
24define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
25
26/**
27* Creates a (compressed) Tar archive
28*
29* @author   Vincent Blavet <[email protected]>
30* @version  $Revision$
31* @package  Archive
32*/
33class Archive_Tar extends PEAR
34{
35    /**
36    * @var string Name of the Tar
37    */
38    var $_tarname='';
39
40    /**
41    * @var boolean if true, the Tar file will be gzipped
42    */
43    var $_compress=false;
44
45    /**
46    * @var string Type of compression : 'none', 'gz' or 'bz2'
47    */
48    var $_compress_type='none';
49
50    /**
51    * @var string Explode separator
52    */
53    var $_separator=' ';
54
55    /**
56    * @var file descriptor
57    */
58    var $_file=0;
59
60    /**
61    * @var string Local Tar name of a remote Tar (http:// or ftp://)
62    */
63    var $_temp_tarname='';
64
65    // {{{ constructor
66    /**
67    * Archive_Tar Class constructor. This flavour of the constructor only
68    * declare a new Archive_Tar object, identifying it by the name of the
69    * tar file.
70    * If the compress argument is set the tar will be read or created as a
71    * gzip or bz2 compressed TAR file.
72    *
73    * @param    string  $p_tarname  The name of the tar archive to create
74    * @param    string  $p_compress can be null, 'gz' or 'bz2'. This
75    *                   parameter indicates if gzip or bz2 compression
76    *                   is required.  For compatibility reason the
77    *                   boolean value 'true' means 'gz'.
78    * @access public
79    */
80    function Archive_Tar($p_tarname, $p_compress = null)
81    {
82        $this->PEAR();
83        $this->_compress = false;
84        $this->_compress_type = 'none';
85        if (($p_compress === null) || ($p_compress == '')) {
86            if (@file_exists($p_tarname)) {
87                if ($fp = @fopen($p_tarname, "rb")) {
88                    // look for gzip magic cookie
89                    $data = fread($fp, 2);
90                    fclose($fp);
91                    if ($data == "\37\213") {
92                        $this->_compress = true;
93                        $this->_compress_type = 'gz';
94                    // No sure it's enought for a magic code ....
95                    } elseif ($data == "BZ") {
96                        $this->_compress = true;
97                        $this->_compress_type = 'bz2';
98                    }
99                }
100            } else {
101                // probably a remote file or some file accessible
102                // through a stream interface
103                if (substr($p_tarname, -2) == 'gz') {
104                    $this->_compress = true;
105                    $this->_compress_type = 'gz';
106                } elseif ((substr($p_tarname, -3) == 'bz2') ||
107                          (substr($p_tarname, -2) == 'bz')) {
108                    $this->_compress = true;
109                    $this->_compress_type = 'bz2';
110                }
111            }
112        } else {
113            if (($p_compress === true) || ($p_compress == 'gz')) {
114                $this->_compress = true;
115                $this->_compress_type = 'gz';
116            } else if ($p_compress == 'bz2') {
117                $this->_compress = true;
118                $this->_compress_type = 'bz2';
119            } else {
120                die("Unsupported compression type '$p_compress'\n".
121                    "Supported types are 'gz' and 'bz2'.\n");
122                return false;
123            }
124        }
125        $this->_tarname = $p_tarname;
126        if ($this->_compress) { // assert zlib or bz2 extension support
127            if ($this->_compress_type == 'gz')
128                $extname = 'zlib';
129            else if ($this->_compress_type == 'bz2')
130                $extname = 'bz2';
131
132            if (!extension_loaded($extname)) {
133                PEAR::loadExtension($extname);
134            }
135            if (!extension_loaded($extname)) {
136                die("The extension '$extname' couldn't be found.\n".
137                    "Please make sure your version of PHP was built ".
138                    "with '$extname' support.\n");
139                return false;
140            }
141        }
142    }
143    // }}}
144
145    // {{{ destructor
146    function _Archive_Tar()
147    {
148        $this->_close();
149        // ----- Look for a local copy to delete
150        if ($this->_temp_tarname != '')
151            @unlink($this->_temp_tarname);
152        $this->_PEAR();
153    }
154    // }}}
155
156    // {{{ create()
157    /**
158    * This method creates the archive file and add the files / directories
159    * that are listed in $p_filelist.
160    * If a file with the same name exist and is writable, it is replaced
161    * by the new tar.
162    * The method return false and a PEAR error text.
163    * The $p_filelist parameter can be an array of string, each string
164    * representing a filename or a directory name with their path if
165    * needed. It can also be a single string with names separated by a
166    * single blank.
167    * For each directory added in the archive, the files and
168    * sub-directories are also added.
169    * See also createModify() method for more details.
170    *
171    * @param array  $p_filelist An array of filenames and directory names, or a
172    *                           single string with names separated by a single
173    *                           blank space.
174    * @return                   true on success, false on error.
175    * @see createModify()
176    * @access public
177    */
178    function create($p_filelist)
179    {
180        return $this->createModify($p_filelist, '', '');
181    }
182    // }}}
183
184    // {{{ add()
185    /**
186    * This method add the files / directories that are listed in $p_filelist in
187    * the archive. If the archive does not exist it is created.
188    * The method return false and a PEAR error text.
189    * The files and directories listed are only added at the end of the archive,
190    * even if a file with the same name is already archived.
191    * See also createModify() method for more details.
192    *
193    * @param array  $p_filelist An array of filenames and directory names, or a
194    *                           single string with names separated by a single
195    *                           blank space.
196    * @return                   true on success, false on error.
197    * @see createModify()
198    * @access public
199    */
200    function add($p_filelist)
201    {
202        return $this->addModify($p_filelist, '', '');
203    }
204    // }}}
205
206    // {{{ extract()
207    function extract($p_path='')
208    {
209        return $this->extractModify($p_path, '');
210    }
211    // }}}
212
213    // {{{ listContent()
214    function listContent()
215    {
216        $v_list_detail = array();
217
218        if ($this->_openRead()) {
219            if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
220                unset($v_list_detail);
221                $v_list_detail = 0;
222            }
223            $this->_close();
224        }
225
226        return $v_list_detail;
227    }
228    // }}}
229
230    // {{{ createModify()
231    /**
232    * This method creates the archive file and add the files / directories
233    * that are listed in $p_filelist.
234    * If the file already exists and is writable, it is replaced by the
235    * new tar. It is a create and not an add. If the file exists and is
236    * read-only or is a directory it is not replaced. The method return
237    * false and a PEAR error text.
238    * The $p_filelist parameter can be an array of string, each string
239    * representing a filename or a directory name with their path if
240    * needed. It can also be a single string with names separated by a
241    * single blank.
242    * The path indicated in $p_remove_dir will be removed from the
243    * memorized path of each file / directory listed when this path
244    * exists. By default nothing is removed (empty path '')
245    * The path indicated in $p_add_dir will be added at the beginning of
246    * the memorized path of each file / directory listed. However it can
247    * be set to empty ''. The adding of a path is done after the removing
248    * of path.
249    * The path add/remove ability enables the user to prepare an archive
250    * for extraction in a different path than the origin files are.
251    * See also addModify() method for file adding properties.
252    *
253    * @param array  $p_filelist     An array of filenames and directory names,
254    *                               or a single string with names separated by
255    *                               a single blank space.
256    * @param string $p_add_dir      A string which contains a path to be added
257    *                               to the memorized path of each element in
258    *                               the list.
259    * @param string $p_remove_dir   A string which contains a path to be
260    *                               removed from the memorized path of each
261    *                               element in the list, when relevant.
262    * @return boolean               true on success, false on error.
263    * @access public
264    * @see addModify()
265    */
266    function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
267    {
268        $v_result = true;
269
270        if (!$this->_openWrite())
271            return false;
272
273        if ($p_filelist != '') {
274            if (is_array($p_filelist))
275                $v_list = $p_filelist;
276            elseif (is_string($p_filelist))
277                $v_list = explode($this->_separator, $p_filelist);
278            else {
279                $this->_cleanFile();
280                $this->_error('Invalid file list');
281                return false;
282            }
283
284            $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
285        }
286
287        if ($v_result) {
288            $this->_writeFooter();
289            $this->_close();
290        } else
291            $this->_cleanFile();
292
293        return $v_result;
294    }
295    // }}}
296
297    // {{{ addModify()
298    /**
299    * This method add the files / directories listed in $p_filelist at the
300    * end of the existing archive. If the archive does not yet exists it
301    * is created.
302    * The $p_filelist parameter can be an array of string, each string
303    * representing a filename or a directory name with their path if
304    * needed. It can also be a single string with names separated by a
305    * single blank.
306    * The path indicated in $p_remove_dir will be removed from the
307    * memorized path of each file / directory listed when this path
308    * exists. By default nothing is removed (empty path '')
309    * The path indicated in $p_add_dir will be added at the beginning of
310    * the memorized path of each file / directory listed. However it can
311    * be set to empty ''. The adding of a path is done after the removing
312    * of path.
313    * The path add/remove ability enables the user to prepare an archive
314    * for extraction in a different path than the origin files are.
315    * If a file/dir is already in the archive it will only be added at the
316    * end of the archive. There is no update of the existing archived
317    * file/dir. However while extracting the archive, the last file will
318    * replace the first one. This results in a none optimization of the
319    * archive size.
320    * If a file/dir does not exist the file/dir is ignored. However an
321    * error text is send to PEAR error.
322    * If a file/dir is not readable the file/dir is ignored. However an
323    * error text is send to PEAR error.
324    *
325    * @param array      $p_filelist     An array of filenames and directory
326    *                                   names, or a single string with names
327    *                                   separated by a single blank space.
328    * @param string     $p_add_dir      A string which contains a path to be
329    *                                   added to the memorized path of each
330    *                                   element in the list.
331    * @param string     $p_remove_dir   A string which contains a path to be
332    *                                   removed from the memorized path of
333    *                                   each element in the list, when
334    *                                   relevant.
335    * @return                           true on success, false on error.
336    * @access public
337    */
338    function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
339    {
340        $v_result = true;
341
342        if (!$this->_isArchive())
343            $v_result = $this->createModify($p_filelist, $p_add_dir,
344                                            $p_remove_dir);
345        else {
346            if (is_array($p_filelist))
347                $v_list = $p_filelist;
348            elseif (is_string($p_filelist))
349                $v_list = explode($this->_separator, $p_filelist);
350            else {
351                $this->_error('Invalid file list');
352                return false;
353            }
354
355            $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
356        }
357
358        return $v_result;
359    }
360    // }}}
361
362    // {{{ addString()
363    /**
364    * This method add a single string as a file at the
365    * end of the existing archive. If the archive does not yet exists it
366    * is created.
367    *
368    * @param string     $p_filename     A string which contains the full
369    *                                   filename path that will be associated
370    *                                   with the string.
371    * @param string     $p_string       The content of the file added in
372    *                                   the archive.
373    * @return                           true on success, false on error.
374    * @access public
375    */
376    function addString($p_filename, $p_string)
377    {
378        $v_result = true;
379       
380        if (!$this->_isArchive()) {
381            if (!$this->_openWrite()) {
382                return false;
383            }
384            $this->_close();
385        }
386       
387        if (!$this->_openAppend())
388            return false;
389
390        // Need to check the get back to the temporary file ? ....
391        $v_result = $this->_addString($p_filename, $p_string);
392
393        $this->_writeFooter();
394
395        $this->_close();
396
397        return $v_result;
398    }
399    // }}}
400
401    // {{{ extractModify()
402    /**
403    * This method extract all the content of the archive in the directory
404    * indicated by $p_path. When relevant the memorized path of the
405    * files/dir can be modified by removing the $p_remove_path path at the
406    * beginning of the file/dir path.
407    * While extracting a file, if the directory path does not exists it is
408    * created.
409    * While extracting a file, if the file already exists it is replaced
410    * without looking for last modification date.
411    * While extracting a file, if the file already exists and is write
412    * protected, the extraction is aborted.
413    * While extracting a file, if a directory with the same name already
414    * exists, the extraction is aborted.
415    * While extracting a directory, if a file with the same name already
416    * exists, the extraction is aborted.
417    * While extracting a file/directory if the destination directory exist
418    * and is write protected, or does not exist but can not be created,
419    * the extraction is aborted.
420    * If after extraction an extracted file does not show the correct
421    * stored file size, the extraction is aborted.
422    * When the extraction is aborted, a PEAR error text is set and false
423    * is returned. However the result can be a partial extraction that may
424    * need to be manually cleaned.
425    *
426    * @param string $p_path         The path of the directory where the
427    *                               files/dir need to by extracted.
428    * @param string $p_remove_path  Part of the memorized path that can be
429    *                               removed if present at the beginning of
430    *                               the file/dir path.
431    * @return boolean               true on success, false on error.
432    * @access public
433    * @see extractList()
434    */
435    function extractModify($p_path, $p_remove_path)
436    {
437        $v_result = true;
438        $v_list_detail = array();
439
440        if ($v_result = $this->_openRead()) {
441            $v_result = $this->_extractList($p_path, $v_list_detail,
442                                            "complete", 0, $p_remove_path);
443            $this->_close();
444        }
445
446        return $v_result;
447    }
448    // }}}
449
450    // {{{ extractInString()
451    /**
452    * This method extract from the archive one file identified by $p_filename.
453    * The return value is a string with the file content, or NULL on error.
454    * @param string $p_filename     The path of the file to extract in a string.
455    * @return                       a string with the file content or NULL.
456    * @access public
457    */
458    function extractInString($p_filename)
459    {
460        if ($this->_openRead()) {
461            $v_result = $this->_extractInString($p_filename);
462            $this->_close();
463        } else {
464            $v_result = NULL;
465        }
466
467        return $v_result;
468    }
469    // }}}
470
471    // {{{ extractList()
472    /**
473    * This method extract from the archive only the files indicated in the
474    * $p_filelist. These files are extracted in the current directory or
475    * in the directory indicated by the optional $p_path parameter.
476    * If indicated the $p_remove_path can be used in the same way as it is
477    * used in extractModify() method.
478    * @param array  $p_filelist     An array of filenames and directory names,
479    *                               or a single string with names separated
480    *                               by a single blank space.
481    * @param string $p_path         The path of the directory where the
482    *                               files/dir need to by extracted.
483    * @param string $p_remove_path  Part of the memorized path that can be
484    *                               removed if present at the beginning of
485    *                               the file/dir path.
486    * @return                       true on success, false on error.
487    * @access public
488    * @see extractModify()
489    */
490    function extractList($p_filelist, $p_path='', $p_remove_path='')
491    {
492        $v_result = true;
493        $v_list_detail = array();
494
495        if (is_array($p_filelist))
496            $v_list = $p_filelist;
497        elseif (is_string($p_filelist))
498            $v_list = explode($this->_separator, $p_filelist);
499        else {
500            $this->_error('Invalid string list');
501            return false;
502        }
503
504        if ($v_result = $this->_openRead()) {
505            $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
506                                            $v_list, $p_remove_path);
507            $this->_close();
508        }
509
510        return $v_result;
511    }
512    // }}}
513
514    // {{{ setAttribute()
515    /**
516    * This method set specific attributes of the archive. It uses a variable
517    * list of parameters, in the format attribute code + attribute values :
518    * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
519    * @param mixed $argv            variable list of attributes and values
520    * @return                       true on success, false on error.
521    * @access public
522    */
523    function setAttribute()
524    {
525        $v_result = true;
526       
527        // ----- Get the number of variable list of arguments
528        if (($v_size = func_num_args()) == 0) {
529            return true;
530        }
531       
532        // ----- Get the arguments
533        $v_att_list = &func_get_args();
534
535        // ----- Read the attributes
536        $i=0;
537        while ($i<$v_size) {
538
539            // ----- Look for next option
540            switch ($v_att_list[$i]) {
541                // ----- Look for options that request a string value
542                case ARCHIVE_TAR_ATT_SEPARATOR :
543                    // ----- Check the number of parameters
544                    if (($i+1) >= $v_size) {
545                        $this->_error('Invalid number of parameters for '
546                                      .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
547                        return false;
548                    }
549
550                    // ----- Get the value
551                    $this->_separator = $v_att_list[$i+1];
552                    $i++;
553                break;
554
555                default :
556                    $this->_error('Unknow attribute code '.$v_att_list[$i].'');
557                    return false;
558            }
559
560            // ----- Next attribute
561            $i++;
562        }
563
564        return $v_result;
565    }
566    // }}}
567
568    // {{{ _error()
569    function _error($p_message)
570    {
571        // ----- To be completed
572        $this->raiseError($p_message);
573    }
574    // }}}
575
576    // {{{ _warning()
577    function _warning($p_message)
578    {
579        // ----- To be completed
580        $this->raiseError($p_message);
581    }
582    // }}}
583
584    // {{{ _isArchive()
585    function _isArchive($p_filename=NULL)
586    {
587        if ($p_filename == NULL) {
588            $p_filename = $this->_tarname;
589        }
590        clearstatcache();
591        return @is_file($p_filename);
592    }
593    // }}}
594
595    // {{{ _openWrite()
596    function _openWrite()
597    {
598        if ($this->_compress_type == 'gz')
599            $this->_file = @gzopen($this->_tarname, "wb9");
600        else if ($this->_compress_type == 'bz2')
601            $this->_file = @bzopen($this->_tarname, "wb");
602        else if ($this->_compress_type == 'none')
603            $this->_file = @fopen($this->_tarname, "wb");
604        else
605            $this->_error('Unknown or missing compression type ('
606                          .$this->_compress_type.')');
607
608        if ($this->_file == 0) {
609            $this->_error('Unable to open in write mode \''
610                          .$this->_tarname.'\'');
611            return false;
612        }
613
614        return true;
615    }
616    // }}}
617
618    // {{{ _openRead()
619    function _openRead()
620    {
621        if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
622
623          // ----- Look if a local copy need to be done
624          if ($this->_temp_tarname == '') {
625              $this->_temp_tarname = uniqid('tar').'.tmp';
626              if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
627                $this->_error('Unable to open in read mode \''
628                              .$this->_tarname.'\'');
629                $this->_temp_tarname = '';
630                return false;
631              }
632              if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
633                $this->_error('Unable to open in write mode \''
634                              .$this->_temp_tarname.'\'');
635                $this->_temp_tarname = '';
636                return false;
637              }
638              while ($v_data = @fread($v_file_from, 1024))
639                  @fwrite($v_file_to, $v_data);
640              @fclose($v_file_from);
641              @fclose($v_file_to);
642          }
643
644          // ----- File to open if the local copy
645          $v_filename = $this->_temp_tarname;
646
647        } else
648          // ----- File to open if the normal Tar file
649          $v_filename = $this->_tarname;
650
651        if ($this->_compress_type == 'gz')
652            $this->_file = @gzopen($v_filename, "rb");
653        else if ($this->_compress_type == 'bz2')
654            $this->_file = @bzopen($v_filename, "rb");
655        else if ($this->_compress_type == 'none')
656            $this->_file = @fopen($v_filename, "rb");
657        else
658            $this->_error('Unknown or missing compression type ('
659                          .$this->_compress_type.')');
660
661        if ($this->_file == 0) {
662            $this->_error('Unable to open in read mode \''.$v_filename.'\'');
663            return false;
664        }
665
666        return true;
667    }
668    // }}}
669
670    // {{{ _openReadWrite()
671    function _openReadWrite()
672    {
673        if ($this->_compress_type == 'gz')
674            $this->_file = @gzopen($this->_tarname, "r+b");
675        else if ($this->_compress_type == 'bz2')
676            $this->_file = @bzopen($this->_tarname, "r+b");
677        else if ($this->_compress_type == 'none')
678            $this->_file = @fopen($this->_tarname, "r+b");
679        else
680            $this->_error('Unknown or missing compression type ('
681                          .$this->_compress_type.')');
682
683        if ($this->_file == 0) {
684            $this->_error('Unable to open in read/write mode \''
685                          .$this->_tarname.'\'');
686            return false;
687        }
688
689        return true;
690    }
691    // }}}
692
693    // {{{ _close()
694    function _close()
695    {
696        //if (isset($this->_file)) {
697        if (is_resource($this->_file)) {
698            if ($this->_compress_type == 'gz')
699                @gzclose($this->_file);
700            else if ($this->_compress_type == 'bz2')
701                @bzclose($this->_file);
702            else if ($this->_compress_type == 'none')
703                @fclose($this->_file);
704            else
705                $this->_error('Unknown or missing compression type ('
706                              .$this->_compress_type.')');
707
708            $this->_file = 0;
709        }
710
711        // ----- Look if a local copy need to be erase
712        // Note that it might be interesting to keep the url for a time : ToDo
713        if ($this->_temp_tarname != '') {
714            @unlink($this->_temp_tarname);
715            $this->_temp_tarname = '';
716        }
717
718        return true;
719    }
720    // }}}
721
722    // {{{ _cleanFile()
723    function _cleanFile()
724    {
725        $this->_close();
726
727        // ----- Look for a local copy
728        if ($this->_temp_tarname != '') {
729            // ----- Remove the local copy but not the remote tarname
730            @unlink($this->_temp_tarname);
731            $this->_temp_tarname = '';
732        } else {
733            // ----- Remove the local tarname file
734            @unlink($this->_tarname);
735        }
736        $this->_tarname = '';
737
738        return true;
739    }
740    // }}}
741
742    // {{{ _writeBlock()
743    function _writeBlock($p_binary_data, $p_len=null)
744    {
745      if (is_resource($this->_file)) {
746          if ($p_len === null) {
747              if ($this->_compress_type == 'gz')
748                  @gzputs($this->_file, $p_binary_data);
749              else if ($this->_compress_type == 'bz2')
750                  @bzwrite($this->_file, $p_binary_data);
751              else if ($this->_compress_type == 'none')
752                  @fputs($this->_file, $p_binary_data);
753              else
754                  $this->_error('Unknown or missing compression type ('
755                                .$this->_compress_type.')');
756          } else {
757              if ($this->_compress_type == 'gz')
758                  @gzputs($this->_file, $p_binary_data, $p_len);
759              else if ($this->_compress_type == 'bz2')
760                  @bzwrite($this->_file, $p_binary_data, $p_len);
761              else if ($this->_compress_type == 'none')
762                  @fputs($this->_file, $p_binary_data, $p_len);
763              else
764                  $this->_error('Unknown or missing compression type ('
765                                .$this->_compress_type.')');
766
767          }
768      }
769      return true;
770    }
771    // }}}
772
773    // {{{ _readBlock()
774    function _readBlock()
775    {
776      $v_block = null;
777      if (is_resource($this->_file)) {
778          if ($this->_compress_type == 'gz')
779              $v_block = @gzread($this->_file, 512);
780          else if ($this->_compress_type == 'bz2')
781              $v_block = @bzread($this->_file, 512);
782          else if ($this->_compress_type == 'none')
783              $v_block = @fread($this->_file, 512);
784          else
785              $this->_error('Unknown or missing compression type ('
786                            .$this->_compress_type.')');
787      }
788      return $v_block;
789    }
790    // }}}
791
792    // {{{ _jumpBlock()
793    function _jumpBlock($p_len=null)
794    {
795      if (is_resource($this->_file)) {
796          if ($p_len === null)
797              $p_len = 1;
798
799          if ($this->_compress_type == 'gz') {
800              @gzseek($this->_file, @gztell($this->_file)+($p_len*512));
801          }
802          else if ($this->_compress_type == 'bz2') {
803              // ----- Replace missing bztell() and bzseek()
804              for ($i=0; $i<$p_len; $i++)
805                  $this->_readBlock();
806          } else if ($this->_compress_type == 'none')
807              @fseek($this->_file, @ftell($this->_file)+($p_len*512));
808          else
809              $this->_error('Unknown or missing compression type ('
810                            .$this->_compress_type.')');
811
812      }
813      return true;
814    }
815    // }}}
816
817    // {{{ _writeFooter()
818    function _writeFooter()
819    {
820      if (is_resource($this->_file)) {
821          // ----- Write the last 0 filled block for end of archive
822          $v_binary_data = pack("a512", '');
823          $this->_writeBlock($v_binary_data);
824      }
825      return true;
826    }
827    // }}}
828
829    // {{{ _addList()
830    function _addList($p_list, $p_add_dir, $p_remove_dir)
831    {
832      $v_result=true;
833      $v_header = array();
834
835      // ----- Remove potential windows directory separator
836      $p_add_dir = $this->_translateWinPath($p_add_dir);
837      $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
838
839      if (!$this->_file) {
840          $this->_error('Invalid file descriptor');
841          return false;
842      }
843
844      if (sizeof($p_list) == 0)
845          return true;
846
847      foreach ($p_list as $v_filename) {
848          if (!$v_result) {
849              break;
850          }
851
852        // ----- Skip the current tar name
853        if ($v_filename == $this->_tarname)
854            continue;
855
856        if ($v_filename == '')
857            continue;
858
859        if (!file_exists($v_filename)) {
860            $this->_warning("File '$v_filename' does not exist");
861            continue;
862        }
863
864        // ----- Add the file or directory header
865        if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
866            return false;
867
868        if (@is_dir($v_filename)) {
869            if (!($p_hdir = opendir($v_filename))) {
870                $this->_warning("Directory '$v_filename' can not be read");
871                continue;
872            }
873            while (false !== ($p_hitem = readdir($p_hdir))) {
874                if (($p_hitem != '.') && ($p_hitem != '..')) {
875                    if ($v_filename != ".")
876                        $p_temp_list[0] = $v_filename.'/'.$p_hitem;
877                    else
878                        $p_temp_list[0] = $p_hitem;
879
880                    $v_result = $this->_addList($p_temp_list,
881                                                $p_add_dir,
882                                                $p_remove_dir);
883                }
884            }
885
886            unset($p_temp_list);
887            unset($p_hdir);
888            unset($p_hitem);
889        }
890      }
891
892      return $v_result;
893    }
894    // }}}
895
896    // {{{ _addFile()
897    function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
898    {
899      if (!$this->_file) {
900          $this->_error('Invalid file descriptor');
901          return false;
902      }
903
904      if ($p_filename == '') {
905          $this->_error('Invalid file name');
906          return false;
907      }
908
909      // ----- Calculate the stored filename
910      $p_filename = $this->_translateWinPath($p_filename, false);;
911      $v_stored_filename = $p_filename;
912      if (strcmp($p_filename, $p_remove_dir) == 0) {
913          return true;
914      }
915      if ($p_remove_dir != '') {
916          if (substr($p_remove_dir, -1) != '/')
917              $p_remove_dir .= '/';
918
919          if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
920              $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
921      }
922      $v_stored_filename = $this->_translateWinPath($v_stored_filename);
923      if ($p_add_dir != '') {
924          if (substr($p_add_dir, -1) == '/')
925              $v_stored_filename = $p_add_dir.$v_stored_filename;
926          else
927              $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
928      }
929
930      $v_stored_filename = $this->_pathReduction($v_stored_filename);
931
932      if ($this->_isArchive($p_filename)) {
933          if (($v_file = @fopen($p_filename, "rb")) == 0) {
934              $this->_warning("Unable to open file '".$p_filename
935                              ."' in binary read mode");
936              return true;
937          }
938
939          if (!$this->_writeHeader($p_filename, $v_stored_filename))
940              return false;
941
942          while (($v_buffer = fread($v_file, 512)) != '') {
943              $v_binary_data = pack("a512", "$v_buffer");
944              $this->_writeBlock($v_binary_data);
945          }
946
947          fclose($v_file);
948
949      } else {
950          // ----- Only header for dir
951          if (!$this->_writeHeader($p_filename, $v_stored_filename))
952              return false;
953      }
954
955      return true;
956    }
957    // }}}
958
959    // {{{ _addString()
960    function _addString($p_filename, $p_string)
961    {
962      if (!$this->_file) {
963          $this->_error('Invalid file descriptor');
964          return false;
965      }
966
967      if ($p_filename == '') {
968          $this->_error('Invalid file name');
969          return false;
970      }
971
972      // ----- Calculate the stored filename
973      $p_filename = $this->_translateWinPath($p_filename, false);;
974
975      if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
976                                    0, 0, "", 0, 0))
977          return false;
978
979      $i=0;
980      while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
981          $v_binary_data = pack("a512", $v_buffer);
982          $this->_writeBlock($v_binary_data);
983      }
984
985      return true;
986    }
987    // }}}
988
989    // {{{ _writeHeader()
990    function _writeHeader($p_filename, $p_stored_filename)
991    {
992        if ($p_stored_filename == '')
993            $p_stored_filename = $p_filename;
994        $v_reduce_filename = $this->_pathReduction($p_stored_filename);
995
996        if (strlen($v_reduce_filename) > 99) {
997          if (!$this->_writeLongHeader($v_reduce_filename))
998            return false;
999        }
1000
1001        $v_info = stat($p_filename);
1002        $v_uid = sprintf("%6s ", DecOct($v_info[4]));
1003        $v_gid = sprintf("%6s ", DecOct($v_info[5]));
1004        $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename)));
1005
1006        $v_mtime = sprintf("%11s", DecOct(filemtime($p_filename)));
1007
1008        if (@is_dir($p_filename)) {
1009          $v_typeflag = "5";
1010          $v_size = sprintf("%11s ", DecOct(0));
1011        } else {
1012          $v_typeflag = '';
1013          clearstatcache();
1014          $v_size = sprintf("%11s ", DecOct(filesize($p_filename)));
1015        }
1016
1017        $v_linkname = '';
1018
1019        $v_magic = '';
1020
1021        $v_version = '';
1022
1023        $v_uname = '';
1024
1025        $v_gname = '';
1026
1027        $v_devmajor = '';
1028
1029        $v_devminor = '';
1030
1031        $v_prefix = '';
1032
1033        $v_binary_data_first = pack("a100a8a8a8a12A12",
1034                                    $v_reduce_filename, $v_perms, $v_uid,
1035                                    $v_gid, $v_size, $v_mtime);
1036        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
1037                                   $v_typeflag, $v_linkname, $v_magic,
1038                                   $v_version, $v_uname, $v_gname,
1039                                   $v_devmajor, $v_devminor, $v_prefix, '');
1040
1041        // ----- Calculate the checksum
1042        $v_checksum = 0;
1043        // ..... First part of the header
1044        for ($i=0; $i<148; $i++)
1045            $v_checksum += ord(substr($v_binary_data_first,$i,1));
1046        // ..... Ignore the checksum value and replace it by ' ' (space)
1047        for ($i=148; $i<156; $i++)
1048            $v_checksum += ord(' ');
1049        // ..... Last part of the header
1050        for ($i=156, $j=0; $i<512; $i++, $j++)
1051            $v_checksum += ord(substr($v_binary_data_last,$j,1));
1052
1053        // ----- Write the first 148 bytes of the header in the archive
1054        $this->_writeBlock($v_binary_data_first, 148);
1055
1056        // ----- Write the calculated checksum
1057        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
1058        $v_binary_data = pack("a8", $v_checksum);
1059        $this->_writeBlock($v_binary_data, 8);
1060
1061        // ----- Write the last 356 bytes of the header in the archive
1062        $this->_writeBlock($v_binary_data_last, 356);
1063
1064        return true;
1065    }
1066    // }}}
1067
1068    // {{{ _writeHeaderBlock()
1069    function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
1070                               $p_type='', $p_uid=0, $p_gid=0)
1071    {
1072        $p_filename = $this->_pathReduction($p_filename);
1073
1074        if (strlen($p_filename) > 99) {
1075          if (!$this->_writeLongHeader($p_filename))
1076            return false;
1077        }
1078
1079        if ($p_type == "5") {
1080          $v_size = sprintf("%11s ", DecOct(0));
1081        } else {
1082          $v_size = sprintf("%11s ", DecOct($p_size));
1083        }
1084
1085        $v_uid = sprintf("%6s ", DecOct($p_uid));
1086        $v_gid = sprintf("%6s ", DecOct($p_gid));
1087        $v_perms = sprintf("%6s ", DecOct($p_perms));
1088
1089        $v_mtime = sprintf("%11s", DecOct($p_mtime));
1090
1091        $v_linkname = '';
1092
1093        $v_magic = '';
1094
1095        $v_version = '';
1096
1097        $v_uname = '';
1098
1099        $v_gname = '';
1100
1101        $v_devmajor = '';
1102
1103        $v_devminor = '';
1104
1105        $v_prefix = '';
1106
1107        $v_binary_data_first = pack("a100a8a8a8a12A12",
1108                                    $p_filename, $v_perms, $v_uid, $v_gid,
1109                                    $v_size, $v_mtime);
1110        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
1111                                   $p_type, $v_linkname, $v_magic,
1112                                   $v_version, $v_uname, $v_gname,
1113                                   $v_devmajor, $v_devminor, $v_prefix, '');
1114
1115        // ----- Calculate the checksum
1116        $v_checksum = 0;
1117        // ..... First part of the header
1118        for ($i=0; $i<148; $i++)
1119            $v_checksum += ord(substr($v_binary_data_first,$i,1));
1120        // ..... Ignore the checksum value and replace it by ' ' (space)
1121        for ($i=148; $i<156; $i++)
1122            $v_checksum += ord(' ');
1123        // ..... Last part of the header
1124        for ($i=156, $j=0; $i<512; $i++, $j++)
1125            $v_checksum += ord(substr($v_binary_data_last,$j,1));
1126
1127        // ----- Write the first 148 bytes of the header in the archive
1128        $this->_writeBlock($v_binary_data_first, 148);
1129
1130        // ----- Write the calculated checksum
1131        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
1132        $v_binary_data = pack("a8", $v_checksum);
1133        $this->_writeBlock($v_binary_data, 8);
1134
1135        // ----- Write the last 356 bytes of the header in the archive
1136        $this->_writeBlock($v_binary_data_last, 356);
1137
1138        return true;
1139    }
1140    // }}}
1141
1142    // {{{ _writeLongHeader()
1143    function _writeLongHeader($p_filename)
1144    {
1145        $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
1146
1147        $v_typeflag = 'L';
1148
1149        $v_linkname = '';
1150
1151        $v_magic = '';
1152
1153        $v_version = '';
1154
1155        $v_uname = '';
1156
1157        $v_gname = '';
1158
1159        $v_devmajor = '';
1160
1161        $v_devminor = '';
1162
1163        $v_prefix = '';
1164
1165        $v_binary_data_first = pack("a100a8a8a8a12A12",
1166                                    '././@LongLink', 0, 0, 0, $v_size, 0);
1167        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
1168                                   $v_typeflag, $v_linkname, $v_magic,
1169                                   $v_version, $v_uname, $v_gname,
1170                                   $v_devmajor, $v_devminor, $v_prefix, '');
1171
1172        // ----- Calculate the checksum
1173        $v_checksum = 0;
1174        // ..... First part of the header
1175        for ($i=0; $i<148; $i++)
1176            $v_checksum += ord(substr($v_binary_data_first,$i,1));
1177        // ..... Ignore the checksum value and replace it by ' ' (space)
1178        for ($i=148; $i<156; $i++)
1179            $v_checksum += ord(' ');
1180        // ..... Last part of the header
1181        for ($i=156, $j=0; $i<512; $i++, $j++)
1182            $v_checksum += ord(substr($v_binary_data_last,$j,1));
1183
1184        // ----- Write the first 148 bytes of the header in the archive
1185        $this->_writeBlock($v_binary_data_first, 148);
1186
1187        // ----- Write the calculated checksum
1188        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
1189        $v_binary_data = pack("a8", $v_checksum);
1190        $this->_writeBlock($v_binary_data, 8);
1191
1192        // ----- Write the last 356 bytes of the header in the archive
1193        $this->_writeBlock($v_binary_data_last, 356);
1194
1195        // ----- Write the filename as content of the block
1196        $i=0;
1197        while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
1198            $v_binary_data = pack("a512", "$v_buffer");
1199            $this->_writeBlock($v_binary_data);
1200        }
1201
1202        return true;
1203    }
1204    // }}}
1205
1206    // {{{ _readHeader()
1207    function _readHeader($v_binary_data, &$v_header)
1208    {
1209        if (strlen($v_binary_data)==0) {
1210            $v_header['filename'] = '';
1211            return true;
1212        }
1213
1214        if (strlen($v_binary_data) != 512) {
1215            $v_header['filename'] = '';
1216            $this->_error('Invalid block size : '.strlen($v_binary_data));
1217            return false;
1218        }
1219
1220        // ----- Calculate the checksum
1221        $v_checksum = 0;
1222        // ..... First part of the header
1223        for ($i=0; $i<148; $i++)
1224            $v_checksum+=ord(substr($v_binary_data,$i,1));
1225        // ..... Ignore the checksum value and replace it by ' ' (space)
1226        for ($i=148; $i<156; $i++)
1227            $v_checksum += ord(' ');
1228        // ..... Last part of the header
1229        for ($i=156; $i<512; $i++)
1230           $v_checksum+=ord(substr($v_binary_data,$i,1));
1231
1232        $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/"
1233                         ."a8checksum/a1typeflag/a100link/a6magic/a2version/"
1234                         ."a32uname/a32gname/a8devmajor/a8devminor",
1235                         $v_binary_data);
1236
1237        // ----- Extract the checksum
1238        $v_header['checksum'] = OctDec(trim($v_data['checksum']));
1239        if ($v_header['checksum'] != $v_checksum) {
1240            $v_header['filename'] = '';
1241
1242            // ----- Look for last block (empty block)
1243            if (($v_checksum == 256) && ($v_header['checksum'] == 0))
1244                return true;
1245
1246            $this->_error('Invalid checksum for file "'.$v_data['filename']
1247                          .'" : '.$v_checksum.' calculated, '
1248                          .$v_header['checksum'].' expected');
1249            return false;
1250        }
1251
1252        // ----- Extract the properties
1253        $v_header['filename'] = trim($v_data['filename']);
1254        $v_header['mode'] = OctDec(trim($v_data['mode']));
1255        $v_header['uid'] = OctDec(trim($v_data['uid']));
1256        $v_header['gid'] = OctDec(trim($v_data['gid']));
1257        $v_header['size'] = OctDec(trim($v_data['size']));
1258        $v_header['mtime'] = OctDec(trim($v_data['mtime']));
1259        if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
1260          $v_header['size'] = 0;
1261        }
1262        /* ----- All these fields are removed form the header because
1263        they do not carry interesting info
1264        $v_header[link] = trim($v_data[link]);
1265        $v_header[magic] = trim($v_data[magic]);
1266        $v_header[version] = trim($v_data[version]);
1267        $v_header[uname] = trim($v_data[uname]);
1268        $v_header[gname] = trim($v_data[gname]);
1269        $v_header[devmajor] = trim($v_data[devmajor]);
1270        $v_header[devminor] = trim($v_data[devminor]);
1271        */
1272
1273        return true;
1274    }
1275    // }}}
1276
1277    // {{{ _readLongHeader()
1278    function _readLongHeader(&$v_header)
1279    {
1280      $v_filename = '';
1281      $n = floor($v_header['size']/512);
1282      for ($i=0; $i<$n; $i++) {
1283        $v_content = $this->_readBlock();
1284        $v_filename .= $v_content;
1285      }
1286      if (($v_header['size'] % 512) != 0) {
1287        $v_content = $this->_readBlock();
1288        $v_filename .= $v_content;
1289      }
1290
1291      // ----- Read the next header
1292      $v_binary_data = $this->_readBlock();
1293
1294      if (!$this->_readHeader($v_binary_data, $v_header))
1295        return false;
1296
1297      $v_header['filename'] = $v_filename;
1298
1299      return true;
1300    }
1301    // }}}
1302
1303    // {{{ _extractInString()
1304    /**
1305    * This method extract from the archive one file identified by $p_filename.
1306    * The return value is a string with the file content, or NULL on error.
1307    * @param string $p_filename     The path of the file to extract in a string.
1308    * @return                       a string with the file content or NULL.
1309    * @access private
1310    */
1311    function _extractInString($p_filename)
1312    {
1313        $v_result_str = "";
1314
1315        While (strlen($v_binary_data = $this->_readBlock()) != 0)
1316        {
1317          if (!$this->_readHeader($v_binary_data, $v_header))
1318            return NULL;
1319
1320          if ($v_header['filename'] == '')
1321            continue;
1322
1323          // ----- Look for long filename
1324          if ($v_header['typeflag'] == 'L') {
1325            if (!$this->_readLongHeader($v_header))
1326              return NULL;
1327          }
1328
1329          if ($v_header['filename'] == $p_filename) {
1330              if ($v_header['typeflag'] == "5") {
1331                  $this->_error('Unable to extract in string a directory '
1332                                .'entry {'.$v_header['filename'].'}');
1333                  return NULL;
1334              } else {
1335                  $n = floor($v_header['size']/512);
1336                  for ($i=0; $i<$n; $i++) {
1337                      $v_result_str .= $this->_readBlock();
1338                  }
1339                  if (($v_header['size'] % 512) != 0) {
1340                      $v_content = $this->_readBlock();
1341                      $v_result_str .= substr($v_content, 0,
1342                                              ($v_header['size'] % 512));
1343                  }
1344                  return $v_result_str;
1345              }
1346          } else {
1347              $this->_jumpBlock(ceil(($v_header['size']/512)));
1348          }
1349        }
1350
1351        return NULL;
1352    }
1353    // }}}
1354
1355    // {{{ _extractList()
1356    function _extractList($p_path, &$p_list_detail, $p_mode,
1357                          $p_file_list, $p_remove_path)
1358    {
1359    $v_result=true;
1360    $v_nb = 0;
1361    $v_extract_all = true;
1362    $v_listing = false;
1363
1364    $p_path = $this->_translateWinPath($p_path, false);
1365    if ($p_path == '' || (substr($p_path, 0, 1) != '/'
1366        && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
1367      $p_path = "./".$p_path;
1368    }
1369    $p_remove_path = $this->_translateWinPath($p_remove_path);
1370
1371    // ----- Look for path to remove format (should end by /)
1372    if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
1373      $p_remove_path .= '/';
1374    $p_remove_path_size = strlen($p_remove_path);
1375
1376    switch ($p_mode) {
1377      case "complete" :
1378        $v_extract_all = TRUE;
1379        $v_listing = FALSE;
1380      break;
1381      case "partial" :
1382          $v_extract_all = FALSE;
1383          $v_listing = FALSE;
1384      break;
1385      case "list" :
1386          $v_extract_all = FALSE;
1387          $v_listing = TRUE;
1388      break;
1389      default :
1390        $this->_error('Invalid extract mode ('.$p_mode.')');
1391        return false;
1392    }
1393
1394    clearstatcache();
1395
1396    while (strlen($v_binary_data = $this->_readBlock()) != 0)
1397    {
1398      $v_extract_file = FALSE;
1399      $v_extraction_stopped = 0;
1400
1401      if (!$this->_readHeader($v_binary_data, $v_header))
1402        return false;
1403
1404      if ($v_header['filename'] == '') {
1405        continue;
1406      }
1407
1408      // ----- Look for long filename
1409      if ($v_header['typeflag'] == 'L') {
1410        if (!$this->_readLongHeader($v_header))
1411          return false;
1412      }
1413
1414      if ((!$v_extract_all) && (is_array($p_file_list))) {
1415        // ----- By default no unzip if the file is not found
1416        $v_extract_file = false;
1417
1418        for ($i=0; $i<sizeof($p_file_list); $i++) {
1419          // ----- Look if it is a directory
1420          if (substr($p_file_list[$i], -1) == '/') {
1421            // ----- Look if the directory is in the filename path
1422            if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
1423                && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
1424                    == $p_file_list[$i])) {
1425              $v_extract_file = TRUE;
1426              break;
1427            }
1428          }
1429
1430          // ----- It is a file, so compare the file names
1431          elseif ($p_file_list[$i] == $v_header['filename']) {
1432            $v_extract_file = TRUE;
1433            break;
1434          }
1435        }
1436      } else {
1437        $v_extract_file = TRUE;
1438      }
1439
1440      // ----- Look if this file need to be extracted
1441      if (($v_extract_file) && (!$v_listing))
1442      {
1443        if (($p_remove_path != '')
1444            && (substr($v_header['filename'], 0, $p_remove_path_size)
1445                == $p_remove_path))
1446          $v_header['filename'] = substr($v_header['filename'],
1447                                         $p_remove_path_size);
1448        if (($p_path != './') && ($p_path != '/')) {
1449          while (substr($p_path, -1) == '/')
1450            $p_path = substr($p_path, 0, strlen($p_path)-1);
1451
1452          if (substr($v_header['filename'], 0, 1) == '/')
1453              $v_header['filename'] = $p_path.$v_header['filename'];
1454          else
1455            $v_header['filename'] = $p_path.'/'.$v_header['filename'];
1456        }
1457        if (file_exists($v_header['filename'])) {
1458          if (   (@is_dir($v_header['filename']))
1459              && ($v_header['typeflag'] == '')) {
1460            $this->_error('File '.$v_header['filename']
1461                          .' already exists as a directory');
1462            return false;
1463          }
1464          if (   ($this->_isArchive($v_header['filename']))
1465              && ($v_header['typeflag'] == "5")) {
1466            $this->_error('Directory '.$v_header['filename']
1467                          .' already exists as a file');
1468            return false;
1469          }
1470          if (!is_writeable($v_header['filename'])) {
1471            $this->_error('File '.$v_header['filename']
1472                          .' already exists and is write protected');
1473            return false;
1474          }
1475          if (filemtime($v_header['filename']) > $v_header['mtime']) {
1476            // To be completed : An error or silent no replace ?
1477          }
1478        }
1479
1480        // ----- Check the directory availability and create it if necessary
1481        elseif (($v_result
1482                 = $this->_dirCheck(($v_header['typeflag'] == "5"
1483                                    ?$v_header['filename']
1484                                    :dirname($v_header['filename'])))) != 1) {
1485            $this->_error('Unable to create path for '.$v_header['filename']);
1486            return false;
1487        }
1488
1489        if ($v_extract_file) {
1490          if ($v_header['typeflag'] == "5") {
1491            if (!@file_exists($v_header['filename'])) {
1492                if (!@mkdir($v_header['filename'], 0777)) {
1493                    $this->_error('Unable to create directory {'
1494                                  .$v_header['filename'].'}');
1495                    return false;
1496                }
1497            }
1498          } else {
1499              if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
1500                  $this->_error('Error while opening {'.$v_header['filename']
1501                                .'} in write binary mode');
1502                  return false;
1503              } else {
1504                  $n = floor($v_header['size']/512);
1505                  for ($i=0; $i<$n; $i++) {
1506                      $v_content = $this->_readBlock();
1507                      fwrite($v_dest_file, $v_content, 512);
1508                  }
1509            if (($v_header['size'] % 512) != 0) {
1510              $v_content = $this->_readBlock();
1511              fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
1512            }
1513
1514            @fclose($v_dest_file);
1515
1516            // ----- Change the file mode, mtime
1517            @touch($v_header['filename'], $v_header['mtime']);
1518            // To be completed
1519            //chmod($v_header[filename], DecOct($v_header[mode]));
1520          }
1521
1522          // ----- Check the file size
1523          clearstatcache();
1524          if (filesize($v_header['filename']) != $v_header['size']) {
1525              $this->_error('Extracted file '.$v_header['filename']
1526                            .' does not have the correct file size \''
1527                            .filesize($v_header['filename'])
1528                            .'\' ('.$v_header['size']
1529                            .' expected). Archive may be corrupted.');
1530              return false;
1531          }
1532          }
1533        } else {
1534          $this->_jumpBlock(ceil(($v_header['size']/512)));
1535        }
1536      } else {
1537          $this->_jumpBlock(ceil(($v_header['size']/512)));
1538      }
1539
1540      /* TBC : Seems to be unused ...
1541      if ($this->_compress)
1542        $v_end_of_file = @gzeof($this->_file);
1543      else
1544        $v_end_of_file = @feof($this->_file);
1545        */
1546
1547      if ($v_listing || $v_extract_file || $v_extraction_stopped) {
1548        // ----- Log extracted files
1549        if (($v_file_dir = dirname($v_header['filename']))
1550            == $v_header['filename'])
1551          $v_file_dir = '';
1552        if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
1553          $v_file_dir = '/';
1554
1555        $p_list_detail[$v_nb++] = $v_header;
1556      }
1557    }
1558
1559        return true;
1560    }
1561    // }}}
1562
1563    // {{{ _openAppend()
1564    function _openAppend()
1565    {
1566        if (filesize($this->_tarname) == 0)
1567          return $this->_openWrite();
1568         
1569        if ($this->_compress) {
1570            $this->_close();
1571
1572            if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
1573                $this->_error('Error while renaming \''.$this->_tarname
1574                              .'\' to temporary file \''.$this->_tarname
1575                              .'.tmp\'');
1576                return false;
1577            }
1578
1579            if ($this->_compress_type == 'gz')
1580                $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
1581            elseif ($this->_compress_type == 'bz2')
1582                $v_temp_tar = @bzopen($this->_tarname.".tmp", "rb");
1583               
1584            if ($v_temp_tar == 0) {
1585                $this->_error('Unable to open file \''.$this->_tarname
1586                              .'.tmp\' in binary read mode');
1587                @rename($this->_tarname.".tmp", $this->_tarname);
1588                return false;
1589            }
1590
1591            if (!$this->_openWrite()) {
1592                @rename($this->_tarname.".tmp", $this->_tarname);
1593                return false;
1594            }
1595
1596            if ($this->_compress_type == 'gz') {
1597                $v_buffer = @gzread($v_temp_tar, 512);
1598
1599                // ----- Read the following blocks but not the last one
1600                if (!@gzeof($v_temp_tar)) {
1601                    do{
1602                        $v_binary_data = pack("a512", $v_buffer);
1603                        $this->_writeBlock($v_binary_data);
1604                        $v_buffer = @gzread($v_temp_tar, 512);
1605
1606                    } while (!@gzeof($v_temp_tar));
1607                }
1608
1609                @gzclose($v_temp_tar);
1610            }
1611            elseif ($this->_compress_type == 'bz2') {
1612                $v_buffered_lines   = array();
1613                $v_buffered_lines[] = @bzread($v_temp_tar, 512);
1614
1615                // ----- Read the following blocks but not the last one
1616                while (strlen($v_buffered_lines[]
1617                              = @bzread($v_temp_tar, 512)) > 0) {
1618                    $v_binary_data = pack("a512",
1619                                          array_shift($v_buffered_lines));
1620                    $this->_writeBlock($v_binary_data);
1621                }
1622
1623                @bzclose($v_temp_tar);
1624            }
1625
1626            if (!@unlink($this->_tarname.".tmp")) {
1627                $this->_error('Error while deleting temporary file \''
1628                              .$this->_tarname.'.tmp\'');
1629            }
1630
1631        } else {
1632            // ----- For not compressed tar, just add files before the last
1633            //       512 bytes block
1634            if (!$this->_openReadWrite())
1635               return false;
1636
1637            clearstatcache();
1638            $v_size = filesize($this->_tarname);
1639            fseek($this->_file, $v_size-512);
1640        }
1641
1642        return true;
1643    }
1644    // }}}
1645
1646    // {{{ _append()
1647    function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
1648    {
1649        if (!$this->_openAppend())
1650            return false;
1651           
1652        if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
1653           $this->_writeFooter();
1654
1655        $this->_close();
1656
1657        return true;
1658    }
1659    // }}}
1660
1661    // {{{ _dirCheck()
1662
1663    /**
1664     * Check if a directory exists and create it (including parent
1665     * dirs) if not.
1666     *
1667     * @param string $p_dir directory to check
1668     *
1669     * @return bool TRUE if the directory exists or was created
1670     */
1671    function _dirCheck($p_dir)
1672    {
1673        if ((@is_dir($p_dir)) || ($p_dir == ''))
1674            return true;
1675
1676        $p_parent_dir = dirname($p_dir);
1677
1678        if (($p_parent_dir != $p_dir) &&
1679            ($p_parent_dir != '') &&
1680            (!$this->_dirCheck($p_parent_dir)))
1681             return false;
1682
1683        if (!@mkdir($p_dir, 0777)) {
1684            $this->_error("Unable to create directory '$p_dir'");
1685            return false;
1686        }
1687
1688        return true;
1689    }
1690
1691    // }}}
1692
1693    // {{{ _pathReduction()
1694
1695    /**
1696     * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
1697     * rand emove double slashes.
1698     *
1699     * @param string $p_dir path to reduce
1700     *
1701     * @return string reduced path
1702     *
1703     * @access private
1704     *
1705     */
1706    function _pathReduction($p_dir)
1707    {
1708        $v_result = '';
1709
1710        // ----- Look for not empty path
1711        if ($p_dir != '') {
1712            // ----- Explode path by directory names
1713            $v_list = explode('/', $p_dir);
1714
1715            // ----- Study directories from last to first
1716            for ($i=sizeof($v_list)-1; $i>=0; $i--) {
1717                // ----- Look for current path
1718                if ($v_list[$i] == ".") {
1719                    // ----- Ignore this directory
1720                    // Should be the first $i=0, but no check is done
1721                }
1722                else if ($v_list[$i] == "..") {
1723                    // ----- Ignore it and ignore the $i-1
1724                    $i--;
1725                }
1726                else if (   ($v_list[$i] == '')
1727                         && ($i!=(sizeof($v_list)-1))
1728                         && ($i!=0)) {
1729                    // ----- Ignore only the double '//' in path,
1730                    // but not the first and last /
1731                } else {
1732                    $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'
1733                                .$v_result:'');
1734                }
1735            }
1736        }
1737        $v_result = strtr($v_result, '\\', '/');
1738        return $v_result;
1739    }
1740
1741    // }}}
1742
1743    // {{{ _translateWinPath()
1744    function _translateWinPath($p_path, $p_remove_disk_letter=true)
1745    {
1746      if (OS_WINDOWS) {
1747          // ----- Look for potential disk letter
1748          if (   ($p_remove_disk_letter)
1749              && (($v_position = strpos($p_path, ':')) != false)) {
1750              $p_path = substr($p_path, $v_position+1);
1751          }
1752          // ----- Change potential windows directory separator
1753          if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
1754              $p_path = strtr($p_path, '\\', '/');
1755          }
1756      }
1757      return $p_path;
1758    }
1759    // }}}
1760
1761}
1762?>
Note: See TracBrowser for help on using the repository browser.