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