1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4/**
5 * Uncompress a file that was compressed in the Bzip2 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: Bzip2.php,v 1.19 2005/07/26 09:06:03 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 Bzip2 format
37 */
38class File_Archive_Reader_Bzip2 extends File_Archive_Reader_Archive
39{
40    var $nbRead = 0;
41    var $bzfile = null;
42    var $tmpName = null;
43    var $filePos = 0;
44
45    /**
46     * @see File_Archive_Reader::close()
47     */
48    function close($innerClose = true)
49    {
50        if ($this->bzfile !== null)
51            bzclose($this->bzfile);
52        if ($this->tmpName !== null)
53            unlink($this->tmpName);
54
55        $this->bzfile = null;
56        $this->tmpName = null;
57        $this->nbRead = 0;
58        $this->filePos = 0;
59        return parent::close($innerClose);
60    }
61
62    /**
63     * @see File_Archive_Reader::next()
64     */
65    function next()
66    {
67        if (!parent::next()) {
68            return false;
69        }
70
71        $this->nbRead++;
72        if ($this->nbRead > 1) {
73            return false;
74        }
75
76        $dataFilename = $this->source->getDataFilename();
77        if ($dataFilename !== null)
78        {
79            $this->tmpName = null;
80            $this->bzfile = @bzopen($dataFilename, 'r');
81            if ($this->bzfile === false) {
82                return PEAR::raiseError("bzopen failed to open $dataFilename");
83            }
84        } else {
85            $this->tmpName = tempnam(File_Archive::getOption('tmpDirectory'), 'far');
86
87            //Generate the tmp data
88            $dest = new File_Archive_Writer_Files();
89            $dest->newFile($this->tmpName);
90            $this->source->sendData($dest);
91            $dest->close();
92
93            $this->bzfile = bzopen($this->tmpName, 'r');
94        }
95
96        return true;
97    }
98    /**
99     * Return the name of the single file contained in the archive
100     * deduced from the name of the archive (the extension is removed)
101     *
102     * @see File_Archive_Reader::getFilename()
103     */
104    function getFilename()
105    {
106        $name = $this->source->getFilename();
107        $pos = strrpos($name, ".");
108        if ($pos === false || $pos === 0) {
109            return $name;
110        } else {
111            return substr($name, 0, $pos);
112        }
113    }
114    /**
115     * @see File_Archive_Reader::getData()
116     */
117    function getData($length = -1)
118    {
119        if ($length == -1) {
120            $data = '';
121            do {
122                $newData = bzread($this->bzfile);
123                $data .= $newData;
124            } while ($newData != '');
125            $this->filePos += strlen($data);
126        } else if ($length == 0) {
127            return '';
128        } else {
129            $data = '';
130
131            //The loop is here to correct what appears to be a bzread bug
132            while (strlen($data) < $length) {
133                $newData = bzread($this->bzfile, $length - strlen($data));
134                if ($newData == '') {
135                    break;
136                }
137                $data .= $newData;
138            }
139            $this->filePos += strlen($data);
140        }
141
142        return $data == '' ? null : $data;
143    }
144
145    /**
146     * @see File_Archive_Reader::rewind
147     */
148    function rewind($length = -1)
149    {
150        $before = $this->filePos;
151
152        bzclose($this->bzfile);
153        if ($this->tmpName === null) {
154            $this->bzfile = bzopen($this->source->getDataFilename(), 'r');
155        } else {
156            $this->bzfile = bzopen($this->tmpName, 'r');
157        }
158        $this->filePos = 0;
159
160        if ($length != -1) {
161            $this->skip($before - $length);
162        }
163        return $before - $this->filePos;
164    }
165
166    /**
167     * @see File_Archive_Reader::tell()
168     */
169    function tell()
170    {
171        return $this->filePos;
172    }
173
174    /**
175     * @see File_Archive_Reader::makeAppendWriter()
176     */
177    function makeAppendWriter()
178    {
179        return PEAR::raiseError('Unable to append files to a bzip2 archive');
180    }
181
182    /**
183     * @see File_Archive_Reader::makeWriterRemoveFiles()
184     */
185    function makeWriterRemoveFiles($pred)
186    {
187        return PEAR::raiseError('Unable to remove files from a bzip2 archive');
188    }
189
190    /**
191     * @see File_Archive_Reader::makeWriterRemoveBlocks()
192     */
193    function makeWriterRemoveBlocks($blocks, $seek = 0)
194    {
195        require_once "File/Archive/Writer/Bzip2.php";
196
197        if ($this->nbRead == 0) {
198            return PEAR::raiseError('No file selected');
199        }
200
201        //Uncompress data to a temporary file
202        $tmp = tmpfile();
203        $expectedPos = $this->filePos + $seek;
204
205        $this->rewind();
206
207        //Read the begining of the file
208        while ($this->filePos < $expectedPos &&
209               ($data = $this->getData(min($expectedPos - $this->filePos, 8192))) !== null) {
210            fwrite($tmp, $data);
211        }
212
213        $keep = false;
214        foreach ($blocks as $length) {
215            if ($keep) {
216                $expectedPos = $this->filePos + $length;
217                while ($this->filePos < $expectedPos &&
218                       ($data = $this->getData(min($expectedPos - $this->filePos, 8192))) !== null) {
219                    fwrite($tmp, $data);
220                }
221            } else {
222                $this->skip($length);
223            }
224            $keep = !$keep;
225        }
226        if ($keep) {
227            //Read the end of the file
228            while(($data = $this->getData(8192)) !== null) {
229                fwrite($tmp, $data);
230            }
231        }
232        fseek($tmp, 0);
233
234        //Create the writer
235        $this->source->rewind();
236        $innerWriter = $this->source->makeWriterRemoveBlocks(array());   //Truncate the source
237        unset($this->source);
238        $writer = new File_Archive_Writer_Bzip2(null, $innerWriter);
239
240        //And compress data from the temporary file
241        while (!feof($tmp)) {
242            $data = fread($tmp, 8192);
243            $writer->writeData($data);
244        }
245        fclose($tmp);
246
247        //Do not close inner writer since makeWriter was called
248        $this->close();
249
250        return $writer;
251    }
252}
253
254?>