1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4/**
5 * Write the files as a TAR archive
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: Tar.php,v 1.18 2005/06/02 12:24:43 vincentlascaux Exp $
29 * @link       http://pear.php.net/package/File_Archive
30 */
31
32require_once "File/Archive/Writer/Archive.php";
33
34/**
35 * Write the files as a TAR archive
36 */
37class File_Archive_Writer_Tar extends File_Archive_Writer_Archive
38{
39    var $buffer;
40    var $useBuffer;
41
42    var $filename = null;
43    var $stats = null;
44
45
46    /**
47     * Creates the TAR header for a file
48     *
49     * @param string $filename name of the file
50     * @param array $stat statistics of the file
51     * @return string A 512 byte header for the file
52     * @access private
53     */
54    function tarHeader($filename, $stat)
55    {
56        $mode = isset($stat[2]) ? $stat[2] : 0x8000;
57        $uid  = isset($stat[4]) ? $stat[4] : 0;
58        $gid  = isset($stat[5]) ? $stat[5] : 0;
59        $size = $stat[7];
60        $time = isset($stat[9]) ? $stat[9] : time();
61        $link = "";
62
63        if ($mode & 0x4000) {
64            $type = 5;        // Directory
65        } else if ($mode & 0x8000) {
66            $type = 0;        // Regular
67        } else if ($mode & 0xA000) {
68            $type = 1;        // Link
69            $link = @readlink($current);
70        } else {
71            $type = 9;        // Unknown
72        }
73
74        $filePrefix = '';
75        if (strlen($filename) > 255) {
76            return PEAR::raiseError(
77                "$filename is too long to be put in a tar archive"
78            );
79        } else if (strlen($filename) > 100) {
80            $filePrefix = substr($filename, 0, strlen($filename)-100);
81            $filename = substr($filename, -100);
82        }
83
84        $blockbeg = pack("a100a8a8a8a12a12",
85            $filename,
86            decoct($mode),
87            sprintf("%6s ",decoct($uid)),
88            sprintf("%6s ",decoct($gid)),
89            sprintf("%11s ",decoct($size)),
90            sprintf("%11s ",decoct($time))
91            );
92
93        $blockend = pack("a1a100a6a2a32a32a8a8a155a12",
94            $type,
95            $link,
96            "ustar",
97            "00",
98            "Unknown",
99            "Unknown",
100            "",
101            "",
102            $filePrefix,
103            "");
104
105        $checksum = 8*ord(" ");
106        for ($i = 0; $i < 148; $i++) {
107            $checksum += ord($blockbeg{$i});
108        }
109        for ($i = 0; $i < 356; $i++) {
110            $checksum += ord($blockend{$i});
111        }
112
113        $checksum = pack("a8",sprintf("%6s ",decoct($checksum)));
114
115        return $blockbeg . $checksum . $blockend;
116    }
117    /**
118     * Creates the TAR footer for a file
119     *
120     * @param  int $size the size of the data that has been written to the TAR
121     * @return string A string made of less than 512 characteres to fill the
122     *         last 512 byte long block
123     * @access private
124     */
125    function tarFooter($size)
126    {
127        if ($size % 512 > 0) {
128            return pack("a".(512 - $size%512), "");
129        } else {
130            return "";
131        }
132    }
133
134    function flush()
135    {
136        if ($this->filename !== null) {
137            if ($this->useBuffer) {
138                $this->stats[7] = strlen($this->buffer);
139
140                $this->innerWriter->writeData(
141                    $this->tarHeader($this->filename, $this->stats)
142                );
143                $this->innerWriter->writeData(
144                    $this->buffer
145                );
146            }
147            $this->innerWriter->writeData(
148                $this->tarFooter($this->stats[7])
149            );
150        }
151        $this->buffer = "";
152    }
153
154    function newFile($filename, $stats = array(),
155                     $mime = "application/octet-stream")
156    {
157        $this->flush();
158
159        $this->useBuffer = !isset($stats[7]);
160        $this->filename = $filename;
161        $this->stats = $stats;
162
163        if (!$this->useBuffer) {
164            return $this->innerWriter->writeData(
165                $this->tarHeader($filename, $stats)
166            );
167        }
168    }
169
170    /**
171     * @see File_Archive_Writer::close()
172     */
173    function close()
174    {
175        $this->flush();
176        $this->innerWriter->writeData(pack("a1024", ""));
177        parent::close();
178    }
179    /**
180     * @see File_Archive_Writer::writeData()
181     */
182    function writeData($data)
183    {
184        if ($this->useBuffer) {
185            $this->buffer .= $data;
186        } else {
187            $this->innerWriter->writeData($data);
188        }
189
190    }
191    /**
192     * @see File_Archive_Writer::writeFile()
193     */
194    function writeFile($filename)
195    {
196        if ($this->useBuffer) {
197            $this->buffer .= file_get_contents($filename);
198        } else {
199            $this->innerWriter->writeFile($filename);
200        }
201    }
202    /**
203     * @see File_Archive_Writer::getMime()
204     */
205    function getMime() { return "application/x-tar"; }
206}
207
208
209/**
210 * A tar archive cannot contain files with name of folders longer than 255 chars
211 * This filter removes them
212 *
213 * @see File_Archive_Predicate, File_Archive_Reader_Filter
214 */
215require_once "File/Archive/Predicate.php";
216class File_Archive_Predicate_TARCompatible extends File_Archive_Predicate
217{
218    function isTrue($source)
219    {
220        return strlen($source->getFilename()) <= 255;
221    }
222}
223
224?>