]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/FileFinder.php
Remove PLUGIN_MARKUP_MAP
[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 seperator.
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      * Fixme:
230      * On Mac it cannot be : because this is the seperator there!
231      *
232      * @return string path_separator.
233      */
234     public function _get_ini_separator()
235     {
236         return isWindows() ? ';' : ':';
237         // return preg_match('/^Windows/', php_uname())
238     }
239
240     /**
241      * Get the value of PHP's include_path.
242      *
243      * @return array Include path.
244      */
245     public function _get_include_path()
246     {
247         if (defined("INCLUDE_PATH"))
248             $path = INCLUDE_PATH;
249         else {
250             $path = @get_cfg_var('include_path'); // FIXME: report warning
251             if (empty($path)) $path = @ini_get('include_path');
252         }
253         if (empty($path))
254             $path = '.';
255         return explode($this->_get_ini_separator(), $this->slashifyPath($path));
256     }
257
258     /**
259      * Add a directory to the end of PHP's include_path.
260      *
261      * The directory is appended only if it is not already listed in
262      * the include_path.
263      *
264      * @param $dir string Directory to add.
265      */
266     public function _append_to_include_path($dir)
267     {
268         $dir = $this->slashifyPath($dir);
269         if (!in_array($dir, $this->_path)) {
270             $this->_path[] = $dir;
271         }
272         /*
273          * Some (buggy) PHP's (notable SourceForge's PHP 4.0.6)
274          * sometimes don't seem to heed their include_path.
275          * I.e. sometimes a file is not found even though it seems to
276          * be in the current include_path. A simple
277          * ini_set('include_path', ini_get('include_path')) seems to
278          * be enough to fix the problem
279          *
280          * This following line should be in the above if-block, but we
281          * put it here, as it seems to work-around the bug.
282          */
283         $GLOBALS['INCLUDE_PATH'] = implode($this->_get_ini_separator(), $this->_path);
284         @ini_set('include_path', $GLOBALS['INCLUDE_PATH']);
285     }
286
287     /**
288      * Add a directory to the front of PHP's include_path.
289      *
290      * The directory is prepended, and removed from the tail if already existing.
291      *
292      * @param $dir string Directory to add.
293      */
294     public function _prepend_to_include_path($dir)
295     {
296         $dir = $this->slashifyPath($dir);
297         // remove duplicates
298         if ($i = array_search($dir, $this->_path) !== false) {
299             array_splice($this->_path, $i, 1);
300         }
301         array_unshift($this->_path, $dir);
302         $GLOBALS['INCLUDE_PATH'] = implode($this->_path, $this->_get_ini_separator());
303         @ini_set('include_path', $GLOBALS['INCLUDE_PATH']);
304     }
305
306     // Return all the possible shortened locale specifiers for the given locale.
307     // Most specific first.
308     // de_DE.iso8859-1@euro => de_DE.iso8859-1, de_DE, de
309     // This code might needed somewhere else also.
310     function locale_versions($lang)
311     {
312         // Try less specific versions of the locale
313         $langs[] = $lang;
314         foreach (array('@', '.', '_') as $sep) {
315             if (($tail = strchr($lang, $sep)))
316                 $langs[] = substr($lang, 0, -strlen($tail));
317         }
318         return $langs;
319     }
320
321     /**
322      * Try to figure out the appropriate value for $LANG.
323      *
324      * @return string The value of $LANG.
325      */
326     public static function _get_lang()
327     {
328         if (!empty($GLOBALS['LANG']))
329             return $GLOBALS['LANG'];
330
331         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES') as $var) {
332             $lang = setlocale(constant($var), 0);
333             if (!empty($lang))
334                 return $lang;
335         }
336
337         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES', 'LANG') as $var) {
338             $lang = getenv($var);
339             if (!empty($lang))
340                 return $lang;
341         }
342
343         return "C";
344     }
345 }
346
347 /**
348  * A class for finding PEAR code.
349  *
350  * This is a subclass of FileFinder which searches a standard list of
351  * directories where PEAR code is likely to be installed.
352  *
353  * Example usage:
354  *
355  * <pre>
356  *   $pearFinder = new PearFileFinder;
357  *   $pearFinder->includeOnce('DB.php');
358  * </pre>
359  *
360  * The above code will look for 'DB.php', if found, the directory in
361  * which it was found will be added to PHP's include_path, and the
362  * file will be included. (If the file is not found, and E_USER_ERROR
363  * will be thrown.)
364  */
365 class PearFileFinder
366     extends FileFinder
367 {
368     /**
369      * @param $path array Where to look for PEAR library code.
370      * A good set of defaults is provided, so you can probably leave
371      * this parameter blank.
372      */
373     function __construct($path = array())
374     {
375         parent::__construct(array_merge(
376             $path,
377             array('/usr/share/php',
378                 '/usr/lib/php',
379                 '/usr/local/share/php',
380                 '/usr/local/lib/php',
381                 '/System/Library/PHP',
382                 '/Apache/pear' // Windows
383             )));
384     }
385 }
386
387 /**
388  * Find PhpWiki localized files.
389  *
390  * This is a subclass of FileFinder which searches PHP's include_path
391  * for files. It looks first for "locale/$LANG/$file", then for
392  * "$file".
393  *
394  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
395  * also search under various less specific variations like
396  * "de_DE.iso8859-1", "de_DE" and "de".
397  */
398 class LocalizedFileFinder
399     extends FileFinder
400 {
401     function __construct()
402     {
403         $this->_pathsep = $this->_get_syspath_separator();
404         $include_path = $this->_get_include_path();
405         $path = array();
406
407         $lang = $this->_get_lang();
408         assert(!empty($lang));
409
410         if ($locales = $this->locale_versions($lang)) {
411             foreach ($locales as $lang) {
412                 if ($lang == 'C') $lang = 'en';
413                 foreach ($include_path as $dir) {
414                     $path[] = $this->slashifyPath($dir . "/locale/$lang");
415                 }
416             }
417         }
418         parent::__construct(array_merge($path, $include_path));
419     }
420 }
421
422 /**
423  * Find PhpWiki localized theme buttons.
424  *
425  * This is a subclass of FileFinder which searches PHP's include_path
426  * for files. It looks first for "buttons/$LANG/$file", then for
427  * "$file".
428  *
429  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
430  * also search under various less specific variations like
431  * "de_DE.iso8859-1", "de_DE" and "de".
432  */
433 class LocalizedButtonFinder
434     extends FileFinder
435 {
436     function __construct()
437     {
438         global $WikiTheme;
439         $this->_pathsep = $this->_get_syspath_separator();
440         $include_path = $this->_get_include_path();
441         $path = array();
442
443         $lang = $this->_get_lang();
444         assert(!empty($lang));
445         assert(!empty($WikiTheme));
446
447         if (is_object($WikiTheme)) {
448             $langs = $this->locale_versions($lang);
449             foreach ($langs as $lang) {
450                 if ($lang == 'C') $lang = 'en';
451                 foreach ($include_path as $dir) {
452                     $path[] = $this->slashifyPath($WikiTheme->file("buttons/$lang"));
453                 }
454             }
455         }
456
457         parent::__construct(array_merge($path, $include_path));
458     }
459 }
460
461 // Search PHP's include_path to find file or directory.
462 function FindFile($file, $missing_okay = false, $slashify = false)
463 {
464     static $finder;
465     if (!isset($finder)) {
466         $finder = new FileFinder;
467         // remove "/lib" from dirname(__FILE__)
468         $wikidir = preg_replace('/.lib$/', '', dirname(__FILE__));
469         // let the system favor its local pear?
470         $finder->_append_to_include_path(dirname(__FILE__) . "/pear");
471         $finder->_prepend_to_include_path($wikidir);
472         // Don't override existing INCLUDE_PATH config.
473         if (!defined("INCLUDE_PATH"))
474             define("INCLUDE_PATH", implode($finder->_get_ini_separator(), $finder->_path));
475     }
476     $s = $finder->findFile($file, $missing_okay);
477     if ($slashify)
478         $s = $finder->slashifyPath($s);
479     return $s;
480 }
481
482 // Search PHP's include_path to find file or directory.
483 // Searches for "locale/$LANG/$file", then for "$file".
484 function FindLocalizedFile($file, $missing_okay = false, $re_init = false)
485 {
486     static $finder;
487     if ($re_init or !isset($finder))
488         $finder = new LocalizedFileFinder;
489     return $finder->findFile($file, $missing_okay);
490 }
491
492 function FindLocalizedButtonFile($file, $missing_okay = false, $re_init = false)
493 {
494     static $buttonfinder;
495     if ($re_init or !isset($buttonfinder))
496         $buttonfinder = new LocalizedButtonFinder;
497     return $buttonfinder->findFile($file, $missing_okay);
498 }
499
500 /**
501  * Prefixes with PHPWIKI_DIR and slashify.
502  * For example to unify with
503  *   require_once dirname(__FILE__).'/lib/file.php'
504  *   require_once 'lib/file.php' loading style.
505  * Doesn't expand "~" or symlinks yet. truename would be perfect.
506  *
507  * NormalizeLocalFileName("lib/config.php") => /home/user/phpwiki/lib/config.php
508  */
509 function NormalizeLocalFileName($file)
510 {
511     static $finder;
512     if (!isset($finder)) {
513         $finder = new FileFinder;
514     }
515     // remove "/lib" from dirname(__FILE__)
516     if ($finder->_is_abs($file))
517         return $finder->slashifyPath($file);
518     else {
519         if (defined("PHPWIKI_DIR")) $wikidir = PHPWIKI_DIR;
520         else $wikidir = preg_replace('/.lib$/', '', dirname(__FILE__));
521         $wikidir = $finder->_strip_last_pathchar($wikidir);
522         $pathsep = $finder->_use_path_separator($wikidir);
523         return $finder->slashifyPath($wikidir . $pathsep . $file);
524         // return PHPWIKI_DIR . "/" . $file;
525     }
526 }
527
528 /**
529  * Prefixes with DATA_PATH and slashify
530  */
531 function NormalizeWebFileName($file)
532 {
533     static $finder;
534     if (!isset($finder)) {
535         $finder = new FileFinder;
536     }
537     if (defined("DATA_PATH")) {
538         $wikipath = DATA_PATH;
539         $wikipath = $finder->_strip_last_pathchar($wikipath);
540         if (!$file)
541             return $finder->forcePathSlashes($wikipath);
542         else
543             return $finder->forcePathSlashes($wikipath . '/' . $file);
544     } else {
545         return $finder->forcePathSlashes($file);
546     }
547 }
548
549 function isWindows()
550 {
551     static $win;
552     if (isset($win)) return $win;
553     //return preg_match('/^Windows/', php_uname());
554     $win = (substr(PHP_OS, 0, 3) == 'WIN');
555     return $win;
556 }
557
558 function isWindows95()
559 {
560     static $win95;
561     if (isset($win95)) return $win95;
562     $win95 = isWindows() and !isWindowsNT();
563     return $win95;
564 }
565
566 function isWindowsNT()
567 {
568     static $winnt;
569     if (isset($winnt)) return $winnt;
570     // FIXME: Do this using PHP_OS instead of php_uname().
571     // $winnt = (PHP_OS == "WINNT"); // example from http://www.php.net/manual/en/ref.readline.php
572     if (function_usable('php_uname'))
573         $winnt = preg_match('/^Windows NT/', php_uname());
574     else
575         $winnt = false; // FIXME: punt.
576     return $winnt;
577 }
578
579 // Local Variables:
580 // mode: php
581 // tab-width: 8
582 // c-basic-offset: 4
583 // c-hanging-comment-ender-p: nil
584 // indent-tabs-mode: nil
585 // End: