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