1<?php 2/** 3 * File IO functions 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9/** 10 * Removes empty directories 11 * 12 * Sends IO_NAMESPACE_DELETED events for 'pages' and 'media' namespaces. 13 * Event data: 14 * $data[0] ns: The colon separated namespace path minus the trailing page name. 15 * $data[1] ns_type: 'pages' or 'media' namespace tree. 16 * 17 * @todo use safemode hack 18 * @param string $id - a pageid, the namespace of that id will be tried to deleted 19 * @param string $basedir - the config name of the type to delete (datadir or mediadir usally) 20 * @return bool - true if at least one namespace was deleted 21 * 22 * @author Andreas Gohr <andi@splitbrain.org> 23 * @author Ben Coburn <btcoburn@silicodon.net> 24 */ 25function io_sweepNS($id,$basedir='datadir'){ 26 global $conf; 27 $types = array ('datadir'=>'pages', 'mediadir'=>'media'); 28 $ns_type = (isset($types[$basedir])?$types[$basedir]:false); 29 30 $delone = false; 31 32 //scan all namespaces 33 while(($id = getNS($id)) !== false){ 34 $dir = $conf[$basedir].'/'.utf8_encodeFN(str_replace(':','/',$id)); 35 36 //try to delete dir else return 37 if(@rmdir($dir)) { 38 if ($ns_type!==false) { 39 $data = array($id, $ns_type); 40 $delone = true; // we deleted at least one dir 41 trigger_event('IO_NAMESPACE_DELETED', $data); 42 } 43 } else { return $delone; } 44 } 45 return $delone; 46} 47 48/** 49 * Used to read in a DokuWiki page from file, and send IO_WIKIPAGE_READ events. 50 * 51 * Generates the action event which delegates to io_readFile(). 52 * Action plugins are allowed to modify the page content in transit. 53 * The file path should not be changed. 54 * 55 * Event data: 56 * $data[0] The raw arguments for io_readFile as an array. 57 * $data[1] ns: The colon separated namespace path minus the trailing page name. (false if root ns) 58 * $data[2] page_name: The wiki page name. 59 * $data[3] rev: The page revision, false for current wiki pages. 60 * 61 * @author Ben Coburn <btcoburn@silicodon.net> 62 * 63 * @param string $file filename 64 * @param string $id page id 65 * @param bool|int $rev revision timestamp 66 * @return string 67 */ 68function io_readWikiPage($file, $id, $rev=false) { 69 if (empty($rev)) { $rev = false; } 70 $data = array(array($file, true), getNS($id), noNS($id), $rev); 71 return trigger_event('IO_WIKIPAGE_READ', $data, '_io_readWikiPage_action', false); 72} 73 74/** 75 * Callback adapter for io_readFile(). 76 * 77 * @author Ben Coburn <btcoburn@silicodon.net> 78 * 79 * @param array $data event data 80 * @return string 81 */ 82function _io_readWikiPage_action($data) { 83 if (is_array($data) && is_array($data[0]) && count($data[0])===2) { 84 return call_user_func_array('io_readFile', $data[0]); 85 } else { 86 return ''; //callback error 87 } 88} 89 90/** 91 * Returns content of $file as cleaned string. 92 * 93 * Uses gzip if extension is .gz 94 * 95 * If you want to use the returned value in unserialize 96 * be sure to set $clean to false! 97 * 98 * @author Andreas Gohr <andi@splitbrain.org> 99 * 100 * @param string $file filename 101 * @param bool $clean 102 * @return string|bool the file contents or false on error 103 */ 104function io_readFile($file,$clean=true){ 105 $ret = ''; 106 if(file_exists($file)){ 107 if(substr($file,-3) == '.gz'){ 108 if(!DOKU_HAS_GZIP) return false; 109 $ret = gzfile($file); 110 if(is_array($ret)) $ret = join('', $ret); 111 }else if(substr($file,-4) == '.bz2'){ 112 if(!DOKU_HAS_BZIP) return false; 113 $ret = bzfile($file); 114 }else{ 115 $ret = file_get_contents($file); 116 } 117 } 118 if($ret === null) return false; 119 if($ret !== false && $clean){ 120 return cleanText($ret); 121 }else{ 122 return $ret; 123 } 124} 125/** 126 * Returns the content of a .bz2 compressed file as string 127 * 128 * @author marcel senf <marcel@rucksackreinigung.de> 129 * @author Andreas Gohr <andi@splitbrain.org> 130 * 131 * @param string $file filename 132 * @param bool $array return array of lines 133 * @return string|array|bool content or false on error 134 */ 135function bzfile($file, $array=false) { 136 $bz = bzopen($file,"r"); 137 if($bz === false) return false; 138 139 if($array) $lines = array(); 140 $str = ''; 141 while (!feof($bz)) { 142 //8192 seems to be the maximum buffersize? 143 $buffer = bzread($bz,8192); 144 if(($buffer === false) || (bzerrno($bz) !== 0)) { 145 return false; 146 } 147 $str = $str . $buffer; 148 if($array) { 149 $pos = strpos($str, "\n"); 150 while($pos !== false) { 151 $lines[] = substr($str, 0, $pos+1); 152 $str = substr($str, $pos+1); 153 $pos = strpos($str, "\n"); 154 } 155 } 156 } 157 bzclose($bz); 158 if($array) { 159 if($str !== '') $lines[] = $str; 160 return $lines; 161 } 162 return $str; 163} 164 165/** 166 * Used to write out a DokuWiki page to file, and send IO_WIKIPAGE_WRITE events. 167 * 168 * This generates an action event and delegates to io_saveFile(). 169 * Action plugins are allowed to modify the page content in transit. 170 * The file path should not be changed. 171 * (The append parameter is set to false.) 172 * 173 * Event data: 174 * $data[0] The raw arguments for io_saveFile as an array. 175 * $data[1] ns: The colon separated namespace path minus the trailing page name. (false if root ns) 176 * $data[2] page_name: The wiki page name. 177 * $data[3] rev: The page revision, false for current wiki pages. 178 * 179 * @author Ben Coburn <btcoburn@silicodon.net> 180 * 181 * @param string $file filename 182 * @param string $content 183 * @param string $id page id 184 * @param int|bool $rev timestamp of revision 185 * @return bool 186 */ 187function io_writeWikiPage($file, $content, $id, $rev=false) { 188 if (empty($rev)) { $rev = false; } 189 if ($rev===false) { io_createNamespace($id); } // create namespaces as needed 190 $data = array(array($file, $content, false), getNS($id), noNS($id), $rev); 191 return trigger_event('IO_WIKIPAGE_WRITE', $data, '_io_writeWikiPage_action', false); 192} 193 194/** 195 * Callback adapter for io_saveFile(). 196 * @author Ben Coburn <btcoburn@silicodon.net> 197 * 198 * @param array $data event data 199 * @return bool 200 */ 201function _io_writeWikiPage_action($data) { 202 if (is_array($data) && is_array($data[0]) && count($data[0])===3) { 203 $ok = call_user_func_array('io_saveFile', $data[0]); 204 // for attic files make sure the file has the mtime of the revision 205 if($ok && is_int($data[3]) && $data[3] > 0) { 206 @touch($data[0][0], $data[3]); 207 } 208 return $ok; 209 } else { 210 return false; //callback error 211 } 212} 213 214/** 215 * Internal function to save contents to a file. 216 * 217 * @author Andreas Gohr <andi@splitbrain.org> 218 * 219 * @param string $file filename path to file 220 * @param string $content 221 * @param bool $append 222 * @return bool true on success, otherwise false 223 */ 224function _io_saveFile($file, $content, $append) { 225 global $conf; 226 $mode = ($append) ? 'ab' : 'wb'; 227 $fileexists = file_exists($file); 228 229 if(substr($file,-3) == '.gz'){ 230 if(!DOKU_HAS_GZIP) return false; 231 $fh = @gzopen($file,$mode.'9'); 232 if(!$fh) return false; 233 gzwrite($fh, $content); 234 gzclose($fh); 235 }else if(substr($file,-4) == '.bz2'){ 236 if(!DOKU_HAS_BZIP) return false; 237 if($append) { 238 $bzcontent = bzfile($file); 239 if($bzcontent === false) return false; 240 $content = $bzcontent.$content; 241 } 242 $fh = @bzopen($file,'w'); 243 if(!$fh) return false; 244 bzwrite($fh, $content); 245 bzclose($fh); 246 }else{ 247 $fh = @fopen($file,$mode); 248 if(!$fh) return false; 249 fwrite($fh, $content); 250 fclose($fh); 251 } 252 253 if(!$fileexists and !empty($conf['fperm'])) chmod($file, $conf['fperm']); 254 return true; 255} 256 257/** 258 * Saves $content to $file. 259 * 260 * If the third parameter is set to true the given content 261 * will be appended. 262 * 263 * Uses gzip if extension is .gz 264 * and bz2 if extension is .bz2 265 * 266 * @author Andreas Gohr <andi@splitbrain.org> 267 * 268 * @param string $file filename path to file 269 * @param string $content 270 * @param bool $append 271 * @return bool true on success, otherwise false 272 */ 273function io_saveFile($file, $content, $append=false) { 274 io_makeFileDir($file); 275 io_lock($file); 276 if(!_io_saveFile($file, $content, $append)) { 277 msg("Writing $file failed",-1); 278 io_unlock($file); 279 return false; 280 } 281 io_unlock($file); 282 return true; 283} 284 285/** 286 * Replace one or more occurrences of a line in a file. 287 * 288 * The default, when $maxlines is 0 is to delete all matching lines then append a single line. 289 * A regex that matches any part of the line will remove the entire line in this mode. 290 * Captures in $newline are not available. 291 * 292 * Otherwise each line is matched and replaced individually, up to the first $maxlines lines 293 * or all lines if $maxlines is -1. If $regex is true then captures can be used in $newline. 294 * 295 * Be sure to include the trailing newline in $oldline when replacing entire lines. 296 * 297 * Uses gzip if extension is .gz 298 * and bz2 if extension is .bz2 299 * 300 * @author Steven Danz <steven-danz@kc.rr.com> 301 * @author Christopher Smith <chris@jalakai.co.uk> 302 * @author Patrick Brown <ptbrown@whoopdedo.org> 303 * 304 * @param string $file filename 305 * @param string $oldline exact linematch to remove 306 * @param string $newline new line to insert 307 * @param bool $regex use regexp? 308 * @param int $maxlines number of occurrences of the line to replace 309 * @return bool true on success 310 */ 311function io_replaceInFile($file, $oldline, $newline, $regex=false, $maxlines=0) { 312 if ((string)$oldline === '') { 313 trigger_error('$oldline parameter cannot be empty in io_replaceInFile()', E_USER_WARNING); 314 return false; 315 } 316 317 if (!file_exists($file)) return true; 318 319 io_lock($file); 320 321 // load into array 322 if(substr($file,-3) == '.gz'){ 323 if(!DOKU_HAS_GZIP) return false; 324 $lines = gzfile($file); 325 }else if(substr($file,-4) == '.bz2'){ 326 if(!DOKU_HAS_BZIP) return false; 327 $lines = bzfile($file, true); 328 }else{ 329 $lines = file($file); 330 } 331 332 // make non-regexes into regexes 333 $pattern = $regex ? $oldline : '/^'.preg_quote($oldline,'/').'$/'; 334 $replace = $regex ? $newline : addcslashes($newline, '\$'); 335 336 // remove matching lines 337 if ($maxlines > 0) { 338 $count = 0; 339 $matched = 0; 340 foreach($lines as $i => $line) { 341 if($count >= $maxlines) break; 342 // $matched will be set to 0|1 depending on whether pattern is matched and line replaced 343 $lines[$i] = preg_replace($pattern, $replace, $line, -1, $matched); 344 if ($matched) $count++; 345 } 346 } else if ($maxlines == 0) { 347 $lines = preg_grep($pattern, $lines, PREG_GREP_INVERT); 348 349 if ((string)$newline !== ''){ 350 $lines[] = $newline; 351 } 352 } else { 353 $lines = preg_replace($pattern, $replace, $lines); 354 } 355 356 if(count($lines)){ 357 if(!_io_saveFile($file, join('',$lines), false)) { 358 msg("Removing content from $file failed",-1); 359 io_unlock($file); 360 return false; 361 } 362 }else{ 363 @unlink($file); 364 } 365 366 io_unlock($file); 367 return true; 368} 369 370/** 371 * Delete lines that match $badline from $file. 372 * 373 * Be sure to include the trailing newline in $badline 374 * 375 * @author Patrick Brown <ptbrown@whoopdedo.org> 376 * 377 * @param string $file filename 378 * @param string $badline exact linematch to remove 379 * @param bool $regex use regexp? 380 * @return bool true on success 381 */ 382function io_deleteFromFile($file,$badline,$regex=false){ 383 return io_replaceInFile($file,$badline,null,$regex,0); 384} 385 386/** 387 * Tries to lock a file 388 * 389 * Locking is only done for io_savefile and uses directories 390 * inside $conf['lockdir'] 391 * 392 * It waits maximal 3 seconds for the lock, after this time 393 * the lock is assumed to be stale and the function goes on 394 * 395 * @author Andreas Gohr <andi@splitbrain.org> 396 * 397 * @param string $file filename 398 */ 399function io_lock($file){ 400 global $conf; 401 // no locking if safemode hack 402 if($conf['safemodehack']) return; 403 404 $lockDir = $conf['lockdir'].'/'.md5($file); 405 @ignore_user_abort(1); 406 407 $timeStart = time(); 408 do { 409 //waited longer than 3 seconds? -> stale lock 410 if ((time() - $timeStart) > 3) break; 411 $locked = @mkdir($lockDir, $conf['dmode']); 412 if($locked){ 413 if(!empty($conf['dperm'])) chmod($lockDir, $conf['dperm']); 414 break; 415 } 416 usleep(50); 417 } while ($locked === false); 418} 419 420/** 421 * Unlocks a file 422 * 423 * @author Andreas Gohr <andi@splitbrain.org> 424 * 425 * @param string $file filename 426 */ 427function io_unlock($file){ 428 global $conf; 429 // no locking if safemode hack 430 if($conf['safemodehack']) return; 431 432 $lockDir = $conf['lockdir'].'/'.md5($file); 433 @rmdir($lockDir); 434 @ignore_user_abort(0); 435} 436 437/** 438 * Create missing namespace directories and send the IO_NAMESPACE_CREATED events 439 * in the order of directory creation. (Parent directories first.) 440 * 441 * Event data: 442 * $data[0] ns: The colon separated namespace path minus the trailing page name. 443 * $data[1] ns_type: 'pages' or 'media' namespace tree. 444 * 445 * @author Ben Coburn <btcoburn@silicodon.net> 446 * 447 * @param string $id page id 448 * @param string $ns_type 'pages' or 'media' 449 */ 450function io_createNamespace($id, $ns_type='pages') { 451 // verify ns_type 452 $types = array('pages'=>'wikiFN', 'media'=>'mediaFN'); 453 if (!isset($types[$ns_type])) { 454 trigger_error('Bad $ns_type parameter for io_createNamespace().'); 455 return; 456 } 457 // make event list 458 $missing = array(); 459 $ns_stack = explode(':', $id); 460 $ns = $id; 461 $tmp = dirname( $file = call_user_func($types[$ns_type], $ns) ); 462 while (!@is_dir($tmp) && !(file_exists($tmp) && !is_dir($tmp))) { 463 array_pop($ns_stack); 464 $ns = implode(':', $ns_stack); 465 if (strlen($ns)==0) { break; } 466 $missing[] = $ns; 467 $tmp = dirname(call_user_func($types[$ns_type], $ns)); 468 } 469 // make directories 470 io_makeFileDir($file); 471 // send the events 472 $missing = array_reverse($missing); // inside out 473 foreach ($missing as $ns) { 474 $data = array($ns, $ns_type); 475 trigger_event('IO_NAMESPACE_CREATED', $data); 476 } 477} 478 479/** 480 * Create the directory needed for the given file 481 * 482 * @author Andreas Gohr <andi@splitbrain.org> 483 * 484 * @param string $file file name 485 */ 486function io_makeFileDir($file){ 487 $dir = dirname($file); 488 if(!@is_dir($dir)){ 489 io_mkdir_p($dir) || msg("Creating directory $dir failed",-1); 490 } 491} 492 493/** 494 * Creates a directory hierachy. 495 * 496 * @link http://php.net/manual/en/function.mkdir.php 497 * @author <saint@corenova.com> 498 * @author Andreas Gohr <andi@splitbrain.org> 499 * 500 * @param string $target filename 501 * @return bool|int|string 502 */ 503function io_mkdir_p($target){ 504 global $conf; 505 if (@is_dir($target)||empty($target)) return 1; // best case check first 506 if (file_exists($target) && !is_dir($target)) return 0; 507 //recursion 508 if (io_mkdir_p(substr($target,0,strrpos($target,'/')))){ 509 if($conf['safemodehack']){ 510 $dir = preg_replace('/^'.preg_quote(fullpath($conf['ftp']['root']),'/').'/','', $target); 511 return io_mkdir_ftp($dir); 512 }else{ 513 $ret = @mkdir($target,$conf['dmode']); // crawl back up & create dir tree 514 if($ret && !empty($conf['dperm'])) chmod($target, $conf['dperm']); 515 return $ret; 516 } 517 } 518 return 0; 519} 520 521/** 522 * Recursively delete a directory 523 * 524 * @author Andreas Gohr <andi@splitbrain.org> 525 * @param string $path 526 * @param bool $removefiles defaults to false which will delete empty directories only 527 * @return bool 528 */ 529function io_rmdir($path, $removefiles = false) { 530 if(!is_string($path) || $path == "") return false; 531 if(!file_exists($path)) return true; // it's already gone or was never there, count as success 532 533 if(is_dir($path) && !is_link($path)) { 534 $dirs = array(); 535 $files = array(); 536 537 if(!$dh = @opendir($path)) return false; 538 while(false !== ($f = readdir($dh))) { 539 if($f == '..' || $f == '.') continue; 540 541 // collect dirs and files first 542 if(is_dir("$path/$f") && !is_link("$path/$f")) { 543 $dirs[] = "$path/$f"; 544 } else if($removefiles) { 545 $files[] = "$path/$f"; 546 } else { 547 return false; // abort when non empty 548 } 549 550 } 551 closedir($dh); 552 553 // now traverse into directories first 554 foreach($dirs as $dir) { 555 if(!io_rmdir($dir, $removefiles)) return false; // abort on any error 556 } 557 558 // now delete files 559 foreach($files as $file) { 560 if(!@unlink($file)) return false; //abort on any error 561 } 562 563 // remove self 564 return @rmdir($path); 565 } else if($removefiles) { 566 return @unlink($path); 567 } 568 return false; 569} 570 571/** 572 * Creates a directory using FTP 573 * 574 * This is used when the safemode workaround is enabled 575 * 576 * @author <andi@splitbrain.org> 577 * 578 * @param string $dir name of the new directory 579 * @return false|string 580 */ 581function io_mkdir_ftp($dir){ 582 global $conf; 583 584 if(!function_exists('ftp_connect')){ 585 msg("FTP support not found - safemode workaround not usable",-1); 586 return false; 587 } 588 589 $conn = @ftp_connect($conf['ftp']['host'],$conf['ftp']['port'],10); 590 if(!$conn){ 591 msg("FTP connection failed",-1); 592 return false; 593 } 594 595 if(!@ftp_login($conn, $conf['ftp']['user'], conf_decodeString($conf['ftp']['pass']))){ 596 msg("FTP login failed",-1); 597 return false; 598 } 599 600 //create directory 601 $ok = @ftp_mkdir($conn, $dir); 602 //set permissions 603 @ftp_site($conn,sprintf("CHMOD %04o %s",$conf['dmode'],$dir)); 604 605 @ftp_close($conn); 606 return $ok; 607} 608 609/** 610 * Creates a unique temporary directory and returns 611 * its path. 612 * 613 * @author Michael Klier <chi@chimeric.de> 614 * 615 * @return false|string path to new directory or false 616 */ 617function io_mktmpdir() { 618 global $conf; 619 620 $base = $conf['tmpdir']; 621 $dir = md5(uniqid(mt_rand(), true)); 622 $tmpdir = $base.'/'.$dir; 623 624 if(io_mkdir_p($tmpdir)) { 625 return($tmpdir); 626 } else { 627 return false; 628 } 629} 630 631/** 632 * downloads a file from the net and saves it 633 * 634 * if $useAttachment is false, 635 * - $file is the full filename to save the file, incl. path 636 * - if successful will return true, false otherwise 637 * 638 * if $useAttachment is true, 639 * - $file is the directory where the file should be saved 640 * - if successful will return the name used for the saved file, false otherwise 641 * 642 * @author Andreas Gohr <andi@splitbrain.org> 643 * @author Chris Smith <chris@jalakai.co.uk> 644 * 645 * @param string $url url to download 646 * @param string $file path to file or directory where to save 647 * @param bool $useAttachment true: try to use name of download, uses otherwise $defaultName 648 * false: uses $file as path to file 649 * @param string $defaultName fallback for if using $useAttachment 650 * @param int $maxSize maximum file size 651 * @return bool|string if failed false, otherwise true or the name of the file in the given dir 652 */ 653function io_download($url,$file,$useAttachment=false,$defaultName='',$maxSize=2097152){ 654 global $conf; 655 $http = new DokuHTTPClient(); 656 $http->max_bodysize = $maxSize; 657 $http->timeout = 25; //max. 25 sec 658 $http->keep_alive = false; // we do single ops here, no need for keep-alive 659 660 $data = $http->get($url); 661 if(!$data) return false; 662 663 $name = ''; 664 if ($useAttachment) { 665 if (isset($http->resp_headers['content-disposition'])) { 666 $content_disposition = $http->resp_headers['content-disposition']; 667 $match=array(); 668 if (is_string($content_disposition) && 669 preg_match('/attachment;\s*filename\s*=\s*"([^"]*)"/i', $content_disposition, $match)) { 670 671 $name = utf8_basename($match[1]); 672 } 673 674 } 675 676 if (!$name) { 677 if (!$defaultName) return false; 678 $name = $defaultName; 679 } 680 681 $file = $file.$name; 682 } 683 684 $fileexists = file_exists($file); 685 $fp = @fopen($file,"w"); 686 if(!$fp) return false; 687 fwrite($fp,$data); 688 fclose($fp); 689 if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']); 690 if ($useAttachment) return $name; 691 return true; 692} 693 694/** 695 * Windows compatible rename 696 * 697 * rename() can not overwrite existing files on Windows 698 * this function will use copy/unlink instead 699 * 700 * @param string $from 701 * @param string $to 702 * @return bool succes or fail 703 */ 704function io_rename($from,$to){ 705 global $conf; 706 if(!@rename($from,$to)){ 707 if(@copy($from,$to)){ 708 if($conf['fperm']) chmod($to, $conf['fperm']); 709 @unlink($from); 710 return true; 711 } 712 return false; 713 } 714 return true; 715} 716 717/** 718 * Runs an external command with input and output pipes. 719 * Returns the exit code from the process. 720 * 721 * @author Tom N Harris <tnharris@whoopdedo.org> 722 * 723 * @param string $cmd 724 * @param string $input input pipe 725 * @param string $output output pipe 726 * @return int exit code from process 727 */ 728function io_exec($cmd, $input, &$output){ 729 $descspec = array( 730 0=>array("pipe","r"), 731 1=>array("pipe","w"), 732 2=>array("pipe","w")); 733 $ph = proc_open($cmd, $descspec, $pipes); 734 if(!$ph) return -1; 735 fclose($pipes[2]); // ignore stderr 736 fwrite($pipes[0], $input); 737 fclose($pipes[0]); 738 $output = stream_get_contents($pipes[1]); 739 fclose($pipes[1]); 740 return proc_close($ph); 741} 742 743/** 744 * Search a file for matching lines 745 * 746 * This is probably not faster than file()+preg_grep() but less 747 * memory intensive because not the whole file needs to be loaded 748 * at once. 749 * 750 * @author Andreas Gohr <andi@splitbrain.org> 751 * @param string $file The file to search 752 * @param string $pattern PCRE pattern 753 * @param int $max How many lines to return (0 for all) 754 * @param bool $backref When true returns array with backreferences instead of lines 755 * @return array matching lines or backref, false on error 756 */ 757function io_grep($file,$pattern,$max=0,$backref=false){ 758 $fh = @fopen($file,'r'); 759 if(!$fh) return false; 760 $matches = array(); 761 762 $cnt = 0; 763 $line = ''; 764 while (!feof($fh)) { 765 $line .= fgets($fh, 4096); // read full line 766 if(substr($line,-1) != "\n") continue; 767 768 // check if line matches 769 if(preg_match($pattern,$line,$match)){ 770 if($backref){ 771 $matches[] = $match; 772 }else{ 773 $matches[] = $line; 774 } 775 $cnt++; 776 } 777 if($max && $max == $cnt) break; 778 $line = ''; 779 } 780 fclose($fh); 781 return $matches; 782} 783 784 785/** 786 * Get size of contents of a file, for a compressed file the uncompressed size 787 * Warning: reading uncompressed size of content of bz-files requires uncompressing 788 * 789 * @author Gerrit Uitslag <klapinklapin@gmail.com> 790 * 791 * @param string $file filename path to file 792 * @return int size of file 793 */ 794function io_getSizeFile($file) { 795 if (!file_exists($file)) return 0; 796 797 if(substr($file,-3) == '.gz'){ 798 $fp = @fopen($file, "rb"); 799 if($fp === false) return 0; 800 801 fseek($fp, -4, SEEK_END); 802 $buffer = fread($fp, 4); 803 fclose($fp); 804 $array = unpack("V", $buffer); 805 $uncompressedsize = end($array); 806 }else if(substr($file,-4) == '.bz2'){ 807 if(!DOKU_HAS_BZIP) return 0; 808 809 $bz = bzopen($file,"r"); 810 if($bz === false) return 0; 811 812 $uncompressedsize = 0; 813 while (!feof($bz)) { 814 //8192 seems to be the maximum buffersize? 815 $buffer = bzread($bz,8192); 816 if(($buffer === false) || (bzerrno($bz) !== 0)) { 817 return 0; 818 } 819 $uncompressedsize += strlen($buffer); 820 } 821 }else{ 822 $uncompressedsize = filesize($file); 823 } 824 825 return $uncompressedsize; 826 } 827