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