]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/FileFinder.php
Add return value
[SourceForge/phpwiki.git] / lib / FileFinder.php
1 <?php
2
3 require_once(dirname(__FILE__) . '/stdlib.php');
4
5 /**
6  * A class for finding files.
7  *
8  * This should really provided by pear. We don't want really to mess around
9  * with all the lousy systems. (WindowsNT, Win95, Mac, ...)
10  * But pear has only System and File, which do nothing.
11  * Anyway, in good PHP style we ignore the rest of the world and try to behave
12  * as on unix only. That means we use / as pathsep in all our constants.
13  */
14 class FileFinder
15 {
16     public $_pathsep, $_path;
17
18     /**
19      * @param $path array A list of directories in which to search for files.
20      */
21     function __construct($path = array())
22     {
23         $this->_pathsep = $this->_get_syspath_separator();
24         if (!isset($this->_path) and $path === false)
25             $path = $this->_get_include_path();
26         $this->_path = $path;
27     }
28
29     /**
30      * Find file.
31      *
32      * @param $file string File to search for.
33      * @param bool $missing_okay
34      * @return string The filename (including path), if found, otherwise false.
35      */
36     function findFile($file, $missing_okay = false)
37     {
38         if ($this->_is_abs($file)) {
39             if (file_exists($file))
40                 return $file;
41         } elseif (($dir = $this->_search_path($file))) {
42             return $dir . $this->_use_path_separator($dir) . $file;
43         }
44         return $missing_okay ? false : $this->_not_found($file);
45     }
46
47     /**
48      * Unify used pathsep character.
49      * Accepts array of paths also.
50      * This might not work on Windows95 or FAT volumes. (not tested)
51      */
52     function slashifyPath($path)
53     {
54         return $this->forcePathSlashes($path, $this->_pathsep);
55     }
56
57     /**
58      * Force using '/' as path seperator.
59      */
60     function forcePathSlashes($path, $sep = '/')
61     {
62         if (is_array($path)) {
63             $result = array();
64             foreach ($path as $dir) {
65                 $result[] = $this->forcePathSlashes($dir, $sep);
66             }
67             return $result;
68         } else {
69             if (isWindows() or $this->_isOtherPathsep()) {
70                 if (isWindows()) $from = "\\";
71                 else $from = "\\";
72                 // PHP is stupid enough to use \\ instead of \
73                 if (isWindows()) {
74                     if (substr($path, 0, 2) != '\\\\')
75                         $path = str_replace('\\\\', '\\', $path);
76                     else // UNC paths
77                         $path = '\\\\' . str_replace('\\\\', '\\', substr($path, 2));
78                 }
79                 return strtr($path, $from, $sep);
80             } else
81                 return $path;
82         }
83     }
84
85     /**
86      * Try to include file.
87      *
88      * If file is found in the path, then the files directory is added
89      * to PHP's include_path (if it's not already there.) Then the
90      * file is include_once()'d.
91      *
92      * @param $file string File to include.
93      * @return bool True if file was successfully included.
94      */
95     function includeOnce($file)
96     {
97         if (($ret = @include_once($file)))
98             return $ret;
99
100         if (!$this->_is_abs($file)) {
101             if (($dir = $this->_search_path($file)) && is_file($dir . $this->_pathsep . $file)) {
102                 $this->_append_to_include_path($dir);
103                 return include_once($file);
104             }
105         }
106         return $this->_not_found($file);
107     }
108
109     function _isOtherPathsep()
110     {
111         return $this->_pathsep != '/';
112     }
113
114     /**
115      * The system-dependent path-separator character.
116      * UNIX,WindowsNT,MacOSX: /
117      * Windows95: \
118      * Mac:       :
119      *
120      * @return string path_separator.
121      */
122     public function _get_syspath_separator()
123     {
124         if (!empty($this->_pathsep)) return $this->_pathsep;
125         elseif (isWindowsNT()) return "/"; // we can safely use '/'
126         elseif (isWindows()) return "\\"; // FAT might use '\'
127         // VMS or LispM is really weird, we ignore it.
128         else return '/';
129     }
130
131     /**
132      * The path-separator character of the given path.
133      * Windows accepts "/" also, but gets confused with mixed path_separators,
134      * e.g "C:\Apache\phpwiki/locale/button"
135      * > dir "C:\Apache\phpwiki/locale/button" =>
136      *       Parameterformat nicht korrekt - "locale"
137      * So if there's any '\' in the path, either fix them to '/' (not in Win95 or FAT?)
138      * or use '\' for ours.
139      *
140      * @param string $path
141      * @return string path_separator.
142      */
143     public function _use_path_separator($path)
144     {
145         if (isWindows95()) {
146             if (empty($path)) return "\\";
147             else return (strchr($path, "\\")) ? "\\" : '/';
148         } else {
149             return $this->_get_syspath_separator();
150         }
151     }
152
153     /**
154      * Determine if path is absolute.
155      *
156      * @param $path string Path.
157      * @return bool True if path is absolute.
158      */
159     public function _is_abs($path)
160     {
161         if (substr($path, 0, 1) == '/') {
162             return true;
163         } elseif (isWindows() and preg_match("/^[a-z]:/i", $path)
164             and (substr($path, 2, 1) == "/" or substr($path, 2, 1) == "\\")
165         ) {
166             return true;
167         } else {
168             return false;
169         }
170     }
171
172     /**
173      * Strip ending '/' or '\' from path.
174      *
175      * @param $path string Path.
176      * @return bool New path (destructive)
177      */
178     public function _strip_last_pathchar(&$path)
179     {
180         if (substr($path, -1) == '/' or substr($path, -1) == "\\")
181             $path = substr($path, 0, -1);
182         return $path;
183     }
184
185     /**
186      * Report a "file not found" error.
187      *
188      * @param $file string Name of missing file.
189      * @return bool false.
190      */
191     private function _not_found($file)
192     {
193         trigger_error(sprintf(_("%s: file not found"), $file), E_USER_ERROR);
194         return false;
195     }
196
197     /**
198      * Search our path for a file.
199      *
200      * @param $file string File to find.
201      * @return string Directory which contains $file, or false.
202      * [5x,44ms]
203      */
204     private function _search_path($file)
205     {
206         foreach ($this->_path as $dir) {
207             // ensure we use the same pathsep
208             if ($this->_isOtherPathsep()) {
209                 $dir = $this->slashifyPath($dir);
210                 $file = $this->slashifyPath($file);
211                 if (file_exists($dir . $this->_pathsep . $file))
212                     return $dir;
213             } elseif (@file_exists($dir . $this->_pathsep . $file))
214                 return $dir;
215         }
216         return false;
217     }
218
219     /**
220      * The system-dependent path-separator character. On UNIX systems,
221      * this character is ':'; on Win32 systems it is ';'.
222      * Fixme:
223      * On Mac it cannot be : because this is the seperator there!
224      *
225      * @return string path_separator.
226      */
227     public function _get_ini_separator()
228     {
229         return isWindows() ? ';' : ':';
230         // return preg_match('/^Windows/', php_uname())
231     }
232
233     /**
234      * Get the value of PHP's include_path.
235      *
236      * @return array Include path.
237      */
238     public function _get_include_path()
239     {
240         if (defined("INCLUDE_PATH"))
241             $path = INCLUDE_PATH;
242         else {
243             $path = @get_cfg_var('include_path'); // FIXME: report warning
244             if (empty($path)) $path = @ini_get('include_path');
245         }
246         if (empty($path))
247             $path = '.';
248         return explode($this->_get_ini_separator(), $this->slashifyPath($path));
249     }
250
251     /**
252      * Add a directory to the end of PHP's include_path.
253      *
254      * The directory is appended only if it is not already listed in
255      * the include_path.
256      *
257      * @param $dir string Directory to add.
258      */
259     public function _append_to_include_path($dir)
260     {
261         $dir = $this->slashifyPath($dir);
262         if (!in_array($dir, $this->_path)) {
263             $this->_path[] = $dir;
264         }
265         /*
266          * Some (buggy) PHP's (notable SourceForge's PHP 4.0.6)
267          * sometimes don't seem to heed their include_path.
268          * I.e. sometimes a file is not found even though it seems to
269          * be in the current include_path. A simple
270          * ini_set('include_path', ini_get('include_path')) seems to
271          * be enough to fix the problem
272          *
273          * This following line should be in the above if-block, but we
274          * put it here, as it seems to work-around the bug.
275          */
276         $GLOBALS['INCLUDE_PATH'] = implode($this->_get_ini_separator(), $this->_path);
277         @ini_set('include_path', $GLOBALS['INCLUDE_PATH']);
278     }
279
280     /**
281      * Add a directory to the front of PHP's include_path.
282      *
283      * The directory is prepended, and removed from the tail if already existing.
284      *
285      * @param $dir string Directory to add.
286      */
287     public function _prepend_to_include_path($dir)
288     {
289         $dir = $this->slashifyPath($dir);
290         // remove duplicates
291         if ($i = array_search($dir, $this->_path) !== false) {
292             array_splice($this->_path, $i, 1);
293         }
294         array_unshift($this->_path, $dir);
295         $GLOBALS['INCLUDE_PATH'] = implode($this->_path, $this->_get_ini_separator());
296         @ini_set('include_path', $GLOBALS['INCLUDE_PATH']);
297     }
298
299     // Return all the possible shortened locale specifiers for the given locale.
300     // Most specific first.
301     // de_DE.iso8859-1@euro => de_DE.iso8859-1, de_DE, de
302     // This code might needed somewhere else also.
303     function locale_versions($lang)
304     {
305         // Try less specific versions of the locale
306         $langs[] = $lang;
307         foreach (array('@', '.', '_') as $sep) {
308             if (($tail = strchr($lang, $sep)))
309                 $langs[] = substr($lang, 0, -strlen($tail));
310         }
311         return $langs;
312     }
313
314     /**
315      * Try to figure out the appropriate value for $LANG.
316      *
317      * @return string The value of $LANG.
318      */
319     public function _get_lang()
320     {
321         if (!empty($GLOBALS['LANG']))
322             return $GLOBALS['LANG'];
323
324         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES') as $var) {
325             $lang = setlocale(constant($var), 0);
326             if (!empty($lang))
327                 return $lang;
328         }
329
330         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES', 'LANG') as $var) {
331             $lang = getenv($var);
332             if (!empty($lang))
333                 return $lang;
334         }
335
336         return "C";
337     }
338 }
339
340 /**
341  * A class for finding PEAR code.
342  *
343  * This is a subclass of FileFinder which searches a standard list of
344  * directories where PEAR code is likely to be installed.
345  *
346  * Example usage:
347  *
348  * <pre>
349  *   $pearFinder = new PearFileFinder;
350  *   $pearFinder->includeOnce('DB.php');
351  * </pre>
352  *
353  * The above code will look for 'DB.php', if found, the directory in
354  * which it was found will be added to PHP's include_path, and the
355  * file will be included. (If the file is not found, and E_USER_ERROR
356  * will be thrown.)
357  */
358 class PearFileFinder
359     extends FileFinder
360 {
361     /**
362      * @param $path array Where to look for PEAR library code.
363      * A good set of defaults is provided, so you can probably leave
364      * this parameter blank.
365      */
366     function __construct($path = array())
367     {
368         parent::__construct(array_merge(
369             $path,
370             array('/usr/share/php',
371                 '/usr/lib/php',
372                 '/usr/local/share/php',
373                 '/usr/local/lib/php',
374                 '/System/Library/PHP',
375                 '/Apache/pear' // Windows
376             )));
377     }
378 }
379
380 /**
381  * Find PhpWiki localized files.
382  *
383  * This is a subclass of FileFinder which searches PHP's include_path
384  * for files. It looks first for "locale/$LANG/$file", then for
385  * "$file".
386  *
387  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
388  * also search under various less specific variations like
389  * "de_DE.iso8859-1", "de_DE" and "de".
390  */
391 class LocalizedFileFinder
392     extends FileFinder
393 {
394     function __construct()
395     {
396         $this->_pathsep = $this->_get_syspath_separator();
397         $include_path = $this->_get_include_path();
398         $path = array();
399
400         $lang = $this->_get_lang();
401         assert(!empty($lang));
402
403         if ($locales = $this->locale_versions($lang)) {
404             foreach ($locales as $lang) {
405                 if ($lang == 'C') $lang = 'en';
406                 foreach ($include_path as $dir) {
407                     $path[] = $this->slashifyPath($dir . "/locale/$lang");
408                 }
409             }
410         }
411         parent::__construct(array_merge($path, $include_path));
412     }
413 }
414
415 /**
416  * Find PhpWiki localized theme buttons.
417  *
418  * This is a subclass of FileFinder which searches PHP's include_path
419  * for files. It looks first for "buttons/$LANG/$file", then for
420  * "$file".
421  *
422  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
423  * also search under various less specific variations like
424  * "de_DE.iso8859-1", "de_DE" and "de".
425  */
426 class LocalizedButtonFinder
427     extends FileFinder
428 {
429     function __construct()
430     {
431         global $WikiTheme;
432         $this->_pathsep = $this->_get_syspath_separator();
433         $include_path = $this->_get_include_path();
434         $path = array();
435
436         $lang = $this->_get_lang();
437         assert(!empty($lang));
438         assert(!empty($WikiTheme));
439
440         if (is_object($WikiTheme)) {
441             $langs = $this->locale_versions($lang);
442             foreach ($langs as $lang) {
443                 if ($lang == 'C') $lang = 'en';
444                 foreach ($include_path as $dir) {
445                     $path[] = $this->slashifyPath($WikiTheme->file("buttons/$lang"));
446                 }
447             }
448         }
449
450         $this->FileFinder(array_merge($path, $include_path));
451     }
452 }
453
454 // Search PHP's include_path to find file or directory.
455 function FindFile($file, $missing_okay = false, $slashify = false)
456 {
457     static $finder;
458     if (!isset($finder)) {
459         $finder = new FileFinder;
460         // remove "/lib" from dirname(__FILE__)
461         $wikidir = preg_replace('/.lib$/', '', dirname(__FILE__));
462         // let the system favor its local pear?
463         $finder->_append_to_include_path(dirname(__FILE__) . "/pear");
464         $finder->_prepend_to_include_path($wikidir);
465         // Don't override existing INCLUDE_PATH config.
466         if (!defined("INCLUDE_PATH"))
467             define("INCLUDE_PATH", implode($finder->_get_ini_separator(), $finder->_path));
468     }
469     $s = $finder->findFile($file, $missing_okay);
470     if ($slashify)
471         $s = $finder->slashifyPath($s);
472     return $s;
473 }
474
475 // Search PHP's include_path to find file or directory.
476 // Searches for "locale/$LANG/$file", then for "$file".
477 function FindLocalizedFile($file, $missing_okay = false, $re_init = false)
478 {
479     static $finder;
480     if ($re_init or !isset($finder))
481         $finder = new LocalizedFileFinder;
482     return $finder->findFile($file, $missing_okay);
483 }
484
485 function FindLocalizedButtonFile($file, $missing_okay = false, $re_init = false)
486 {
487     static $buttonfinder;
488     if ($re_init or !isset($buttonfinder))
489         $buttonfinder = new LocalizedButtonFinder;
490     return $buttonfinder->findFile($file, $missing_okay);
491 }
492
493 /**
494  * Prefixes with PHPWIKI_DIR and slashify.
495  * For example to unify with
496  *   require_once dirname(__FILE__).'/lib/file.php'
497  *   require_once 'lib/file.php' loading style.
498  * Doesn't expand "~" or symlinks yet. truename would be perfect.
499  *
500  * NormalizeLocalFileName("lib/config.php") => /home/user/phpwiki/lib/config.php
501  */
502 function NormalizeLocalFileName($file)
503 {
504     static $finder;
505     if (!isset($finder)) {
506         $finder = new FileFinder;
507     }
508     // remove "/lib" from dirname(__FILE__)
509     if ($finder->_is_abs($file))
510         return $finder->slashifyPath($file);
511     else {
512         if (defined("PHPWIKI_DIR")) $wikidir = PHPWIKI_DIR;
513         else $wikidir = preg_replace('/.lib$/', '', dirname(__FILE__));
514         $wikidir = $finder->_strip_last_pathchar($wikidir);
515         $pathsep = $finder->_use_path_separator($wikidir);
516         return $finder->slashifyPath($wikidir . $pathsep . $file);
517         // return PHPWIKI_DIR . "/" . $file;
518     }
519 }
520
521 /**
522  * Prefixes with DATA_PATH and slashify
523  */
524 function NormalizeWebFileName($file)
525 {
526     static $finder;
527     if (!isset($finder)) {
528         $finder = new FileFinder;
529     }
530     if (defined("DATA_PATH")) {
531         $wikipath = DATA_PATH;
532         $wikipath = $finder->_strip_last_pathchar($wikipath);
533         if (!$file)
534             return $finder->forcePathSlashes($wikipath);
535         else
536             return $finder->forcePathSlashes($wikipath . '/' . $file);
537     } else {
538         return $finder->forcePathSlashes($file);
539     }
540 }
541
542 function isWindows()
543 {
544     static $win;
545     if (isset($win)) return $win;
546     //return preg_match('/^Windows/', php_uname());
547     $win = (substr(PHP_OS, 0, 3) == 'WIN');
548     return $win;
549 }
550
551 function isWindows95()
552 {
553     static $win95;
554     if (isset($win95)) return $win95;
555     $win95 = isWindows() and !isWindowsNT();
556     return $win95;
557 }
558
559 function isWindowsNT()
560 {
561     static $winnt;
562     if (isset($winnt)) return $winnt;
563     // FIXME: Do this using PHP_OS instead of php_uname().
564     // $winnt = (PHP_OS == "WINNT"); // example from http://www.php.net/manual/en/ref.readline.php
565     if (function_usable('php_uname'))
566         $winnt = preg_match('/^Windows NT/', php_uname());
567     else
568         $winnt = false; // FIXME: punt.
569     return $winnt;
570 }
571
572 // Local Variables:
573 // mode: php
574 // tab-width: 8
575 // c-basic-offset: 4
576 // c-hanging-comment-ender-p: nil
577 // indent-tabs-mode: nil
578 // End: