1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4/**
5 * Uncompress a file that was compressed in the Gzip format
6 *
7 * PHP versions 4 and 5
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330,Boston,MA 02111-1307 USA
22 *
23 * @category   File Formats
24 * @package    File_Archive
25 * @author     Vincent Lascaux <vincentlascaux@php.net>
26 * @copyright  1997-2005 The PHP Group
27 * @license    http://www.gnu.org/copyleft/lesser.html  LGPL
28 * @version    CVS: $Id: Gzip.php,v 1.27 2005/06/19 20:09:57 vincentlascaux Exp $
29 * @link       http://pear.php.net/package/File_Archive
30 */
31
32require_once "File/Archive/Reader/Archive.php";
33require_once "File/Archive/Writer/Files.php";
34
35/**
36 * Uncompress a file that was compressed in the Gzip format
37 */
38class File_Archive_Reader_Gzip extends File_Archive_Reader_Archive
39{
40    var $nbRead = 0;
41    var $filePos = 0;
42    var $gzfile = null;
43    var $tmpName = null;
44
45    /**
46     * @see File_Archive_Reader::close()
47     */
48    function close($innerClose = true)
49    {
50        if ($this->gzfile !== null) {
51            gzclose($this->gzfile);
52        }
53        if ($this->tmpName !== null) {
54            unlink($this->tmpName);
55        }
56
57        $this->nbRead = 0;
58        $this->filePos = 0;
59        $this->gzfile = null;
60        $this->tmpName = null;
61
62        return parent::close($innerClose);
63    }
64
65    /**
66     * @see File_Archive_Reader::next()
67     */
68    function next()
69    {
70        if (!parent::next()) {
71            return false;
72        }
73
74        $this->nbRead++;
75        $this->filePos = 0;
76        if ($this->nbRead > 1) {
77            return false;
78        }
79
80        $dataFilename = $this->source->getDataFilename();
81        if ($dataFilename !== null)
82        {
83            $this->tmpName = null;
84            $this->gzfile = gzopen($dataFilename, 'r');
85        } else {
86            $this->tmpName = tempnam(File_Archive::getOption('tmpDirectory'), 'far');
87
88            //Generate the tmp data
89            $dest = new File_Archive_Writer_Files();
90            $dest->newFile($this->tmpName);
91            $this->source->sendData($dest);
92            $dest->close();
93
94            $this->gzfile = gzopen($this->tmpName, 'r');
95        }
96
97        return true;
98    }
99
100    /**
101     * Return the name of the single file contained in the archive
102     * deduced from the name of the archive (the extension is removed)
103     *
104     * @see File_Archive_Reader::getFilename()
105     */
106    function getFilename()
107    {
108        $name = $this->source->getFilename();
109        $slashPos = strrpos($name, '/');
110        if ($slashPos !== false) {
111            $name = substr($name, $slashPos+1);
112        }
113        $dotPos = strrpos($name, '.');
114        if ($dotPos !== false && $dotPos > 0) {
115            $name = substr($name, 0, $dotPos);
116        }
117
118        return $name;
119    }
120
121    /**
122     * @see File_Archive_Reader::getData()
123     */
124    function getData($length = -1)
125    {
126        if ($length == -1) {
127            $data = '';
128            do
129            {
130                $newData = gzread($this->gzfile, 8192);
131                $data .= $newData;
132            } while ($newData != '');
133        } else if ($length == 0) {
134            return '';
135        } else {
136            $data = gzread($this->gzfile, $length);
137        }
138
139        $this->filePos += strlen($data);
140        return $data == '' ? null : $data;
141    }
142
143    /**
144     * @see File_Archive_Reader::skip()
145     */
146    function skip($length = -1)
147    {
148        if($length == -1) {
149            do
150            {
151                $tmp = gzread($this->gzfile, 8192);
152                $this->filePos += strlen($tmp);
153            } while ($tmp != '');
154        } else {
155            if (@gzseek($this->gzfile, $this->filePos + $length) === -1) {
156                return parent::skip($length);
157            } else {
158                $this->filePos += $length;
159                return $length;
160            }
161        }
162    }
163
164    /**
165     * @see File_Archive_Reader::rewind()
166     */
167    function rewind($length = -1)
168    {
169        if ($length == -1) {
170            if (@gzseek($this->gzfile, 0) === -1) {
171                return parent::rewind($length);
172            } else {
173                $tmp = $this->filePos;
174                $this->filePos = 0;
175                return $tmp;
176            }
177        } else {
178            $length = min($length, $this->filePos);
179            if (@gzseek($this->gzfile, $this->filePos - $length) === -1) {
180                return parent::rewind($length);
181            } else {
182                $this->filePos -= $length;
183                return $length;
184            }
185        }
186    }
187
188    /**
189     * @see File_Archive_Reader::tell()
190     */
191    function tell()
192    {
193        return $this->filePos;
194    }
195
196    /**
197     * @see File_Archive_Reader::makeAppendWriter()
198     */
199    function makeAppendWriter()
200    {
201        return PEAR::raiseError('Unable to append files to a gzip archive');
202    }
203
204    /**
205     * @see File_Archive_Reader::makeWriterRemoveFiles()
206     */
207    function makeWriterRemoveFiles($pred)
208    {
209        return PEAR::raiseError('Unable to remove files from a gzip archive');
210    }
211
212    /**
213     * @see File_Archive_Reader::makeWriterRemoveBlocks()
214     */
215    function makeWriterRemoveBlocks($blocks, $seek = 0)
216    {
217        require_once "File/Archive/Writer/Gzip.php";
218
219        if ($this->nbRead == 0) {
220            return PEAR::raiseError('No file selected');
221        }
222
223        //Uncompress data to a temporary file
224        $tmp = tmpfile();
225        $expectedPos = $this->filePos + $seek;
226        $this->rewind();
227
228        //Read the begining of the file
229        while ($this->filePos < $expectedPos &&
230               ($data = $this->getData(min($expectedPos - $this->filePos, 8192))) !== null) {
231            fwrite($tmp, $data);
232        }
233
234        $keep = false;
235        foreach ($blocks as $length) {
236            if ($keep) {
237                $expectedPos = $this->filePos + $length;
238                while ($this->filePos < $expectedPos &&
239                       ($data = $this->getData(min($expectedPos - $this->filePos, 8192))) !== null) {
240                    fwrite($tmp, $data);
241                }
242            } else {
243                $this->skip($length);
244            }
245            $keep = !$keep;
246        }
247        if ($keep) {
248            //Read the end of the file
249            while(($data = $this->getData(8192)) !== null) {
250                fwrite($tmp, $data);
251            }
252        }
253        fseek($tmp, 0);
254
255        //Create the writer
256        $this->source->rewind();
257        $innerWriter = $this->source->makeWriterRemoveBlocks(array()); //Truncate the source
258        unset($this->source);
259        $writer = new File_Archive_Writer_Gzip(null, $innerWriter);
260
261        //And compress data from the temporary file
262        while (!feof($tmp)) {
263            $data = fread($tmp, 8192);
264            $writer->writeData($data);
265        }
266        fclose($tmp);
267
268        //Do not close inner writer since makeWriter was called
269        $this->close();
270
271        return $writer;
272    }
273
274}
275
276?>