00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
00029 #include "prc_parameters.h"
00030
00031 #include <set>
00032
00033
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
00045
00046
00047
00048
00049
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
00060 PrcKeyRegistry::get_global_ptr()->record_keys(prc_pubkeys, num_prc_pubkeys);
00061 #endif // PRC_PUBLIC_KEYS_INCLUDE
00062 }
00063
00064
00065
00066
00067
00068
00069
00070
00071 ConfigPageManager::
00072 ~ConfigPageManager() {
00073 prc_cat->error()
00074 << "Internal error--ConfigPageManager destructor called!\n";
00075 }
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086 void ConfigPageManager::
00087 reload_implicit_pages() {
00088 if (_currently_loading) {
00089
00090
00091
00092 return;
00093 }
00094 _currently_loading = true;
00095
00096
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
00104
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
00116
00117 glob.set_case_sensitive(false);
00118 #endif // WIN32
00119 _prc_patterns.push_back(glob);
00120 }
00121 }
00122
00123
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
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
00158 _search_path.clear();
00159
00160
00161
00162
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
00180
00181
00182
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
00206
00207
00208
00209
00210
00211
00212
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
00236
00237 string default_prc_dir = DEFAULT_PRC_DIR;
00238 if (!default_prc_dir.empty()) {
00239
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
00248
00249 ConfigFiles config_files;
00250
00251
00252
00253
00254 set<Filename> unique_dirnames;
00255
00256
00257
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
00268
00269
00270
00271
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
00312
00313
00314
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
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
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
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
00389
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
00402
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
00422
00423
00424
00425
00426
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
00440
00441
00442
00443
00444
00445
00446
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
00464
00465
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
00476
00477
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
00521
00522
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
00533 class CompareConfigPages {
00534 public:
00535 bool operator () (const ConfigPage *a, const ConfigPage *b) const {
00536 return (*a) < (*b);
00537 }
00538 };
00539
00540
00541
00542
00543
00544
00545
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
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
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
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
00585 dir = ExecutionEnvironment::get_environment_variable("MAIN_DIR");
00586 if (scan_up_from(prc_dir, dir, suffix)) {
00587 return true;
00588 }
00589
00590
00591 cerr << "Warning: unable to auto-locate config files in directory named by \""
00592 << prc_dir << "\".\n";
00593 return false;
00594 }
00595
00596
00597 return true;
00598 }
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
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
00647 return false;
00648 }
00649
00650
00651 return scan_up_from(result, parent, suffix);
00652 }
00653
00654
00655
00656
00657
00658
00659
00660
00661
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
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 }