]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/FileFinder.php
fixed line27 error
[SourceForge/phpwiki.git] / lib / FileFinder.php
1 <?php rcs_id('$Id: FileFinder.php,v 1.18 2004-04-26 20:01:21 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     function slashifyPath ($path) {
50         if (is_array($path)) {
51             $result = array();
52             foreach ($path as $dir) { $result[] = $this->slashifyPath($dir); }
53             return $result;
54         } else {
55             if ($this->_isOtherPathsep())
56                 return strtr($path,$this->_use_path_separator($path),'/');
57             else 
58                 return $path;
59         }
60     }
61
62     /**
63      * Try to include file.
64      *
65      * If file is found in the path, then the files directory is added
66      * to PHP's include_path (if it's not already there.) Then the
67      * file is include_once()'d.
68      *
69      * @param $file string File to include.
70      * @return bool True if file was successfully included.
71      */
72     function includeOnce ($file) {
73         if ( ($ret = @include_once($file)) )
74             return $ret;
75
76         if (!$this->_is_abs($file)) {
77             if ( ($dir = $this->_search_path($file)) && is_file("$dir/$file")) {
78                 $this->_append_to_include_path($dir);
79                 return include_once($file);
80             }
81         }
82
83         return $this->_not_found($file);
84     }
85
86     function _isOtherPathsep() {
87         return $this->_pathsep != '/';
88     }
89
90     /**
91      * The system-dependent path-separator character. 
92      * UNIX,WindowsNT,MacOSX: /
93      * Windows95: \
94      * Mac:       :
95      *
96      * @access private
97      * @return string path_separator.
98      */
99     function _get_syspath_separator () {
100         if (!empty($this->_pathsep)) return $this->_pathsep;
101         elseif (isWindowsNT()) return '\\';
102         elseif (isWindows()) return '\\';
103         elseif (isMac()) return ':';    // MacOsX is /
104         // VMS or LispM is really weird, we ignore it.
105         else return '/';
106     }
107
108     /**
109      * The path-separator character of the given path. 
110      * Windows accepts / also, but gets confused with mixed path_separators,
111      * e.g "C:\Apache\phpwiki/locale/button"
112      * > dir C:\Apache\phpwiki/locale/button => 
113      *       Parameterformat nicht korrekt - "locale"
114      * So if there's any \\ in the path, either fix them to / (not in Win95!) 
115      * or use \\ for ours.
116      *
117      * @access private
118      * @return string path_separator.
119      */
120     function _use_path_separator ($path) {
121         if (isWindows95()) {
122             return (strchr($path,'\\')) ? '\\' : '/';
123         } else {
124             return $this->_get_syspath_separator();
125         }
126     }
127
128     /**
129      * Determine if path is absolute.
130      *
131      * @access private
132      * @param $path string Path.
133      * @return bool True if path is absolute. 
134      */
135     function _is_abs($path) {
136         if (ereg('^/', $path)) return true;
137         elseif (isWindows() and (eregi('^[a-z]:[/\\]', $path))) return true;
138         else return false;
139     }
140
141     /**
142      * Report a "file not found" error.
143      *
144      * @access private
145      * @param $file string Name of missing file.
146      * @return bool false.
147      */
148     function _not_found($file) {
149         trigger_error(sprintf(_("%s: file not found"),$file), E_USER_ERROR);
150         return false;
151     }
152
153
154     /**
155      * Search our path for a file.
156      *
157      * @access private
158      * @param $file string File to find.
159      * @return string Directory which contains $file, or false.
160      */
161     function _search_path ($file) {
162         foreach ($this->_path as $dir) {
163             // ensure we use the same pathsep
164             if ($this->_pathsep != '/') {
165                 $dir = $this->slashifyPath($dir);
166                 $file = $this->slashifyPath($file);
167                 if (file_exists($dir . $this->_pathsep . $file))
168                     return $dir;
169             } elseif (@file_exists("$dir/$file"))
170                 return $dir;
171         }
172         return false;
173     }
174
175     /**
176      * The system-dependent path-separator character. On UNIX systems,
177      * this character is ':'; on Win32 systems it is ';'.
178      * Fixme:
179      * On Mac it cannot be : because this is the seperator there!
180      *
181      * @access private
182      * @return string path_separator.
183      */
184     function _get_ini_separator () {
185         return isWindows() ? ';' : ':';
186         // return preg_match('/^Windows/', php_uname()) 
187     }
188
189     /**
190      * Get the value of PHP's include_path.
191      *
192      * @access private
193      * @return array Include path.
194      */
195     function _get_include_path() {
196         if (defined("INCLUDE_PATH"))
197             $path = INCLUDE_PATH;
198         else
199             $path = @get_cfg_var('include_path'); // FIXME: report warning
200         if (empty($path))
201             $path = '.';
202         return explode($this->_get_ini_separator(), $this->slashifyPath($path));
203     }
204
205     /**
206      * Add a directory to the end of PHP's include_path.
207      *
208      * The directory is appended only if it is not already listed in
209      * the include_path.
210      *
211      * @access private
212      * @param $dir string Directory to add.
213      */
214     function _append_to_include_path ($dir) {
215         $dir = $this->slashifyPath($dir);
216         if (!in_array($dir, $this->_path)) {
217             $this->_path[] = $dir;
218             //ini_set('include_path', implode(':', $path));
219         }
220         /*
221          * Some (buggy) PHP's (notable SourceForge's PHP 4.0.6)
222          * sometimes don't seem to heed their include_path.
223          * I.e. sometimes a file is not found even though it seems to
224          * be in the current include_path. A simple
225          * ini_set('include_path', ini_get('include_path')) seems to
226          * be enough to fix the problem
227          *
228          * This following line should be in the above if-block, but we
229          * put it here, as it seems to work-around the bug.
230          */
231         ini_set('include_path', implode($this->_get_ini_separator(), $this->_path));
232     }
233
234     // Return all the possible shortened locale specifiers for the given locale.
235     // Most specific first.
236     // de_DE.iso8859-1@euro => de_DE.iso8859-1, de_DE, de
237     // This code might needed somewhere else also.
238     function locale_versions ($lang) {
239         // Try less specific versions of the locale
240         $langs[] = $lang;
241         foreach (array('@', '.', '_') as $sep) {
242             if ( ($tail = strchr($lang, $sep)) )
243                 $langs[] = substr($lang, 0, -strlen($tail));
244         }
245         return $langs;
246     }
247
248     /**
249      * Try to figure out the appropriate value for $LANG.
250      *
251      *@access private
252      *@return string The value of $LANG.
253      */
254     function _get_lang() {
255         if (!empty($GLOBALS['LANG']))
256             return $GLOBALS['LANG'];
257
258         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES') as $var) {
259             $lang = setlocale(constant($var), 0);
260             if (!empty($lang))
261                 return $lang;
262         }
263             
264         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES', 'LANG') as $var) {
265             $lang = getenv($var);
266             if (!empty($lang))
267                 return $lang;
268         }
269
270         return "C";
271     }
272 }
273
274 /**
275  * A class for finding PEAR code.
276  *
277  * This is a subclass of FileFinder which searches a standard list of
278  * directories where PEAR code is likely to be installed.
279  *
280  * Example usage:
281  *
282  * <pre>
283  *   $pearFinder = new PearFileFinder;
284  *   $pearFinder->includeOnce('DB.php');
285  * </pre>
286  *
287  * The above code will look for 'DB.php', if found, the directory in
288  * which it was found will be added to PHP's include_path, and the
289  * file will be included. (If the file is not found, and E_USER_ERROR
290  * will be thrown.)
291  */
292 class PearFileFinder
293     extends FileFinder
294 {
295     /**
296      * Constructor.
297      *
298      * @param $path array Where to look for PEAR library code.
299      * A good set of defaults is provided, so you can probably leave
300      * this parameter blank.
301      */
302     function PearFileFinder ($path = array()) {
303         $this->FileFinder(array_merge(
304                           $path,
305                           array('/usr/share/php4',
306                                 '/usr/share/php',
307                                 '/usr/lib/php4',
308                                 '/usr/lib/php',
309                                 '/usr/local/share/php4',
310                                 '/usr/local/share/php',
311                                 '/usr/local/lib/php4',
312                                 '/usr/local/lib/php',
313                                 '/System/Library/PHP',
314                                 '/Apache/pear'        // Windows
315                                 )));
316     }
317 }
318
319 /**
320  * Find PhpWiki localized files.
321  *
322  * This is a subclass of FileFinder which searches PHP's include_path
323  * for files. It looks first for "locale/$LANG/$file", then for
324  * "$file".
325  *
326  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
327  * also search under various less specific variations like
328  * "de_DE.iso8859-1", "de_DE" and "de".
329  */
330 class LocalizedFileFinder
331     extends FileFinder
332 {
333     /**
334      * Constructor.
335      */
336     function LocalizedFileFinder () {
337         $this->_pathsep = $this->_get_syspath_separator();
338         $include_path = $this->_get_include_path();
339         $path = array();
340
341         $lang = $this->_get_lang();
342         assert(!empty($lang));
343
344         if ($locales = $this->locale_versions($lang))
345           foreach ($locales as $lang) {
346             if ($lang == 'C') $lang='en';
347             foreach ($include_path as $dir) {
348                 $path[] = $this->slashifyPath($dir) . "/locale/$lang";
349             }
350           }
351         $this->FileFinder(array_merge($path, $include_path));
352     }
353 }
354
355 /**
356  * Find PhpWiki localized theme buttons.
357  *
358  * This is a subclass of FileFinder which searches PHP's include_path
359  * for files. It looks first for "buttons/$LANG/$file", then for
360  * "$file".
361  *
362  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
363  * also search under various less specific variations like
364  * "de_DE.iso8859-1", "de_DE" and "de".
365  */
366 class LocalizedButtonFinder
367     extends FileFinder
368 {
369     /**
370      * Constructor.
371      */
372     function LocalizedButtonFinder () {
373         global $Theme;
374         $this->_pathsep = $this->_get_syspath_separator();
375         $include_path = $this->_get_include_path();
376         $path = array();
377
378         $lang = $this->_get_lang();
379         assert(!empty($lang));
380         assert(!empty($Theme));
381
382         $langs = $this->locale_versions($lang);
383
384         foreach ($langs as $lang) {
385             if ($lang == 'C') $lang='en';
386             foreach ($include_path as $dir) {
387                 $path[] = $Theme->file("buttons/$lang");
388             }
389         }
390
391         $this->FileFinder(array_merge($path, $include_path));
392     }
393 }
394
395 // Search PHP's include_path to find file or directory.
396 function FindFile ($file, $missing_okay = false, $slashify = false)
397 {
398     static $finder;
399     if (!isset($finder)) {
400         $finder = new FileFinder;
401         // remove "/lib" from dirname(__FILE__)
402         $wikidir = preg_replace('/.lib$/','',dirname(__FILE__));
403         $finder->_append_to_include_path($wikidir);
404         $finder->_append_to_include_path(dirname(__FILE__)."/pear");
405         define("INCLUDE_PATH",implode($finder->_get_ini_separator(), $finder->_path));
406     }
407     $s = $finder->findFile($file, $missing_okay);
408     if ($slashify)
409       $s = $finder->slashifyPath($s);
410     return $s;
411 }
412
413 // Search PHP's include_path to find file or directory.
414 // Searches for "locale/$LANG/$file", then for "$file".
415 function FindLocalizedFile ($file, $missing_okay = false, $re_init = false)
416 {
417     static $finder;
418     if ($re_init or !isset($finder))
419         $finder = new LocalizedFileFinder;
420     return $finder->findFile($file, $missing_okay);
421 }
422
423 function FindLocalizedButtonFile ($file, $missing_okay = false, $re_init = false)
424 {
425     static $buttonfinder;
426     if ($re_init or !isset($buttonfinder))
427         $buttonfinder = new LocalizedButtonFinder;
428     return $buttonfinder->findFile($file, $missing_okay);
429 }
430
431 function isWindows() {
432     static $win;
433     if (isset($win)) return $win;
434     //return preg_match('/^Windows/', php_uname());
435     $win = (substr(PHP_OS,0,3) == 'WIN');
436     return $win;
437 }
438
439 function isWindows95() {
440     static $win95;
441     if (isset($win95)) return $win95;
442     $win95 = isWindows() and !isWindowsNT();
443     return $win95;
444 }
445
446 function isWindowsNT() {
447     static $winnt;
448     if (isset($winnt)) return $winnt;
449     // FIXME: Do this using PHP_OS instead of php_uname().
450     // $winnt = (PHP_OS == "WINNT"); // example from http://www.php.net/manual/en/ref.readline.php
451     if (function_usable('php_uname'))
452         $winnt = preg_match('/^Windows NT/', php_uname());
453     else
454         $winnt = false;         // FIXME: punt.
455     return $winnt;
456 }
457
458 // So far not supported. This has really ugly pathname semantics.
459 // :path is relative, Desktop:path (I think) is absolute. Please fix this someone.
460 function isMac() {
461     return (substr(PHP_OS,0,3) == 'MAC'); // not tested!
462 }
463
464 // probably not needed, same behaviour as on unix.
465 function isCygwin() {
466     return (substr(PHP_OS,0,6) == 'CYGWIN');
467 }
468
469 // Local Variables:
470 // mode: php
471 // tab-width: 8
472 // c-basic-offset: 4
473 // c-hanging-comment-ender-p: nil
474 // indent-tabs-mode: nil
475 // End:
476 ?>