]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/FileFinder.php
Valid XHTML code for all themes
[SourceForge/phpwiki.git] / lib / FileFinder.php
1 <?php // rcs_id('$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 (isMac()) $from = ":";
67                 elseif (isWindows()) $from = "\\";
68                 else $from = "\\";
69                 // PHP is stupid enough to use \\ instead of \
70                 if (isWindows()) {
71                     if (substr($path,0,2) != '\\\\')
72                         $path = str_replace('\\\\','\\',$path);
73                     else // UNC paths
74                         $path = '\\\\' . str_replace('\\\\','\\',substr($path,2));
75                 }
76                 return strtr($path, $from, $sep);
77             } else 
78                 return $path;
79         }
80     }
81
82     /**
83      * Try to include file.
84      *
85      * If file is found in the path, then the files directory is added
86      * to PHP's include_path (if it's not already there.) Then the
87      * file is include_once()'d.
88      *
89      * @param $file string File to include.
90      * @return bool True if file was successfully included.
91      */
92     function includeOnce ($file) {
93         if ( ($ret = @include_once($file)) )
94             return $ret;
95
96         if (!$this->_is_abs($file)) {
97             if ( ($dir = $this->_search_path($file)) && is_file($dir . $this->_pathsep . $file)) {
98                 $this->_append_to_include_path($dir);
99                 return include_once($file);
100             }
101         }
102         return $this->_not_found($file);
103     }
104
105     function _isOtherPathsep() {
106         return $this->_pathsep != '/';
107     }
108
109     /**
110      * The system-dependent path-separator character. 
111      * UNIX,WindowsNT,MacOSX: /
112      * Windows95: \
113      * Mac:       :
114      *
115      * @access private
116      * @return string path_separator.
117      */
118     function _get_syspath_separator () {
119         if (!empty($this->_pathsep)) return $this->_pathsep;
120         elseif (isWindowsNT()) return "/"; // we can safely use '/'
121         elseif (isWindows()) return "\\";  // FAT might use '\'
122         elseif (isMac()) return ':';    // MacOsX is /
123         // VMS or LispM is really weird, we ignore it.
124         else return '/';
125     }
126
127     /**
128      * The path-separator character of the given path. 
129      * Windows accepts "/" also, but gets confused with mixed path_separators,
130      * e.g "C:\Apache\phpwiki/locale/button"
131      * > dir "C:\Apache\phpwiki/locale/button" => 
132      *       Parameterformat nicht korrekt - "locale"
133      * So if there's any '\' in the path, either fix them to '/' (not in Win95 or FAT?) 
134      * or use '\' for ours.
135      *
136      * @access private
137      * @return string path_separator.
138      */
139     function _use_path_separator ($path) {
140         if (isWindows95()) {
141             if (empty($path)) return "\\";
142             else return (strchr($path,"\\")) ? "\\" : '/';
143         } elseif (isMac()) {
144             if (empty($path)) return ":";
145             else return (strchr($path,":")) ? ":" : '/';
146         } else {
147             return $this->_get_syspath_separator();
148         }
149     }
150
151     /**
152      * Determine if path is absolute.
153      *
154      * @access private
155      * @param $path string Path.
156      * @return bool True if path is absolute. 
157      */
158     function _is_abs($path) {
159         if (substr($path,0,1) == '/') {
160             return true;
161         } elseif (isWindows() and preg_match("/^[a-z]:/i", $path)
162                   and (substr($path,3,1) == "/" or substr($path,3,1) == "\\")) 
163         {
164             return true;
165         } else {
166             return false;
167         }
168     }
169
170     /**
171      * Strip ending '/' or '\' from path.
172      *
173      * @access private
174      * @param $path string Path.
175      * @return bool New path (destructive)
176      */
177     function _strip_last_pathchar(&$path) {
178         if (isMac()) {
179             if (substr($path,-1) == ':' or substr($path,-1) == "/") 
180                 $path = substr($path,0,-1);
181         } else {
182             if (substr($path,-1) == '/' or substr($path,-1) == "\\") 
183                 $path = substr($path,0,-1);
184         }
185         return $path;
186     }
187
188     /**
189      * Report a "file not found" error.
190      *
191      * @access private
192      * @param $file string Name of missing file.
193      * @return bool false.
194      */
195     function _not_found($file) {
196         if (function_exists("_"))
197             trigger_error(sprintf(_("%s: file not found"), $file), E_USER_ERROR);
198         else
199             trigger_error(sprintf("%s: file not found", $file), E_USER_ERROR);
200         return false;
201     }
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         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      * @access private
233      * @return string path_separator.
234      */
235     function _get_ini_separator () {
236         return isWindows() ? ';' : ':';
237         // return preg_match('/^Windows/', php_uname()) 
238     }
239
240     /**
241      * Get the value of PHP's include_path.
242      *
243      * @access private
244      * @return array Include path.
245      */
246     function _get_include_path() {
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      * @access private
265      * @param $dir string Directory to add.
266      */
267     function _append_to_include_path ($dir) {
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      * @access private
293      * @param $dir string Directory to add.
294      */
295     function _prepend_to_include_path ($dir) {
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         // Try less specific versions of the locale
312         $langs[] = $lang;
313         foreach (array('@', '.', '_') as $sep) {
314             if ( ($tail = strchr($lang, $sep)) )
315                 $langs[] = substr($lang, 0, -strlen($tail));
316         }
317         return $langs;
318     }
319
320     /**
321      * Try to figure out the appropriate value for $LANG.
322      *
323      *@access private
324      *@return string The value of $LANG.
325      */
326     function _get_lang() {
327         if (!empty($GLOBALS['LANG']))
328             return $GLOBALS['LANG'];
329
330         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES') as $var) {
331             $lang = setlocale(constant($var), 0);
332             if (!empty($lang))
333                 return $lang;
334         }
335             
336         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES', 'LANG') as $var) {
337             $lang = getenv($var);
338             if (!empty($lang))
339                 return $lang;
340         }
341
342         return "C";
343     }
344 }
345
346 /**
347  * A class for finding PEAR code.
348  *
349  * This is a subclass of FileFinder which searches a standard list of
350  * directories where PEAR code is likely to be installed.
351  *
352  * Example usage:
353  *
354  * <pre>
355  *   $pearFinder = new PearFileFinder;
356  *   $pearFinder->includeOnce('DB.php');
357  * </pre>
358  *
359  * The above code will look for 'DB.php', if found, the directory in
360  * which it was found will be added to PHP's include_path, and the
361  * file will be included. (If the file is not found, and E_USER_ERROR
362  * will be thrown.)
363  */
364 class PearFileFinder
365     extends FileFinder
366 {
367     /**
368      * Constructor.
369      *
370      * @param $path array Where to look for PEAR library code.
371      * A good set of defaults is provided, so you can probably leave
372      * this parameter blank.
373      */
374     function PearFileFinder ($path = array()) {
375         $this->FileFinder(array_merge(
376                           $path,
377                           array('/usr/share/php4',
378                                 '/usr/share/php',
379                                 '/usr/lib/php4',
380                                 '/usr/lib/php',
381                                 '/usr/local/share/php4',
382                                 '/usr/local/share/php',
383                                 '/usr/local/lib/php4',
384                                 '/usr/local/lib/php',
385                                 '/System/Library/PHP',
386                                 '/Apache/pear'        // Windows
387                                 )));
388     }
389 }
390
391 /**
392  * Find PhpWiki localized files.
393  *
394  * This is a subclass of FileFinder which searches PHP's include_path
395  * for files. It looks first for "locale/$LANG/$file", then for
396  * "$file".
397  *
398  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
399  * also search under various less specific variations like
400  * "de_DE.iso8859-1", "de_DE" and "de".
401  */
402 class LocalizedFileFinder
403 extends FileFinder
404 {
405     /**
406      * Constructor.
407      */
408     function LocalizedFileFinder () {
409         $this->_pathsep = $this->_get_syspath_separator();
410         $include_path = $this->_get_include_path();
411         $path = array();
412
413         $lang = $this->_get_lang();
414         assert(!empty($lang));
415
416         if ($locales = $this->locale_versions($lang)) {
417             foreach ($locales as $lang) {
418                 if ($lang == 'C') $lang = 'en';
419                 foreach ($include_path as $dir) {
420                     $path[] = $this->slashifyPath($dir . "/locale/$lang");
421                 }
422             }
423         }
424         $this->FileFinder(array_merge($path, $include_path));
425     }
426 }
427
428 /**
429  * Find PhpWiki localized theme buttons.
430  *
431  * This is a subclass of FileFinder which searches PHP's include_path
432  * for files. It looks first for "buttons/$LANG/$file", then for
433  * "$file".
434  *
435  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
436  * also search under various less specific variations like
437  * "de_DE.iso8859-1", "de_DE" and "de".
438  */
439 class LocalizedButtonFinder
440 extends FileFinder
441 {
442     /**
443      * Constructor.
444      */
445     function LocalizedButtonFinder () {
446         global $WikiTheme;
447         $this->_pathsep = $this->_get_syspath_separator();
448         $include_path = $this->_get_include_path();
449         $path = array();
450
451         $lang = $this->_get_lang();
452         assert(!empty($lang));
453         assert(!empty($WikiTheme));
454
455         if (is_object($WikiTheme)) {
456             $langs = $this->locale_versions($lang);
457             foreach ($langs as $lang) {
458                 if ($lang == 'C') $lang = 'en';
459                 foreach ($include_path as $dir) {
460                     $path[] = $this->slashifyPath($WikiTheme->file("buttons/$lang"));
461                 }
462             }
463         }
464
465         $this->FileFinder(array_merge($path, $include_path));
466     }
467 }
468
469 // Search PHP's include_path to find file or directory.
470 function FindFile ($file, $missing_okay = false, $slashify = false)
471 {
472     static $finder;
473     if (!isset($finder)) {
474         $finder = new FileFinder;
475         // remove "/lib" from dirname(__FILE__)
476         $wikidir = preg_replace('/.lib$/','',dirname(__FILE__));
477         // let the system favor its local pear?
478         $finder->_append_to_include_path(dirname(__FILE__)."/pear");
479         $finder->_prepend_to_include_path($wikidir);
480         // Don't override existing INCLUDE_PATH config.
481         if (!defined("INCLUDE_PATH"))
482             define("INCLUDE_PATH", implode($finder->_get_ini_separator(), $finder->_path));
483     }
484     $s = $finder->findFile($file, $missing_okay);
485     if ($slashify)
486         $s = $finder->slashifyPath($s);
487     return $s;
488 }
489
490 // Search PHP's include_path to find file or directory.
491 // Searches for "locale/$LANG/$file", then for "$file".
492 function FindLocalizedFile ($file, $missing_okay = false, $re_init = false)
493 {
494     static $finder;
495     if ($re_init or !isset($finder))
496         $finder = new LocalizedFileFinder;
497     return $finder->findFile($file, $missing_okay);
498 }
499
500 function FindLocalizedButtonFile ($file, $missing_okay = false, $re_init = false)
501 {
502     static $buttonfinder;
503     if ($re_init or !isset($buttonfinder))
504         $buttonfinder = new LocalizedButtonFinder;
505     return $buttonfinder->findFile($file, $missing_okay);
506 }
507
508 /** 
509  * Prefixes with PHPWIKI_DIR and slashify.
510  * For example to unify with 
511  *   require_once dirname(__FILE__).'/lib/file.php'
512  *   require_once 'lib/file.php' loading style.
513  * Doesn't expand "~" or symlinks yet. truename would be perfect.
514  *
515  * NormalizeLocalFileName("lib/config.php") => /home/user/phpwiki/lib/config.php
516  */
517 function NormalizeLocalFileName($file) {
518     static $finder;
519     if (!isset($finder)) {
520         $finder = new FileFinder;
521     }
522     // remove "/lib" from dirname(__FILE__)
523     if ($finder->_is_abs($file))
524         return $finder->slashifyPath($file);
525     else {
526         if (defined("PHPWIKI_DIR")) $wikidir = PHPWIKI_DIR;
527         else $wikidir = preg_replace('/.lib$/','',dirname(__FILE__));
528         $wikidir = $finder->_strip_last_pathchar($wikidir);
529         $pathsep = $finder->_use_path_separator($wikidir);
530         return $finder->slashifyPath($wikidir . $pathsep . $file);
531         // return PHPWIKI_DIR . "/" . $file;
532     }
533 }
534
535 /** 
536  * Prefixes with DATA_PATH and slashify
537  */
538 function NormalizeWebFileName($file) {
539     static $finder;
540     if (!isset($finder)) {
541         $finder = new FileFinder;
542     }
543     if (defined("DATA_PATH")) {
544         $wikipath = DATA_PATH;
545         $wikipath = $finder->_strip_last_pathchar($wikipath);
546         if (!$file)
547             return $finder->forcePathSlashes($wikipath);
548         else
549             return $finder->forcePathSlashes($wikipath . '/' . $file);
550     } else {
551         return $finder->forcePathSlashes($file);
552     }
553 }
554
555 function isWindows() {
556     static $win;
557     if (isset($win)) return $win;
558     //return preg_match('/^Windows/', php_uname());
559     $win = (substr(PHP_OS,0,3) == 'WIN');
560     return $win;
561 }
562
563 function isWindows95() {
564     static $win95;
565     if (isset($win95)) return $win95;
566     $win95 = isWindows() and !isWindowsNT();
567     return $win95;
568 }
569
570 function isWindowsNT() {
571     static $winnt;
572     if (isset($winnt)) return $winnt;
573     // FIXME: Do this using PHP_OS instead of php_uname().
574     // $winnt = (PHP_OS == "WINNT"); // example from http://www.php.net/manual/en/ref.readline.php
575     if (function_usable('php_uname'))
576         $winnt = preg_match('/^Windows NT/', php_uname());
577     else
578         $winnt = false;         // FIXME: punt.
579     return $winnt;
580 }
581
582 /** 
583  * This is for the OLD Macintosh OS, NOT MacOSX or Darwin!
584  * This has really ugly pathname semantics.
585  * ":path" is relative, "Desktop:path" (I think) is absolute. 
586  * FIXME: Please fix this someone. So far not supported.
587  */
588 function isMac() {
589     return (substr(PHP_OS,0,9) == 'Macintosh'); // not tested!
590 }
591
592 // probably not needed, same behaviour as on unix.
593 function isCygwin() {
594     return (substr(PHP_OS,0,6) == 'CYGWIN');
595 }
596
597 // Local Variables:
598 // mode: php
599 // tab-width: 8
600 // c-basic-offset: 4
601 // c-hanging-comment-ender-p: nil
602 // indent-tabs-mode: nil
603 // End:
604 ?>