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