1 <?php rcs_id('$Id: FileFinder.php,v 1.30 2004-11-10 19:32:21 rurban Exp $');
3 require_once(dirname(__FILE__).'/stdlib.php');
6 * A class for finding files.
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.
16 //var $_pathsep, $_path;
21 * @param $path array A list of directories in which to search for files.
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();
33 * @param $file string File to search for.
34 * @return string The filename (including path), if found, otherwise false.
36 function findFile ($file, $missing_okay = false) {
37 if ($this->_is_abs($file)) {
38 if (file_exists($file))
41 elseif ( ($dir = $this->_search_path($file)) ) {
42 return $dir . $this->_use_path_separator($dir) . $file;
44 return $missing_okay ? false : $this->_not_found($file);
48 * Unify used pathsep character.
49 * Accepts array of paths also.
50 * This might not work on Windows95 or FAT volumes. (not tested)
52 function slashifyPath ($path) {
53 return $this->forcePathSlashes($path, $this->_pathsep);
57 * Force using '/' as path seperator.
59 function forcePathSlashes ($path, $sep='/') {
60 if (is_array($path)) {
62 foreach ($path as $dir) { $result[] = $this->forcePathSlashes($dir,$sep); }
65 if (isWindows() or $this->_isOtherPathsep()) {
66 if (isMac()) $from = ":";
67 elseif (isWindows()) $from = "\\";
69 // PHP is stupid enough to use \\ instead of \
71 if (substr($path,0,2) != '\\\\')
72 $path = str_replace('\\\\','\\',$path);
74 $path = '\\\\' . str_replace('\\\\','\\',substr($path,2));
76 return strtr($path, $from, $sep);
83 * Try to include file.
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.
89 * @param $file string File to include.
90 * @return bool True if file was successfully included.
92 function includeOnce ($file) {
93 if ( ($ret = @include_once($file)) )
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);
102 return $this->_not_found($file);
105 function _isOtherPathsep() {
106 return $this->_pathsep != '/';
110 * The system-dependent path-separator character.
111 * UNIX,WindowsNT,MacOSX: /
116 * @return string path_separator.
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.
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.
137 * @return string path_separator.
139 function _use_path_separator ($path) {
141 if (empty($path)) return "\\";
142 else return (strchr($path,"\\")) ? "\\" : '/';
144 if (empty($path)) return ":";
145 else return (strchr($path,":")) ? ":" : '/';
147 return $this->_get_syspath_separator();
152 * Determine if path is absolute.
155 * @param $path string Path.
156 * @return bool True if path is absolute.
158 function _is_abs($path) {
159 if (ereg('^/', $path)) return true;
160 elseif (isWindows() and (eregi('^[a-z]:[/\\]', $path))) return true;
165 * Strip ending '/' or '\' from path.
168 * @param $path string Path.
169 * @return bool New path (destructive)
171 function _strip_last_pathchar(&$path) {
173 if (substr($path,-1) == ':' or substr($path,-1) == "/")
174 $path = substr($path,0,-1);
176 if (substr($path,-1) == '/' or substr($path,-1) == "\\")
177 $path = substr($path,0,-1);
183 * Report a "file not found" error.
186 * @param $file string Name of missing file.
187 * @return bool false.
189 function _not_found($file) {
190 if (function_exists("_"))
191 trigger_error(sprintf(_("%s: file not found"), $file), E_USER_ERROR);
193 trigger_error(sprintf("%s: file not found", $file), E_USER_ERROR);
199 * Search our path for a file.
202 * @param $file string File to find.
203 * @return string Directory which contains $file, or false.
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))
214 } elseif (@file_exists($dir . $this->_pathsep . $file))
221 * The system-dependent path-separator character. On UNIX systems,
222 * this character is ':'; on Win32 systems it is ';'.
224 * On Mac it cannot be : because this is the seperator there!
227 * @return string path_separator.
229 function _get_ini_separator () {
230 return isWindows() ? ';' : ':';
231 // return preg_match('/^Windows/', php_uname())
235 * Get the value of PHP's include_path.
238 * @return array Include path.
240 function _get_include_path() {
241 if (defined("INCLUDE_PATH"))
242 $path = INCLUDE_PATH;
244 $path = @get_cfg_var('include_path'); // FIXME: report warning
245 if (empty($path)) $path = @ini_get('include_path');
249 return explode($this->_get_ini_separator(), $this->slashifyPath($path));
253 * Add a directory to the end of PHP's include_path.
255 * The directory is appended only if it is not already listed in
259 * @param $dir string Directory to add.
261 function _append_to_include_path ($dir) {
262 $dir = $this->slashifyPath($dir);
263 if (!in_array($dir, $this->_path)) {
264 $this->_path[] = $dir;
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
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.
277 ini_set('include_path', implode($this->_get_ini_separator(), $this->_path));
281 * Add a directory to the front of PHP's include_path.
283 * The directory is prepended, and removed from the tail if already existing.
286 * @param $dir string Directory to add.
288 function _prepend_to_include_path ($dir) {
289 $dir = $this->slashifyPath($dir);
291 if ($i = array_search($dir, $this->_path) !== false) {
292 array_splice($this->_path, $i, 1);
294 array_unshift($this->_path, $dir);
295 ini_set('include_path', implode($this->_path, $this->_get_ini_separator()));
298 // Return all the possible shortened locale specifiers for the given locale.
299 // Most specific first.
300 // de_DE.iso8859-1@euro => de_DE.iso8859-1, de_DE, de
301 // This code might needed somewhere else also.
302 function locale_versions ($lang) {
303 // Try less specific versions of the locale
305 foreach (array('@', '.', '_') as $sep) {
306 if ( ($tail = strchr($lang, $sep)) )
307 $langs[] = substr($lang, 0, -strlen($tail));
313 * Try to figure out the appropriate value for $LANG.
316 *@return string The value of $LANG.
318 function _get_lang() {
319 if (!empty($GLOBALS['LANG']))
320 return $GLOBALS['LANG'];
322 foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES') as $var) {
323 $lang = setlocale(constant($var), 0);
328 foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES', 'LANG') as $var) {
329 $lang = getenv($var);
339 * A class for finding PEAR code.
341 * This is a subclass of FileFinder which searches a standard list of
342 * directories where PEAR code is likely to be installed.
347 * $pearFinder = new PearFileFinder;
348 * $pearFinder->includeOnce('DB.php');
351 * The above code will look for 'DB.php', if found, the directory in
352 * which it was found will be added to PHP's include_path, and the
353 * file will be included. (If the file is not found, and E_USER_ERROR
362 * @param $path array Where to look for PEAR library code.
363 * A good set of defaults is provided, so you can probably leave
364 * this parameter blank.
366 function PearFileFinder ($path = array()) {
367 $this->FileFinder(array_merge(
369 array('/usr/share/php4',
373 '/usr/local/share/php4',
374 '/usr/local/share/php',
375 '/usr/local/lib/php4',
376 '/usr/local/lib/php',
377 '/System/Library/PHP',
378 '/Apache/pear' // Windows
384 * Find PhpWiki localized files.
386 * This is a subclass of FileFinder which searches PHP's include_path
387 * for files. It looks first for "locale/$LANG/$file", then for
390 * If $LANG is something like "de_DE.iso8859-1@euro", this class will
391 * also search under various less specific variations like
392 * "de_DE.iso8859-1", "de_DE" and "de".
394 class LocalizedFileFinder
400 function LocalizedFileFinder () {
401 $this->_pathsep = $this->_get_syspath_separator();
402 $include_path = $this->_get_include_path();
405 $lang = $this->_get_lang();
406 assert(!empty($lang));
408 if ($locales = $this->locale_versions($lang)) {
409 foreach ($locales as $lang) {
410 if ($lang == 'C') $lang = 'en';
411 foreach ($include_path as $dir) {
412 $path[] = $this->slashifyPath($dir . "/locale/$lang");
416 $this->FileFinder(array_merge($path, $include_path));
421 * Find PhpWiki localized theme buttons.
423 * This is a subclass of FileFinder which searches PHP's include_path
424 * for files. It looks first for "buttons/$LANG/$file", then for
427 * If $LANG is something like "de_DE.iso8859-1@euro", this class will
428 * also search under various less specific variations like
429 * "de_DE.iso8859-1", "de_DE" and "de".
431 class LocalizedButtonFinder
437 function LocalizedButtonFinder () {
439 $this->_pathsep = $this->_get_syspath_separator();
440 $include_path = $this->_get_include_path();
443 $lang = $this->_get_lang();
444 assert(!empty($lang));
445 assert(!empty($WikiTheme));
447 if (is_object($WikiTheme)) {
448 $langs = $this->locale_versions($lang);
449 foreach ($langs as $lang) {
450 if ($lang == 'C') $lang = 'en';
451 foreach ($include_path as $dir) {
452 $path[] = $this->slashifyPath($WikiTheme->file("buttons/$lang"));
457 $this->FileFinder(array_merge($path, $include_path));
461 // Search PHP's include_path to find file or directory.
462 function FindFile ($file, $missing_okay = false, $slashify = false)
465 if (!isset($finder)) {
466 $finder = new FileFinder;
467 // remove "/lib" from dirname(__FILE__)
468 $wikidir = preg_replace('/.lib$/','',dirname(__FILE__));
469 // let the system favor its local pear?
470 $finder->_append_to_include_path(dirname(__FILE__)."/pear");
471 $finder->_prepend_to_include_path($wikidir);
472 // Don't override existing INCLUDE_PATH config.
473 if (!defined("INCLUDE_PATH"))
474 define("INCLUDE_PATH", implode($finder->_get_ini_separator(), $finder->_path));
476 $s = $finder->findFile($file, $missing_okay);
478 $s = $finder->slashifyPath($s);
482 // Search PHP's include_path to find file or directory.
483 // Searches for "locale/$LANG/$file", then for "$file".
484 function FindLocalizedFile ($file, $missing_okay = false, $re_init = false)
487 if ($re_init or !isset($finder))
488 $finder = new LocalizedFileFinder;
489 return $finder->findFile($file, $missing_okay);
492 function FindLocalizedButtonFile ($file, $missing_okay = false, $re_init = false)
494 static $buttonfinder;
495 if ($re_init or !isset($buttonfinder))
496 $buttonfinder = new LocalizedButtonFinder;
497 return $buttonfinder->findFile($file, $missing_okay);
501 * Prefixes with PHPWIKI_DIR and slashify.
502 * For example to unify with
503 * require_once dirname(__FILE__).'/lib/file.php'
504 * require_once 'lib/file.php' loading style.
505 * Doesn't expand "~" or symlinks yet. truename would be perfect.
507 * NormalizeLocalFileName("lib/config.php") => /home/user/phpwiki/lib/config.php
509 function NormalizeLocalFileName($file) {
511 if (!isset($finder)) {
512 $finder = new FileFinder;
514 // remove "/lib" from dirname(__FILE__)
515 if ($finder->_is_abs($file))
516 return $finder->slashifyPath($file);
518 if (defined("PHPWIKI_DIR")) $wikidir = PHPWIKI_DIR;
519 else $wikidir = preg_replace('/.lib$/','',dirname(__FILE__));
520 $wikidir = $finder->_strip_last_pathchar($wikidir);
521 $pathsep = $finder->_use_path_separator($wikidir);
522 return $finder->slashifyPath($wikidir . $pathsep . $file);
523 // return PHPWIKI_DIR . "/" . $file;
528 * Prefixes with DATA_PATH and slashify
530 function NormalizeWebFileName($file) {
532 if (!isset($finder)) {
533 $finder = new FileFinder;
535 if (defined("DATA_PATH")) {
536 $wikipath = DATA_PATH;
537 $wikipath = $finder->_strip_last_pathchar($wikipath);
539 return $finder->forcePathSlashes($wikipath);
541 return $finder->forcePathSlashes($wikipath . '/' . $file);
543 return $finder->forcePathSlashes($file);
547 function isWindows() {
549 if (isset($win)) return $win;
550 //return preg_match('/^Windows/', php_uname());
551 $win = (substr(PHP_OS,0,3) == 'WIN');
555 function isWindows95() {
557 if (isset($win95)) return $win95;
558 $win95 = isWindows() and !isWindowsNT();
562 function isWindowsNT() {
564 if (isset($winnt)) return $winnt;
565 // FIXME: Do this using PHP_OS instead of php_uname().
566 // $winnt = (PHP_OS == "WINNT"); // example from http://www.php.net/manual/en/ref.readline.php
567 if (function_usable('php_uname'))
568 $winnt = preg_match('/^Windows NT/', php_uname());
570 $winnt = false; // FIXME: punt.
575 * This is for the OLD Macintosh OS, NOT MacOSX or Darwin!
576 * This has really ugly pathname semantics.
577 * ":path" is relative, "Desktop:path" (I think) is absolute.
578 * FIXME: Please fix this someone. So far not supported.
581 return (substr(PHP_OS,0,9) == 'Macintosh'); // not tested!
584 // probably not needed, same behaviour as on unix.
585 function isCygwin() {
586 return (substr(PHP_OS,0,6) == 'CYGWIN');
589 // $Log: not supported by cvs2svn $
590 // Revision 1.29 2004/11/09 17:11:03 rurban
591 // * revert to the wikidb ref passing. there's no memory abuse there.
592 // * use new wikidb->_cache->_id_cache[] instead of wikidb->_iwpcache, to effectively
593 // store page ids with getPageLinks (GleanDescription) of all existing pages, which
594 // are also needed at the rendering for linkExistingWikiWord().
595 // pass options to pageiterator.
596 // use this cache also for _get_pageid()
597 // This saves about 8 SELECT count per page (num all pagelinks).
598 // * fix passing of all page fields to the pageiterator.
599 // * fix overlarge session data which got broken with the latest ACCESS_LOG_SQL changes
601 // Revision 1.28 2004/11/06 17:02:33 rurban
602 // Workaround some php-win \\ duplication bug
604 // Revision 1.27 2004/10/14 19:23:58 rurban
605 // remove debugging prints
607 // Revision 1.26 2004/10/14 19:19:33 rurban
608 // loadsave: check if the dumped file will be accessible from outside.
609 // and some other minor fixes. (cvsclient native not yet ready)
611 // Revision 1.25 2004/10/12 13:13:19 rurban
612 // php5 compatibility (5.0.1 ok)
614 // Revision 1.24 2004/08/05 17:33:51 rurban
615 // strange problem with WikiTheme
617 // Revision 1.23 2004/06/19 12:33:25 rurban
618 // prevent some warnings in corner cases
620 // Revision 1.22 2004/06/14 11:31:20 rurban
621 // renamed global $Theme to $WikiTheme (gforge nameclash)
622 // inherit PageList default options from PageList
623 // default sortby=pagename
624 // use options in PageList_Selectable (limit, sortby, ...)
625 // added action revert, with button at action=diff
626 // added option regex to WikiAdminSearchReplace
628 // Revision 1.21 2004/06/02 18:01:45 rurban
629 // init global FileFinder to add proper include paths at startup
630 // adds PHPWIKI_DIR if started from another dir, lib/pear also
631 // fix slashify for Windows
632 // fix USER_AUTH_POLICY=old, use only USER_AUTH_ORDER methods (besides HttpAuth)
634 // Revision 1.20 2004/05/27 17:49:05 rurban
635 // renamed DB_Session to DbSession (in CVS also)
636 // added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
637 // remove leading slash in error message
638 // added force_unlock parameter to File_Passwd (no return on stale locks)
639 // fixed adodb session AffectedRows
640 // added FileFinder helpers to unify local filenames and DATA_PATH names
641 // editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
649 // c-hanging-comment-ender-p: nil
650 // indent-tabs-mode: nil