1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 4/** 5 * Recursively uncompress every file it finds 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: Uncompress.php,v 1.32 2005/07/09 12:54:35 vincentlascaux Exp $ 29 * @link http://pear.php.net/package/File_Archive 30 */ 31 32require_once "File/Archive/Reader.php"; 33require_once "File/Archive/Reader/ChangeName.php"; 34 35/** 36 * Recursively uncompress every file it finds 37 */ 38class File_Archive_Reader_Uncompress extends File_Archive_Reader_Relay 39{ 40 /** 41 * @var Array Stack of readers 42 * @access private 43 */ 44 var $readers = array(); 45 46 /** 47 * @var array of readers to close when closing $this 48 * @access private 49 */ 50 var $toClose = array(); 51 52 /** 53 * @var File_Archive_Reader Reader from which all started (usefull to be 54 * able to close) 55 * @access private 56 */ 57 var $startReader; 58 59 /** 60 * @var Int Maximum depth of uncompression after the basicDir 61 * (that may contain some uncompression also) 62 * -1 means no limit 63 * @access private 64 */ 65 var $uncompressionLevel; 66 67 /** 68 * @var array Only files starting with $baseDir will be reported 69 * This array contains explode('/', $directoryName) 70 * @access private 71 */ 72 var $baseDir = ''; 73 74 /** 75 * @var int Compression level required to go to reach the baseDir 76 * or null if it is currently being computed 77 * @access private 78 */ 79 var $baseDirCompressionLevel = null; 80 81 /** 82 * @var int We are selecting substr($baseDir, 0, $baseDirProgression) 83 */ 84 var $baseDirProgression = 0; 85 86 /** 87 * @var boolean Flag set to indicate that the current file has not been 88 * displayed 89 */ 90 var $currentFileNotDisplayed = false; 91 92 function File_Archive_Reader_Uncompress( 93 &$innerReader, $uncompressionLevel = -1) 94 { 95 parent::File_Archive_Reader_Relay($innerReader); 96 $this->startReader =& $innerReader; 97 $this->uncompressionLevel = $uncompressionLevel; 98 } 99 100 /** 101 * Attempt to change the current source (if the current file is an archive) 102 * If this is the case, push the current source onto the stack and make the 103 * good archive reader the current source. A file is considered as an 104 * archive if its extension is one of tar, gz, zip, tgz 105 * 106 * @return bool whether the source has been pushed or not 107 * @access private 108 */ 109 function push() 110 { 111 if ($this->uncompressionLevel >= 0 && 112 $this->baseDirCompressionLevel !== null && 113 count($this->readers) >= $this->uncompressionLevel 114 ) { 115 return false; 116 } 117 118 // Check the extension of the file (maybe we need to uncompress it?) 119 $filename = $this->source->getFilename(); 120 121 $extensions = explode('.', strtolower($filename)); 122 123 $reader =& $this->source; 124 $nbUncompressions = 0; 125 126 while (($extension = array_pop($extensions)) !== null) { 127 $nbUncompressions++; 128 unset($next); 129 $next = File_Archive::readArchive($extension, $reader, $nbUncompressions == 1); 130 if ($next === false) { 131 $extensions = array(); 132 } else { 133 unset($reader); 134 $reader =& $next; 135 } 136 } 137 if ($nbUncompressions == 1) { 138 return false; 139 } else { 140 $this->readers[count($this->readers)] =& $this->source; 141 unset($this->source); 142 $this->source = new File_Archive_Reader_AddBaseName( 143 $filename, $reader 144 ); 145 return true; 146 } 147 } 148 /** 149 * @see File_Archive_Reader::close() 150 */ 151 function next() 152 { 153 if ($this->currentFileNotDisplayed) { 154 $this->currentFileNotDisplayed = false; 155 return true; 156 } 157 158 do { 159 do { 160 $selection = substr($this->baseDir, 0, $this->baseDirProgression); 161 if ($selection === false) { 162 $selection = ''; 163 } 164 165 $error = $this->source->select($selection, false); 166 if (PEAR::isError($error)) { 167 return $error; 168 } 169 if (!$error) { 170 if (empty($this->readers)) { 171 return false; 172 } 173 $this->source->close(); 174 unset($this->source); 175 $this->source =& $this->readers[count($this->readers)-1]; 176 unset($this->readers[count($this->readers)-1]); 177 } 178 } while (!$error); 179 180 $filename = $this->source->getFilename(); 181 if (strlen($filename) < strlen($this->baseDir)) { 182 $goodFile = (strncmp($filename, $this->baseDir, strlen($filename)) == 0 && 183 $this->baseDir{strlen($filename)} == '/'); 184 if ($goodFile) { 185 if (strlen($filename) + 2 < strlen($this->baseDirProgression)) { 186 $this->baseDirProgression = strpos($this->baseDir, '/', strlen($filename)+2); 187 if ($this->baseDirProgression === false) { 188 $this->baseDirProgression = strlen($this->baseDir); 189 } 190 } else { 191 $this->baseDirProgression = strlen($this->baseDir); 192 } 193 } 194 } else { 195 $goodFile = (strncmp($filename, $this->baseDir, strlen($this->baseDir)) == 0); 196 if ($goodFile) { 197 $this->baseDirProgression = strlen($this->baseDir); 198 } 199 } 200 } while ($goodFile && $this->push()); 201 202 return true; 203 } 204 205 /** 206 * Efficiently filter out the files which URL does not start with $baseDir 207 * Throws an error if the $baseDir can't be found 208 * @return bool Whether baseDir was a directory or a file 209 */ 210 function setBaseDir($baseDir) 211 { 212 $this->baseDir = $baseDir; 213 $this->baseDirProgression = strpos($baseDir, '/'); 214 if ($this->baseDirProgression === false) { 215 $this->baseDirProgression = strlen($baseDir); 216 } 217 218 $error = $this->next(); 219 if ($error === false) { 220 return PEAR::raiseError("No directory $baseDir in inner reader"); 221 } else if (PEAR::isError($error)) { 222 return $error; 223 } 224 225 $this->currentFileNotDisplayed = true; 226 return strlen($this->getFilename())>strlen($baseDir); 227 } 228 /** 229 * @see File_Archive_Reader::select() 230 */ 231 function select($filename, $close = true) 232 { 233 if ($close) { 234 $error = $this->close(); 235 if (PEAR::isError($close)) { 236 return $error; 237 } 238 } 239 240 $oldBaseDir = $this->baseDir; 241 $oldProgression = $this->baseDirProgression; 242 243 $this->baseDir = $filename; 244 $this->baseDirProgression = 0; 245 246 $res = $this->next(); 247 248 $this->baseDir = $oldBaseDir; 249 $this->baseDirProgression = $oldProgression; 250 251 return $res; 252 } 253 254 /** 255 * @see File_Archive_Reader::close() 256 */ 257 function close() 258 { 259 for ($i=0; $i<count($this->readers); ++$i) { 260 $this->readers[$i]->close(); 261 } 262 //var_dump($this->toClose); 263 for ($i=0; $i<count($this->toClose); ++$i) { 264 if ($this->toClose[$i] !== null) { 265 $this->toClose[$i]->close(); 266 } 267 } 268 269 $this->readers = array(); 270 $this->toClose = array(); 271 $error = parent::close(); 272 $this->baseDirCompressionLevel = null; 273 $this->baseDirProgression = 0; 274 275 unset($this->source); 276 $this->source =& $this->startReader; 277 $this->source->close(); 278 $this->currentFileNotDisplayed = false; 279 280 return $error; 281 } 282 283 /** 284 * @see File_Archive_Reader::makeAppendWriter() 285 */ 286 function makeAppendWriter() 287 { 288 //The reader needs to be open so that the base dir is found 289 $error = $this->next(); 290 if (PEAR::isError($error)) { 291 return $error; 292 } 293 294 return parent::makeAppendWriter(); 295 } 296 297 /** 298 * @see File_Archive_Reader::makeWriterRemoveFiles() 299 */ 300 function makeWriterRemoveFiles($pred) 301 { 302 //The reader needs to be open so that the base dir is found 303 $error = $this->next(); 304 if (PEAR::isError($error)) { 305 return $error; 306 } 307 308 return parent::makeWriterRemoveFiles($pred); 309 } 310} 311 312?>