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

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

blank

  • 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            print("error");
612            return false;
613        }
614
615        return true;
616    }
617    // }}}
618
619    // {{{ _openRead()
620    function _openRead()
621    {
622        if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
623
624          // ----- Look if a local copy need to be done
625          if ($this->_temp_tarname == '') {
626              $this->_temp_tarname = uniqid('tar').'.tmp';
627              if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
628                $this->_error('Unable to open in read mode \''
629                              .$this->_tarname.'\'');
630                $this->_temp_tarname = '';
631                return false;
632              }
633              if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
634                $this->_error('Unable to open in write mode \''
635                              .$this->_temp_tarname.'\'');
636                $this->_temp_tarname = '';
637                return false;
638              }
639              while ($v_data = @fread($v_file_from, 1024))
640                  @fwrite($v_file_to, $v_data);
641              @fclose($v_file_from);
642              @fclose($v_file_to);
643          }
644
645          // ----- File to open if the local copy
646          $v_filename = $this->_temp_tarname;
647
648        } else
649          // ----- File to open if the normal Tar file
650          $v_filename = $this->_tarname;
651
652        if ($this->_compress_type == 'gz')
653            $this->_file = @gzopen($v_filename, "rb");
654        else if ($this->_compress_type == 'bz2')
655            $this->_file = @bzopen($v_filename, "rb");
656        else if ($this->_compress_type == 'none')
657            $this->_file = @fopen($v_filename, "rb");
658        else
659            $this->_error('Unknown or missing compression type ('
660                          .$this->_compress_type.')');
661
662        if ($this->_file == 0) {
663            $this->_error('Unable to open in read mode \''.$v_filename.'\'');
664            return false;
665        }
666
667        return true;
668    }
669    // }}}
670
671    // {{{ _openReadWrite()
672    function _openReadWrite()
673    {
674        if ($this->_compress_type == 'gz')
675            $this->_file = @gzopen($this->_tarname, "r+b");
676        else if ($this->_compress_type == 'bz2')
677            $this->_file = @bzopen($this->_tarname, "r+b");
678        else if ($this->_compress_type == 'none')
679            $this->_file = @fopen($this->_tarname, "r+b");
680        else
681            $this->_error('Unknown or missing compression type ('
682                          .$this->_compress_type.')');
683
684        if ($this->_file == 0) {
685            $this->_error('Unable to open in read/write mode \''
686                          .$this->_tarname.'\'');
687            return false;
688        }
689
690        return true;
691    }
692    // }}}
693
694    // {{{ _close()
695    function _close()
696    {
697        //if (isset($this->_file)) {
698        if (is_resource($this->_file)) {
699            if ($this->_compress_type == 'gz')
700                @gzclose($this->_file);
701            else if ($this->_compress_type == 'bz2')
702                @bzclose($this->_file);
703            else if ($this->_compress_type == 'none')
704                @fclose($this->_file);
705            else
706                $this->_error('Unknown or missing compression type ('
707                              .$this->_compress_type.')');
708
709            $this->_file = 0;
710        }
711
712        // ----- Look if a local copy need to be erase
713        // Note that it might be interesting to keep the url for a time : ToDo
714        if ($this->_temp_tarname != '') {
715            @unlink($this->_temp_tarname);
716            $this->_temp_tarname = '';
717        }
718
719        return true;
720    }
721    // }}}
722
723    // {{{ _cleanFile()
724    function _cleanFile()
725    {
726        $this->_close();
727
728        // ----- Look for a local copy
729        if ($this->_temp_tarname != '') {
730            // ----- Remove the local copy but not the remote tarname
731            @unlink($this->_temp_tarname);
732            $this->_temp_tarname = '';
733        } else {
734            // ----- Remove the local tarname file
735            @unlink($this->_tarname);
736        }
737        $this->_tarname = '';
738
739        return true;
740    }
741    // }}}
742
743    // {{{ _writeBlock()
744    function _writeBlock($p_binary_data, $p_len=null)
745    {
746      if (is_resource($this->_file)) {
747          if ($p_len === null) {
748              if ($this->_compress_type == 'gz')
749                  @gzputs($this->_file, $p_binary_data);
750              else if ($this->_compress_type == 'bz2')
751                  @bzwrite($this->_file, $p_binary_data);
752              else if ($this->_compress_type == 'none')
753                  @fputs($this->_file, $p_binary_data);
754              else
755                  $this->_error('Unknown or missing compression type ('
756                                .$this->_compress_type.')');
757          } else {
758              if ($this->_compress_type == 'gz')
759                  @gzputs($this->_file, $p_binary_data, $p_len);
760              else if ($this->_compress_type == 'bz2')
761                  @bzwrite($this->_file, $p_binary_data, $p_len);
762              else if ($this->_compress_type == 'none')
763                  @fputs($this->_file, $p_binary_data, $p_len);
764              else
765                  $this->_error('Unknown or missing compression type ('
766                                .$this->_compress_type.')');
767
768          }
769      }
770      return true;
771    }
772    // }}}
773
774    // {{{ _readBlock()
775    function _readBlock()
776    {
777      $v_block = null;
778      if (is_resource($this->_file)) {
779          if ($this->_compress_type == 'gz')
780              $v_block = @gzread($this->_file, 512);
781          else if ($this->_compress_type == 'bz2')
782              $v_block = @bzread($this->_file, 512);
783          else if ($this->_compress_type == 'none')
784              $v_block = @fread($this->_file, 512);
785          else
786              $this->_error('Unknown or missing compression type ('
787                            .$this->_compress_type.')');
788      }
789      return $v_block;
790    }
791    // }}}
792
793    // {{{ _jumpBlock()
794    function _jumpBlock($p_len=null)
795    {
796      if (is_resource($this->_file)) {
797          if ($p_len === null)
798              $p_len = 1;
799
800          if ($this->_compress_type == 'gz') {
801              @gzseek($this->_file, @gztell($this->_file)+($p_len*512));
802          }
803          else if ($this->_compress_type == 'bz2') {
804              // ----- Replace missing bztell() and bzseek()
805              for ($i=0; $i<$p_len; $i++)
806                  $this->_readBlock();
807          } else if ($this->_compress_type == 'none')
808              @fseek($this->_file, @ftell($this->_file)+($p_len*512));
809          else
810              $this->_error('Unknown or missing compression type ('
811                            .$this->_compress_type.')');
812
813      }
814      return true;
815    }
816    // }}}
817
818    // {{{ _writeFooter()
819    function _writeFooter()
820    {
821      if (is_resource($this->_file)) {
822          // ----- Write the last 0 filled block for end of archive
823          $v_binary_data = pack("a512", '');
824          $this->_writeBlock($v_binary_data);
825      }
826      return true;
827    }
828    // }}}
829
830    // {{{ _addList()
831    function _addList($p_list, $p_add_dir, $p_remove_dir)
832    {
833      $v_result=true;
834      $v_header = array();
835
836      // ----- Remove potential windows directory separator
837      $p_add_dir = $this->_translateWinPath($p_add_dir);
838      $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
839
840      if (!$this->_file) {
841          $this->_error('Invalid file descriptor');
842          return false;
843      }
844
845      if (sizeof($p_list) == 0)
846          return true;
847
848      foreach ($p_list as $v_filename) {
849          if (!$v_result) {
850              break;
851          }
852
853        // ----- Skip the current tar name
854        if ($v_filename == $this->_tarname)
855            continue;
856
857        if ($v_filename == '')
858            continue;
859
860        if (!file_exists($v_filename)) {
861            $this->_warning("File '$v_filename' does not exist");
862            continue;
863        }
864
865        // ----- Add the file or directory header
866        if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
867            return false;
868
869        if (@is_dir($v_filename)) {
870            if (!($p_hdir = opendir($v_filename))) {
871                $this->_warning("Directory '$v_filename' can not be read");
872                continue;
873            }
874            while (false !== ($p_hitem = readdir($p_hdir))) {
875                if (($p_hitem != '.') && ($p_hitem != '..')) {
876                    if ($v_filename != ".")
877                        $p_temp_list[0] = $v_filename.'/'.$p_hitem;
878                    else
879                        $p_temp_list[0] = $p_hitem;
880
881                    $v_result = $this->_addList($p_temp_list,
882                                                $p_add_dir,
883                                                $p_remove_dir);
884                }
885            }
886
887            unset($p_temp_list);
888            unset($p_hdir);
889            unset($p_hitem);
890        }
891      }
892
893      return $v_result;
894    }
895    // }}}
896
897    // {{{ _addFile()
898    function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
899    {
900      if (!$this->_file) {
901          $this->_error('Invalid file descriptor');
902          return false;
903      }
904
905      if ($p_filename == '') {
906          $this->_error('Invalid file name');
907          return false;
908      }
909
910      // ----- Calculate the stored filename
911      $p_filename = $this->_translateWinPath($p_filename, false);;
912      $v_stored_filename = $p_filename;
913      if (strcmp($p_filename, $p_remove_dir) == 0) {
914          return true;
915      }
916      if ($p_remove_dir != '') {
917          if (substr($p_remove_dir, -1) != '/')
918              $p_remove_dir .= '/';
919
920          if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
921              $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
922      }
923      $v_stored_filename = $this->_translateWinPath($v_stored_filename);
924      if ($p_add_dir != '') {
925          if (substr($p_add_dir, -1) == '/')
926              $v_stored_filename = $p_add_dir.$v_stored_filename;
927          else
928              $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
929      }
930
931      $v_stored_filename = $this->_pathReduction($v_stored_filename);
932
933      if ($this->_isArchive($p_filename)) {
934          if (($v_file = @fopen($p_filename, "rb")) == 0) {
935              $this->_warning("Unable to open file '".$p_filename
936                              ."' in binary read mode");
937              return true;
938          }
939
940          if (!$this->_writeHeader($p_filename, $v_stored_filename))
941              return false;
942
943          while (($v_buffer = fread($v_file, 512)) != '') {
944              $v_binary_data = pack("a512", "$v_buffer");
945              $this->_writeBlock($v_binary_data);
946          }
947
948          fclose($v_file);
949
950      } else {
951          // ----- Only header for dir
952          if (!$this->_writeHeader($p_filename, $v_stored_filename))
953              return false;
954      }
955
956      return true;
957    }
958    // }}}
959
960    // {{{ _addString()
961    function _addString($p_filename, $p_string)
962    {
963      if (!$this->_file) {
964          $this->_error('Invalid file descriptor');
965          return false;
966      }
967
968      if ($p_filename == '') {
969          $this->_error('Invalid file name');
970          return false;
971      }
972
973      // ----- Calculate the stored filename
974      $p_filename = $this->_translateWinPath($p_filename, false);;
975
976      if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
977                                    0, 0, "", 0, 0))
978          return false;
979
980      $i=0;
981      while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
982          $v_binary_data = pack("a512", $v_buffer);
983          $this->_writeBlock($v_binary_data);
984      }
985
986      return true;
987    }
988    // }}}
989
990    // {{{ _writeHeader()
991    function _writeHeader($p_filename, $p_stored_filename)
992    {
993        if ($p_stored_filename == '')
994            $p_stored_filename = $p_filename;
995        $v_reduce_filename = $this->_pathReduction($p_stored_filename);
996
997        if (strlen($v_reduce_filename) > 99) {
998          if (!$this->_writeLongHeader($v_reduce_filename))
999            return false;
1000        }
1001
1002        $v_info = stat($p_filename);
1003        $v_uid = sprintf("%6s ", DecOct($v_info[4]));
1004        $v_gid = sprintf("%6s ", DecOct($v_info[5]));
1005        $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename)));
1006
1007        $v_mtime = sprintf("%11s", DecOct(filemtime($p_filename)));
1008
1009        if (@is_dir($p_filename)) {
1010          $v_typeflag = "5";
1011          $v_size = sprintf("%11s ", DecOct(0));
1012        } else {
1013          $v_typeflag = '';
1014          clearstatcache();
1015          $v_size = sprintf("%11s ", DecOct(filesize($p_filename)));
1016        }
1017
1018        $v_linkname = '';
1019
1020        $v_magic = '';
1021
1022        $v_version = '';
1023
1024        $v_uname = '';
1025
1026        $v_gname = '';
1027
1028        $v_devmajor = '';
1029
1030        $v_devminor = '';
1031
1032        $v_prefix = '';
1033
1034        $v_binary_data_first = pack("a100a8a8a8a12A12",
1035                                    $v_reduce_filename, $v_perms, $v_uid,
1036                                    $v_gid, $v_size, $v_mtime);
1037        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
1038                                   $v_typeflag, $v_linkname, $v_magic,
1039                                   $v_version, $v_uname, $v_gname,
1040                                   $v_devmajor, $v_devminor, $v_prefix, '');
1041
1042        // ----- Calculate the checksum
1043        $v_checksum = 0;
1044        // ..... First part of the header
1045        for ($i=0; $i<148; $i++)
1046            $v_checksum += ord(substr($v_binary_data_first,$i,1));
1047        // ..... Ignore the checksum value and replace it by ' ' (space)
1048        for ($i=148; $i<156; $i++)
1049            $v_checksum += ord(' ');
1050        // ..... Last part of the header
1051        for ($i=156, $j=0; $i<512; $i++, $j++)
1052            $v_checksum += ord(substr($v_binary_data_last,$j,1));
1053
1054        // ----- Write the first 148 bytes of the header in the archive
1055        $this->_writeBlock($v_binary_data_first, 148);
1056
1057        // ----- Write the calculated checksum
1058        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
1059        $v_binary_data = pack("a8", $v_checksum);
1060        $this->_writeBlock($v_binary_data, 8);
1061
1062        // ----- Write the last 356 bytes of the header in the archive
1063        $this->_writeBlock($v_binary_data_last, 356);
1064
1065        return true;
1066    }
1067    // }}}
1068
1069    // {{{ _writeHeaderBlock()
1070    function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
1071                               $p_type='', $p_uid=0, $p_gid=0)
1072    {
1073        $p_filename = $this->_pathReduction($p_filename);
1074
1075        if (strlen($p_filename) > 99) {
1076          if (!$this->_writeLongHeader($p_filename))
1077            return false;
1078        }
1079
1080        if ($p_type == "5") {
1081          $v_size = sprintf("%11s ", DecOct(0));
1082        } else {
1083          $v_size = sprintf("%11s ", DecOct($p_size));
1084        }
1085
1086        $v_uid = sprintf("%6s ", DecOct($p_uid));
1087        $v_gid = sprintf("%6s ", DecOct($p_gid));
1088        $v_perms = sprintf("%6s ", DecOct($p_perms));
1089
1090        $v_mtime = sprintf("%11s", DecOct($p_mtime));
1091
1092        $v_linkname = '';
1093
1094        $v_magic = '';
1095
1096        $v_version = '';
1097
1098        $v_uname = '';
1099
1100        $v_gname = '';
1101
1102        $v_devmajor = '';
1103
1104        $v_devminor = '';
1105
1106        $v_prefix = '';
1107
1108        $v_binary_data_first = pack("a100a8a8a8a12A12",
1109                                    $p_filename, $v_perms, $v_uid, $v_gid,
1110                                    $v_size, $v_mtime);
1111        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
1112                                   $p_type, $v_linkname, $v_magic,
1113                                   $v_version, $v_uname, $v_gname,
1114                                   $v_devmajor, $v_devminor, $v_prefix, '');
1115
1116        // ----- Calculate the checksum
1117        $v_checksum = 0;
1118        // ..... First part of the header
1119        for ($i=0; $i<148; $i++)
1120            $v_checksum += ord(substr($v_binary_data_first,$i,1));
1121        // ..... Ignore the checksum value and replace it by ' ' (space)
1122        for ($i=148; $i<156; $i++)
1123            $v_checksum += ord(' ');
1124        // ..... Last part of the header
1125        for ($i=156, $j=0; $i<512; $i++, $j++)
1126            $v_checksum += ord(substr($v_binary_data_last,$j,1));
1127
1128        // ----- Write the first 148 bytes of the header in the archive
1129        $this->_writeBlock($v_binary_data_first, 148);
1130
1131        // ----- Write the calculated checksum
1132        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
1133        $v_binary_data = pack("a8", $v_checksum);
1134        $this->_writeBlock($v_binary_data, 8);
1135
1136        // ----- Write the last 356 bytes of the header in the archive
1137        $this->_writeBlock($v_binary_data_last, 356);
1138
1139        return true;
1140    }
1141    // }}}
1142
1143    // {{{ _writeLongHeader()
1144    function _writeLongHeader($p_filename)
1145    {
1146        $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
1147
1148        $v_typeflag = 'L';
1149
1150        $v_linkname = '';
1151
1152        $v_magic = '';
1153
1154        $v_version = '';
1155
1156        $v_uname = '';
1157
1158        $v_gname = '';
1159
1160        $v_devmajor = '';
1161
1162        $v_devminor = '';
1163
1164        $v_prefix = '';
1165
1166        $v_binary_data_first = pack("a100a8a8a8a12A12",
1167                                    '././@LongLink', 0, 0, 0, $v_size, 0);
1168        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
1169                                   $v_typeflag, $v_linkname, $v_magic,
1170                                   $v_version, $v_uname, $v_gname,
1171                                   $v_devmajor, $v_devminor, $v_prefix, '');
1172
1173        // ----- Calculate the checksum
1174        $v_checksum = 0;
1175        // ..... First part of the header
1176        for ($i=0; $i<148; $i++)
1177            $v_checksum += ord(substr($v_binary_data_first,$i,1));
1178        // ..... Ignore the checksum value and replace it by ' ' (space)
1179        for ($i=148; $i<156; $i++)
1180            $v_checksum += ord(' ');
1181        // ..... Last part of the header
1182        for ($i=156, $j=0; $i<512; $i++, $j++)
1183            $v_checksum += ord(substr($v_binary_data_last,$j,1));
1184
1185        // ----- Write the first 148 bytes of the header in the archive
1186        $this->_writeBlock($v_binary_data_first, 148);
1187
1188        // ----- Write the calculated checksum
1189        $v_checksum = sprintf("%6s ", DecOct($v_checksum));
1190        $v_binary_data = pack("a8", $v_checksum);
1191        $this->_writeBlock($v_binary_data, 8);
1192
1193        // ----- Write the last 356 bytes of the header in the archive
1194        $this->_writeBlock($v_binary_data_last, 356);
1195
1196        // ----- Write the filename as content of the block
1197        $i=0;
1198        while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
1199            $v_binary_data = pack("a512", "$v_buffer");
1200            $this->_writeBlock($v_binary_data);
1201        }
1202
1203        return true;
1204    }
1205    // }}}
1206
1207    // {{{ _readHeader()
1208    function _readHeader($v_binary_data, &$v_header)
1209    {
1210        if (strlen($v_binary_data)==0) {
1211            $v_header['filename'] = '';
1212            return true;
1213        }
1214
1215        if (strlen($v_binary_data) != 512) {
1216            $v_header['filename'] = '';
1217            $this->_error('Invalid block size : '.strlen($v_binary_data));
1218            return false;
1219        }
1220
1221        // ----- Calculate the checksum
1222        $v_checksum = 0;
1223        // ..... First part of the header
1224        for ($i=0; $i<148; $i++)
1225            $v_checksum+=ord(substr($v_binary_data,$i,1));
1226        // ..... Ignore the checksum value and replace it by ' ' (space)
1227        for ($i=148; $i<156; $i++)
1228            $v_checksum += ord(' ');
1229        // ..... Last part of the header
1230        for ($i=156; $i<512; $i++)
1231           $v_checksum+=ord(substr($v_binary_data,$i,1));
1232
1233        $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/"
1234                         ."a8checksum/a1typeflag/a100link/a6magic/a2version/"
1235                         ."a32uname/a32gname/a8devmajor/a8devminor",
1236                         $v_binary_data);
1237
1238        // ----- Extract the checksum
1239        $v_header['checksum'] = OctDec(trim($v_data['checksum']));
1240        if ($v_header['checksum'] != $v_checksum) {
1241            $v_header['filename'] = '';
1242
1243            // ----- Look for last block (empty block)
1244            if (($v_checksum == 256) && ($v_header['checksum'] == 0))
1245                return true;
1246
1247            $this->_error('Invalid checksum for file "'.$v_data['filename']
1248                          .'" : '.$v_checksum.' calculated, '
1249                          .$v_header['checksum'].' expected');
1250            return false;
1251        }
1252
1253        // ----- Extract the properties
1254        $v_header['filename'] = trim($v_data['filename']);
1255        $v_header['mode'] = OctDec(trim($v_data['mode']));
1256        $v_header['uid'] = OctDec(trim($v_data['uid']));
1257        $v_header['gid'] = OctDec(trim($v_data['gid']));
1258        $v_header['size'] = OctDec(trim($v_data['size']));
1259        $v_header['mtime'] = OctDec(trim($v_data['mtime']));
1260        if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
1261          $v_header['size'] = 0;
1262        }
1263        /* ----- All these fields are removed form the header because
1264        they do not carry interesting info
1265        $v_header[link] = trim($v_data[link]);
1266        $v_header[magic] = trim($v_data[magic]);
1267        $v_header[version] = trim($v_data[version]);
1268        $v_header[uname] = trim($v_data[uname]);
1269        $v_header[gname] = trim($v_data[gname]);
1270        $v_header[devmajor] = trim($v_data[devmajor]);
1271        $v_header[devminor] = trim($v_data[devminor]);
1272        */
1273
1274        return true;
1275    }
1276    // }}}
1277
1278    // {{{ _readLongHeader()
1279    function _readLongHeader(&$v_header)
1280    {
1281      $v_filename = '';
1282      $n = floor($v_header['size']/512);
1283      for ($i=0; $i<$n; $i++) {
1284        $v_content = $this->_readBlock();
1285        $v_filename .= $v_content;
1286      }
1287      if (($v_header['size'] % 512) != 0) {
1288        $v_content = $this->_readBlock();
1289        $v_filename .= $v_content;
1290      }
1291
1292      // ----- Read the next header
1293      $v_binary_data = $this->_readBlock();
1294
1295      if (!$this->_readHeader($v_binary_data, $v_header))
1296        return false;
1297
1298      $v_header['filename'] = $v_filename;
1299
1300      return true;
1301    }
1302    // }}}
1303
1304    // {{{ _extractInString()
1305    /**
1306    * This method extract from the archive one file identified by $p_filename.
1307    * The return value is a string with the file content, or NULL on error.
1308    * @param string $p_filename     The path of the file to extract in a string.
1309    * @return                       a string with the file content or NULL.
1310    * @access private
1311    */
1312    function _extractInString($p_filename)
1313    {
1314        $v_result_str = "";
1315
1316        While (strlen($v_binary_data = $this->_readBlock()) != 0)
1317        {
1318          if (!$this->_readHeader($v_binary_data, $v_header))
1319            return NULL;
1320
1321          if ($v_header['filename'] == '')
1322            continue;
1323
1324          // ----- Look for long filename
1325          if ($v_header['typeflag'] == 'L') {
1326            if (!$this->_readLongHeader($v_header))
1327              return NULL;
1328          }
1329
1330          if ($v_header['filename'] == $p_filename) {
1331              if ($v_header['typeflag'] == "5") {
1332                  $this->_error('Unable to extract in string a directory '
1333                                .'entry {'.$v_header['filename'].'}');
1334                  return NULL;
1335              } else {
1336                  $n = floor($v_header['size']/512);
1337                  for ($i=0; $i<$n; $i++) {
1338                      $v_result_str .= $this->_readBlock();
1339                  }
1340                  if (($v_header['size'] % 512) != 0) {
1341                      $v_content = $this->_readBlock();
1342                      $v_result_str .= substr($v_content, 0,
1343                                              ($v_header['size'] % 512));
1344                  }
1345                  return $v_result_str;
1346              }
1347          } else {
1348              $this->_jumpBlock(ceil(($v_header['size']/512)));
1349          }
1350        }
1351
1352        return NULL;
1353    }
1354    // }}}
1355
1356    // {{{ _extractList()
1357    function _extractList($p_path, &$p_list_detail, $p_mode,
1358                          $p_file_list, $p_remove_path)
1359    {
1360    $v_result=true;
1361    $v_nb = 0;
1362    $v_extract_all = true;
1363    $v_listing = false;
1364
1365    $p_path = $this->_translateWinPath($p_path, false);
1366    if ($p_path == '' || (substr($p_path, 0, 1) != '/'
1367        && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
1368      $p_path = "./".$p_path;
1369    }
1370    $p_remove_path = $this->_translateWinPath($p_remove_path);
1371
1372    // ----- Look for path to remove format (should end by /)
1373    if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
1374      $p_remove_path .= '/';
1375    $p_remove_path_size = strlen($p_remove_path);
1376
1377    switch ($p_mode) {
1378      case "complete" :
1379        $v_extract_all = TRUE;
1380        $v_listing = FALSE;
1381      break;
1382      case "partial" :
1383          $v_extract_all = FALSE;
1384          $v_listing = FALSE;
1385      break;
1386      case "list" :
1387          $v_extract_all = FALSE;
1388          $v_listing = TRUE;
1389      break;
1390      default :
1391        $this->_error('Invalid extract mode ('.$p_mode.')');
1392        return false;
1393    }
1394
1395    clearstatcache();
1396
1397    while (strlen($v_binary_data = $this->_readBlock()) != 0)
1398    {
1399      $v_extract_file = FALSE;
1400      $v_extraction_stopped = 0;
1401
1402      if (!$this->_readHeader($v_binary_data, $v_header))
1403        return false;
1404
1405      if ($v_header['filename'] == '') {
1406        continue;
1407      }
1408
1409      // ----- Look for long filename
1410      if ($v_header['typeflag'] == 'L') {
1411        if (!$this->_readLongHeader($v_header))
1412          return false;
1413      }
1414
1415      if ((!$v_extract_all) && (is_array($p_file_list))) {
1416        // ----- By default no unzip if the file is not found
1417        $v_extract_file = false;
1418
1419        for ($i=0; $i<sizeof($p_file_list); $i++) {
1420          // ----- Look if it is a directory
1421          if (substr($p_file_list[$i], -1) == '/') {
1422            // ----- Look if the directory is in the filename path
1423            if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
1424                && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
1425                    == $p_file_list[$i])) {
1426              $v_extract_file = TRUE;
1427              break;
1428            }
1429          }
1430
1431          // ----- It is a file, so compare the file names
1432          elseif ($p_file_list[$i] == $v_header['filename']) {
1433            $v_extract_file = TRUE;
1434            break;
1435          }
1436        }
1437      } else {
1438        $v_extract_file = TRUE;
1439      }
1440
1441      // ----- Look if this file need to be extracted
1442      if (($v_extract_file) && (!$v_listing))
1443      {
1444        if (($p_remove_path != '')
1445            && (substr($v_header['filename'], 0, $p_remove_path_size)
1446                == $p_remove_path))
1447          $v_header['filename'] = substr($v_header['filename'],
1448                                         $p_remove_path_size);
1449        if (($p_path != './') && ($p_path != '/')) {
1450          while (substr($p_path, -1) == '/')
1451            $p_path = substr($p_path, 0, strlen($p_path)-1);
1452
1453          if (substr($v_header['filename'], 0, 1) == '/')
1454              $v_header['filename'] = $p_path.$v_header['filename'];
1455          else
1456            $v_header['filename'] = $p_path.'/'.$v_header['filename'];
1457        }
1458        if (file_exists($v_header['filename'])) {
1459          if (   (@is_dir($v_header['filename']))
1460              && ($v_header['typeflag'] == '')) {
1461            $this->_error('File '.$v_header['filename']
1462                          .' already exists as a directory');
1463            return false;
1464          }
1465          if (   ($this->_isArchive($v_header['filename']))
1466              && ($v_header['typeflag'] == "5")) {
1467            $this->_error('Directory '.$v_header['filename']
1468                          .' already exists as a file');
1469            return false;
1470          }
1471          if (!is_writeable($v_header['filename'])) {
1472            $this->_error('File '.$v_header['filename']
1473                          .' already exists and is write protected');
1474            return false;
1475          }
1476          if (filemtime($v_header['filename']) > $v_header['mtime']) {
1477            // To be completed : An error or silent no replace ?
1478          }
1479        }
1480
1481        // ----- Check the directory availability and create it if necessary
1482        elseif (($v_result
1483                 = $this->_dirCheck(($v_header['typeflag'] == "5"
1484                                    ?$v_header['filename']
1485                                    :dirname($v_header['filename'])))) != 1) {
1486            $this->_error('Unable to create path for '.$v_header['filename']);
1487            return false;
1488        }
1489
1490        if ($v_extract_file) {
1491          if ($v_header['typeflag'] == "5") {
1492            if (!@file_exists($v_header['filename'])) {
1493                if (!@mkdir($v_header['filename'], 0777)) {
1494                    $this->_error('Unable to create directory {'
1495                                  .$v_header['filename'].'}');
1496                    return false;
1497                }
1498            }
1499          } else {
1500              if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
1501                  $this->_error('Error while opening {'.$v_header['filename']
1502                                .'} in write binary mode');
1503                  return false;
1504              } else {
1505                  $n = floor($v_header['size']/512);
1506                  for ($i=0; $i<$n; $i++) {
1507                      $v_content = $this->_readBlock();
1508                      fwrite($v_dest_file, $v_content, 512);
1509                  }
1510            if (($v_header['size'] % 512) != 0) {
1511              $v_content = $this->_readBlock();
1512              fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
1513            }
1514
1515            @fclose($v_dest_file);
1516
1517            // ----- Change the file mode, mtime
1518            @touch($v_header['filename'], $v_header['mtime']);
1519            // To be completed
1520            //chmod($v_header[filename], DecOct($v_header[mode]));
1521          }
1522
1523          // ----- Check the file size
1524          clearstatcache();
1525          if (filesize($v_header['filename']) != $v_header['size']) {
1526              $this->_error('Extracted file '.$v_header['filename']
1527                            .' does not have the correct file size \''
1528                            .filesize($v_header['filename'])
1529                            .'\' ('.$v_header['size']
1530                            .' expected). Archive may be corrupted.');
1531              return false;
1532          }
1533          }
1534        } else {
1535          $this->_jumpBlock(ceil(($v_header['size']/512)));
1536        }
1537      } else {
1538          $this->_jumpBlock(ceil(($v_header['size']/512)));
1539      }
1540
1541      /* TBC : Seems to be unused ...
1542      if ($this->_compress)
1543        $v_end_of_file = @gzeof($this->_file);
1544      else
1545        $v_end_of_file = @feof($this->_file);
1546        */
1547
1548      if ($v_listing || $v_extract_file || $v_extraction_stopped) {
1549        // ----- Log extracted files
1550        if (($v_file_dir = dirname($v_header['filename']))
1551            == $v_header['filename'])
1552          $v_file_dir = '';
1553        if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
1554          $v_file_dir = '/';
1555
1556        $p_list_detail[$v_nb++] = $v_header;
1557      }
1558    }
1559
1560        return true;
1561    }
1562    // }}}
1563
1564    // {{{ _openAppend()
1565    function _openAppend()
1566    {
1567        if (filesize($this->_tarname) == 0)
1568          return $this->_openWrite();
1569         
1570        if ($this->_compress) {
1571            $this->_close();
1572
1573            if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
1574                $this->_error('Error while renaming \''.$this->_tarname
1575                              .'\' to temporary file \''.$this->_tarname
1576                              .'.tmp\'');
1577                return false;
1578            }
1579
1580            if ($this->_compress_type == 'gz')
1581                $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
1582            elseif ($this->_compress_type == 'bz2')
1583                $v_temp_tar = @bzopen($this->_tarname.".tmp", "rb");
1584               
1585            if ($v_temp_tar == 0) {
1586                $this->_error('Unable to open file \''.$this->_tarname
1587                              .'.tmp\' in binary read mode');
1588                @rename($this->_tarname.".tmp", $this->_tarname);
1589                return false;
1590            }
1591
1592            if (!$this->_openWrite()) {
1593                @rename($this->_tarname.".tmp", $this->_tarname);
1594                return false;
1595            }
1596
1597            if ($this->_compress_type == 'gz') {
1598                $v_buffer = @gzread($v_temp_tar, 512);
1599
1600                // ----- Read the following blocks but not the last one
1601                if (!@gzeof($v_temp_tar)) {
1602                    do{
1603                        $v_binary_data = pack("a512", $v_buffer);
1604                        $this->_writeBlock($v_binary_data);
1605                        $v_buffer = @gzread($v_temp_tar, 512);
1606
1607                    } while (!@gzeof($v_temp_tar));
1608                }
1609
1610                @gzclose($v_temp_tar);
1611            }
1612            elseif ($this->_compress_type == 'bz2') {
1613                $v_buffered_lines   = array();
1614                $v_buffered_lines[] = @bzread($v_temp_tar, 512);
1615
1616                // ----- Read the following blocks but not the last one
1617                while (strlen($v_buffered_lines[]
1618                              = @bzread($v_temp_tar, 512)) > 0) {
1619                    $v_binary_data = pack("a512",
1620                                          array_shift($v_buffered_lines));
1621                    $this->_writeBlock($v_binary_data);
1622                }
1623
1624                @bzclose($v_temp_tar);
1625            }
1626
1627            if (!@unlink($this->_tarname.".tmp")) {
1628                $this->_error('Error while deleting temporary file \''
1629                              .$this->_tarname.'.tmp\'');
1630            }
1631
1632        } else {
1633            // ----- For not compressed tar, just add files before the last
1634            //       512 bytes block
1635            if (!$this->_openReadWrite())
1636               return false;
1637
1638            clearstatcache();
1639            $v_size = filesize($this->_tarname);
1640            fseek($this->_file, $v_size-512);
1641        }
1642
1643        return true;
1644    }
1645    // }}}
1646
1647    // {{{ _append()
1648    function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
1649    {
1650        if (!$this->_openAppend())
1651            return false;
1652           
1653        if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
1654           $this->_writeFooter();
1655
1656        $this->_close();
1657
1658        return true;
1659    }
1660    // }}}
1661
1662    // {{{ _dirCheck()
1663
1664    /**
1665     * Check if a directory exists and create it (including parent
1666     * dirs) if not.
1667     *
1668     * @param string $p_dir directory to check
1669     *
1670     * @return bool TRUE if the directory exists or was created
1671     */
1672    function _dirCheck($p_dir)
1673    {
1674        if ((@is_dir($p_dir)) || ($p_dir == ''))
1675            return true;
1676
1677        $p_parent_dir = dirname($p_dir);
1678
1679        if (($p_parent_dir != $p_dir) &&
1680            ($p_parent_dir != '') &&
1681            (!$this->_dirCheck($p_parent_dir)))
1682             return false;
1683
1684        if (!@mkdir($p_dir, 0777)) {
1685            $this->_error("Unable to create directory '$p_dir'");
1686            return false;
1687        }
1688
1689        return true;
1690    }
1691
1692    // }}}
1693
1694    // {{{ _pathReduction()
1695
1696    /**
1697     * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
1698     * rand emove double slashes.
1699     *
1700     * @param string $p_dir path to reduce
1701     *
1702     * @return string reduced path
1703     *
1704     * @access private
1705     *
1706     */
1707    function _pathReduction($p_dir)
1708    {
1709        $v_result = '';
1710
1711        // ----- Look for not empty path
1712        if ($p_dir != '') {
1713            // ----- Explode path by directory names
1714            $v_list = explode('/', $p_dir);
1715
1716            // ----- Study directories from last to first
1717            for ($i=sizeof($v_list)-1; $i>=0; $i--) {
1718                // ----- Look for current path
1719                if ($v_list[$i] == ".") {
1720                    // ----- Ignore this directory
1721                    // Should be the first $i=0, but no check is done
1722                }
1723                else if ($v_list[$i] == "..") {
1724                    // ----- Ignore it and ignore the $i-1
1725                    $i--;
1726                }
1727                else if (   ($v_list[$i] == '')
1728                         && ($i!=(sizeof($v_list)-1))
1729                         && ($i!=0)) {
1730                    // ----- Ignore only the double '//' in path,
1731                    // but not the first and last /
1732                } else {
1733                    $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'
1734                                .$v_result:'');
1735                }
1736            }
1737        }
1738        $v_result = strtr($v_result, '\\', '/');
1739        return $v_result;
1740    }
1741
1742    // }}}
1743
1744    // {{{ _translateWinPath()
1745    function _translateWinPath($p_path, $p_remove_disk_letter=true)
1746    {
1747      if (OS_WINDOWS) {
1748          // ----- Look for potential disk letter
1749          if (   ($p_remove_disk_letter)
1750              && (($v_position = strpos($p_path, ':')) != false)) {
1751              $p_path = substr($p_path, $v_position+1);
1752          }
1753          // ----- Change potential windows directory separator
1754          if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
1755              $p_path = strtr($p_path, '\\', '/');
1756          }
1757      }
1758      return $p_path;
1759    }
1760    // }}}
1761
1762}
1763?>
Note: See TracBrowser for help on using the repository browser.