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