1<?php
2// stolen from PEAR2_Pyrus_Developer_Creator_Tar by Greg Beaver, the original author, for use in unit tests
3class tarmaker
4{
5    /**
6     * Path to archive file
7     *
8     * @var string
9     */
10    protected $archive;
11    /**
12     * Temporary stream used for creating the archive
13     *
14     * @var stream
15     */
16    protected $tmp;
17    protected $path;
18    protected $compress;
19    function __construct($path, $compress = 'zlib')
20    {
21        $this->compress = $compress;
22        if ($compress === 'bz2' && !function_exists('bzopen')) {
23            throw new PEAR2_Pyrus_Developer_Creator_Exception(
24                'bzip2 extension not available');
25        }
26        if ($compress === 'zlib' && !function_exists('gzopen')) {
27            throw new PEAR2_Pyrus_Developer_Creator_Exception(
28                'zlib extension not available');
29        }
30        $this->path = $path;
31    }
32
33    /**
34     * save a file inside this package
35     *
36     * This code is modified from Vincent Lascaux's File_Archive
37     * package, which is licensed under the LGPL license.
38     * @param string relative path within the package
39     * @param string|resource file contents or open file handle
40     */
41    function addFile($path, $fileOrStream, $stat = null)
42    {
43        clearstatcache();
44        if ($stat === null) {
45            if (is_resource($fileOrStream)) {
46                $stat = fstat($fileOrStream);
47            } else {
48                $stat = array(
49                    'mode' => 0x8000 + 0644,
50                    'uid' => 0,
51                    'gid' => 0,
52                    'size' => strlen($fileOrStream),
53                    'mtime' => time(),
54                );
55            }
56        }
57
58        $link = null;
59        if ($stat['mode'] & 0x4000) {
60            $type = 5;        // Directory
61        } else if ($stat['mode'] & 0x8000) {
62            $type = 0;        // Regular
63        } else if ($stat['mode'] & 0xA000) {
64            $type = 1;        // Link
65            $link = @readlink($current);
66        } else {
67            $type = 9;        // Unknown
68        }
69
70        $filePrefix = '';
71        if (strlen($path) > 255) {
72            throw new Exception(
73                "$path is too long, must be 255 characters or less"
74            );
75        } else if (strlen($path) > 100) {
76            $filePrefix = substr($path, 0, strlen($path)-100);
77            $path = substr($path, -100);
78        }
79
80        $block = pack('a100a8a8a8a12A12',
81                $path,
82                decoct($stat['mode']),
83                sprintf('%6s ',decoct($stat['uid'])),
84                sprintf('%6s ',decoct($stat['gid'])),
85                sprintf('%11s ',decoct($stat['size'])),
86                sprintf('%11s ',decoct($stat['mtime']))
87            );
88
89        $blockend = pack('a1a100a6a2a32a32a8a8a155a12',
90            $type,
91            $link,
92            'ustar',
93            '00',
94            'Pyrus',
95            'Pyrus',
96            '',
97            '',
98            $filePrefix,
99            '');
100
101        $checkheader = array_merge(str_split($block), str_split($blockend));
102        if (!function_exists('_pear2tarchecksum')) {
103            function _pear2tarchecksum($a, $b) {return $a + ord($b);}
104        }
105        $checksum = 256; // 8 * ord(' ');
106        $checksum += array_reduce($checkheader, '_pear2tarchecksum');
107
108        $checksum = pack('a8', sprintf('%6s ', decoct($checksum)));
109
110        fwrite($this->tmp, (binary)$block . $checksum . $blockend, 512);
111        if (is_resource($fileOrStream)) {
112            stream_copy_to_stream($fileOrStream, $this->tmp);
113            if ($stat['size'] % 512) {
114                fwrite($this->tmp, (binary)str_repeat("\0", 512 - $stat['size'] % 512));
115            }
116        } else {
117            fwrite($this->tmp, (binary)$fileOrStream);
118            if (strlen($fileOrStream) % 512) {
119                fwrite($this->tmp, (binary)str_repeat("\0", 512 - strlen($fileOrStream) % 512));
120            }
121        }
122    }
123
124    /**
125     * Initialize the package creator
126     */
127    function init()
128    {
129        switch ($this->compress) {
130            case 'zlib' :
131                $this->tmp = gzopen($this->path, 'wb');
132                break;
133            case 'bz2' :
134                $this->tmp = bzopen($this->path, 'w');
135                break;
136            case 'none' :
137                $this->tmp = fopen($this->path, 'wb');
138                break;
139            default :
140                throw new Exception(
141                    'unknown compression type ' . $this->compress);
142        }
143    }
144
145    /**
146     * Create an internal directory, creating parent directories as needed
147     *
148     * @param string $dir
149     */
150    function mkdir($dir)
151    {
152        $this->addFile($dir, "", array(
153                    'mode' => 0x4000 + 0644,
154                    'uid' => 0,
155                    'gid' => 0,
156                    'size' => 0,
157                    'mtime' => time(),
158                ));
159    }
160
161    /**
162     * Finish saving the package
163     */
164    function close()
165    {
166        fwrite($this->tmp, pack('a1024', ''));
167        fclose($this->tmp);
168    }
169}