Panda3D

configPageManager.cxx

00001 // Filename: configPageManager.cxx
00002 // Created by:  drose (15Oct04)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "configPageManager.h"
00016 #include "configDeclaration.h"
00017 #include "configVariableString.h"
00018 #include "configPage.h"
00019 #include "prcKeyRegistry.h"
00020 #include "dSearchPath.h"
00021 #include "executionEnvironment.h"
00022 #include "config_prc.h"
00023 #include "pfstream.h"
00024 #include "pandaSystem.h"
00025 #include "textEncoder.h"
00026 #include "stringDecoder.h"
00027 
00028 // This file is generated by ppremake.
00029 #include "prc_parameters.h"
00030 
00031 #include <set>
00032 
00033 // Pick up the public key definitions.
00034 #ifdef PRC_PUBLIC_KEYS_INCLUDE
00035 #include PRC_PUBLIC_KEYS_INCLUDE
00036 #endif
00037 
00038 #include <algorithm>
00039 #include <ctype.h>
00040 
00041 ConfigPageManager *ConfigPageManager::_global_ptr = NULL;
00042 
00043 ////////////////////////////////////////////////////////////////////
00044 //     Function: ConfigPageManager::Constructor
00045 //       Access: Protected
00046 //  Description: The constructor is private (actually, just protected,
00047 //               but only to avoid a gcc compiler warning) because it
00048 //               should not be explicitly constructed.  There is only
00049 //               one ConfigPageManager, and it constructs itself.
00050 ////////////////////////////////////////////////////////////////////
00051 ConfigPageManager::
00052 ConfigPageManager() {
00053   _next_page_seq = 1;
00054   _loaded_implicit = false;
00055   _currently_loading = false;
00056   _pages_sorted = true;
00057 
00058 #ifdef PRC_PUBLIC_KEYS_INCLUDE
00059   // Record the public keys in the registry at startup time.
00060   PrcKeyRegistry::get_global_ptr()->record_keys(prc_pubkeys, num_prc_pubkeys);
00061 #endif  // PRC_PUBLIC_KEYS_INCLUDE
00062 }
00063 
00064 ////////////////////////////////////////////////////////////////////
00065 //     Function: ConfigPageManager::Destructor
00066 //       Access: Protected
00067 //  Description: The ConfigPageManager destructor should never be
00068 //               called, because this is a global object that is never
00069 //               freed.
00070 ////////////////////////////////////////////////////////////////////
00071 ConfigPageManager::
00072 ~ConfigPageManager() {
00073   prc_cat->error()
00074     << "Internal error--ConfigPageManager destructor called!\n";
00075 }
00076 
00077 ////////////////////////////////////////////////////////////////////
00078 //     Function: ConfigPageManager::reload_implicit_pages
00079 //       Access: Published
00080 //  Description: Searches the PRC_DIR and/or PRC_PATH directories for
00081 //               *.prc files and loads them in as pages.
00082 //
00083 //               This may be called after startup, to force the system
00084 //               to re-read all of the implicit prc files.
00085 ////////////////////////////////////////////////////////////////////
00086 void ConfigPageManager::
00087 reload_implicit_pages() {
00088   if (_currently_loading) {
00089     // This is a recursion protector.  We can get recursion feedback
00090     // between config and notify, as each tries to use the other at
00091     // construction.
00092     return;
00093   }
00094   _currently_loading = true;
00095 
00096   // First, remove all the previously-loaded pages.
00097   Pages::iterator pi;
00098   for (pi = _implicit_pages.begin(); pi != _implicit_pages.end(); ++pi) {
00099     delete (*pi);
00100   }
00101   _implicit_pages.clear();
00102 
00103   // PRC_PATTERNS lists one or more filename templates separated by
00104   // spaces.  Pull them out and store them in _prc_patterns.
00105   _prc_patterns.clear();
00106 
00107   string prc_patterns = PRC_PATTERNS;
00108   if (!prc_patterns.empty()) {
00109     vector_string pat_list;
00110     ConfigDeclaration::extract_words(prc_patterns, pat_list);
00111     _prc_patterns.reserve(pat_list.size());
00112     for (size_t i = 0; i < pat_list.size(); ++i) {
00113       GlobPattern glob(pat_list[i]);
00114 #ifdef WIN32
00115       // On windows, the file system is case-insensitive, so the
00116       // pattern should be too.
00117       glob.set_case_sensitive(false);
00118 #endif  // WIN32
00119       _prc_patterns.push_back(glob);
00120     }
00121   }
00122 
00123   // Similarly for PRC_ENCRYPTED_PATTERNS.
00124   _prc_encrypted_patterns.clear();
00125 
00126   string prc_encrypted_patterns = PRC_ENCRYPTED_PATTERNS;
00127   if (!prc_encrypted_patterns.empty()) {
00128     vector_string pat_list;
00129     ConfigDeclaration::extract_words(prc_encrypted_patterns, pat_list);
00130     _prc_encrypted_patterns.reserve(pat_list.size());
00131     for (size_t i = 0; i < pat_list.size(); ++i) {
00132       GlobPattern glob(pat_list[i]);
00133 #ifdef WIN32
00134       glob.set_case_sensitive(false);
00135 #endif  // WIN32
00136       _prc_encrypted_patterns.push_back(glob);
00137     }
00138   }
00139 
00140   // And again for PRC_EXECUTABLE_PATTERNS.
00141   _prc_executable_patterns.clear();
00142 
00143   string prc_executable_patterns = PRC_EXECUTABLE_PATTERNS;
00144   if (!prc_executable_patterns.empty()) {
00145     vector_string pat_list;
00146     ConfigDeclaration::extract_words(prc_executable_patterns, pat_list);
00147     _prc_executable_patterns.reserve(pat_list.size());
00148     for (size_t i = 0; i < pat_list.size(); ++i) {
00149       GlobPattern glob(pat_list[i]);
00150 #ifdef WIN32
00151       glob.set_case_sensitive(false);
00152 #endif  // WIN32
00153       _prc_executable_patterns.push_back(glob);
00154     }
00155   }
00156 
00157   // Now build up the search path for .prc files.
00158   _search_path.clear();
00159 
00160   // PRC_DIR_ENVVARS lists one or more environment variables separated
00161   // by spaces.  Pull them out, and each of those contains the name of
00162   // a single directory to search.  Add it to the search path.
00163   string prc_dir_envvars = PRC_DIR_ENVVARS;
00164   if (!prc_dir_envvars.empty()) {
00165     vector_string prc_dir_envvar_list;
00166     ConfigDeclaration::extract_words(prc_dir_envvars, prc_dir_envvar_list);
00167     for (size_t i = 0; i < prc_dir_envvar_list.size(); ++i) {
00168       string prc_dir = ExecutionEnvironment::get_environment_variable(prc_dir_envvar_list[i]);
00169       if (!prc_dir.empty()) {
00170         Filename prc_dir_filename = Filename::from_os_specific(prc_dir);
00171         prc_dir_filename.make_true_case();
00172         if (scan_auto_prc_dir(prc_dir_filename)) {
00173           _search_path.append_directory(prc_dir_filename);
00174         }
00175       }
00176     }
00177   }
00178   
00179   // PRC_PATH_ENVVARS lists one or more environment variables separated
00180   // by spaces.  Pull them out, and then each one of those contains a
00181   // list of directories to search.  Add each of those to the search
00182   // path.
00183   string prc_path_envvars = PRC_PATH_ENVVARS;
00184   if (!prc_path_envvars.empty()) {
00185     vector_string prc_path_envvar_list;
00186     ConfigDeclaration::extract_words(prc_path_envvars, prc_path_envvar_list);
00187     for (size_t i = 0; i < prc_path_envvar_list.size(); ++i) {
00188       string path = ExecutionEnvironment::get_environment_variable(prc_path_envvar_list[i]);
00189       size_t p = 0;
00190       while (p < path.length()) {
00191         size_t q = path.find_first_of(DEFAULT_PATHSEP, p);
00192         if (q == string::npos) {
00193           q = path.length();
00194         }
00195         Filename prc_dir_filename = Filename::from_os_specific(path.substr(p, q - p));
00196         prc_dir_filename.make_true_case();
00197         if (scan_auto_prc_dir(prc_dir_filename)) {
00198           _search_path.append_directory(prc_dir_filename);
00199         }
00200         p = q + 1;
00201       }
00202     }
00203   }
00204   
00205   // PRC_PATH2_ENVVARS is a special variable that is rarely used; it
00206   // exists primarily to support the Cygwin-based "ctattach" tools
00207   // used by the Walt Disney VR Studio.  This defines a set of
00208   // environment variable(s) that define a search path, as above;
00209   // except that the directory names on these search paths are
00210   // Panda-style filenames, not Windows-style filenames; and the path
00211   // separator is always a space character, regardless of
00212   // DEFAULT_PATHSEP.
00213   string prc_path2_envvars = PRC_PATH2_ENVVARS;
00214   if (!prc_path2_envvars.empty()) {
00215     vector_string prc_path_envvar_list;
00216     ConfigDeclaration::extract_words(prc_path2_envvars, prc_path_envvar_list);
00217     for (size_t i = 0; i < prc_path_envvar_list.size(); ++i) {
00218       string path = ExecutionEnvironment::get_environment_variable(prc_path_envvar_list[i]);
00219       size_t p = 0;
00220       while (p < path.length()) {
00221         size_t q = path.find_first_of(' ', p);
00222         if (q == string::npos) {
00223           q = path.length();
00224         }
00225         Filename prc_dir_filename = path.substr(p, q - p);
00226         if (scan_auto_prc_dir(prc_dir_filename)) {
00227           _search_path.append_directory(prc_dir_filename);
00228         }
00229         p = q + 1;
00230       }
00231     }
00232   }
00233   
00234   if (_search_path.is_empty()) {
00235     // If nothing's on the search path (PRC_DIR and PRC_PATH were not
00236     // defined), then use the DEFAULT_PRC_DIR.
00237     string default_prc_dir = DEFAULT_PRC_DIR;
00238     if (!default_prc_dir.empty()) {
00239       // It's already from-os-specific by ppremake.
00240       Filename prc_dir_filename = default_prc_dir;
00241       if (scan_auto_prc_dir(prc_dir_filename)) {
00242         _search_path.append_directory(prc_dir_filename);
00243       }
00244     }
00245   }
00246 
00247   // Now find all of the *.prc files (or whatever matches
00248   // PRC_PATTERNS) on the path.
00249   ConfigFiles config_files;
00250 
00251   // Use a set to ensure that we only visit each directory once, even
00252   // if it appears multiple times (under different aliases!) in the
00253   // path.
00254   set<Filename> unique_dirnames;
00255 
00256   // We walk through the list of directories in forward order, so that
00257   // the most important directories are visited first.
00258   for (int di = 0; di < _search_path.get_num_directories(); ++di) {
00259     const Filename &directory = _search_path.get_directory(di);
00260     if (directory.is_directory()) {
00261       Filename canonical(directory, ".");
00262       canonical.make_canonical();
00263       if (unique_dirnames.insert(canonical).second) {
00264         vector_string files;
00265         directory.scan_directory(files);
00266 
00267         // We walk through the directory's list of files in reverse
00268         // alphabetical order, because for historical reasons, the
00269         // most important file within a directory is the
00270         // alphabetically last file of that directory, and we still
00271         // want to visit the most important files first.
00272         vector_string::reverse_iterator fi;
00273         for (fi = files.rbegin(); fi != files.rend(); ++fi) {
00274           int file_flags = 0;
00275           Globs::const_iterator gi;
00276           for (gi = _prc_patterns.begin();
00277                gi != _prc_patterns.end();
00278                ++gi) {
00279             if ((*gi).matches(*fi)) {
00280               file_flags |= FF_read;
00281               break;
00282             }
00283           }
00284           for (gi = _prc_encrypted_patterns.begin();
00285                gi != _prc_encrypted_patterns.end();
00286                ++gi) {
00287             if ((*gi).matches(*fi)) {
00288               file_flags |= FF_read | FF_decrypt;
00289               break;
00290             }
00291           }
00292           for (gi = _prc_executable_patterns.begin();
00293                gi != _prc_executable_patterns.end();
00294                ++gi) {
00295             if ((*gi).matches(*fi)) {
00296               file_flags |= FF_execute;
00297               break;
00298             }
00299           }
00300           if (file_flags != 0) {
00301             ConfigFile file;
00302             file._file_flags = file_flags;
00303             file._filename = Filename(directory, (*fi));
00304             config_files.push_back(file);
00305           }
00306         }
00307       }
00308     }
00309   }
00310 
00311   // Now we have a list of filenames in order from most important to
00312   // least important.  Walk through the list in reverse order to load
00313   // their contents, because we want the first file in the list (the
00314   // most important) to be on the top of the stack.
00315   ConfigFiles::reverse_iterator ci;
00316   int i = 1;
00317   for (ci = config_files.rbegin(); ci != config_files.rend(); ++ci) {
00318     const ConfigFile &file = (*ci);
00319     Filename filename = file._filename;
00320 
00321     if ((file._file_flags & FF_execute) != 0 &&
00322         filename.is_executable()) {
00323       // Attempt to execute the file as a command.
00324       string command = filename.to_os_specific();
00325 
00326       string envvar = PRC_EXECUTABLE_ARGS_ENVVAR;
00327       if (!envvar.empty()) {
00328         string args = ExecutionEnvironment::get_environment_variable(envvar);
00329         if (!args.empty()) {
00330           command += " ";
00331           command += args;
00332         }
00333       }
00334       IPipeStream ifs(command);
00335 
00336       ConfigPage *page = new ConfigPage(filename, true, i);
00337       ++i;
00338       _implicit_pages.push_back(page);
00339       _pages_sorted = false;
00340       
00341       page->read_prc(ifs);
00342 
00343     } else if ((file._file_flags & FF_decrypt) != 0) {
00344       // Read and decrypt the file.
00345       filename.set_binary();
00346       
00347       pifstream in;
00348       if (!filename.open_read(in)) {
00349         prc_cat.error()
00350           << "Unable to read " << filename << "\n";
00351       } else {
00352         ConfigPage *page = new ConfigPage(filename, true, i);
00353         ++i;
00354         _implicit_pages.push_back(page);
00355         _pages_sorted = false;
00356         
00357         page->read_encrypted_prc(in, PRC_ENCRYPTION_KEY);
00358       }
00359 
00360     } else if ((file._file_flags & FF_read) != 0) {
00361       // Just read the file.
00362       filename.set_text();
00363       
00364       pifstream in;
00365       if (!filename.open_read(in)) {
00366         prc_cat.error()
00367           << "Unable to read " << filename << "\n";
00368       } else {
00369         ConfigPage *page = new ConfigPage(filename, true, i);
00370         ++i;
00371         _implicit_pages.push_back(page);
00372         _pages_sorted = false;
00373         
00374         page->read_prc(in);
00375       }
00376     }
00377   }
00378 
00379   if (!_loaded_implicit) {
00380     config_initialized();
00381     _loaded_implicit = true;
00382   }
00383 
00384   _currently_loading = false;
00385   invalidate_cache();
00386 
00387 #ifdef USE_PANDAFILESTREAM
00388   // Update this very low-level config variable here, for lack of any
00389   // better place.
00390   ConfigVariableEnum<PandaFileStreamBuf::NewlineMode> newline_mode
00391     ("newline-mode", PandaFileStreamBuf::NM_native,
00392      PRC_DESC("Controls how newlines are written by Panda applications writing "
00393               "to a text file.  The default, \"native\", means to write newlines "
00394               "appropriate to the current platform.  You may also specify \"binary\", "
00395               "to avoid molesting the file data, or one of \"msdos\", \"unix\", "
00396               "or \"mac\"."));
00397   PandaFileStreamBuf::_newline_mode = newline_mode;
00398 #endif  // USE_PANDAFILESTREAM
00399 
00400 #ifdef WIN32
00401   // We don't necessarily want an error dialog when we fail to load a
00402   // .dll file.  But sometimes it is useful for debugging.
00403   ConfigVariableBool show_dll_error_dialog
00404     ("show-dll-error-dialog", false,
00405      PRC_DESC("Set this true to enable the Windows system dialog that pops "
00406               "up when a DLL fails to load, or false to disable it.  It is "
00407               "normally false, but it may be useful to set it true to debug "
00408               "why a DLL is not loading.  (Note that this actually disables "
00409               "*all* critical error messages, and that it's a global setting "
00410               "that some other libraries might un-set.)"));
00411   if (show_dll_error_dialog) {
00412     SetErrorMode(0);
00413   } else {
00414     SetErrorMode(SEM_FAILCRITICALERRORS);
00415   } 
00416 #endif
00417 
00418 }
00419 
00420 ////////////////////////////////////////////////////////////////////
00421 //     Function: ConfigPageManager::make_explicit_page
00422 //       Access: Published
00423 //  Description: Creates and returns a new, empty ConfigPage.  This
00424 //               page will be stacked on top of any pages that were
00425 //               created before; it may shadow variable declarations
00426 //               that are defined in previous pages.
00427 ////////////////////////////////////////////////////////////////////
00428 ConfigPage *ConfigPageManager::
00429 make_explicit_page(const string &name) {
00430   ConfigPage *page = new ConfigPage(name, false, _next_page_seq);
00431   ++_next_page_seq;
00432   _explicit_pages.push_back(page);
00433   _pages_sorted = false;
00434   invalidate_cache();
00435   return page;
00436 }
00437 
00438 ////////////////////////////////////////////////////////////////////
00439 //     Function: ConfigPageManager::delete_explicit_page
00440 //       Access: Published
00441 //  Description: Removes a previously-constructed ConfigPage from the
00442 //               set of active pages, and deletes it.  The ConfigPage
00443 //               object is no longer valid after this call.  Returns
00444 //               true if the page is successfully deleted, or false if
00445 //               it was unknown (which should never happen if the page
00446 //               was legitimately constructed).
00447 ////////////////////////////////////////////////////////////////////
00448 bool ConfigPageManager::
00449 delete_explicit_page(ConfigPage *page) {
00450   Pages::iterator pi;
00451   for (pi = _explicit_pages.begin(); pi != _explicit_pages.end(); ++pi) {
00452     if ((*pi) == page) {
00453       _explicit_pages.erase(pi);
00454       delete page;
00455       invalidate_cache();
00456       return true;
00457     }
00458   }
00459   return false;
00460 }
00461 
00462 ////////////////////////////////////////////////////////////////////
00463 //     Function: ConfigPageManager::output
00464 //       Access: Published
00465 //  Description: 
00466 ////////////////////////////////////////////////////////////////////
00467 void ConfigPageManager::
00468 output(ostream &out) const {
00469   out << "ConfigPageManager, " 
00470       << _explicit_pages.size() + _implicit_pages.size() 
00471       << " pages.";
00472 }
00473 
00474 ////////////////////////////////////////////////////////////////////
00475 //     Function: ConfigPageManager::write
00476 //       Access: Published
00477 //  Description: 
00478 ////////////////////////////////////////////////////////////////////
00479 void ConfigPageManager::
00480 write(ostream &out) const {
00481   check_sort_pages();
00482   out << _explicit_pages.size() << " explicit pages:\n";
00483 
00484   Pages::const_iterator pi;
00485   for (pi = _explicit_pages.begin(); pi != _explicit_pages.end(); ++pi) {
00486     const ConfigPage *page = (*pi);
00487     out << "  " << page->get_name();
00488     if (page->get_trust_level() > 0) {
00489       out << "  (signed " << page->get_trust_level() << ": ";
00490       page->output_brief_signature(out);
00491       out << ")\n";
00492     } else if (!page->get_signature().empty()) {
00493       out << "  (invalid signature: ";
00494       page->output_brief_signature(out);
00495       out << ")\n";
00496     } else {
00497       out << "\n";
00498     }
00499   }
00500 
00501   out << "\n" << _implicit_pages.size() << " implicit pages:\n";
00502   for (pi = _implicit_pages.begin(); pi != _implicit_pages.end(); ++pi) {
00503     const ConfigPage *page = (*pi);
00504     out << "  " << page->get_name();
00505     if (page->get_trust_level() > 0) {
00506       out << "  (signed " << page->get_trust_level() << ": ";
00507       page->output_brief_signature(out);
00508       out << ")\n";
00509     } else if (!page->get_signature().empty()) {
00510       out << "  (invalid signature: ";
00511       page->output_brief_signature(out);
00512       out << ")\n";
00513     } else {
00514       out << "\n";
00515     }
00516   }
00517 }
00518 
00519 ////////////////////////////////////////////////////////////////////
00520 //     Function: ConfigPageManager::get_global_ptr
00521 //       Access: Published
00522 //  Description: 
00523 ////////////////////////////////////////////////////////////////////
00524 ConfigPageManager *ConfigPageManager::
00525 get_global_ptr() {
00526   if (_global_ptr == (ConfigPageManager *)NULL) {
00527     _global_ptr = new ConfigPageManager;
00528   }
00529   return _global_ptr;
00530 }
00531 
00532 // This class is used in sort_pages, below.
00533 class CompareConfigPages {
00534 public:
00535   bool operator () (const ConfigPage *a, const ConfigPage *b) const {
00536     return (*a) < (*b);
00537   }
00538 };
00539 
00540 ////////////////////////////////////////////////////////////////////
00541 //     Function: ConfigPageManager::sort_pages
00542 //       Access: Private
00543 //  Description: Sorts the list of pages into priority order,
00544 //               so that the page at the front of the list is
00545 //               the one that shadows all following pages.
00546 ////////////////////////////////////////////////////////////////////
00547 void ConfigPageManager::
00548 sort_pages() {
00549   sort(_implicit_pages.begin(), _implicit_pages.end(), CompareConfigPages());
00550   sort(_explicit_pages.begin(), _explicit_pages.end(), CompareConfigPages());
00551 
00552   _pages_sorted = true;
00553 }
00554 
00555 ////////////////////////////////////////////////////////////////////
00556 //     Function: ConfigPageManager::scan_auto_prc_dir
00557 //       Access: Private
00558 //  Description: Checks for the prefix "<auto>" in the value of the
00559 //               $PRC_DIR environment variable (or in the compiled-in
00560 //               DEFAULT_PRC_DIR value).  If it is found, then the
00561 //               actual directory is determined by searching upward
00562 //               from the executable's starting directory, or from the
00563 //               current working directory, until at least one .prc
00564 //               file is found.
00565 //
00566 //               Returns true if the prc_dir has been filled with a
00567 //               valid directory name, false if no good directory name
00568 //               was found.
00569 ////////////////////////////////////////////////////////////////////
00570 bool ConfigPageManager::
00571 scan_auto_prc_dir(Filename &prc_dir) const {
00572   string prc_dir_string = prc_dir;
00573   if (prc_dir_string.substr(0, 6) == "<auto>") {
00574     Filename suffix = prc_dir_string.substr(6);
00575     
00576     // Start at the dtool directory.
00577     Filename dtool = ExecutionEnvironment::get_dtool_name();
00578     Filename dir = dtool.get_dirname();
00579 
00580     if (scan_up_from(prc_dir, dir, suffix)) {
00581       return true;
00582     }
00583     
00584     // Try the program's directory.
00585     dir = ExecutionEnvironment::get_environment_variable("MAIN_DIR");
00586     if (scan_up_from(prc_dir, dir, suffix)) {
00587       return true;
00588     }
00589 
00590     // Didn't find it; too bad.
00591     cerr << "Warning: unable to auto-locate config files in directory named by \""
00592          << prc_dir << "\".\n";
00593     return false;
00594   }
00595 
00596   // The filename did not begin with "<auto>", so it stands unchanged.
00597   return true;
00598 }
00599 
00600 ////////////////////////////////////////////////////////////////////
00601 //     Function: ConfigPageManager::scan_up_from
00602 //       Access: Private
00603 //  Description: Used to implement scan_auto_prc_dir(), above, this
00604 //               scans upward from the indicated directory name until
00605 //               a directory is found that includes at least one .prc
00606 //               file, or the root directory is reached.  
00607 //
00608 //               If a match is found, puts it result and returns true;
00609 //               otherwise, returns false.
00610 ////////////////////////////////////////////////////////////////////
00611 bool ConfigPageManager::
00612 scan_up_from(Filename &result, const Filename &dir, 
00613              const Filename &suffix) const {
00614   Filename consider(dir, suffix);
00615   
00616   vector_string files;
00617   if (consider.is_directory()) {
00618     if (consider.scan_directory(files)) {
00619       vector_string::const_iterator fi;
00620       for (fi = files.begin(); fi != files.end(); ++fi) {
00621         Globs::const_iterator gi;
00622         for (gi = _prc_patterns.begin();
00623              gi != _prc_patterns.end();
00624              ++gi) {
00625           if ((*gi).matches(*fi)) {
00626             result = consider;
00627             return true;
00628           }
00629         }
00630 
00631         for (gi = _prc_executable_patterns.begin();
00632              gi != _prc_executable_patterns.end();
00633              ++gi) {
00634           if ((*gi).matches(*fi)) {
00635             result = consider;
00636             return true;
00637           }
00638         }
00639       }
00640     }
00641   }
00642 
00643   Filename parent = dir.get_dirname();
00644 
00645   if (dir == parent) {
00646     // Too bad; couldn't find a match.
00647     return false;
00648   }
00649 
00650   // Recursively try again on the parent.
00651   return scan_up_from(result, parent, suffix);
00652 }
00653 
00654 ////////////////////////////////////////////////////////////////////
00655 //     Function: ConfigPageManager::config_initialized
00656 //       Access: Private
00657 //  Description: This is called once, at startup, the first time that
00658 //               the config system has been initialized and is ready
00659 //               to read config variables.  It's intended to be a
00660 //               place to initialize values that are defined at a
00661 //               lower level than the config system itself.
00662 ////////////////////////////////////////////////////////////////////
00663 void ConfigPageManager::
00664 config_initialized() {
00665   Notify::ptr()->config_initialized();
00666 
00667 #ifndef NDEBUG
00668   ConfigVariableString panda_package_version
00669     ("panda-package-version", "local_dev",
00670      PRC_DESC("This can be used to specify the value returned by "
00671               "PandaSystem::get_package_version_str(), in development mode only, "
00672               "and only if another value has not already been compiled in.  This "
00673               "is intended for developer convenience, to masquerade a development "
00674               "build of Panda as a different runtime version.  Use with caution."));
00675   ConfigVariableString panda_package_host_url
00676     ("panda-package-host-url", "",
00677      PRC_DESC("This can be used to specify the value returned by "
00678               "PandaSystem::get_package_host_url(), in development mode only, "
00679               "and only if another value has not already been compiled in.  This "
00680               "is intended for developer convenience, to masquerade a development "
00681               "build of Panda as a different runtime version.  Use with caution."));
00682 
00683   PandaSystem *panda_sys = PandaSystem::get_global_ptr();
00684   panda_sys->set_package_version_string(panda_package_version);
00685   panda_sys->set_package_host_url(panda_package_host_url);
00686 #endif  // NDEBUG
00687 
00688   // Also set up some other low-level things.
00689   ConfigVariableEnum<TextEncoder::Encoding> text_encoding
00690     ("text-encoding", TextEncoder::E_utf8,
00691      PRC_DESC("Specifies how international characters are represented in strings "
00692               "of 8-byte characters presented to Panda.  See TextEncoder::set_encoding()."));
00693   TextEncoder::set_default_encoding(text_encoding);
00694 
00695   ConfigVariableEnum<TextEncoder::Encoding> filesystem_encoding
00696     ("filesystem-encoding", TextEncoder::E_utf8,
00697      PRC_DESC("Specifies the default encoding used for wide-character filenames."));
00698   Filename::set_filesystem_encoding(filesystem_encoding);
00699 
00700   StringDecoder::set_notify_ptr(&Notify::out());
00701 }
 All Classes Functions Variables Enumerations