diff --git a/pear/Archive/Tar.php b/pear/Archive/Tar.php index ff20b7eb568..9f695375b90 100644 --- a/pear/Archive/Tar.php +++ b/pear/Archive/Tar.php @@ -39,6 +39,11 @@ class Archive_Tar extends PEAR */ var $_compress=false; + /** + * @var string Type of compression : 'none', 'gz' or 'bz2' + */ + var $_compress_type='none'; + /** * @var file descriptor */ @@ -64,41 +69,61 @@ class Archive_Tar extends PEAR 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; } // }}} @@ -311,6 +336,43 @@ class Archive_Tar extends PEAR } // }}} + // {{{ 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 @@ -358,6 +420,27 @@ class Archive_Tar extends PEAR } // }}} + // {{{ 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 @@ -417,10 +500,14 @@ class Archive_Tar extends PEAR // {{{ _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.'\''); @@ -462,10 +549,14 @@ class Archive_Tar extends PEAR // ----- 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.'\''); @@ -479,10 +570,14 @@ class Archive_Tar extends PEAR // {{{ _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.'\''); @@ -497,10 +592,14 @@ class Archive_Tar extends PEAR 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; } @@ -536,16 +635,87 @@ class Archive_Tar extends PEAR } // }}} + // {{{ _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; } @@ -661,10 +831,7 @@ class Archive_Tar extends PEAR 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); @@ -679,6 +846,35 @@ class Archive_Tar extends PEAR } // }}} + // {{{ _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) { @@ -687,7 +883,7 @@ class Archive_Tar extends PEAR $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; } @@ -739,24 +935,83 @@ class Archive_Tar extends PEAR $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; } @@ -801,33 +1056,21 @@ class Archive_Tar extends PEAR $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; @@ -871,7 +1114,7 @@ class Archive_Tar extends PEAR 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; } @@ -905,25 +1148,16 @@ class Archive_Tar extends PEAR $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; @@ -934,6 +1168,56 @@ class Archive_Tar extends PEAR } // }}} + // {{{ _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) { @@ -973,16 +1257,11 @@ class Archive_Tar extends PEAR 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; @@ -1073,17 +1352,11 @@ class Archive_Tar extends PEAR } 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)); } @@ -1103,24 +1376,18 @@ class Archive_Tar extends PEAR } } } 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 @@ -1137,9 +1404,12 @@ class Archive_Tar extends PEAR } // }}} - // {{{ _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(); @@ -1148,7 +1418,12 @@ class Archive_Tar extends PEAR 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; @@ -1159,39 +1434,58 @@ class Archive_Tar extends PEAR 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)); + } + + @gzclose($v_temp_tar); } + elseif ($this->_compress_type == 'bz2') { + $v_buffered_lines = array(); + $v_buffered_lines[] = @bzread($v_temp_tar, 512); - if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) - $this->_writeFooter(); + // ----- 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; + + clearstatcache(); + $v_size = filesize($this->_tarname); + fseek($this->_file, $v_size-512); } - // ----- 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); + 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(); @@ -1298,4 +1592,4 @@ class Archive_Tar extends PEAR // }}} } -?> \ No newline at end of file +?>