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