Panda3D
|
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 }