Failed Conditions
Push — interwiki-remove-golucky ( 52fcdb...768be5 )
by Henry
12:48 queued 09:48
created

inc/io.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
use dokuwiki\HTTP\DokuHTTPClient;
0 ignored issues
show
This use statement conflicts with another class in this namespace, DokuHTTPClient.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
10
use dokuwiki\Extension\Event;
11
12
/**
13
 * Removes empty directories
14
 *
15
 * Sends IO_NAMESPACE_DELETED events for 'pages' and 'media' namespaces.
16
 * Event data:
17
 * $data[0]    ns: The colon separated namespace path minus the trailing page name.
18
 * $data[1]    ns_type: 'pages' or 'media' namespace tree.
19
 *
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 <[email protected]>
25
 * @author Ben Coburn <[email protected]>
26
 */
27
function 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
                Event::createAndTrigger('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 <[email protected]>
64
 *
65
 * @param string   $file filename
66
 * @param string   $id page id
67
 * @param bool|int $rev revision timestamp
68
 * @return string
69
 */
70
function 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 Event::createAndTrigger('IO_WIKIPAGE_READ', $data, '_io_readWikiPage_action', false);
74
}
75
76
/**
77
 * Callback adapter for io_readFile().
78
 *
79
 * @author Ben Coburn <[email protected]>
80
 *
81
 * @param array $data event data
82
 * @return string
83
 */
84
function _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 <[email protected]>
101
 *
102
 * @param string $file  filename
103
 * @param bool   $clean
104
 * @return string|bool the file contents or false on error
105
 */
106
function 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 <[email protected]>
131
 * @author  Andreas Gohr <[email protected]>
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
 */
137
function 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 <[email protected]>
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
 */
189
function 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 Event::createAndTrigger('IO_WIKIPAGE_WRITE', $data, '_io_writeWikiPage_action', false);
194
}
195
196
/**
197
 * Callback adapter for io_saveFile().
198
 * @author Ben Coburn <[email protected]>
199
 *
200
 * @param array $data event data
201
 * @return bool
202
 */
203
function _io_writeWikiPage_action($data) {
204
    if (is_array($data) && is_array($data[0]) && count($data[0])===3) {
205
        $ok = call_user_func_array('io_saveFile', $data[0]);
206
        // for attic files make sure the file has the mtime of the revision
207
        if($ok && is_int($data[3]) && $data[3] > 0) {
208
            @touch($data[0][0], $data[3]);
209
        }
210
        return $ok;
211
    } else {
212
        return false; //callback error
213
    }
214
}
215
216
/**
217
 * Internal function to save contents to a file.
218
 *
219
 * @author  Andreas Gohr <[email protected]>
220
 *
221
 * @param string $file filename path to file
222
 * @param string $content
223
 * @param bool   $append
224
 * @return bool true on success, otherwise false
225
 */
226
function _io_saveFile($file, $content, $append) {
227
    global $conf;
228
    $mode = ($append) ? 'ab' : 'wb';
229
    $fileexists = file_exists($file);
230
231
    if(substr($file,-3) == '.gz'){
232
        if(!DOKU_HAS_GZIP) return false;
233
        $fh = @gzopen($file,$mode.'9');
234
        if(!$fh) return false;
235
        gzwrite($fh, $content);
236
        gzclose($fh);
237
    }else if(substr($file,-4) == '.bz2'){
238
        if(!DOKU_HAS_BZIP) return false;
239
        if($append) {
240
            $bzcontent = bzfile($file);
241
            if($bzcontent === false) return false;
242
            $content = $bzcontent.$content;
243
        }
244
        $fh = @bzopen($file,'w');
245
        if(!$fh) return false;
246
        bzwrite($fh, $content);
247
        bzclose($fh);
248
    }else{
249
        $fh = @fopen($file,$mode);
250
        if(!$fh) return false;
251
        fwrite($fh, $content);
252
        fclose($fh);
253
    }
254
255
    if(!$fileexists and !empty($conf['fperm'])) chmod($file, $conf['fperm']);
256
    return true;
257
}
258
259
/**
260
 * Saves $content to $file.
261
 *
262
 * If the third parameter is set to true the given content
263
 * will be appended.
264
 *
265
 * Uses gzip if extension is .gz
266
 * and bz2 if extension is .bz2
267
 *
268
 * @author  Andreas Gohr <[email protected]>
269
 *
270
 * @param string $file filename path to file
271
 * @param string $content
272
 * @param bool   $append
273
 * @return bool true on success, otherwise false
274
 */
275
function io_saveFile($file, $content, $append=false) {
276
    io_makeFileDir($file);
277
    io_lock($file);
278
    if(!_io_saveFile($file, $content, $append)) {
279
        msg("Writing $file failed",-1);
280
        io_unlock($file);
281
        return false;
282
    }
283
    io_unlock($file);
284
    return true;
285
}
286
287
/**
288
 * Replace one or more occurrences of a line in a file.
289
 *
290
 * The default, when $maxlines is 0 is to delete all matching lines then append a single line.
291
 * A regex that matches any part of the line will remove the entire line in this mode.
292
 * Captures in $newline are not available.
293
 *
294
 * Otherwise each line is matched and replaced individually, up to the first $maxlines lines
295
 * or all lines if $maxlines is -1. If $regex is true then captures can be used in $newline.
296
 *
297
 * Be sure to include the trailing newline in $oldline when replacing entire lines.
298
 *
299
 * Uses gzip if extension is .gz
300
 * and bz2 if extension is .bz2
301
 *
302
 * @author Steven Danz <[email protected]>
303
 * @author Christopher Smith <[email protected]>
304
 * @author Patrick Brown <[email protected]>
305
 *
306
 * @param string $file     filename
307
 * @param string $oldline  exact linematch to remove
308
 * @param string $newline  new line to insert
309
 * @param bool   $regex    use regexp?
310
 * @param int    $maxlines number of occurrences of the line to replace
311
 * @return bool true on success
312
 */
313
function io_replaceInFile($file, $oldline, $newline, $regex=false, $maxlines=0) {
314
    if ((string)$oldline === '') {
315
        trigger_error('$oldline parameter cannot be empty in io_replaceInFile()', E_USER_WARNING);
316
        return false;
317
    }
318
319
    if (!file_exists($file)) return true;
320
321
    io_lock($file);
322
323
    // load into array
324
    if(substr($file,-3) == '.gz'){
325
        if(!DOKU_HAS_GZIP) return false;
326
        $lines = gzfile($file);
327
    }else if(substr($file,-4) == '.bz2'){
328
        if(!DOKU_HAS_BZIP) return false;
329
        $lines = bzfile($file, true);
330
    }else{
331
        $lines = file($file);
332
    }
333
334
    // make non-regexes into regexes
335
    $pattern = $regex ? $oldline : '/^'.preg_quote($oldline,'/').'$/';
336
    $replace = $regex ? $newline : addcslashes($newline, '\$');
337
338
    // remove matching lines
339
    if ($maxlines > 0) {
340
        $count = 0;
341
        $matched = 0;
342
        foreach($lines as $i => $line) {
343
            if($count >= $maxlines) break;
344
            // $matched will be set to 0|1 depending on whether pattern is matched and line replaced
345
            $lines[$i] = preg_replace($pattern, $replace, $line, -1, $matched);
346
            if ($matched) $count++;
347
        }
348
    } else if ($maxlines == 0) {
349
        $lines = preg_grep($pattern, $lines, PREG_GREP_INVERT);
350
351
        if ((string)$newline !== ''){
352
            $lines[] = $newline;
353
        }
354
    } else {
355
        $lines = preg_replace($pattern, $replace, $lines);
356
    }
357
358
    if(count($lines)){
359
        if(!_io_saveFile($file, join('',$lines), false)) {
360
            msg("Removing content from $file failed",-1);
361
            io_unlock($file);
362
            return false;
363
        }
364
    }else{
365
        @unlink($file);
366
    }
367
368
    io_unlock($file);
369
    return true;
370
}
371
372
/**
373
 * Delete lines that match $badline from $file.
374
 *
375
 * Be sure to include the trailing newline in $badline
376
 *
377
 * @author Patrick Brown <[email protected]>
378
 *
379
 * @param string $file    filename
380
 * @param string $badline exact linematch to remove
381
 * @param bool   $regex   use regexp?
382
 * @return bool true on success
383
 */
384
function io_deleteFromFile($file,$badline,$regex=false){
385
    return io_replaceInFile($file,$badline,null,$regex,0);
386
}
387
388
/**
389
 * Tries to lock a file
390
 *
391
 * Locking is only done for io_savefile and uses directories
392
 * inside $conf['lockdir']
393
 *
394
 * It waits maximal 3 seconds for the lock, after this time
395
 * the lock is assumed to be stale and the function goes on
396
 *
397
 * @author Andreas Gohr <[email protected]>
398
 *
399
 * @param string $file filename
400
 */
401
function io_lock($file){
402
    global $conf;
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 <[email protected]>
424
 *
425
 * @param string $file filename
426
 */
427
function io_unlock($file){
428
    global $conf;
429
430
    $lockDir = $conf['lockdir'].'/'.md5($file);
431
    @rmdir($lockDir);
432
    @ignore_user_abort(0);
433
}
434
435
/**
436
 * Create missing namespace directories and send the IO_NAMESPACE_CREATED events
437
 * in the order of directory creation. (Parent directories first.)
438
 *
439
 * Event data:
440
 * $data[0]    ns: The colon separated namespace path minus the trailing page name.
441
 * $data[1]    ns_type: 'pages' or 'media' namespace tree.
442
 *
443
 * @author Ben Coburn <[email protected]>
444
 *
445
 * @param string $id page id
446
 * @param string $ns_type 'pages' or 'media'
447
 */
448
function io_createNamespace($id, $ns_type='pages') {
449
    // verify ns_type
450
    $types = array('pages'=>'wikiFN', 'media'=>'mediaFN');
451
    if (!isset($types[$ns_type])) {
452
        trigger_error('Bad $ns_type parameter for io_createNamespace().');
453
        return;
454
    }
455
    // make event list
456
    $missing = array();
457
    $ns_stack = explode(':', $id);
458
    $ns = $id;
459
    $tmp = dirname( $file = call_user_func($types[$ns_type], $ns) );
460
    while (!@is_dir($tmp) && !(file_exists($tmp) && !is_dir($tmp))) {
461
        array_pop($ns_stack);
462
        $ns = implode(':', $ns_stack);
463
        if (strlen($ns)==0) { break; }
464
        $missing[] = $ns;
465
        $tmp = dirname(call_user_func($types[$ns_type], $ns));
466
    }
467
    // make directories
468
    io_makeFileDir($file);
469
    // send the events
470
    $missing = array_reverse($missing); // inside out
471
    foreach ($missing as $ns) {
472
        $data = array($ns, $ns_type);
473
        Event::createAndTrigger('IO_NAMESPACE_CREATED', $data);
474
    }
475
}
476
477
/**
478
 * Create the directory needed for the given file
479
 *
480
 * @author  Andreas Gohr <[email protected]>
481
 *
482
 * @param string $file file name
483
 */
484
function io_makeFileDir($file){
485
    $dir = dirname($file);
486
    if(!@is_dir($dir)){
487
        io_mkdir_p($dir) || msg("Creating directory $dir failed",-1);
488
    }
489
}
490
491
/**
492
 * Creates a directory hierachy.
493
 *
494
 * @link    http://php.net/manual/en/function.mkdir.php
495
 * @author  <[email protected]>
496
 * @author  Andreas Gohr <[email protected]>
497
 *
498
 * @param string $target filename
499
 * @return bool|int|string
500
 */
501
function io_mkdir_p($target){
502
    global $conf;
503
    if (@is_dir($target)||empty($target)) return 1; // best case check first
504
    if (file_exists($target) && !is_dir($target)) return 0;
505
    //recursion
506
    if (io_mkdir_p(substr($target,0,strrpos($target,'/')))){
507
        $ret = @mkdir($target,$conf['dmode']); // crawl back up & create dir tree
508
        if($ret && !empty($conf['dperm'])) chmod($target, $conf['dperm']);
509
        return $ret;
510
    }
511
    return 0;
512
}
513
514
/**
515
 * Recursively delete a directory
516
 *
517
 * @author Andreas Gohr <[email protected]>
518
 * @param string $path
519
 * @param bool   $removefiles defaults to false which will delete empty directories only
520
 * @return bool
521
 */
522
function io_rmdir($path, $removefiles = false) {
523
    if(!is_string($path) || $path == "") return false;
524
    if(!file_exists($path)) return true; // it's already gone or was never there, count as success
525
526
    if(is_dir($path) && !is_link($path)) {
527
        $dirs  = array();
528
        $files = array();
529
530
        if(!$dh = @opendir($path)) return false;
531
        while(false !== ($f = readdir($dh))) {
532
            if($f == '..' || $f == '.') continue;
533
534
            // collect dirs and files first
535
            if(is_dir("$path/$f") && !is_link("$path/$f")) {
536
                $dirs[] = "$path/$f";
537
            } else if($removefiles) {
538
                $files[] = "$path/$f";
539
            } else {
540
                return false; // abort when non empty
541
            }
542
543
        }
544
        closedir($dh);
545
546
        // now traverse into  directories first
547
        foreach($dirs as $dir) {
548
            if(!io_rmdir($dir, $removefiles)) return false; // abort on any error
549
        }
550
551
        // now delete files
552
        foreach($files as $file) {
553
            if(!@unlink($file)) return false; //abort on any error
554
        }
555
556
        // remove self
557
        return @rmdir($path);
558
    } else if($removefiles) {
559
        return @unlink($path);
560
    }
561
    return false;
562
}
563
564
/**
565
 * Creates a unique temporary directory and returns
566
 * its path.
567
 *
568
 * @author Michael Klier <[email protected]>
569
 *
570
 * @return false|string path to new directory or false
571
 */
572
function io_mktmpdir() {
573
    global $conf;
574
575
    $base = $conf['tmpdir'];
576
    $dir  = md5(uniqid(mt_rand(), true));
577
    $tmpdir = $base.'/'.$dir;
578
579
    if(io_mkdir_p($tmpdir)) {
580
        return($tmpdir);
581
    } else {
582
        return false;
583
    }
584
}
585
586
/**
587
 * downloads a file from the net and saves it
588
 *
589
 * if $useAttachment is false,
590
 * - $file is the full filename to save the file, incl. path
591
 * - if successful will return true, false otherwise
592
 *
593
 * if $useAttachment is true,
594
 * - $file is the directory where the file should be saved
595
 * - if successful will return the name used for the saved file, false otherwise
596
 *
597
 * @author Andreas Gohr <[email protected]>
598
 * @author Chris Smith <[email protected]>
599
 *
600
 * @param string $url           url to download
601
 * @param string $file          path to file or directory where to save
602
 * @param bool   $useAttachment true: try to use name of download, uses otherwise $defaultName
603
 *                              false: uses $file as path to file
604
 * @param string $defaultName   fallback for if using $useAttachment
605
 * @param int    $maxSize       maximum file size
606
 * @return bool|string          if failed false, otherwise true or the name of the file in the given dir
607
 */
608
function io_download($url,$file,$useAttachment=false,$defaultName='',$maxSize=2097152){
609
    global $conf;
610
    $http = new DokuHTTPClient();
611
    $http->max_bodysize = $maxSize;
612
    $http->timeout = 25; //max. 25 sec
613
    $http->keep_alive = false; // we do single ops here, no need for keep-alive
614
615
    $data = $http->get($url);
616
    if(!$data) return false;
617
618
    $name = '';
619
    if ($useAttachment) {
620
        if (isset($http->resp_headers['content-disposition'])) {
621
            $content_disposition = $http->resp_headers['content-disposition'];
622
            $match=array();
623
            if (is_string($content_disposition) &&
624
                    preg_match('/attachment;\s*filename\s*=\s*"([^"]*)"/i', $content_disposition, $match)) {
625
626
                $name = \dokuwiki\Utf8\PhpString::basename($match[1]);
627
            }
628
629
        }
630
631
        if (!$name) {
632
            if (!$defaultName) return false;
633
            $name = $defaultName;
634
        }
635
636
        $file = $file.$name;
637
    }
638
639
    $fileexists = file_exists($file);
640
    $fp = @fopen($file,"w");
641
    if(!$fp) return false;
642
    fwrite($fp,$data);
643
    fclose($fp);
644
    if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']);
645
    if ($useAttachment) return $name;
646
    return true;
647
}
648
649
/**
650
 * Windows compatible rename
651
 *
652
 * rename() can not overwrite existing files on Windows
653
 * this function will use copy/unlink instead
654
 *
655
 * @param string $from
656
 * @param string $to
657
 * @return bool succes or fail
658
 */
659
function io_rename($from,$to){
660
    global $conf;
661
    if(!@rename($from,$to)){
662
        if(@copy($from,$to)){
663
            if($conf['fperm']) chmod($to, $conf['fperm']);
664
            @unlink($from);
665
            return true;
666
        }
667
        return false;
668
    }
669
    return true;
670
}
671
672
/**
673
 * Runs an external command with input and output pipes.
674
 * Returns the exit code from the process.
675
 *
676
 * @author Tom N Harris <[email protected]>
677
 *
678
 * @param string $cmd
679
 * @param string $input  input pipe
680
 * @param string $output output pipe
681
 * @return int exit code from process
682
 */
683
function io_exec($cmd, $input, &$output){
684
    $descspec = array(
685
            0=>array("pipe","r"),
686
            1=>array("pipe","w"),
687
            2=>array("pipe","w"));
688
    $ph = proc_open($cmd, $descspec, $pipes);
689
    if(!$ph) return -1;
690
    fclose($pipes[2]); // ignore stderr
691
    fwrite($pipes[0], $input);
692
    fclose($pipes[0]);
693
    $output = stream_get_contents($pipes[1]);
694
    fclose($pipes[1]);
695
    return proc_close($ph);
696
}
697
698
/**
699
 * Search a file for matching lines
700
 *
701
 * This is probably not faster than file()+preg_grep() but less
702
 * memory intensive because not the whole file needs to be loaded
703
 * at once.
704
 *
705
 * @author Andreas Gohr <[email protected]>
706
 * @param  string $file    The file to search
707
 * @param  string $pattern PCRE pattern
708
 * @param  int    $max     How many lines to return (0 for all)
709
 * @param  bool   $backref When true returns array with backreferences instead of lines
710
 * @return array matching lines or backref, false on error
711
 */
712
function io_grep($file,$pattern,$max=0,$backref=false){
713
    $fh = @fopen($file,'r');
714
    if(!$fh) return false;
715
    $matches = array();
716
717
    $cnt  = 0;
718
    $line = '';
719
    while (!feof($fh)) {
720
        $line .= fgets($fh, 4096);  // read full line
721
        if(substr($line,-1) != "\n") continue;
722
723
        // check if line matches
724
        if(preg_match($pattern,$line,$match)){
725
            if($backref){
726
                $matches[] = $match;
727
            }else{
728
                $matches[] = $line;
729
            }
730
            $cnt++;
731
        }
732
        if($max && $max == $cnt) break;
733
        $line = '';
734
    }
735
    fclose($fh);
736
    return $matches;
737
}
738
739
740
/**
741
 * Get size of contents of a file, for a compressed file the uncompressed size
742
 * Warning: reading uncompressed size of content of bz-files requires uncompressing
743
 *
744
 * @author  Gerrit Uitslag <[email protected]>
745
 *
746
 * @param string $file filename path to file
747
 * @return int size of file
748
 */
749
function io_getSizeFile($file) {
750
    if (!file_exists($file)) return 0;
751
752
    if(substr($file,-3) == '.gz'){
753
        $fp = @fopen($file, "rb");
754
        if($fp === false) return 0;
755
756
        fseek($fp, -4, SEEK_END);
757
        $buffer = fread($fp, 4);
758
        fclose($fp);
759
        $array = unpack("V", $buffer);
760
        $uncompressedsize = end($array);
761
    }else if(substr($file,-4) == '.bz2'){
762
        if(!DOKU_HAS_BZIP) return 0;
763
764
        $bz = bzopen($file,"r");
765
        if($bz === false) return 0;
766
767
        $uncompressedsize = 0;
768
        while (!feof($bz)) {
769
            //8192 seems to be the maximum buffersize?
770
            $buffer = bzread($bz,8192);
771
            if(($buffer === false) || (bzerrno($bz) !== 0)) {
772
                return 0;
773
            }
774
            $uncompressedsize += strlen($buffer);
775
        }
776
    }else{
777
        $uncompressedsize = filesize($file);
778
    }
779
780
    return $uncompressedsize;
781
 }
782