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