1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4/**
5 * Writer to files
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: Files.php,v 1.21 2005/06/18 23:08:16 vincentlascaux Exp $
29 * @link       http://pear.php.net/package/File_Archive
30 */
31
32require_once "File/Archive/Writer.php";
33
34/**
35  * Writer to files
36  */
37class File_Archive_Writer_Files extends File_Archive_Writer
38{
39    /**
40     * @var Object Handle to the file where the data are currently written
41     * @access private
42     */
43    var $handle = null;
44    var $basePath;
45    var $stat = array();
46    var $filename;
47
48    function File_Archive_Writer_Files($base = '')
49    {
50        if ($base === null || $base == '') {
51            $this->basePath = '';
52        } else {
53            if (substr($base, -1) == '/') {
54                $this->basePath = $base;
55            } else {
56                $this->basePath = $base.'/';
57            }
58        }
59    }
60
61    function getFilename($filename)
62    {
63        return $this->basePath.$filename;
64    }
65
66    /**
67     * Ensure that $pathname exists, or create it if it does not
68     * @access private
69     */
70    function mkdirr($pathname)
71    {
72        // Check if directory already exists
73        if (is_dir($pathname) || empty($pathname)) {
74            return;
75        }
76
77        // Ensure a file does not already exist with the same name
78        if (is_file($pathname)) {
79            return PEAR::raiseError(
80                "File $pathname exists, unable to create directory"
81            );
82        }
83
84        // Crawl up the directory tree
85        $next_pathname = substr(
86                    $pathname,
87                    0, strrpos($pathname, "/"));
88        $error = $this->mkdirr($next_pathname);
89        if (PEAR::isError($error)) {
90            return $error;
91        }
92        if (!@mkdir($pathname)) {
93            return PEAR::raiseError("Unable to create directory $pathname");
94        }
95    }
96
97    /**
98     * Open a file for writing from a given position
99     *
100     * @param string $filename The name of the file to open
101     * @param int $pos the initial position in the file
102     * @param $stat the stats of the file
103     */
104    function openFile($filename, $pos = 0)
105    {
106        $this->close();
107
108        $this->handle = fopen($filename, 'r+');
109        $this->stat = array();
110        $this->filename = $filename;
111
112        if (!is_resource($this->handle)) {
113            return PEAR::raiseError("Unable to open file $filename");
114        }
115
116        if ($pos > 0) {
117            if (fseek($this->handle, $pos) == -1) {
118                fread($this->handle, $pos);
119            }
120        }
121    }
122
123    /**
124     * Open a file for appending after having removed a block of data from it
125     * See File_Archive_Reader::makeWriterRemoveBlocks
126     */
127    function openFileRemoveBlock($filename, $pos, $blocks)
128    {
129        $error = $this->openFile($filename, $pos);
130        if (PEAR::isError($error)) {
131            return $error;
132        }
133
134        if (!empty($blocks)) {
135            //This will be used to read the initial file
136            //The data, with the unusefull block removed will be written to $this->handle
137            $read = fopen($filename, 'r');
138            if ($pos > 0) {
139                if (fseek($this->handle, $pos) == -1) {
140                    fread($this->handle, $pos);
141                }
142            }
143
144            $keep = false;
145            $data = '';
146            foreach ($blocks as $length) {
147                if ($keep) {
148                    while ($length > 0 &&
149                           ($data = fread($read, min($length, 8192))) != '') {
150                        $length -= strlen($data);
151                        fwrite($this->handle, $data);
152                    }
153                } else {
154                    fseek($read, $length, SEEK_CUR);
155                }
156                $keep = !$keep;
157            }
158            if ($keep) {
159                while(!feof($this->handle)) {
160                    fwrite($this->handle, fread($read, 8196));
161                }
162            }
163
164            fclose($read);
165        }
166
167        ftruncate($this->handle, ftell($this->handle));
168    }
169
170
171    /**
172     * @see File_Archive_Writer::newFile()
173     */
174    function newFile($filename, $stat = array(), $mime = "application/octet-stream")
175    {
176        $this->close();
177        $this->stat = $stat;
178        $this->filename = $this->getFilename($filename);
179
180        $pos = strrpos($this->filename, "/");
181        if ($pos !== false) {
182            $error = $this->mkdirr(substr($this->filename, 0, $pos));
183            if (PEAR::isError($error)) {
184                return $error;
185            }
186        }
187        $this->handle = @fopen($this->filename, "w");
188        if (!is_resource($this->handle)) {
189            return PEAR::raiseError("Unable to write to file $filename");
190        }
191    }
192    /**
193     * @see File_Archive_Writer::writeData()
194     */
195    function writeData($data) { fwrite($this->handle, $data); }
196    /**
197     * @see File_Archive_Writer::newFromTempFile()
198     */
199    function newFromTempFile($tmpfile, $filename, $stat = array(), $mime = "application/octet-stream")
200    {
201        $this->filename = filename;
202        $complete = $this->getFilename($filename);
203        $pos = strrpos($complete, "/");
204        if ($pos !== false) {
205            $error = $this->mkdirr(substr($complete, 0, $pos));
206            if (PEAR::isError($error)) {
207                return $error;
208            }
209        }
210
211        if ((file_exists($complete) && !@unlink($complete)) ||
212            !@rename($tmpfile, $complete)) {
213            return parent::newFromTempFile($tmpfile, $filename, $stat, $mime);
214        }
215    }
216
217
218    /**
219     * @see File_Archive_Writer::close()
220     */
221    function close()
222    {
223        if ($this->handle !== null) {
224            fclose($this->handle);
225            $this->handle = null;
226
227            if (isset($this->stat[9])) {
228                if (isset($this->stat[8])) {
229                    touch($this->filename, $this->stat[9], $this->stat[8]);
230                } else {
231                    touch($this->filename, $this->stat[9]);
232                }
233            } else if (isset($this->stat[8])) {
234                touch($this->filename, time(), $this->stat[8]);
235            }
236
237            if (isset($this->stat[2])) {
238                chmod($this->filename, $this->stat[2]);
239            }
240            if (isset($this->stat[5])) {
241                chgrp($this->filename, $this->stat[5]);
242            }
243            if (isset($this->stat[4])) {
244                chown($this->filename, $this->stat[4]);
245            }
246        }
247    }
248}
249
250?>