1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4/**
5 * Reader that represents a single file
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: File.php,v 1.30 2005/07/11 11:53:53 vincentlascaux Exp $
29 * @link       http://pear.php.net/package/File_Archive
30 */
31
32require_once "File/Archive/Reader.php";
33require_once "MIME/Type.php";
34
35/**
36 * Reader that represents a single file
37 */
38class File_Archive_Reader_File extends File_Archive_Reader
39{
40    /**
41     * @var object Handle to the file being read
42     * @access private
43     */
44    var $handle = null;
45    /**
46     * @var string Name of the physical file being read
47     * @access private
48     */
49    var $filename;
50    /**
51     * @var string Name of the file returned by the reader
52     * @access private
53     */
54    var $symbolic;
55    /**
56     * @var array Stats of the file
57     *      Will only be set after a call to $this->getStat()
58     * @access private
59     */
60    var $stat = null;
61    /**
62     * @var string Mime type of the file
63     *      Will only be set after a call to $this->getMime()
64     */
65    var $mime = null;
66    /**
67     * @var boolean Has the file already been read
68     * @access private
69     */
70    var $alreadyRead = false;
71
72    /**
73     * $filename is the physical file to read
74     * $symbolic is the name declared by the reader
75     * If $symbolic is not specified, $filename is assumed
76     */
77    function File_Archive_Reader_File($filename, $symbolic = null, $mime = null)
78    {
79        $this->filename = $filename;
80        $this->mime = $mime;
81        if ($symbolic === null) {
82            $this->symbolic = $this->getStandardURL($filename);
83        } else {
84            $this->symbolic = $this->getStandardURL($symbolic);
85        }
86    }
87    /**
88     * @see File_Archive_Reader::close()
89     *
90     * Close the file handle
91     */
92    function close()
93    {
94        $this->alreadyRead = false;
95        if ($this->handle !== null) {
96            fclose($this->handle);
97            $this->handle = null;
98        }
99    }
100    /**
101     * @see File_Archive_Reader::next()
102     *
103     * The first time next is called, it will open the file handle and return
104     * true. Then it will return false
105     * Raise an error if the file does not exist
106     */
107    function next()
108    {
109        if ($this->alreadyRead) {
110            return false;
111        } else {
112            $this->alreadyRead = true;
113            return true;
114        }
115    }
116    /**
117     * @see File_Archive_Reader::getFilename()
118     */
119    function getFilename() { return $this->symbolic; }
120    /**
121     * @see File_Archive_Reader::getDataFilename()
122     *
123     * Return the name of the file
124     */
125    function getDataFilename() { return $this->filename; }
126    /**
127     * @see File_Archive_Reader::getStat() stat()
128     */
129    function getStat()
130    {
131        if ($this->stat === null) {
132            $this->stat = @stat($this->filename);
133
134            //If we can't use the stat function
135            if ($this->stat === false) {
136                $this->stat = array();
137            }
138        }
139        return $this->stat;
140    }
141
142    /**
143     * @see File_Archive_Reader::getMime
144     */
145    function getMime()
146    {
147        if ($this->mime === null) {
148            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
149            $this->mime = MIME_Type::autoDetect($this->getDataFilename());
150            PEAR::popErrorHandling();
151
152            if (PEAR::isError($this->mime)) {
153                $this->mime = parent::getMime();
154            }
155        }
156        return $this->mime;
157    }
158
159    /**
160     * Opens the file if it was not already opened
161     */
162    function _ensureFileOpened()
163    {
164        if ($this->handle === null) {
165            $this->handle = @fopen($this->filename, "r");
166
167            if (!is_resource($this->handle)) {
168                $this->handle = null;
169                return PEAR::raiseError("Can't open {$this->filename} for reading");
170            }
171            if ($this->handle === false) {
172                $this->handle = null;
173                return PEAR::raiseError("File {$this->filename} not found");
174            }
175        }
176    }
177
178    /**
179     * @see File_Archive_Reader::getData()
180     */
181    function getData($length = -1)
182    {
183        $error = $this->_ensureFileOpened();
184        if (PEAR::isError($error)) {
185            return $error;
186        }
187
188        if (feof($this->handle)) {
189            return null;
190        }
191        if ($length == -1) {
192            $contents = '';
193            $blockSize = File_Archive::getOption('blockSize');
194            while (!feof($this->handle)) {
195                $contents .= fread($this->handle, $blockSize);
196            }
197            return $contents;
198        } else {
199            if ($length == 0) {
200                return "";
201            } else {
202                return fread($this->handle, $length);
203            }
204        }
205    }
206
207    /**
208     * @see File_Archive_Reader::skip()
209     */
210    function skip($length = -1)
211    {
212        $error = $this->_ensureFileOpened();
213        if (PEAR::isError($error)) {
214            return $error;
215        }
216
217        $before = ftell($this->handle);
218        if (($length == -1 && @fseek($this->handle, 0, SEEK_END) === -1) ||
219            ($length >= 0  && @fseek($this->handle, $length, SEEK_CUR) === -1)) {
220            return parent::skip($length);
221        } else {
222            return ftell($this->handle) - $before;
223        }
224    }
225
226    /**
227     * @see File_Archive_Reader::rewind
228     */
229    function rewind($length = -1)
230    {
231        if ($this->handle === null) {
232            return 0;
233        }
234
235        $before = ftell($this->handle);
236        if (($length == -1 && @fseek($this->handle, 0, SEEK_SET) === -1) ||
237            ($length >= 0  && @fseek($this->handle, -$length, SEEK_CUR) === -1)) {
238            return parent::rewind($length);
239        } else {
240            return $before - ftell($this->handle);
241        }
242    }
243
244    /**
245     * @see File_Archive_Reader::tell()
246     */
247    function tell()
248    {
249        if ($this->handle === null) {
250            return 0;
251        } else {
252            return ftell($this->handle);
253        }
254    }
255
256
257    /**
258     * @see File_Archive_Reader::makeWriterRemoveFiles()
259     */
260    function makeWriterRemoveFiles($pred)
261    {
262        return PEAR::raiseError(
263            'File_Archive_Reader_File represents a single file, you cant remove it');
264    }
265
266    /**
267     * @see File_Archive_Reader::makeWriterRemoveBlocks()
268     */
269    function makeWriterRemoveBlocks($blocks, $seek = 0)
270    {
271        require_once "File/Archive/Writer/Files.php";
272
273        $writer = new File_Archive_Writer_Files();
274
275        $file = $this->getDataFilename();
276        $pos = $this->tell();
277        $this->close();
278
279        $writer->openFileRemoveBlock($file, $pos + $seek, $blocks);
280
281        return $writer;
282    }
283
284    /**
285     * @see File_Archive_Reader::makeAppendWriter
286     */
287    function makeAppendWriter()
288    {
289        return PEAR::raiseError(
290            'File_Archive_Reader_File represents a single file.'.
291            ' makeAppendWriter cant be executed on it'
292        );
293    }
294}
295
296?>