*/
var $_compress=false;
+ /**
+ * @var string Type of compression : 'none', 'gz' or 'bz2'
+ */
+ var $_compress_type='none';
+
/**
* @var file descriptor
*/
function Archive_Tar($p_tarname, $p_compress = null)
{
$this->PEAR();
+ $this->_compress = false;
+ $this->_compress_type = 'none';
if ($p_compress === null) {
if (@file_exists($p_tarname)) {
- if ($fp = @fopen($p_tarname, "r")) {
+ if ($fp = @fopen($p_tarname, "rb")) {
// look for gzip magic cookie
$data = fread($fp, 2);
+ fclose($fp);
if ($data == "\37\213") {
- $p_compress = true;
+ $this->_compress = true;
+ $this->_compress_type = 'gz';
+ // No sure it's enought for a magic code ....
+ } elseif ($data == "BZ") {
+ $this->_compress = true;
+ $this->_compress_type = 'bz2';
}
}
} else {
// probably a remote file or some file accessible
// through a stream interface
if (substr($p_tarname, -2) == 'gz') {
- $p_compress = true;
+ $this->_compress = true;
+ $this->_compress_type = 'gz';
+ } elseif ((substr($p_tarname, -3) == 'bz2') ||
+ (substr($p_tarname, -2) == 'bz')) {
+ $this->_compress = true;
+ $this->_compress_type = 'bz2';
}
}
+ } else {
+ if ($p_compress == 'gz') {
+ $this->_compress = true;
+ $this->_compress_type = 'gz';
+ } else if ($p_compress == 'bz2') {
+ $this->_compress = true;
+ $this->_compress_type = 'bz2';
+ }
}
$this->_tarname = $p_tarname;
- if ($p_compress) { // assert zlib extension support
- $extname = 'zlib';
+ if ($this->_compress) { // assert zlib or bz2 extension support
+ if ($this->_compress_type == 'gz')
+ $extname = 'zlib';
+ else if ($this->_compress_type == 'bz2')
+ $extname = 'bz2';
+
if (!extension_loaded($extname)) {
- if (OS_WINDOWS) {
- @dl("php_$extname.dll");
- } else {
- @dl("$extname.so");
- }
+ PEAR::loadExtension($extname);
}
if (!extension_loaded($extname)) {
die("The extension '$extname' couldn't be found.\n".
- "Please make sure your version of PHP was built".
+ "Please make sure your version of PHP was built ".
"with '$extname' support.\n");
return false;
}
}
- $this->_compress = (bool)$p_compress;
}
// }}}
}
// }}}
+ // {{{ addString()
+ /**
+ * This method add a single string as a file at the
+ * end of the existing archive. If the archive does not yet exists it
+ * is created.
+ *
+ * @param string $p_filename A string which contains the full filename path
+ * that will be associated with the string.
+ * @param string $p_string The content of the file added in the archive.
+ * @return true on success, false on error.
+ * @access public
+ */
+ function addString($p_filename, $p_string)
+ {
+ $v_result = true;
+
+ if (!@is_file($this->_tarname)) {
+ if (!$this->_openWrite()) {
+ return false;
+ }
+ $this->_close();
+ }
+
+ if (!$this->_openAppend())
+ return false;
+
+ // Need to check the get back to the temporary file ? ....
+ $v_result = $this->_addString($p_filename, $p_string);
+
+ $this->_writeFooter();
+
+ $this->_close();
+
+ return $v_result;
+ }
+ // }}}
+
// {{{ extractModify()
/**
* This method extract all the content of the archive in the directory
}
// }}}
+ // {{{ extractInString()
+ /**
+ * This method extract from the archive one file identified by $p_filename.
+ * The return value is a string with the file content, or NULL on error.
+ * @param string $p_filename The path of the file to extract in a string.
+ * @return a string with the file content or NULL.
+ * @access public
+ */
+ function extractInString($p_filename)
+ {
+ if ($this->_openRead()) {
+ $v_result = $this->_extractInString($p_filename);
+ $this->_close();
+ } else {
+ $v_result = NULL;
+ }
+
+ return $v_result;
+ }
+ // }}}
+
// {{{ extractList()
/**
* This method extract from the archive only the files indicated in the
// {{{ _openWrite()
function _openWrite()
{
- if ($this->_compress)
- $this->_file = @gzopen($this->_tarname, "w");
+ if ($this->_compress_type == 'gz')
+ $this->_file = @gzopen($this->_tarname, "wb");
+ else if ($this->_compress_type == 'bz2')
+ $this->_file = @bzopen($this->_tarname, "wb");
+ else if ($this->_compress_type == 'none')
+ $this->_file = @fopen($this->_tarname, "wb");
else
- $this->_file = @fopen($this->_tarname, "w");
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
if ($this->_file == 0) {
$this->_error('Unable to open in write mode \''.$this->_tarname.'\'');
// ----- File to open if the normal Tar file
$v_filename = $this->_tarname;
- if ($this->_compress)
+ if ($this->_compress_type == 'gz')
$this->_file = @gzopen($v_filename, "rb");
- else
+ else if ($this->_compress_type == 'bz2')
+ $this->_file = @bzopen($v_filename, "rb");
+ else if ($this->_compress_type == 'none')
$this->_file = @fopen($v_filename, "rb");
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
if ($this->_file == 0) {
$this->_error('Unable to open in read mode \''.$v_filename.'\'');
// {{{ _openReadWrite()
function _openReadWrite()
{
- if ($this->_compress)
+ if ($this->_compress_type == 'gz')
$this->_file = @gzopen($this->_tarname, "r+b");
- else
+ else if ($this->_compress_type == 'bz2')
+ $this->_file = @bzopen($this->_tarname, "r+b");
+ else if ($this->_compress_type == 'none')
$this->_file = @fopen($this->_tarname, "r+b");
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
if ($this->_file == 0) {
$this->_error('Unable to open in read/write mode \''.$this->_tarname.'\'');
function _close()
{
if (isset($this->_file)) {
- if ($this->_compress)
+ if ($this->_compress_type == 'gz')
@gzclose($this->_file);
- else
+ else if ($this->_compress_type == 'bz2')
+ @bzclose($this->_file);
+ else if ($this->_compress_type == 'none')
@fclose($this->_file);
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
$this->_file = 0;
}
}
// }}}
+ // {{{ _writeBlock()
+ function _writeBlock($p_binary_data, $p_len=null)
+ {
+ if ($this->_file) {
+ if ($p_len === null) {
+ if ($this->_compress_type == 'gz')
+ @gzputs($this->_file, $p_binary_data);
+ else if ($this->_compress_type == 'bz2')
+ @bzwrite($this->_file, $p_binary_data);
+ else if ($this->_compress_type == 'none')
+ @fputs($this->_file, $p_binary_data);
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+ } else {
+ if ($this->_compress_type == 'gz')
+ @gzputs($this->_file, $p_binary_data, $p_len);
+ else if ($this->_compress_type == 'bz2')
+ @bzwrite($this->_file, $p_binary_data, $p_len);
+ else if ($this->_compress_type == 'none')
+ @fputs($this->_file, $p_binary_data, $p_len);
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+
+ }
+ }
+ return true;
+ }
+ // }}}
+
+ // {{{ _readBlock()
+ function _readBlock($p_len=null)
+ {
+ $v_block = null;
+ if ($this->_file) {
+ if ($p_len === null)
+ $p_len = 512;
+
+ if ($this->_compress_type == 'gz')
+ $v_block = @gzread($this->_file, 512);
+ else if ($this->_compress_type == 'bz2')
+ $v_block = @bzread($this->_file, 512);
+ else if ($this->_compress_type == 'none')
+ $v_block = @fread($this->_file, 512);
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+
+ }
+ return $v_block;
+ }
+ // }}}
+
+ // {{{ _jumpBlock()
+ function _jumpBlock($p_len=null)
+ {
+ if ($this->_file) {
+ if ($p_len === null)
+ $p_len = 1;
+
+ if ($this->_compress_type == 'gz')
+ @gzseek($this->_file, @gztell($this->_file)+($p_len*512));
+ else if ($this->_compress_type == 'bz2') {
+ // ----- Replace missing bztell() and bzseek()
+ for ($i=0; $i<$p_len; $i++)
+ $this->_readBlock();
+ } else if ($this->_compress_type == 'none')
+ @fseek($this->_file, @ftell($this->_file)+($p_len*512));
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+
+ }
+ return true;
+ }
+ // }}}
+
// {{{ _writeFooter()
function _writeFooter()
{
if ($this->_file) {
// ----- Write the last 0 filled block for end of archive
$v_binary_data = pack("a512", '');
- if ($this->_compress)
- @gzputs($this->_file, $v_binary_data);
- else
- @fputs($this->_file, $v_binary_data);
+ $this->_writeBlock($v_binary_data);
}
return true;
}
while (($v_buffer = fread($v_file, 512)) != '') {
$v_binary_data = pack("a512", "$v_buffer");
- if ($this->_compress)
- @gzputs($this->_file, $v_binary_data);
- else
- @fputs($this->_file, $v_binary_data);
+ $this->_writeBlock($v_binary_data);
}
fclose($v_file);
}
// }}}
+ // {{{ _addString()
+ function _addString($p_filename, $p_string)
+ {
+ if (!$this->_file) {
+ $this->_error('Invalid file descriptor');
+ return false;
+ }
+
+ if ($p_filename == '') {
+ $this->_error('Invalid file name');
+ return false;
+ }
+
+ // ----- Calculate the stored filename
+ $p_filename = $this->_translateWinPath($p_filename, false);;
+
+ if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), 0, 0, "", 0, 0))
+ return false;
+
+ $i=0;
+ while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
+ $v_binary_data = pack("a512", $v_buffer);
+ $this->_writeBlock($v_binary_data);
+ }
+
+ return true;
+ }
+ // }}}
+
// {{{ _writeHeader()
function _writeHeader($p_filename, $p_stored_filename)
{
$v_reduce_filename = $this->_pathReduction($p_stored_filename);
if (strlen($v_reduce_filename) > 99) {
- if (!$this->_writeLongHeader($p_stored_filename))
+ if (!$this->_writeLongHeader($v_reduce_filename))
return false;
}
$v_checksum += ord(substr($v_binary_data_last,$j,1));
// ----- Write the first 148 bytes of the header in the archive
- if ($this->_compress)
- @gzputs($this->_file, $v_binary_data_first, 148);
- else
- @fputs($this->_file, $v_binary_data_first, 148);
+ $this->_writeBlock($v_binary_data_first, 148);
// ----- Write the calculated checksum
$v_checksum = sprintf("%6s ", DecOct($v_checksum));
$v_binary_data = pack("a8", $v_checksum);
- if ($this->_compress)
- @gzputs($this->_file, $v_binary_data, 8);
- else
- @fputs($this->_file, $v_binary_data, 8);
+ $this->_writeBlock($v_binary_data, 8);
// ----- Write the last 356 bytes of the header in the archive
- if ($this->_compress)
- @gzputs($this->_file, $v_binary_data_last, 356);
- else
- @fputs($this->_file, $v_binary_data_last, 356);
+ $this->_writeBlock($v_binary_data_last, 356);
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _writeHeaderBlock()
+ function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0, $p_type='', $p_uid=0, $p_gid=0)
+ {
+ $p_filename = $this->_pathReduction($p_filename);
+
+ if (strlen($p_filename) > 99) {
+ if (!$this->_writeLongHeader($p_filename))
+ return false;
+ }
+
+ if ($p_type == "5") {
+ $v_size = sprintf("%11s ", DecOct(0));
+ } else {
+ $v_size = sprintf("%11s ", DecOct($p_size));
+ }
+
+ $v_uid = sprintf("%6s ", DecOct($p_uid));
+ $v_gid = sprintf("%6s ", DecOct($p_gid));
+ $v_perms = sprintf("%6s ", DecOct($p_perms));
+
+ $v_mtime = sprintf("%11s", DecOct($p_mtime));
+
+ $v_linkname = '';
+
+ $v_magic = '';
+
+ $v_version = '';
+
+ $v_uname = '';
+
+ $v_gname = '';
+
+ $v_devmajor = '';
+
+ $v_devminor = '';
+
+ $v_prefix = '';
+
+ $v_binary_data_first = pack("a100a8a8a8a12A12", $p_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
+ $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $p_type, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
+
+ // ----- Calculate the checksum
+ $v_checksum = 0;
+ // ..... First part of the header
+ for ($i=0; $i<148; $i++)
+ $v_checksum += ord(substr($v_binary_data_first,$i,1));
+ // ..... Ignore the checksum value and replace it by ' ' (space)
+ for ($i=148; $i<156; $i++)
+ $v_checksum += ord(' ');
+ // ..... Last part of the header
+ for ($i=156, $j=0; $i<512; $i++, $j++)
+ $v_checksum += ord(substr($v_binary_data_last,$j,1));
+
+ // ----- Write the first 148 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_first, 148);
+
+ // ----- Write the calculated checksum
+ $v_checksum = sprintf("%6s ", DecOct($v_checksum));
+ $v_binary_data = pack("a8", $v_checksum);
+ $this->_writeBlock($v_binary_data, 8);
+
+ // ----- Write the last 356 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_last, 356);
return true;
}
$v_checksum += ord(substr($v_binary_data_last,$j,1));
// ----- Write the first 148 bytes of the header in the archive
- if ($this->_compress)
- @gzputs($this->_file, $v_binary_data_first, 148);
- else
- @fputs($this->_file, $v_binary_data_first, 148);
+ $this->_writeBlock($v_binary_data_first, 148);
// ----- Write the calculated checksum
$v_checksum = sprintf("%6s ", DecOct($v_checksum));
$v_binary_data = pack("a8", $v_checksum);
- if ($this->_compress)
- @gzputs($this->_file, $v_binary_data, 8);
- else
- @fputs($this->_file, $v_binary_data, 8);
+ $this->_writeBlock($v_binary_data, 8);
// ----- Write the last 356 bytes of the header in the archive
- if ($this->_compress)
- @gzputs($this->_file, $v_binary_data_last, 356);
- else
- @fputs($this->_file, $v_binary_data_last, 356);
+ $this->_writeBlock($v_binary_data_last, 356);
// ----- Write the filename as content of the block
$i=0;
while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
$v_binary_data = pack("a512", "$v_buffer");
- if ($this->_compress)
- @gzputs($this->_file, $v_binary_data);
- else
- @fputs($this->_file, $v_binary_data);
+ $this->_writeBlock($v_binary_data);
}
return true;
if (($v_checksum == 256) && ($v_header['checksum'] == 0))
return true;
- $this->_error('Invalid checksum : '.$v_checksum.' calculated, '.$v_header['checksum'].' expected');
+ $this->_error('Invalid checksum for file "'.$v_data['filename'].'" : '.$v_checksum.' calculated, '.$v_header['checksum'].' expected');
return false;
}
$v_filename = '';
$n = floor($v_header['size']/512);
for ($i=0; $i<$n; $i++) {
- if ($this->_compress)
- $v_content = @gzread($this->_file, 512);
- else
- $v_content = @fread($this->_file, 512);
+ $v_content = $this->_readBlock();
$v_filename .= $v_content;
}
if (($v_header['size'] % 512) != 0) {
- if ($this->_compress)
- $v_content = @gzread($this->_file, 512);
- else
- $v_content = @fread($this->_file, 512);
+ $v_content = $this->_readBlock();
$v_filename .= $v_content;
}
// ----- Read the next header
- if ($this->_compress)
- $v_binary_data = @gzread($this->_file, 512);
- else
- $v_binary_data = @fread($this->_file, 512);
+ $v_binary_data = $this->_readBlock();
if (!$this->_readHeader($v_binary_data, $v_header))
return false;
}
// }}}
+ // {{{ _extractInString()
+ /**
+ * This method extract from the archive one file identified by $p_filename.
+ * The return value is a string with the file content, or NULL on error.
+ * @param string $p_filename The path of the file to extract in a string.
+ * @return a string with the file content or NULL.
+ * @access private
+ */
+ function _extractInString($p_filename)
+ {
+ $v_result_str = "";
+
+ While (strlen($v_binary_data = $this->_readBlock()) != 0)
+ {
+ if (!$this->_readHeader($v_binary_data, $v_header))
+ return NULL;
+
+ if ($v_header['filename'] == '')
+ continue;
+
+ // ----- Look for long filename
+ if ($v_header['typeflag'] == 'L') {
+ if (!$this->_readLongHeader($v_header))
+ return NULL;
+ }
+
+ if ($v_header['filename'] == $p_filename) {
+ if ($v_header['typeflag'] == "5") {
+ $this->_error('Unable to extract in string a directory entry {'.$v_header['filename'].'}');
+ return NULL;
+ } else {
+ $n = floor($v_header['size']/512);
+ for ($i=0; $i<$n; $i++) {
+ $v_result_str .= $this->_readBlock();
+ }
+ if (($v_header['size'] % 512) != 0) {
+ $v_content = $this->_readBlock();
+ $v_result_str .= substr($v_content, 0, ($v_header['size'] % 512));
+ }
+ return $v_result_str;
+ }
+ } else {
+ $this->_jumpBlock(ceil(($v_header['size']/512)));
+ }
+ }
+
+ return NULL;
+ }
+ // }}}
+
// {{{ _extractList()
function _extractList($p_path, &$p_list_detail, $p_mode, $p_file_list, $p_remove_path)
{
clearstatcache();
- While (!($v_end_of_file = ($this->_compress?@gzeof($this->_file):@feof($this->_file))))
+ While (strlen($v_binary_data = $this->_readBlock()) != 0)
{
$v_extract_file = FALSE;
$v_extraction_stopped = 0;
- if ($this->_compress)
- $v_binary_data = @gzread($this->_file, 512);
- else
- $v_binary_data = @fread($this->_file, 512);
-
if (!$this->_readHeader($v_binary_data, $v_header))
return false;
} else {
$n = floor($v_header['size']/512);
for ($i=0; $i<$n; $i++) {
- if ($this->_compress)
- $v_content = @gzread($this->_file, 512);
- else
- $v_content = @fread($this->_file, 512);
+ $v_content = $this->_readBlock();
fwrite($v_dest_file, $v_content, 512);
}
if (($v_header['size'] % 512) != 0) {
- if ($this->_compress)
- $v_content = @gzread($this->_file, 512);
- else
- $v_content = @fread($this->_file, 512);
+ $v_content = $this->_readBlock();
fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
}
}
}
} else {
- // ----- Jump to next file
- if ($this->_compress)
- @gzseek($this->_file, @gztell($this->_file)+(ceil(($v_header['size']/512))*512));
- else
- @fseek($this->_file, @ftell($this->_file)+(ceil(($v_header['size']/512))*512));
+ $this->_jumpBlock(ceil(($v_header['size']/512)));
}
} else {
- // ----- Jump to next file
- if ($this->_compress)
- @gzseek($this->_file, @gztell($this->_file)+(ceil(($v_header['size']/512))*512));
- else
- @fseek($this->_file, @ftell($this->_file)+(ceil(($v_header['size']/512))*512));
+ $this->_jumpBlock(ceil(($v_header['size']/512)));
}
+ /* TBC : Seems to be unused ...
if ($this->_compress)
$v_end_of_file = @gzeof($this->_file);
else
$v_end_of_file = @feof($this->_file);
+ */
if ($v_listing || $v_extract_file || $v_extraction_stopped) {
// ----- Log extracted files
}
// }}}
- // {{{ _append()
- function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
+ // {{{ _openAppend()
+ function _openAppend()
{
+ if (filesize($this->_tarname) == 0)
+ return $this->_openWrite();
+
if ($this->_compress) {
$this->_close();
return false;
}
- if (($v_temp_tar = @gzopen($this->_tarname.".tmp", "rb")) == 0) {
+ if ($this->_compress_type == 'gz')
+ $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
+ elseif ($this->_compress_type == 'bz2')
+ $v_temp_tar = @bzopen($this->_tarname.".tmp", "rb");
+
+ if ($v_temp_tar == 0) {
$this->_error('Unable to open file \''.$this->_tarname.'.tmp\' in binary read mode');
@rename($this->_tarname.".tmp", $this->_tarname);
return false;
return false;
}
- $v_buffer = @gzread($v_temp_tar, 512);
+ if ($this->_compress_type == 'gz') {
+ $v_buffer = @gzread($v_temp_tar, 512);
- // ----- Read the following blocks but not the last one
- if (!@gzeof($v_temp_tar)) {
- do{
- $v_binary_data = pack("a512", "$v_buffer");
- @gzputs($this->_file, $v_binary_data);
- $v_buffer = @gzread($v_temp_tar, 512);
+ // ----- Read the following blocks but not the last one
+ if (!@gzeof($v_temp_tar)) {
+ do{
+ $v_binary_data = pack("a512", $v_buffer);
+ $this->_writeBlock($v_binary_data);
+ $v_buffer = @gzread($v_temp_tar, 512);
- } while (!@gzeof($v_temp_tar));
- }
+ } while (!@gzeof($v_temp_tar));
+ }
- if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
- $this->_writeFooter();
+ @gzclose($v_temp_tar);
+ }
+ elseif ($this->_compress_type == 'bz2') {
+ $v_buffered_lines = array();
+ $v_buffered_lines[] = @bzread($v_temp_tar, 512);
+
+ // ----- Read the following blocks but not the last one
+ while (strlen($v_buffered_lines[] = @bzread($v_temp_tar, 512)) > 0) {
+ $v_binary_data = pack("a512", array_shift($v_buffered_lines));
+ $this->_writeBlock($v_binary_data);
+ }
- $this->_close();
- @gzclose($v_temp_tar);
+ @bzclose($v_temp_tar);
+ }
if (!@unlink($this->_tarname.".tmp")) {
$this->_error('Error while deleting temporary file \''.$this->_tarname.'.tmp\'');
}
- return true;
- }
+ } else {
+ // ----- For not compressed tar, just add files before the last 512 bytes block
+ if (!$this->_openReadWrite())
+ return false;
- // ----- For not compressed tar, just add files before the last 512 bytes block
- if (!$this->_openReadWrite())
- return false;
+ clearstatcache();
+ $v_size = filesize($this->_tarname);
+ fseek($this->_file, $v_size-512);
+ }
- clearstatcache();
- $v_size = filesize($this->_tarname);
- fseek($this->_file, $v_size-512);
+ return true;
+ }
+ // }}}
+ // {{{ _append()
+ function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
+ {
+ if (!$this->_openAppend())
+ return false;
+
if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
$this->_writeFooter();
// }}}
}
-?>
\ No newline at end of file
+?>