Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * Read a file saved in Ar file format
4
 *
5
 * PHP versions 4 and 5
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 59 Temple Place, Suite 330,Boston,MA 02111-1307 USA
20
 *
21
 * @category   File Formats
22
 * @package    File_Archive
23
 * @author     Pablo Fischer <pablo@pablo.com.mx>
24
 * @copyright  1997-2005 The PHP Group
25
 * @license    http://www.gnu.org/copyleft/lesser.html  LGPL
26
 * @version    CVS: $Id:
27
 * @link       http://pear.php.net/package/File_Archive
28
 */
29
 
30
require_once "File/Archive/Reader/Archive.php";
31
 
32
/**
33
 * Read an Ar archive
34
 */
35
class File_Archive_Reader_Ar extends File_Archive_Reader_Archive
36
{
37
    /**
38
     * @var    int       The number of files to read to reach the end of the
39
     *                   current ar file
40
     *
41
     * @access private
42
     */
43
    var $_nbBytesLeft = 0;
44
 
45
    /**
46
     * @var    int      The size of the header in number of bytes
47
     *                  The header is not always 60 bytes since it sometimes
48
     *                  contains a long filename
49
     * @access private
50
     */
51
    var $_header = 0;
52
 
53
    /**
54
     * @var    boolean   Flag set if their is a 1 byte footer after the data
55
     *                   of the current ar file
56
     *
57
     * @access private
58
     */
59
    var $_footer = false;
60
 
61
    /**
62
     * @var    boolean Flag that has tell us if we have read the header of the
63
     *                 current file
64
     * @access private
65
     */
66
    var $_alreadyRead = false;
67
 
68
    /**
69
     * @var    string  Name of the file being read
70
     * @access private
71
     */
72
    var $_currentFilename = null;
73
 
74
    /**
75
     * @var    string  Stat properties of the file being read
76
     *                 It has: name, utime, uid, gid, mode, size and data
77
     * @access private
78
     */
79
    var $_currentStat = null;
80
 
81
    /**
82
     * @see File_Archive_Reader::getFilename()
83
     */
84
    function getFilename()
85
    {
86
        return $this->_currentFilename;
87
    }
88
 
89
    /**
90
     * @see File_Archive_Reader::close()
91
     */
92
    function close()
93
    {
94
        $this->_currentFilename = null;
95
        $this->_currentStat = null;
96
        $this->_nbBytesLeft = 0;
97
        $this->_header = 0;
98
        $this->_footer = false;
99
        $this->_alreadyRead = false;
100
        return parent::close();
101
    }
102
 
103
    /**
104
     * @see File_Archive_Reader::getStat()
105
     */
106
    function getStat()
107
    {
108
        return $this->_currentStat;
109
    }
110
 
111
    /**
112
     * @see File_Archive_Reader::next()
113
     */
114
    function next()
115
    {
116
        $error = parent::next();
117
        if ($error !== true) {
118
            return $error;
119
        }
120
 
121
        $this->source->skip(
122
            $this->_nbBytesLeft + ($this->_footer ? 1 : 0)
123
        );
124
 
125
        $filename = $this->source->getDataFilename();
126
 
127
        if (!$this->_alreadyRead) {
128
            $header = $this->source->getData(8);
129
            if ($header != "!<arch>\n") {
130
                return PEAR::raiseError("File {$filename} is not a valid Ar file format (starts with $header)");
131
            }
132
            $this->_alreadyRead = true;
133
        }
134
 
135
 
136
        $name  = $this->source->getData(16);
137
        $mtime = $this->source->getData(12);
138
        $uid   = $this->source->getData(6);
139
        $gid   = $this->source->getData(6);
140
        $mode  = $this->source->getData(8);
141
        $size  = $this->source->getData(10);
142
        $delim = $this->source->getData(2);
143
 
144
        if ($delim === null) {
145
            return false;
146
        }
147
 
148
        // All files inside should have more than 0 bytes of size
149
        if ($size < 0) {
150
            return PEAR::raiseError("Files must be at least one byte long");
151
        }
152
 
153
        $this->_footer = ($size % 2 == 1);
154
 
155
        // if the filename starts with a length, then just read the bytes of it
156
        if (preg_match("/\#1\/(\d+)/", $name, $matches)) {
157
            $this->_header = 60 + $matches[1];
158
            $name = $this->source->getData($matches[1]);
159
            $size -= $matches[1];
160
        } else {
161
            // strip trailing spaces in name, so we can distinguish spaces in a filename with padding
162
            $this->_header = 60;
163
            $name = preg_replace ("/\s+$/", "", $name);
164
        }
165
 
166
        $this->_nbBytesLeft = $size;
167
        if (empty($name) || empty($mtime) || empty($uid) ||
168
            empty($gid)  || empty($mode)  || empty($size)) {
169
            return PEAR::raiseError("An ar field is empty");
170
        }
171
 
172
        $this->_currentFilename = $this->getStandardURL($name);
173
        $this->_currentStat = array(
174
                                    2       => $mode,
175
                                    'mode'  => $mode,
176
                                    4       => $uid,
177
                                    'uid'   => $uid,
178
                                    5       => $gid,
179
                                    'gid'   => $gid,
180
                                    7       => $size,
181
                                    'size'  => $size,
182
                                    9       => $mtime,
183
                                    'mtime' => $mtime
184
                                    );
185
 
186
        return true;
187
    }
188
 
189
    /**
190
     * @see File_Archive_Reader::getData()
191
     */
192
    function getData($length = -1)
193
    {
194
        if ($length == -1) {
195
            $length = $this->_nbBytesLeft;
196
        } else {
197
            $length = min($length, $this->_nbBytesLeft);
198
        }
199
        if ($length == 0) {
200
            return null;
201
        } else {
202
            $this->_nbBytesLeft -= $length;
203
            $data = $this->source->getData($length);
204
            if (PEAR::isError($data)) {
205
                return $data;
206
            }
207
            if (strlen($data) != $length) {
208
                return PEAR::raiseError('Unexpected end of Ar archive');
209
            }
210
            return $data;
211
        }
212
    }
213
 
214
    /**
215
     * @see File_Archive_Reader::skip
216
     */
217
    function skip($length = -1)
218
    {
219
        if ($length == -1) {
220
            $length = $this->_nbBytesLeft;
221
        } else {
222
            $length = min($length, $this->_nbBytesLeft);
223
        }
224
        if ($length == 0) {
225
            return 0;
226
        } else {
227
            $this->_nbBytesLeft -= $length;
228
            $skipped = $this->source->skip($length);
229
            if (PEAR::isError($skipped)) {
230
                return $skipped;
231
            }
232
            if ($skipped != $length) {
233
                return PEAR::raiseError('Unexpected end of Ar archive');
234
            }
235
            return $skipped;
236
        }
237
    }
238
 
239
    /**
240
     * @see File_Archive_Reader::rewind
241
     */
242
    function rewind($length = -1)
243
    {
244
        if ($length == -1) {
245
            $length = $this->_currentStat[7] - $this->_nbBytesLeft;
246
        } else {
247
            $length = min($length, $this->_currentStat[7] - $this->_nbBytesLeft);
248
        }
249
        if ($length == 0) {
250
            return 0;
251
        } else {
252
            $rewinded = $this->source->rewind($length);
253
            if (!PEAR::isError($rewinded)) {
254
                $this->_nbBytesLeft += $rewinded;
255
            }
256
            return $rewinded;
257
        }
258
    }
259
 
260
    /**
261
     * @see File_Archive_Reader::tell()
262
     */
263
    function tell()
264
    {
265
        return $this->_currentStat[7] - $this->_nbBytesLeft;
266
    }
267
 
268
    /**
269
     * @see File_Archive_Reader::makeWriterRemoveFiles()
270
     */
271
    function makeWriterRemoveFiles($pred)
272
    {
273
        require_once "File/Archive/Writer/Ar.php";
274
 
275
        $blocks = array();
276
        $seek = null;
277
        $gap = 0;
278
        if ($this->_currentFilename !== null && $pred->isTrue($this)) {
279
            $seek = $this->_header + $this->_currentStat[7] + ($this->_footer ? 1 : 0);
280
            $blocks[] = $seek; //Remove this file
281
        }
282
 
283
        while (($error = $this->next()) === true) {
284
            $size = $this->_header + $this->_currentStat[7] + ($this->_footer ? 1 : 0);
285
            if ($pred->isTrue($this)) {
286
                if ($seek === null) {
287
                    $seek = $size;
288
                    $blocks[] = $size;
289
                } else if ($gap > 0) {
290
                    $blocks[] = $gap; //Don't remove the files between the gap
291
                    $blocks[] = $size;
292
                    $seek += $size;
293
                } else {
294
                    $blocks[count($blocks)-1] += $size;   //Also remove this file
295
                    $seek += $size;
296
                }
297
                $gap = 0;
298
            } else {
299
                if ($seek !== null) {
300
                    $seek += $size;
301
                    $gap += $size;
302
                }
303
            }
304
        }
305
        if ($seek === null) {
306
            $seek = 0;
307
        } else {
308
            if ($gap == 0) {
309
                array_pop($blocks);
310
            } else {
311
                $blocks[] = $gap;
312
            }
313
        }
314
 
315
        $writer = new File_Archive_Writer_Ar(null,
316
            $this->source->makeWriterRemoveBlocks($blocks, -$seek)
317
        );
318
        $this->close();
319
        return $writer;
320
    }
321
 
322
    /**
323
     * @see File_Archive_Reader::makeWriterRemoveBlocks()
324
     */
325
    function makeWriterRemoveBlocks($blocks, $seek = 0)
326
    {
327
        if ($this->_currentStat === null) {
328
            return PEAR::raiseError('No file selected');
329
        }
330
 
331
        $blockPos = $this->_currentStat[7] - $this->_nbBytesLeft + $seek;
332
 
333
        $this->rewind();
334
        $keep = false;
335
 
336
        $data = $this->getData($blockPos);
337
        foreach ($blocks as $length) {
338
            if ($keep) {
339
                $data .= $this->getData($length);
340
            } else {
341
                $this->skip($length);
342
            }
343
            $keep = !$keep;
344
        }
345
        if ($keep) {
346
            $data .= $this->getData();
347
        }
348
 
349
        $filename = $this->_currentFilename;
350
        $stat = $this->_currentStat;
351
 
352
        $writer = $this->makeWriterRemove();
353
        if (PEAR::isError($writer)) {
354
            return $writer;
355
        }
356
 
357
        unset($stat[7]);
358
        $writer->newFile($filename, $stat);
359
        $writer->writeData($data);
360
        return $writer;
361
    }
362
 
363
    /**
364
     * @see File_Archive_Reader::makeAppendWriter
365
     */
366
    function makeAppendWriter()
367
    {
368
        require_once "File/Archive/Writer/Ar.php";
369
 
370
        while (($error = $this->next()) === true) { }
371
        if (PEAR::isError($error)) {
372
            $this->close();
373
            return $error;
374
        }
375
 
376
        $innerWriter = $this->source->makeWriterRemoveBlocks(array());
377
        if (PEAR::isError($innerWriter)) {
378
            return $innerWriter;
379
        }
380
 
381
        unset($this->source);
382
        $this->close();
383
 
384
        return new File_Archive_Writer_Ar(null, $innerWriter);
385
    }
386
}
387
?>