1 | <?php
|
---|
2 | /**
|
---|
3 | * File IO functions
|
---|
4 | *
|
---|
5 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
---|
6 | * @author Andreas Gohr <[email protected]>
|
---|
7 | */
|
---|
8 |
|
---|
9 | if(!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 $basadir - the config name of the type to delete (datadir or mediadir usally)
|
---|
22 | * @returns bool - true if at least one namespace was deleted
|
---|
23 | * @author Andreas Gohr <[email protected]>
|
---|
24 | * @author Ben Coburn <[email protected]>
|
---|
25 | */
|
---|
26 | function io_sweepNS($id,$basedir='datadir'){
|
---|
27 | global $conf;
|
---|
28 | $types = array ('datadir'=>'pages', 'mediadir'=>'media');
|
---|
29 | $ns_type = (isset($types[$basedir])?$types[$basedir]:false);
|
---|
30 |
|
---|
31 | $delone = false;
|
---|
32 |
|
---|
33 | //scan all namespaces
|
---|
34 | while(($id = getNS($id)) !== false){
|
---|
35 | $dir = $conf[$basedir].'/'.utf8_encodeFN(str_replace(':','/',$id));
|
---|
36 |
|
---|
37 | //try to delete dir else return
|
---|
38 | if(@rmdir($dir)) {
|
---|
39 | if ($ns_type!==false) {
|
---|
40 | $data = array($id, $ns_type);
|
---|
41 | $delone = true; // we deleted at least one dir
|
---|
42 | trigger_event('IO_NAMESPACE_DELETED', $data);
|
---|
43 | }
|
---|
44 | } else { return $delone; }
|
---|
45 | }
|
---|
46 | return $delone;
|
---|
47 | }
|
---|
48 |
|
---|
49 | /**
|
---|
50 | * Used to read in a DokuWiki page from file, and send IO_WIKIPAGE_READ events.
|
---|
51 | *
|
---|
52 | * Generates the action event which delegates to io_readFile().
|
---|
53 | * Action plugins are allowed to modify the page content in transit.
|
---|
54 | * The file path should not be changed.
|
---|
55 | *
|
---|
56 | * Event data:
|
---|
57 | * $data[0] The raw arguments for io_readFile as an array.
|
---|
58 | * $data[1] ns: The colon separated namespace path minus the trailing page name. (false if root ns)
|
---|
59 | * $data[2] page_name: The wiki page name.
|
---|
60 | * $data[3] rev: The page revision, false for current wiki pages.
|
---|
61 | *
|
---|
62 | * @author Ben Coburn <[email protected]>
|
---|
63 | */
|
---|
64 | function io_readWikiPage($file, $id, $rev=false) {
|
---|
65 | if (empty($rev)) { $rev = false; }
|
---|
66 | $data = array(array($file, false), getNS($id), noNS($id), $rev);
|
---|
67 | return trigger_event('IO_WIKIPAGE_READ', $data, '_io_readWikiPage_action', false);
|
---|
68 | }
|
---|
69 |
|
---|
70 | /**
|
---|
71 | * Callback adapter for io_readFile().
|
---|
72 | * @author Ben Coburn <[email protected]>
|
---|
73 | */
|
---|
74 | function _io_readWikiPage_action($data) {
|
---|
75 | if (is_array($data) && is_array($data[0]) && count($data[0])===2) {
|
---|
76 | return call_user_func_array('io_readFile', $data[0]);
|
---|
77 | } else {
|
---|
78 | return ''; //callback error
|
---|
79 | }
|
---|
80 | }
|
---|
81 |
|
---|
82 | /**
|
---|
83 | * Returns content of $file as cleaned string.
|
---|
84 | *
|
---|
85 | * Uses gzip if extension is .gz
|
---|
86 | *
|
---|
87 | * If you want to use the returned value in unserialize
|
---|
88 | * be sure to set $clean to false!
|
---|
89 | *
|
---|
90 | * @author Andreas Gohr <[email protected]>
|
---|
91 | */
|
---|
92 | function io_readFile($file,$clean=true){
|
---|
93 | $ret = '';
|
---|
94 | if(@file_exists($file)){
|
---|
95 | if(substr($file,-3) == '.gz'){
|
---|
96 | $ret = join('',gzfile($file));
|
---|
97 | }else if(substr($file,-4) == '.bz2'){
|
---|
98 | $ret = bzfile($file);
|
---|
99 | }else{
|
---|
100 | $ret = file_get_contents($file);
|
---|
101 | }
|
---|
102 | }
|
---|
103 | if($clean){
|
---|
104 | return cleanText($ret);
|
---|
105 | }else{
|
---|
106 | return $ret;
|
---|
107 | }
|
---|
108 | }
|
---|
109 | /**
|
---|
110 | * Returns the content of a .bz2 compressed file as string
|
---|
111 | * @author marcel senf <[email protected]>
|
---|
112 | */
|
---|
113 |
|
---|
114 | function bzfile($file){
|
---|
115 | $bz = bzopen($file,"r");
|
---|
116 | while (!feof($bz)){
|
---|
117 | //8192 seems to be the maximum buffersize?
|
---|
118 | $str = $str . bzread($bz,8192);
|
---|
119 | }
|
---|
120 | bzclose($bz);
|
---|
121 | return $str;
|
---|
122 | }
|
---|
123 |
|
---|
124 |
|
---|
125 | /**
|
---|
126 | * Used to write out a DokuWiki page to file, and send IO_WIKIPAGE_WRITE events.
|
---|
127 | *
|
---|
128 | * This generates an action event and delegates to io_saveFile().
|
---|
129 | * Action plugins are allowed to modify the page content in transit.
|
---|
130 | * The file path should not be changed.
|
---|
131 | * (The append parameter is set to false.)
|
---|
132 | *
|
---|
133 | * Event data:
|
---|
134 | * $data[0] The raw arguments for io_saveFile as an array.
|
---|
135 | * $data[1] ns: The colon separated namespace path minus the trailing page name. (false if root ns)
|
---|
136 | * $data[2] page_name: The wiki page name.
|
---|
137 | * $data[3] rev: The page revision, false for current wiki pages.
|
---|
138 | *
|
---|
139 | * @author Ben Coburn <[email protected]>
|
---|
140 | */
|
---|
141 | function io_writeWikiPage($file, $content, $id, $rev=false) {
|
---|
142 | if (empty($rev)) { $rev = false; }
|
---|
143 | if ($rev===false) { io_createNamespace($id); } // create namespaces as needed
|
---|
144 | $data = array(array($file, $content, false), getNS($id), noNS($id), $rev);
|
---|
145 | return trigger_event('IO_WIKIPAGE_WRITE', $data, '_io_writeWikiPage_action', false);
|
---|
146 | }
|
---|
147 |
|
---|
148 | /**
|
---|
149 | * Callback adapter for io_saveFile().
|
---|
150 | * @author Ben Coburn <[email protected]>
|
---|
151 | */
|
---|
152 | function _io_writeWikiPage_action($data) {
|
---|
153 | if (is_array($data) && is_array($data[0]) && count($data[0])===3) {
|
---|
154 | return call_user_func_array('io_saveFile', $data[0]);
|
---|
155 | } else {
|
---|
156 | return false; //callback error
|
---|
157 | }
|
---|
158 | }
|
---|
159 |
|
---|
160 | /**
|
---|
161 | * Saves $content to $file.
|
---|
162 | *
|
---|
163 | * If the third parameter is set to true the given content
|
---|
164 | * will be appended.
|
---|
165 | *
|
---|
166 | * Uses gzip if extension is .gz
|
---|
167 | * and bz2 if extension is .bz2
|
---|
168 | *
|
---|
169 | * @author Andreas Gohr <[email protected]>
|
---|
170 | * @return bool true on success
|
---|
171 | */
|
---|
172 | function io_saveFile($file,$content,$append=false){
|
---|
173 | global $conf;
|
---|
174 | $mode = ($append) ? 'ab' : 'wb';
|
---|
175 |
|
---|
176 | $fileexists = @file_exists($file);
|
---|
177 | io_makeFileDir($file);
|
---|
178 | io_lock($file);
|
---|
179 | if(substr($file,-3) == '.gz'){
|
---|
180 | $fh = @gzopen($file,$mode.'9');
|
---|
181 | if(!$fh){
|
---|
182 | msg("Writing $file failed",-1);
|
---|
183 | io_unlock($file);
|
---|
184 | return false;
|
---|
185 | }
|
---|
186 | gzwrite($fh, $content);
|
---|
187 | gzclose($fh);
|
---|
188 | }else if(substr($file,-4) == '.bz2'){
|
---|
189 | $fh = @bzopen($file,$mode{0});
|
---|
190 | if(!$fh){
|
---|
191 | msg("Writing $file failed", -1);
|
---|
192 | io_unlock($file);
|
---|
193 | return false;
|
---|
194 | }
|
---|
195 | bzwrite($fh, $content);
|
---|
196 | bzclose($fh);
|
---|
197 | }else{
|
---|
198 | $fh = @fopen($file,$mode);
|
---|
199 | if(!$fh){
|
---|
200 | msg("Writing $file failed",-1);
|
---|
201 | io_unlock($file);
|
---|
202 | return false;
|
---|
203 | }
|
---|
204 | fwrite($fh, $content);
|
---|
205 | fclose($fh);
|
---|
206 | }
|
---|
207 |
|
---|
208 | if(!$fileexists and !empty($conf['fperm'])) chmod($file, $conf['fperm']);
|
---|
209 | io_unlock($file);
|
---|
210 | return true;
|
---|
211 | }
|
---|
212 |
|
---|
213 | /**
|
---|
214 | * Delete exact linematch for $badline from $file.
|
---|
215 | *
|
---|
216 | * Be sure to include the trailing newline in $badline
|
---|
217 | *
|
---|
218 | * Uses gzip if extension is .gz
|
---|
219 | *
|
---|
220 | * 2005-10-14 : added regex option -- Christopher Smith <[email protected]>
|
---|
221 | *
|
---|
222 | * @author Steven Danz <[email protected]>
|
---|
223 | * @return bool true on success
|
---|
224 | */
|
---|
225 | function io_deleteFromFile($file,$badline,$regex=false){
|
---|
226 | if (!@file_exists($file)) return true;
|
---|
227 |
|
---|
228 | io_lock($file);
|
---|
229 |
|
---|
230 | // load into array
|
---|
231 | if(substr($file,-3) == '.gz'){
|
---|
232 | $lines = gzfile($file);
|
---|
233 | }else{
|
---|
234 | $lines = file($file);
|
---|
235 | }
|
---|
236 |
|
---|
237 | // remove all matching lines
|
---|
238 | if ($regex) {
|
---|
239 | $lines = preg_grep($badline,$lines,PREG_GREP_INVERT);
|
---|
240 | } else {
|
---|
241 | $pos = array_search($badline,$lines); //return null or false if not found
|
---|
242 | while(is_int($pos)){
|
---|
243 | unset($lines[$pos]);
|
---|
244 | $pos = array_search($badline,$lines);
|
---|
245 | }
|
---|
246 | }
|
---|
247 |
|
---|
248 | if(count($lines)){
|
---|
249 | $content = join('',$lines);
|
---|
250 | if(substr($file,-3) == '.gz'){
|
---|
251 | $fh = @gzopen($file,'wb9');
|
---|
252 | if(!$fh){
|
---|
253 | msg("Removing content from $file failed",-1);
|
---|
254 | io_unlock($file);
|
---|
255 | return false;
|
---|
256 | }
|
---|
257 | gzwrite($fh, $content);
|
---|
258 | gzclose($fh);
|
---|
259 | }else{
|
---|
260 | $fh = @fopen($file,'wb');
|
---|
261 | if(!$fh){
|
---|
262 | msg("Removing content from $file failed",-1);
|
---|
263 | io_unlock($file);
|
---|
264 | return false;
|
---|
265 | }
|
---|
266 | fwrite($fh, $content);
|
---|
267 | fclose($fh);
|
---|
268 | }
|
---|
269 | }else{
|
---|
270 | @unlink($file);
|
---|
271 | }
|
---|
272 |
|
---|
273 | io_unlock($file);
|
---|
274 | return true;
|
---|
275 | }
|
---|
276 |
|
---|
277 | /**
|
---|
278 | * Tries to lock a file
|
---|
279 | *
|
---|
280 | * Locking is only done for io_savefile and uses directories
|
---|
281 | * inside $conf['lockdir']
|
---|
282 | *
|
---|
283 | * It waits maximal 3 seconds for the lock, after this time
|
---|
284 | * the lock is assumed to be stale and the function goes on
|
---|
285 | *
|
---|
286 | * @author Andreas Gohr <[email protected]>
|
---|
287 | */
|
---|
288 | function io_lock($file){
|
---|
289 | global $conf;
|
---|
290 | // no locking if safemode hack
|
---|
291 | if($conf['safemodehack']) return;
|
---|
292 |
|
---|
293 | $lockDir = $conf['lockdir'].'/'.md5($file);
|
---|
294 | @ignore_user_abort(1);
|
---|
295 |
|
---|
296 | $timeStart = time();
|
---|
297 | do {
|
---|
298 | //waited longer than 3 seconds? -> stale lock
|
---|
299 | if ((time() - $timeStart) > 3) break;
|
---|
300 | $locked = @mkdir($lockDir, $conf['dmode']);
|
---|
301 | if($locked){
|
---|
302 | if(!empty($conf['dperm'])) chmod($lockDir, $conf['dperm']);
|
---|
303 | break;
|
---|
304 | }
|
---|
305 | usleep(50);
|
---|
306 | } while ($locked === false);
|
---|
307 | }
|
---|
308 |
|
---|
309 | /**
|
---|
310 | * Unlocks a file
|
---|
311 | *
|
---|
312 | * @author Andreas Gohr <[email protected]>
|
---|
313 | */
|
---|
314 | function io_unlock($file){
|
---|
315 | global $conf;
|
---|
316 | // no locking if safemode hack
|
---|
317 | if($conf['safemodehack']) return;
|
---|
318 |
|
---|
319 | $lockDir = $conf['lockdir'].'/'.md5($file);
|
---|
320 | @rmdir($lockDir);
|
---|
321 | @ignore_user_abort(0);
|
---|
322 | }
|
---|
323 |
|
---|
324 | /**
|
---|
325 | * Create missing namespace directories and send the IO_NAMESPACE_CREATED events
|
---|
326 | * in the order of directory creation. (Parent directories first.)
|
---|
327 | *
|
---|
328 | * Event data:
|
---|
329 | * $data[0] ns: The colon separated namespace path minus the trailing page name.
|
---|
330 | * $data[1] ns_type: 'pages' or 'media' namespace tree.
|
---|
331 | *
|
---|
332 | * @author Ben Coburn <[email protected]>
|
---|
333 | */
|
---|
334 | function io_createNamespace($id, $ns_type='pages') {
|
---|
335 | // verify ns_type
|
---|
336 | $types = array('pages'=>'wikiFN', 'media'=>'mediaFN');
|
---|
337 | if (!isset($types[$ns_type])) {
|
---|
338 | trigger_error('Bad $ns_type parameter for io_createNamespace().');
|
---|
339 | return;
|
---|
340 | }
|
---|
341 | // make event list
|
---|
342 | $missing = array();
|
---|
343 | $ns_stack = explode(':', $id);
|
---|
344 | $ns = $id;
|
---|
345 | $tmp = dirname( $file = call_user_func($types[$ns_type], $ns) );
|
---|
346 | while (!@is_dir($tmp) && !(@file_exists($tmp) && !is_dir($tmp))) {
|
---|
347 | array_pop($ns_stack);
|
---|
348 | $ns = implode(':', $ns_stack);
|
---|
349 | if (strlen($ns)==0) { break; }
|
---|
350 | $missing[] = $ns;
|
---|
351 | $tmp = dirname(call_user_func($types[$ns_type], $ns));
|
---|
352 | }
|
---|
353 | // make directories
|
---|
354 | io_makeFileDir($file);
|
---|
355 | // send the events
|
---|
356 | $missing = array_reverse($missing); // inside out
|
---|
357 | foreach ($missing as $ns) {
|
---|
358 | $data = array($ns, $ns_type);
|
---|
359 | trigger_event('IO_NAMESPACE_CREATED', $data);
|
---|
360 | }
|
---|
361 | }
|
---|
362 |
|
---|
363 | /**
|
---|
364 | * Create the directory needed for the given file
|
---|
365 | *
|
---|
366 | * @author Andreas Gohr <[email protected]>
|
---|
367 | */
|
---|
368 | function io_makeFileDir($file){
|
---|
369 | global $conf;
|
---|
370 |
|
---|
371 | $dir = dirname($file);
|
---|
372 | if(!@is_dir($dir)){
|
---|
373 | io_mkdir_p($dir) || msg("Creating directory $dir failed",-1);
|
---|
374 | }
|
---|
375 | }
|
---|
376 |
|
---|
377 | /**
|
---|
378 | * Creates a directory hierachy.
|
---|
379 | *
|
---|
380 | * @link http://www.php.net/manual/en/function.mkdir.php
|
---|
381 | * @author <[email protected]>
|
---|
382 | * @author Andreas Gohr <[email protected]>
|
---|
383 | */
|
---|
384 | function io_mkdir_p($target){
|
---|
385 | global $conf;
|
---|
386 | if (@is_dir($target)||empty($target)) return 1; // best case check first
|
---|
387 | if (@file_exists($target) && !is_dir($target)) return 0;
|
---|
388 | //recursion
|
---|
389 | if (io_mkdir_p(substr($target,0,strrpos($target,'/')))){
|
---|
390 | if($conf['safemodehack']){
|
---|
391 | $dir = preg_replace('/^'.preg_quote(fullpath($conf['ftp']['root']),'/').'/','', $target);
|
---|
392 | return io_mkdir_ftp($dir);
|
---|
393 | }else{
|
---|
394 | $ret = @mkdir($target,$conf['dmode']); // crawl back up & create dir tree
|
---|
395 | if($ret && $conf['dperm']) chmod($target, $conf['dperm']);
|
---|
396 | return $ret;
|
---|
397 | }
|
---|
398 | }
|
---|
399 | return 0;
|
---|
400 | }
|
---|
401 |
|
---|
402 | /**
|
---|
403 | * Creates a directory using FTP
|
---|
404 | *
|
---|
405 | * This is used when the safemode workaround is enabled
|
---|
406 | *
|
---|
407 | * @author <[email protected]>
|
---|
408 | */
|
---|
409 | function io_mkdir_ftp($dir){
|
---|
410 | global $conf;
|
---|
411 |
|
---|
412 | if(!function_exists('ftp_connect')){
|
---|
413 | msg("FTP support not found - safemode workaround not usable",-1);
|
---|
414 | return false;
|
---|
415 | }
|
---|
416 |
|
---|
417 | $conn = @ftp_connect($conf['ftp']['host'],$conf['ftp']['port'],10);
|
---|
418 | if(!$conn){
|
---|
419 | msg("FTP connection failed",-1);
|
---|
420 | return false;
|
---|
421 | }
|
---|
422 |
|
---|
423 | if(!@ftp_login($conn, $conf['ftp']['user'], conf_decodeString($conf['ftp']['pass']))){
|
---|
424 | msg("FTP login failed",-1);
|
---|
425 | return false;
|
---|
426 | }
|
---|
427 |
|
---|
428 | //create directory
|
---|
429 | $ok = @ftp_mkdir($conn, $dir);
|
---|
430 | //set permissions
|
---|
431 | @ftp_site($conn,sprintf("CHMOD %04o %s",$conf['dmode'],$dir));
|
---|
432 |
|
---|
433 | @ftp_close($conn);
|
---|
434 | return $ok;
|
---|
435 | }
|
---|
436 |
|
---|
437 | /**
|
---|
438 | * Creates a unique temporary directory and returns
|
---|
439 | * its path.
|
---|
440 | *
|
---|
441 | * @author Michael Klier <[email protected]>
|
---|
442 | */
|
---|
443 | function io_mktmpdir() {
|
---|
444 | global $conf;
|
---|
445 |
|
---|
446 | $base = $conf['tmpdir'];
|
---|
447 | $dir = md5(uniqid(mt_rand(), true));
|
---|
448 | $tmpdir = $base.'/'.$dir;
|
---|
449 |
|
---|
450 | if(io_mkdir_p($tmpdir)) {
|
---|
451 | return($tmpdir);
|
---|
452 | } else {
|
---|
453 | return false;
|
---|
454 | }
|
---|
455 | }
|
---|
456 |
|
---|
457 | /**
|
---|
458 | * downloads a file from the net and saves it
|
---|
459 | *
|
---|
460 | * if $useAttachment is false,
|
---|
461 | * - $file is the full filename to save the file, incl. path
|
---|
462 | * - if successful will return true, false otherwise
|
---|
463 | *
|
---|
464 | * if $useAttachment is true,
|
---|
465 | * - $file is the directory where the file should be saved
|
---|
466 | * - if successful will return the name used for the saved file, false otherwise
|
---|
467 | *
|
---|
468 | * @author Andreas Gohr <[email protected]>
|
---|
469 | * @author Chris Smith <[email protected]>
|
---|
470 | */
|
---|
471 | function io_download($url,$file,$useAttachment=false,$defaultName='',$maxSize=2097152){
|
---|
472 | global $conf;
|
---|
473 | $http = new DokuHTTPClient();
|
---|
474 | $http->max_bodysize = $maxSize;
|
---|
475 | $http->timeout = 25; //max. 25 sec
|
---|
476 |
|
---|
477 | $data = $http->get($url);
|
---|
478 | if(!$data) return false;
|
---|
479 |
|
---|
480 | if ($useAttachment) {
|
---|
481 | $name = '';
|
---|
482 | if (isset($http->resp_headers['content-disposition'])) {
|
---|
483 | $content_disposition = $http->resp_headers['content-disposition'];
|
---|
484 | $match=array();
|
---|
485 | if (is_string($content_disposition) &&
|
---|
486 | preg_match('/attachment;\s*filename\s*=\s*"([^"]*)"/i', $content_disposition, $match)) {
|
---|
487 |
|
---|
488 | $name = basename($match[1]);
|
---|
489 | }
|
---|
490 |
|
---|
491 | }
|
---|
492 |
|
---|
493 | if (!$name) {
|
---|
494 | if (!$defaultName) return false;
|
---|
495 | $name = $defaultName;
|
---|
496 | }
|
---|
497 |
|
---|
498 | $file = $file.$name;
|
---|
499 | }
|
---|
500 |
|
---|
501 | $fileexists = @file_exists($file);
|
---|
502 | $fp = @fopen($file,"w");
|
---|
503 | if(!$fp) return false;
|
---|
504 | fwrite($fp,$data);
|
---|
505 | fclose($fp);
|
---|
506 | if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
|
---|
507 | if ($useAttachment) return $name;
|
---|
508 | return true;
|
---|
509 | }
|
---|
510 |
|
---|
511 | /**
|
---|
512 | * Windows compatible rename
|
---|
513 | *
|
---|
514 | * rename() can not overwrite existing files on Windows
|
---|
515 | * this function will use copy/unlink instead
|
---|
516 | */
|
---|
517 | function io_rename($from,$to){
|
---|
518 | global $conf;
|
---|
519 | if(!@rename($from,$to)){
|
---|
520 | if(@copy($from,$to)){
|
---|
521 | if($conf['fperm']) chmod($to, $conf['fperm']);
|
---|
522 | @unlink($from);
|
---|
523 | return true;
|
---|
524 | }
|
---|
525 | return false;
|
---|
526 | }
|
---|
527 | return true;
|
---|
528 | }
|
---|
529 |
|
---|
530 |
|
---|
531 | /**
|
---|
532 | * Runs an external command and returns its output as string
|
---|
533 | *
|
---|
534 | * @author Harry Brueckner <[email protected]>
|
---|
535 | * @author Andreas Gohr <[email protected]>
|
---|
536 | * @deprecated
|
---|
537 | */
|
---|
538 | function io_runcmd($cmd){
|
---|
539 | $fh = popen($cmd, "r");
|
---|
540 | if(!$fh) return false;
|
---|
541 | $ret = '';
|
---|
542 | while (!feof($fh)) {
|
---|
543 | $ret .= fread($fh, 8192);
|
---|
544 | }
|
---|
545 | pclose($fh);
|
---|
546 | return $ret;
|
---|
547 | }
|
---|
548 |
|
---|
549 | /**
|
---|
550 | * Runs an external command with input and output pipes.
|
---|
551 | * Returns the exit code from the process.
|
---|
552 | *
|
---|
553 | * @author Tom N Harris <[email protected]>
|
---|
554 | */
|
---|
555 | function io_exec($cmd, $input, &$output){
|
---|
556 | $descspec = array(
|
---|
557 | 0=>array("pipe","r"),
|
---|
558 | 1=>array("pipe","w"),
|
---|
559 | 2=>array("pipe","w"));
|
---|
560 | $ph = proc_open($cmd, $descspec, $pipes);
|
---|
561 | if(!$ph) return -1;
|
---|
562 | fclose($pipes[2]); // ignore stderr
|
---|
563 | fwrite($pipes[0], $input);
|
---|
564 | fclose($pipes[0]);
|
---|
565 | $output = stream_get_contents($pipes[1]);
|
---|
566 | fclose($pipes[1]);
|
---|
567 | return proc_close($ph);
|
---|
568 | }
|
---|
569 |
|
---|
570 | /**
|
---|
571 | * Search a file for matching lines
|
---|
572 | *
|
---|
573 | * This is probably not faster than file()+preg_grep() but less
|
---|
574 | * memory intensive because not the whole file needs to be loaded
|
---|
575 | * at once.
|
---|
576 | *
|
---|
577 | * @author Andreas Gohr <[email protected]>
|
---|
578 | * @param string $file The file to search
|
---|
579 | * @param string $pattern PCRE pattern
|
---|
580 | * @param int $max How many lines to return (0 for all)
|
---|
581 | * @param bool $baxkref When true returns array with backreferences instead of lines
|
---|
582 | * @return matching lines or backref, false on error
|
---|
583 | */
|
---|
584 | function io_grep($file,$pattern,$max=0,$backref=false){
|
---|
585 | $fh = @fopen($file,'r');
|
---|
586 | if(!$fh) return false;
|
---|
587 | $matches = array();
|
---|
588 |
|
---|
589 | $cnt = 0;
|
---|
590 | $line = '';
|
---|
591 | while (!feof($fh)) {
|
---|
592 | $line .= fgets($fh, 4096); // read full line
|
---|
593 | if(substr($line,-1) != "\n") continue;
|
---|
594 |
|
---|
595 | // check if line matches
|
---|
596 | if(preg_match($pattern,$line,$match)){
|
---|
597 | if($backref){
|
---|
598 | $matches[] = $match;
|
---|
599 | }else{
|
---|
600 | $matches[] = $line;
|
---|
601 | }
|
---|
602 | $cnt++;
|
---|
603 | }
|
---|
604 | if($max && $max == $cnt) break;
|
---|
605 | $line = '';
|
---|
606 | }
|
---|
607 | fclose($fh);
|
---|
608 | return $matches;
|
---|
609 | }
|
---|
610 |
|
---|