]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/FileFinder.php
Activated Id substitution for Subversion
[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 (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         }
266         /*
267          * Some (buggy) PHP's (notable SourceForge's PHP 4.0.6)
268          * sometimes don't seem to heed their include_path.
269          * I.e. sometimes a file is not found even though it seems to
270          * be in the current include_path. A simple
271          * ini_set('include_path', ini_get('include_path')) seems to
272          * be enough to fix the problem
273          *
274          * This following line should be in the above if-block, but we
275          * put it here, as it seems to work-around the bug.
276          */
277         $GLOBALS['INCLUDE_PATH'] = implode($this->_get_ini_separator(), $this->_path);
278         @ini_set('include_path', $GLOBALS['INCLUDE_PATH']);
279     }
280
281     /**
282      * Add a directory to the front of PHP's include_path.
283      *
284      * The directory is prepended, and removed from the tail if already existing.
285      *
286      * @access private
287      * @param $dir string Directory to add.
288      */
289     function _prepend_to_include_path ($dir) {
290         $dir = $this->slashifyPath($dir);
291         // remove duplicates
292         if ($i = array_search($dir, $this->_path) !== false) {
293             array_splice($this->_path, $i, 1);
294         }
295         array_unshift($this->_path, $dir);
296         $GLOBALS['INCLUDE_PATH'] = implode($this->_path, $this->_get_ini_separator());
297         @ini_set('include_path', $GLOBALS['INCLUDE_PATH']);
298     }
299
300     // Return all the possible shortened locale specifiers for the given locale.
301     // Most specific first.
302     // de_DE.iso8859-1@euro => de_DE.iso8859-1, de_DE, de
303     // This code might needed somewhere else also.
304     function locale_versions ($lang) {
305         // Try less specific versions of the locale
306         $langs[] = $lang;
307         foreach (array('@', '.', '_') as $sep) {
308             if ( ($tail = strchr($lang, $sep)) )
309                 $langs[] = substr($lang, 0, -strlen($tail));
310         }
311         return $langs;
312     }
313
314     /**
315      * Try to figure out the appropriate value for $LANG.
316      *
317      *@access private
318      *@return string The value of $LANG.
319      */
320     function _get_lang() {
321         if (!empty($GLOBALS['LANG']))
322             return $GLOBALS['LANG'];
323
324         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES') as $var) {
325             $lang = setlocale(constant($var), 0);
326             if (!empty($lang))
327                 return $lang;
328         }
329             
330         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES', 'LANG') as $var) {
331             $lang = getenv($var);
332             if (!empty($lang))
333                 return $lang;
334         }
335
336         return "C";
337     }
338 }
339
340 /**
341  * A class for finding PEAR code.
342  *
343  * This is a subclass of FileFinder which searches a standard list of
344  * directories where PEAR code is likely to be installed.
345  *
346  * Example usage:
347  *
348  * <pre>
349  *   $pearFinder = new PearFileFinder;
350  *   $pearFinder->includeOnce('DB.php');
351  * </pre>
352  *
353  * The above code will look for 'DB.php', if found, the directory in
354  * which it was found will be added to PHP's include_path, and the
355  * file will be included. (If the file is not found, and E_USER_ERROR
356  * will be thrown.)
357  */
358 class PearFileFinder
359     extends FileFinder
360 {
361     /**
362      * Constructor.
363      *
364      * @param $path array Where to look for PEAR library code.
365      * A good set of defaults is provided, so you can probably leave
366      * this parameter blank.
367      */
368     function PearFileFinder ($path = array()) {
369         $this->FileFinder(array_merge(
370                           $path,
371                           array('/usr/share/php4',
372                                 '/usr/share/php',
373                                 '/usr/lib/php4',
374                                 '/usr/lib/php',
375                                 '/usr/local/share/php4',
376                                 '/usr/local/share/php',
377                                 '/usr/local/lib/php4',
378                                 '/usr/local/lib/php',
379                                 '/System/Library/PHP',
380                                 '/Apache/pear'        // Windows
381                                 )));
382     }
383 }
384
385 /**
386  * Find PhpWiki localized files.
387  *
388  * This is a subclass of FileFinder which searches PHP's include_path
389  * for files. It looks first for "locale/$LANG/$file", then for
390  * "$file".
391  *
392  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
393  * also search under various less specific variations like
394  * "de_DE.iso8859-1", "de_DE" and "de".
395  */
396 class LocalizedFileFinder
397 extends FileFinder
398 {
399     /**
400      * Constructor.
401      */
402     function LocalizedFileFinder () {
403         $this->_pathsep = $this->_get_syspath_separator();
404         $include_path = $this->_get_include_path();
405         $path = array();
406
407         $lang = $this->_get_lang();
408         assert(!empty($lang));
409
410         if ($locales = $this->locale_versions($lang)) {
411             foreach ($locales as $lang) {
412                 if ($lang == 'C') $lang = 'en';
413                 foreach ($include_path as $dir) {
414                     $path[] = $this->slashifyPath($dir . "/locale/$lang");
415                 }
416             }
417         }
418         $this->FileFinder(array_merge($path, $include_path));
419     }
420 }
421
422 /**
423  * Find PhpWiki localized theme buttons.
424  *
425  * This is a subclass of FileFinder which searches PHP's include_path
426  * for files. It looks first for "buttons/$LANG/$file", then for
427  * "$file".
428  *
429  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
430  * also search under various less specific variations like
431  * "de_DE.iso8859-1", "de_DE" and "de".
432  */
433 class LocalizedButtonFinder
434 extends FileFinder
435 {
436     /**
437      * Constructor.
438      */
439     function LocalizedButtonFinder () {
440         global $WikiTheme;
441         $this->_pathsep = $this->_get_syspath_separator();
442         $include_path = $this->_get_include_path();
443         $path = array();
444
445         $lang = $this->_get_lang();
446         assert(!empty($lang));
447         assert(!empty($WikiTheme));
448
449         if (is_object($WikiTheme)) {
450             $langs = $this->locale_versions($lang);
451             foreach ($langs as $lang) {
452                 if ($lang == 'C') $lang = 'en';
453                 foreach ($include_path as $dir) {
454                     $path[] = $this->slashifyPath($WikiTheme->file("buttons/$lang"));
455                 }
456             }
457         }
458
459         $this->FileFinder(array_merge($path, $include_path));
460     }
461 }
462
463 // Search PHP's include_path to find file or directory.
464 function FindFile ($file, $missing_okay = false, $slashify = false)
465 {
466     static $finder;
467     if (!isset($finder)) {
468         $finder = new FileFinder;
469         // remove "/lib" from dirname(__FILE__)
470         $wikidir = preg_replace('/.lib$/','',dirname(__FILE__));
471         // let the system favor its local pear?
472         $finder->_append_to_include_path(dirname(__FILE__)."/pear");
473         $finder->_prepend_to_include_path($wikidir);
474         // Don't override existing INCLUDE_PATH config.
475         if (!defined("INCLUDE_PATH"))
476             define("INCLUDE_PATH", implode($finder->_get_ini_separator(), $finder->_path));
477     }
478     $s = $finder->findFile($file, $missing_okay);
479     if ($slashify)
480         $s = $finder->slashifyPath($s);
481     return $s;
482 }
483
484 // Search PHP's include_path to find file or directory.
485 // Searches for "locale/$LANG/$file", then for "$file".
486 function FindLocalizedFile ($file, $missing_okay = false, $re_init = false)
487 {
488     static $finder;
489     if ($re_init or !isset($finder))
490         $finder = new LocalizedFileFinder;
491     return $finder->findFile($file, $missing_okay);
492 }
493
494 function FindLocalizedButtonFile ($file, $missing_okay = false, $re_init = false)
495 {
496     static $buttonfinder;
497     if ($re_init or !isset($buttonfinder))
498         $buttonfinder = new LocalizedButtonFinder;
499     return $buttonfinder->findFile($file, $missing_okay);
500 }
501
502 /** 
503  * Prefixes with PHPWIKI_DIR and slashify.
504  * For example to unify with 
505  *   require_once dirname(__FILE__).'/lib/file.php'
506  *   require_once 'lib/file.php' loading style.
507  * Doesn't expand "~" or symlinks yet. truename would be perfect.
508  *
509  * NormalizeLocalFileName("lib/config.php") => /home/user/phpwiki/lib/config.php
510  */
511 function NormalizeLocalFileName($file) {
512     static $finder;
513     if (!isset($finder)) {
514         $finder = new FileFinder;
515     }
516     // remove "/lib" from dirname(__FILE__)
517     if ($finder->_is_abs($file))
518         return $finder->slashifyPath($file);
519     else {
520         if (defined("PHPWIKI_DIR")) $wikidir = PHPWIKI_DIR;
521         else $wikidir = preg_replace('/.lib$/','',dirname(__FILE__));
522         $wikidir = $finder->_strip_last_pathchar($wikidir);
523         $pathsep = $finder->_use_path_separator($wikidir);
524         return $finder->slashifyPath($wikidir . $pathsep . $file);
525         // return PHPWIKI_DIR . "/" . $file;
526     }
527 }
528
529 /** 
530  * Prefixes with DATA_PATH and slashify
531  */
532 function NormalizeWebFileName($file) {
533     static $finder;
534     if (!isset($finder)) {
535         $finder = new FileFinder;
536     }
537     if (defined("DATA_PATH")) {
538         $wikipath = DATA_PATH;
539         $wikipath = $finder->_strip_last_pathchar($wikipath);
540         if (!$file)
541             return $finder->forcePathSlashes($wikipath);
542         else
543             return $finder->forcePathSlashes($wikipath . '/' . $file);
544     } else {
545         return $finder->forcePathSlashes($file);
546     }
547 }
548
549 function isWindows() {
550     static $win;
551     if (isset($win)) return $win;
552     //return preg_match('/^Windows/', php_uname());
553     $win = (substr(PHP_OS,0,3) == 'WIN');
554     return $win;
555 }
556
557 function isWindows95() {
558     static $win95;
559     if (isset($win95)) return $win95;
560     $win95 = isWindows() and !isWindowsNT();
561     return $win95;
562 }
563
564 function isWindowsNT() {
565     static $winnt;
566     if (isset($winnt)) return $winnt;
567     // FIXME: Do this using PHP_OS instead of php_uname().
568     // $winnt = (PHP_OS == "WINNT"); // example from http://www.php.net/manual/en/ref.readline.php
569     if (function_usable('php_uname'))
570         $winnt = preg_match('/^Windows NT/', php_uname());
571     else
572         $winnt = false;         // FIXME: punt.
573     return $winnt;
574 }
575
576 /** 
577  * This is for the OLD Macintosh OS, NOT MacOSX or Darwin!
578  * This has really ugly pathname semantics.
579  * ":path" is relative, "Desktop:path" (I think) is absolute. 
580  * FIXME: Please fix this someone. So far not supported.
581  */
582 function isMac() {
583     return (substr(PHP_OS,0,9) == 'Macintosh'); // not tested!
584 }
585
586 // probably not needed, same behaviour as on unix.
587 function isCygwin() {
588     return (substr(PHP_OS,0,6) == 'CYGWIN');
589 }
590
591 // $Log: not supported by cvs2svn $
592 // Revision 1.32  2007/01/04 16:44:47  rurban
593 // Remove VMS from internal note.
594 //
595 // Revision 1.31  2005/02/28 21:24:32  rurban
596 // ignore forbidden ini_set warnings. Bug #1117254 by Xavier Roche
597 //
598 // Revision 1.30  2004/11/10 19:32:21  rurban
599 // * optimize increaseHitCount, esp. for mysql.
600 // * prepend dirs to the include_path (phpwiki_dir for faster searches)
601 // * Pear_DB version logic (awful but needed)
602 // * fix broken ADODB quote
603 // * _extract_page_data simplification
604 //
605 // Revision 1.29  2004/11/09 17:11:03  rurban
606 // * revert to the wikidb ref passing. there's no memory abuse there.
607 // * use new wikidb->_cache->_id_cache[] instead of wikidb->_iwpcache, to effectively
608 //   store page ids with getPageLinks (GleanDescription) of all existing pages, which
609 //   are also needed at the rendering for linkExistingWikiWord().
610 //   pass options to pageiterator.
611 //   use this cache also for _get_pageid()
612 //   This saves about 8 SELECT count per page (num all pagelinks).
613 // * fix passing of all page fields to the pageiterator.
614 // * fix overlarge session data which got broken with the latest ACCESS_LOG_SQL changes
615 //
616 // Revision 1.28  2004/11/06 17:02:33  rurban
617 // Workaround some php-win \\ duplication bug
618 //
619 // Revision 1.27  2004/10/14 19:23:58  rurban
620 // remove debugging prints
621 //
622 // Revision 1.26  2004/10/14 19:19:33  rurban
623 // loadsave: check if the dumped file will be accessible from outside.
624 // and some other minor fixes. (cvsclient native not yet ready)
625 //
626 // Revision 1.25  2004/10/12 13:13:19  rurban
627 // php5 compatibility (5.0.1 ok)
628 //
629 // Revision 1.24  2004/08/05 17:33:51  rurban
630 // strange problem with WikiTheme
631 //
632 // Revision 1.23  2004/06/19 12:33:25  rurban
633 // prevent some warnings in corner cases
634 //
635 // Revision 1.22  2004/06/14 11:31:20  rurban
636 // renamed global $Theme to $WikiTheme (gforge nameclash)
637 // inherit PageList default options from PageList
638 //   default sortby=pagename
639 // use options in PageList_Selectable (limit, sortby, ...)
640 // added action revert, with button at action=diff
641 // added option regex to WikiAdminSearchReplace
642 //
643 // Revision 1.21  2004/06/02 18:01:45  rurban
644 // init global FileFinder to add proper include paths at startup
645 //   adds PHPWIKI_DIR if started from another dir, lib/pear also
646 // fix slashify for Windows
647 // fix USER_AUTH_POLICY=old, use only USER_AUTH_ORDER methods (besides HttpAuth)
648 //
649 // Revision 1.20  2004/05/27 17:49:05  rurban
650 // renamed DB_Session to DbSession (in CVS also)
651 // added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
652 // remove leading slash in error message
653 // added force_unlock parameter to File_Passwd (no return on stale locks)
654 // fixed adodb session AffectedRows
655 // added FileFinder helpers to unify local filenames and DATA_PATH names
656 // editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
657 //
658 //
659
660 // Local Variables:
661 // mode: php
662 // tab-width: 8
663 // c-basic-offset: 4
664 // c-hanging-comment-ender-p: nil
665 // indent-tabs-mode: nil
666 // End:
667 ?>