]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/FileFinder.php
* revert to the wikidb ref passing. there's no memory abuse there.
[SourceForge/phpwiki.git] / lib / FileFinder.php
1 <?php rcs_id('$Id: FileFinder.php,v 1.29 2004-11-09 17:11:03 rurban Exp $');
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, VMS, ...)
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 (ereg('^/', $path)) return true;
160         elseif (isWindows() and (eregi('^[a-z]:[/\\]', $path))) return true;
161         else return false;
162     }
163
164     /**
165      * Strip ending '/' or '\' from path.
166      *
167      * @access private
168      * @param $path string Path.
169      * @return bool New path (destructive)
170      */
171     function _strip_last_pathchar(&$path) {
172         if (isMac()) {
173             if (substr($path,-1) == ':' or substr($path,-1) == "/") 
174                 $path = substr($path,0,-1);
175         } else {
176             if (substr($path,-1) == '/' or substr($path,-1) == "\\") 
177                 $path = substr($path,0,-1);
178         }
179         return $path;
180     }
181
182     /**
183      * Report a "file not found" error.
184      *
185      * @access private
186      * @param $file string Name of missing file.
187      * @return bool false.
188      */
189     function _not_found($file) {
190         if (function_exists("_"))
191             trigger_error(sprintf(_("%s: file not found"), $file), E_USER_ERROR);
192         else
193             trigger_error(sprintf("%s: file not found", $file), E_USER_ERROR);
194         return false;
195     }
196
197
198     /**
199      * Search our path for a file.
200      *
201      * @access private
202      * @param $file string File to find.
203      * @return string Directory which contains $file, or false.
204      * [5x,44ms]
205      */
206     function _search_path ($file) {
207         foreach ($this->_path as $dir) {
208             // ensure we use the same pathsep
209             if ($this->_isOtherPathsep()) {
210                 $dir = $this->slashifyPath($dir);
211                 $file = $this->slashifyPath($file);
212                 if (file_exists($dir . $this->_pathsep . $file))
213                     return $dir;
214             } elseif (@file_exists($dir . $this->_pathsep . $file))
215                 return $dir;
216         }
217         return false;
218     }
219
220     /**
221      * The system-dependent path-separator character. On UNIX systems,
222      * this character is ':'; on Win32 systems it is ';'.
223      * Fixme:
224      * On Mac it cannot be : because this is the seperator there!
225      *
226      * @access private
227      * @return string path_separator.
228      */
229     function _get_ini_separator () {
230         return isWindows() ? ';' : ':';
231         // return preg_match('/^Windows/', php_uname()) 
232     }
233
234     /**
235      * Get the value of PHP's include_path.
236      *
237      * @access private
238      * @return array Include path.
239      */
240     function _get_include_path() {
241         if (defined("INCLUDE_PATH"))
242             $path = INCLUDE_PATH;
243         else {
244             $path = @get_cfg_var('include_path'); // FIXME: report warning
245             if (empty($path)) $path = @ini_get('include_path');
246         }
247         if (empty($path))
248             $path = '.';
249         return explode($this->_get_ini_separator(), $this->slashifyPath($path));
250     }
251
252     /**
253      * Add a directory to the end of PHP's include_path.
254      *
255      * The directory is appended only if it is not already listed in
256      * the include_path.
257      *
258      * @access private
259      * @param $dir string Directory to add.
260      */
261     function _append_to_include_path ($dir) {
262         $dir = $this->slashifyPath($dir);
263         if (!in_array($dir, $this->_path)) {
264             $this->_path[] = $dir;
265             //ini_set('include_path', implode(':', $path));
266         }
267         /*
268          * Some (buggy) PHP's (notable SourceForge's PHP 4.0.6)
269          * sometimes don't seem to heed their include_path.
270          * I.e. sometimes a file is not found even though it seems to
271          * be in the current include_path. A simple
272          * ini_set('include_path', ini_get('include_path')) seems to
273          * be enough to fix the problem
274          *
275          * This following line should be in the above if-block, but we
276          * put it here, as it seems to work-around the bug.
277          */
278         ini_set('include_path', implode($this->_get_ini_separator(), $this->_path));
279     }
280
281     // Return all the possible shortened locale specifiers for the given locale.
282     // Most specific first.
283     // de_DE.iso8859-1@euro => de_DE.iso8859-1, de_DE, de
284     // This code might needed somewhere else also.
285     function locale_versions ($lang) {
286         // Try less specific versions of the locale
287         $langs[] = $lang;
288         foreach (array('@', '.', '_') as $sep) {
289             if ( ($tail = strchr($lang, $sep)) )
290                 $langs[] = substr($lang, 0, -strlen($tail));
291         }
292         return $langs;
293     }
294
295     /**
296      * Try to figure out the appropriate value for $LANG.
297      *
298      *@access private
299      *@return string The value of $LANG.
300      */
301     function _get_lang() {
302         if (!empty($GLOBALS['LANG']))
303             return $GLOBALS['LANG'];
304
305         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES') as $var) {
306             $lang = setlocale(constant($var), 0);
307             if (!empty($lang))
308                 return $lang;
309         }
310             
311         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES', 'LANG') as $var) {
312             $lang = getenv($var);
313             if (!empty($lang))
314                 return $lang;
315         }
316
317         return "C";
318     }
319 }
320
321 /**
322  * A class for finding PEAR code.
323  *
324  * This is a subclass of FileFinder which searches a standard list of
325  * directories where PEAR code is likely to be installed.
326  *
327  * Example usage:
328  *
329  * <pre>
330  *   $pearFinder = new PearFileFinder;
331  *   $pearFinder->includeOnce('DB.php');
332  * </pre>
333  *
334  * The above code will look for 'DB.php', if found, the directory in
335  * which it was found will be added to PHP's include_path, and the
336  * file will be included. (If the file is not found, and E_USER_ERROR
337  * will be thrown.)
338  */
339 class PearFileFinder
340     extends FileFinder
341 {
342     /**
343      * Constructor.
344      *
345      * @param $path array Where to look for PEAR library code.
346      * A good set of defaults is provided, so you can probably leave
347      * this parameter blank.
348      */
349     function PearFileFinder ($path = array()) {
350         $this->FileFinder(array_merge(
351                           $path,
352                           array('/usr/share/php4',
353                                 '/usr/share/php',
354                                 '/usr/lib/php4',
355                                 '/usr/lib/php',
356                                 '/usr/local/share/php4',
357                                 '/usr/local/share/php',
358                                 '/usr/local/lib/php4',
359                                 '/usr/local/lib/php',
360                                 '/System/Library/PHP',
361                                 '/Apache/pear'        // Windows
362                                 )));
363     }
364 }
365
366 /**
367  * Find PhpWiki localized files.
368  *
369  * This is a subclass of FileFinder which searches PHP's include_path
370  * for files. It looks first for "locale/$LANG/$file", then for
371  * "$file".
372  *
373  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
374  * also search under various less specific variations like
375  * "de_DE.iso8859-1", "de_DE" and "de".
376  */
377 class LocalizedFileFinder
378 extends FileFinder
379 {
380     /**
381      * Constructor.
382      */
383     function LocalizedFileFinder () {
384         $this->_pathsep = $this->_get_syspath_separator();
385         $include_path = $this->_get_include_path();
386         $path = array();
387
388         $lang = $this->_get_lang();
389         assert(!empty($lang));
390
391         if ($locales = $this->locale_versions($lang)) {
392             foreach ($locales as $lang) {
393                 if ($lang == 'C') $lang = 'en';
394                 foreach ($include_path as $dir) {
395                     $path[] = $this->slashifyPath($dir . "/locale/$lang");
396                 }
397             }
398         }
399         $this->FileFinder(array_merge($path, $include_path));
400     }
401 }
402
403 /**
404  * Find PhpWiki localized theme buttons.
405  *
406  * This is a subclass of FileFinder which searches PHP's include_path
407  * for files. It looks first for "buttons/$LANG/$file", then for
408  * "$file".
409  *
410  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
411  * also search under various less specific variations like
412  * "de_DE.iso8859-1", "de_DE" and "de".
413  */
414 class LocalizedButtonFinder
415 extends FileFinder
416 {
417     /**
418      * Constructor.
419      */
420     function LocalizedButtonFinder () {
421         global $WikiTheme;
422         $this->_pathsep = $this->_get_syspath_separator();
423         $include_path = $this->_get_include_path();
424         $path = array();
425
426         $lang = $this->_get_lang();
427         assert(!empty($lang));
428         assert(!empty($WikiTheme));
429
430         if (is_object($WikiTheme)) {
431             $langs = $this->locale_versions($lang);
432             foreach ($langs as $lang) {
433                 if ($lang == 'C') $lang = 'en';
434                 foreach ($include_path as $dir) {
435                     $path[] = $this->slashifyPath($WikiTheme->file("buttons/$lang"));
436                 }
437             }
438         }
439
440         $this->FileFinder(array_merge($path, $include_path));
441     }
442 }
443
444 // Search PHP's include_path to find file or directory.
445 function FindFile ($file, $missing_okay = false, $slashify = false)
446 {
447     static $finder;
448     if (!isset($finder)) {
449         $finder = new FileFinder;
450         // remove "/lib" from dirname(__FILE__)
451         $wikidir = preg_replace('/.lib$/','',dirname(__FILE__));
452         $finder->_append_to_include_path($wikidir);
453         $finder->_append_to_include_path(dirname(__FILE__)."/pear");
454         // Don't override existing INCLUDE_PATH config.
455         if (!defined("INCLUDE_PATH"))
456             define("INCLUDE_PATH", implode($finder->_get_ini_separator(), $finder->_path));
457     }
458     $s = $finder->findFile($file, $missing_okay);
459     if ($slashify)
460         $s = $finder->slashifyPath($s);
461     return $s;
462 }
463
464 // Search PHP's include_path to find file or directory.
465 // Searches for "locale/$LANG/$file", then for "$file".
466 function FindLocalizedFile ($file, $missing_okay = false, $re_init = false)
467 {
468     static $finder;
469     if ($re_init or !isset($finder))
470         $finder = new LocalizedFileFinder;
471     return $finder->findFile($file, $missing_okay);
472 }
473
474 function FindLocalizedButtonFile ($file, $missing_okay = false, $re_init = false)
475 {
476     static $buttonfinder;
477     if ($re_init or !isset($buttonfinder))
478         $buttonfinder = new LocalizedButtonFinder;
479     return $buttonfinder->findFile($file, $missing_okay);
480 }
481
482 /** 
483  * Prefixes with PHPWIKI_DIR and slashify.
484  * For example to unify with 
485  *   require_once dirname(__FILE__).'/lib/file.php'
486  *   require_once 'lib/file.php' loading style.
487  * Doesn't expand "~" or symlinks yet. truename would be perfect.
488  *
489  * NormalizeLocalFileName("lib/config.php") => /home/user/phpwiki/lib/config.php
490  */
491 function NormalizeLocalFileName($file) {
492     static $finder;
493     if (!isset($finder)) {
494         $finder = new FileFinder;
495     }
496     // remove "/lib" from dirname(__FILE__)
497     if ($finder->_is_abs($file))
498         return $finder->slashifyPath($file);
499     else {
500         if (defined("PHPWIKI_DIR")) $wikidir = PHPWIKI_DIR;
501         else $wikidir = preg_replace('/.lib$/','',dirname(__FILE__));
502         $wikidir = $finder->_strip_last_pathchar($wikidir);
503         $pathsep = $finder->_use_path_separator($wikidir);
504         return $finder->slashifyPath($wikidir . $pathsep . $file);
505         // return PHPWIKI_DIR . "/" . $file;
506     }
507 }
508
509 /** 
510  * Prefixes with DATA_PATH and slashify
511  */
512 function NormalizeWebFileName($file) {
513     static $finder;
514     if (!isset($finder)) {
515         $finder = new FileFinder;
516     }
517     if (defined("DATA_PATH")) {
518         $wikipath = DATA_PATH;
519         $wikipath = $finder->_strip_last_pathchar($wikipath);
520         if (!$file)
521             return $finder->forcePathSlashes($wikipath);
522         else
523             return $finder->forcePathSlashes($wikipath . '/' . $file);
524     } else {
525         return $finder->forcePathSlashes($file);
526     }
527 }
528
529 function isWindows() {
530     static $win;
531     if (isset($win)) return $win;
532     //return preg_match('/^Windows/', php_uname());
533     $win = (substr(PHP_OS,0,3) == 'WIN');
534     return $win;
535 }
536
537 function isWindows95() {
538     static $win95;
539     if (isset($win95)) return $win95;
540     $win95 = isWindows() and !isWindowsNT();
541     return $win95;
542 }
543
544 function isWindowsNT() {
545     static $winnt;
546     if (isset($winnt)) return $winnt;
547     // FIXME: Do this using PHP_OS instead of php_uname().
548     // $winnt = (PHP_OS == "WINNT"); // example from http://www.php.net/manual/en/ref.readline.php
549     if (function_usable('php_uname'))
550         $winnt = preg_match('/^Windows NT/', php_uname());
551     else
552         $winnt = false;         // FIXME: punt.
553     return $winnt;
554 }
555
556 /** 
557  * This is for the OLD Macintosh OS, NOT MacOSX or Darwin!
558  * This has really ugly pathname semantics.
559  * ":path" is relative, "Desktop:path" (I think) is absolute. 
560  * FIXME: Please fix this someone. So far not supported.
561  */
562 function isMac() {
563     return (substr(PHP_OS,0,9) == 'Macintosh'); // not tested!
564 }
565
566 // probably not needed, same behaviour as on unix.
567 function isCygwin() {
568     return (substr(PHP_OS,0,6) == 'CYGWIN');
569 }
570
571 // $Log: not supported by cvs2svn $
572 // Revision 1.28  2004/11/06 17:02:33  rurban
573 // Workaround some php-win \\ duplication bug
574 //
575 // Revision 1.27  2004/10/14 19:23:58  rurban
576 // remove debugging prints
577 //
578 // Revision 1.26  2004/10/14 19:19:33  rurban
579 // loadsave: check if the dumped file will be accessible from outside.
580 // and some other minor fixes. (cvsclient native not yet ready)
581 //
582 // Revision 1.25  2004/10/12 13:13:19  rurban
583 // php5 compatibility (5.0.1 ok)
584 //
585 // Revision 1.24  2004/08/05 17:33:51  rurban
586 // strange problem with WikiTheme
587 //
588 // Revision 1.23  2004/06/19 12:33:25  rurban
589 // prevent some warnings in corner cases
590 //
591 // Revision 1.22  2004/06/14 11:31:20  rurban
592 // renamed global $Theme to $WikiTheme (gforge nameclash)
593 // inherit PageList default options from PageList
594 //   default sortby=pagename
595 // use options in PageList_Selectable (limit, sortby, ...)
596 // added action revert, with button at action=diff
597 // added option regex to WikiAdminSearchReplace
598 //
599 // Revision 1.21  2004/06/02 18:01:45  rurban
600 // init global FileFinder to add proper include paths at startup
601 //   adds PHPWIKI_DIR if started from another dir, lib/pear also
602 // fix slashify for Windows
603 // fix USER_AUTH_POLICY=old, use only USER_AUTH_ORDER methods (besides HttpAuth)
604 //
605 // Revision 1.20  2004/05/27 17:49:05  rurban
606 // renamed DB_Session to DbSession (in CVS also)
607 // added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
608 // remove leading slash in error message
609 // added force_unlock parameter to File_Passwd (no return on stale locks)
610 // fixed adodb session AffectedRows
611 // added FileFinder helpers to unify local filenames and DATA_PATH names
612 // editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
613 //
614 //
615
616 // Local Variables:
617 // mode: php
618 // tab-width: 8
619 // c-basic-offset: 4
620 // c-hanging-comment-ender-p: nil
621 // indent-tabs-mode: nil
622 // End:
623 ?>