Panda3D
configPageManager.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file configPageManager.cxx
10  * @author drose
11  * @date 2004-10-15
12  */
13 
14 #include "configPageManager.h"
15 #include "configDeclaration.h"
16 #include "configVariableBool.h"
17 #include "configVariableString.h"
18 #include "configPage.h"
19 #include "prcKeyRegistry.h"
20 #include "dSearchPath.h"
21 #include "executionEnvironment.h"
22 #include "config_prc.h"
23 #include "pfstream.h"
24 #include "pandaSystem.h"
25 #include "textEncoder.h"
26 #include "stringDecoder.h"
27 
28 // This file is generated by ppremake.
29 #include "prc_parameters.h"
30 
31 #include <set>
32 
33 // Pick up the public key definitions.
34 #ifdef PRC_PUBLIC_KEYS_INCLUDE
35 #include PRC_PUBLIC_KEYS_INCLUDE
36 #endif
37 
38 #include <algorithm>
39 #include <ctype.h>
40 
41 #ifndef _MSC_VER
42 #include <dlfcn.h>
43 #endif
44 
45 using std::string;
46 
47 ConfigPageManager *ConfigPageManager::_global_ptr = nullptr;
48 
49 /**
50  * The constructor is private (actually, just protected, but only to avoid a
51  * gcc compiler warning) because it should not be explicitly constructed.
52  * There is only one ConfigPageManager, and it constructs itself.
53  */
54 ConfigPageManager::
55 ConfigPageManager() {
56  _next_page_seq = 1;
57  _loaded_implicit = false;
58  _currently_loading = false;
59  _pages_sorted = true;
60 
61 #ifdef PRC_PUBLIC_KEYS_INCLUDE
62  // Record the public keys in the registry at startup time.
63  PrcKeyRegistry::get_global_ptr()->record_keys(prc_pubkeys, num_prc_pubkeys);
64 #endif // PRC_PUBLIC_KEYS_INCLUDE
65 }
66 
67 /**
68  * The ConfigPageManager destructor should never be called, because this is a
69  * global object that is never freed.
70  */
71 ConfigPageManager::
72 ~ConfigPageManager() {
73  prc_cat->error()
74  << "Internal error--ConfigPageManager destructor called!\n";
75 }
76 
77 /**
78  * Searches the PRC_DIR and/or PRC_PATH directories for *.prc files and loads
79  * them in as pages.
80  *
81  * This may be called after startup, to force the system to re-read all of the
82  * implicit prc files.
83  */
86  if (_currently_loading) {
87  // This is a recursion protector. We can get recursion feedback between
88  // config and notify, as each tries to use the other at construction.
89  return;
90  }
91  _currently_loading = true;
92 
93  // First, remove all the previously-loaded pages.
94  Pages::iterator pi;
95  for (pi = _implicit_pages.begin(); pi != _implicit_pages.end(); ++pi) {
96  delete (*pi);
97  }
98  _implicit_pages.clear();
99 
100  // If we are running inside a deployed application, see if it exposes
101  // information about how the PRC data should be initialized.
102  struct BlobInfo {
103  uint64_t blob_offset;
104  uint64_t blob_size;
105  uint16_t version;
106  uint16_t num_pointers;
107  uint16_t codepage;
108  uint16_t flags;
109  uint64_t reserved;
110  const void *module_table;
111  const char *prc_data;
112  const char *default_prc_dir;
113  const char *prc_dir_envvars;
114  const char *prc_path_envvars;
115  const char *prc_patterns;
116  const char *prc_encrypted_patterns;
117  const char *prc_encryption_key;
118  const char *prc_executable_patterns;
119  const char *prc_executable_args_envvar;
120  const char *main_dir;
121  const char *log_filename;
122  };
123 #ifdef _MSC_VER
124  const BlobInfo *blobinfo = (const BlobInfo *)GetProcAddress(GetModuleHandle(NULL), "blobinfo");
125 #elif defined(RTLD_MAIN_ONLY)
126  const BlobInfo *blobinfo = (const BlobInfo *)dlsym(RTLD_MAIN_ONLY, "blobinfo");
127 //#elif defined(RTLD_SELF)
128 // const BlobInfo *blobinfo = (const BlobInfo *)dlsym(RTLD_SELF, "blobinfo");
129 #else
130  const BlobInfo *blobinfo = (const BlobInfo *)dlsym(dlopen(NULL, RTLD_NOW), "blobinfo");
131 #endif
132  if (blobinfo == nullptr) {
133 #ifndef _MSC_VER
134  // Clear the error flag.
135  dlerror();
136 #endif
137  } else if (blobinfo->version == 0 || blobinfo->num_pointers < 10) {
138  blobinfo = nullptr;
139  }
140 
141  if (blobinfo != nullptr) {
142  if (blobinfo->num_pointers >= 11 && blobinfo->main_dir != nullptr) {
143  ExecutionEnvironment::set_environment_variable("MAIN_DIR", blobinfo->main_dir);
144  } else {
145  // Make sure that py_panda.cxx won't override MAIN_DIR.
148  }
149  }
150 
151  // PRC_PATTERNS lists one or more filename templates separated by spaces.
152  // Pull them out and store them in _prc_patterns.
153  _prc_patterns.clear();
154 
155  string prc_patterns = PRC_PATTERNS;
156  if (blobinfo != nullptr && blobinfo->prc_patterns != nullptr) {
157  prc_patterns = blobinfo->prc_patterns;
158  }
159  if (!prc_patterns.empty()) {
160  vector_string pat_list;
161  ConfigDeclaration::extract_words(prc_patterns, pat_list);
162  _prc_patterns.reserve(pat_list.size());
163  for (size_t i = 0; i < pat_list.size(); ++i) {
164  GlobPattern glob(pat_list[i]);
165 #ifdef WIN32
166  // On windows, the file system is case-insensitive, so the pattern
167  // should be too.
168  glob.set_case_sensitive(false);
169 #endif // WIN32
170  _prc_patterns.push_back(glob);
171  }
172  }
173 
174  // Similarly for PRC_ENCRYPTED_PATTERNS.
175  _prc_encrypted_patterns.clear();
176 
177  string prc_encrypted_patterns = PRC_ENCRYPTED_PATTERNS;
178  if (blobinfo != nullptr && blobinfo->prc_encrypted_patterns != nullptr) {
179  prc_encrypted_patterns = blobinfo->prc_encrypted_patterns;
180  }
181  if (!prc_encrypted_patterns.empty()) {
182  vector_string pat_list;
183  ConfigDeclaration::extract_words(prc_encrypted_patterns, pat_list);
184  _prc_encrypted_patterns.reserve(pat_list.size());
185  for (size_t i = 0; i < pat_list.size(); ++i) {
186  GlobPattern glob(pat_list[i]);
187 #ifdef WIN32
188  glob.set_case_sensitive(false);
189 #endif // WIN32
190  _prc_encrypted_patterns.push_back(glob);
191  }
192  }
193 
194  // And again for PRC_EXECUTABLE_PATTERNS.
195  _prc_executable_patterns.clear();
196 
197  string prc_executable_patterns = PRC_EXECUTABLE_PATTERNS;
198  if (blobinfo != nullptr && blobinfo->prc_executable_patterns != nullptr) {
199  prc_executable_patterns = blobinfo->prc_executable_patterns;
200  }
201  if (!prc_executable_patterns.empty()) {
202  vector_string pat_list;
203  ConfigDeclaration::extract_words(prc_executable_patterns, pat_list);
204  _prc_executable_patterns.reserve(pat_list.size());
205  for (size_t i = 0; i < pat_list.size(); ++i) {
206  GlobPattern glob(pat_list[i]);
207 #ifdef WIN32
208  glob.set_case_sensitive(false);
209 #endif // WIN32
210  _prc_executable_patterns.push_back(glob);
211  }
212  }
213 
214  // Now build up the search path for .prc files.
215  _search_path.clear();
216 
217  // PRC_DIR_ENVVARS lists one or more environment variables separated by
218  // spaces. Pull them out, and each of those contains the name of a single
219  // directory to search. Add it to the search path.
220  string prc_dir_envvars = PRC_DIR_ENVVARS;
221  if (blobinfo != nullptr && blobinfo->prc_dir_envvars != nullptr) {
222  prc_dir_envvars = blobinfo->prc_dir_envvars;
223  }
224  if (!prc_dir_envvars.empty()) {
225  vector_string prc_dir_envvar_list;
226  ConfigDeclaration::extract_words(prc_dir_envvars, prc_dir_envvar_list);
227  for (size_t i = 0; i < prc_dir_envvar_list.size(); ++i) {
228  string prc_dir = ExecutionEnvironment::get_environment_variable(prc_dir_envvar_list[i]);
229  if (!prc_dir.empty()) {
230  Filename prc_dir_filename = Filename::from_os_specific(prc_dir);
231  prc_dir_filename.make_true_case();
232  if (scan_auto_prc_dir(prc_dir_filename)) {
233  _search_path.append_directory(prc_dir_filename);
234  }
235  }
236  }
237  }
238 
239  // PRC_PATH_ENVVARS lists one or more environment variables separated by
240  // spaces. Pull them out, and then each one of those contains a list of
241  // directories to search. Add each of those to the search path.
242  string prc_path_envvars = PRC_PATH_ENVVARS;
243  if (blobinfo != nullptr && blobinfo->prc_path_envvars != nullptr) {
244  prc_path_envvars = blobinfo->prc_path_envvars;
245  }
246  if (!prc_path_envvars.empty()) {
247  vector_string prc_path_envvar_list;
248  ConfigDeclaration::extract_words(prc_path_envvars, prc_path_envvar_list);
249  for (size_t i = 0; i < prc_path_envvar_list.size(); ++i) {
250  string path = ExecutionEnvironment::get_environment_variable(prc_path_envvar_list[i]);
251  size_t p = 0;
252  while (p < path.length()) {
253  size_t q = path.find_first_of(DEFAULT_PATHSEP, p);
254  if (q == string::npos) {
255  q = path.length();
256  }
257  Filename prc_dir_filename = Filename::from_os_specific(path.substr(p, q - p));
258  prc_dir_filename.make_true_case();
259  if (scan_auto_prc_dir(prc_dir_filename)) {
260  _search_path.append_directory(prc_dir_filename);
261  }
262  p = q + 1;
263  }
264  }
265  }
266 
267 /*
268  * PRC_PATH2_ENVVARS is a special variable that is rarely used; it exists
269  * primarily to support the Cygwin-based "ctattach" tools used by the Walt
270  * Disney VR Studio. This defines a set of environment variable(s) that
271  * define a search path, as above; except that the directory names on these
272  * search paths are Panda-style filenames, not Windows-style filenames; and
273  * the path separator is always a space character, regardless of
274  * DEFAULT_PATHSEP.
275  */
276  string prc_path2_envvars = PRC_PATH2_ENVVARS;
277  if (!prc_path2_envvars.empty() && blobinfo == nullptr) {
278  vector_string prc_path_envvar_list;
279  ConfigDeclaration::extract_words(prc_path2_envvars, prc_path_envvar_list);
280  for (size_t i = 0; i < prc_path_envvar_list.size(); ++i) {
281  string path = ExecutionEnvironment::get_environment_variable(prc_path_envvar_list[i]);
282  size_t p = 0;
283  while (p < path.length()) {
284  size_t q = path.find_first_of(' ', p);
285  if (q == string::npos) {
286  q = path.length();
287  }
288  Filename prc_dir_filename = path.substr(p, q - p);
289  if (scan_auto_prc_dir(prc_dir_filename)) {
290  _search_path.append_directory(prc_dir_filename);
291  }
292  p = q + 1;
293  }
294  }
295  }
296 
297  if (_search_path.is_empty()) {
298  // If nothing's on the search path (PRC_DIR and PRC_PATH were not
299  // defined), then use the DEFAULT_PRC_DIR.
300  string default_prc_dir = DEFAULT_PRC_DIR;
301  if (blobinfo != nullptr && blobinfo->default_prc_dir != nullptr) {
302  default_prc_dir = blobinfo->default_prc_dir;
303  }
304  if (!default_prc_dir.empty()) {
305  // It's already from-os-specific by ppremake.
306  Filename prc_dir_filename = default_prc_dir;
307  if (scan_auto_prc_dir(prc_dir_filename)) {
308  _search_path.append_directory(prc_dir_filename);
309  }
310  }
311  }
312 
313  // Now find all of the *.prc files (or whatever matches PRC_PATTERNS) on the
314  // path.
315  ConfigFiles config_files;
316 
317  // Use a set to ensure that we only visit each directory once, even if it
318  // appears multiple times (under different aliases!) in the path.
319  std::set<Filename> unique_dirnames;
320 
321  // We walk through the list of directories in forward order, so that the
322  // most important directories are visited first.
323  for (size_t di = 0; di < _search_path.get_num_directories(); ++di) {
324  const Filename &directory = _search_path.get_directory(di);
325  if (directory.is_directory()) {
326  Filename canonical(directory, ".");
327  canonical.make_canonical();
328  if (unique_dirnames.insert(canonical).second) {
329  vector_string files;
330  directory.scan_directory(files);
331 
332  // We walk through the directory's list of files in reverse
333  // alphabetical order, because for historical reasons, the most
334  // important file within a directory is the alphabetically last file
335  // of that directory, and we still want to visit the most important
336  // files first.
337  vector_string::reverse_iterator fi;
338  for (fi = files.rbegin(); fi != files.rend(); ++fi) {
339  int file_flags = 0;
340  Globs::const_iterator gi;
341  for (gi = _prc_patterns.begin();
342  gi != _prc_patterns.end();
343  ++gi) {
344  if ((*gi).matches(*fi)) {
345  file_flags |= FF_read;
346  break;
347  }
348  }
349  for (gi = _prc_encrypted_patterns.begin();
350  gi != _prc_encrypted_patterns.end();
351  ++gi) {
352  if ((*gi).matches(*fi)) {
353  file_flags |= FF_read | FF_decrypt;
354  break;
355  }
356  }
357  for (gi = _prc_executable_patterns.begin();
358  gi != _prc_executable_patterns.end();
359  ++gi) {
360  if ((*gi).matches(*fi)) {
361  file_flags |= FF_execute;
362  break;
363  }
364  }
365  if (file_flags != 0) {
366  ConfigFile file;
367  file._file_flags = file_flags;
368  file._filename = Filename(directory, (*fi));
369  config_files.push_back(file);
370  }
371  }
372  }
373  }
374  }
375 
376  int i = 1;
377 
378  // If prc_data is predefined, we load it as an implicit page.
379  if (blobinfo != nullptr && blobinfo->prc_data != nullptr) {
380  ConfigPage *page = new ConfigPage("builtin", true, i);
381  ++i;
382  _implicit_pages.push_back(page);
383  _pages_sorted = false;
384 
385  std::istringstream in(blobinfo->prc_data);
386  page->read_prc(in);
387  }
388 
389  // Now we have a list of filenames in order from most important to least
390  // important. Walk through the list in reverse order to load their
391  // contents, because we want the first file in the list (the most important)
392  // to be on the top of the stack.
393  ConfigFiles::reverse_iterator ci;
394  for (ci = config_files.rbegin(); ci != config_files.rend(); ++ci) {
395  const ConfigFile &file = (*ci);
396  Filename filename = file._filename;
397 
398  if ((file._file_flags & FF_execute) != 0 &&
399  filename.is_executable()) {
400  // Attempt to execute the file as a command.
401  string command = filename.to_os_specific();
402 
403  string envvar = PRC_EXECUTABLE_ARGS_ENVVAR;
404  if (blobinfo != nullptr && blobinfo->prc_executable_args_envvar != nullptr) {
405  envvar = blobinfo->prc_executable_args_envvar;
406  }
407  if (!envvar.empty()) {
409  if (!args.empty()) {
410  command += " ";
411  command += args;
412  }
413  }
414  IPipeStream ifs(command);
415 
416  ConfigPage *page = new ConfigPage(filename, true, i);
417  ++i;
418  _implicit_pages.push_back(page);
419  _pages_sorted = false;
420 
421  page->read_prc(ifs);
422 
423  } else if ((file._file_flags & FF_decrypt) != 0) {
424  // Read and decrypt the file.
425  filename.set_binary();
426 
427  pifstream in;
428  if (!filename.open_read(in)) {
429  prc_cat.error()
430  << "Unable to read " << filename << "\n";
431  } else {
432  ConfigPage *page = new ConfigPage(filename, true, i);
433  ++i;
434  _implicit_pages.push_back(page);
435  _pages_sorted = false;
436 
437  if (blobinfo != nullptr && blobinfo->prc_encryption_key != nullptr) {
438  page->read_encrypted_prc(in, blobinfo->prc_encryption_key);
439  } else {
440  page->read_encrypted_prc(in, PRC_ENCRYPTION_KEY);
441  }
442  }
443 
444  } else if ((file._file_flags & FF_read) != 0) {
445  // Just read the file.
446  filename.set_text();
447 
448  pifstream in;
449  if (!filename.open_read(in)) {
450  prc_cat.error()
451  << "Unable to read " << filename << "\n";
452  } else {
453  ConfigPage *page = new ConfigPage(filename, true, i);
454  ++i;
455  _implicit_pages.push_back(page);
456  _pages_sorted = false;
457 
458  page->read_prc(in);
459  }
460  }
461  }
462 
463  if (!_loaded_implicit) {
464  config_initialized();
465  _loaded_implicit = true;
466  }
467 
468  _currently_loading = false;
469  invalidate_cache();
470 
471 #ifdef USE_PANDAFILESTREAM
472  // Update this very low-level config variable here, for lack of any better
473  // place.
475  ("newline-mode", PandaFileStreamBuf::NM_native,
476  PRC_DESC("Controls how newlines are written by Panda applications writing "
477  "to a text file. The default, \"native\", means to write newlines "
478  "appropriate to the current platform. You may also specify \"binary\", "
479  "to avoid molesting the file data, or one of \"msdos\", \"unix\", "
480  "or \"mac\"."));
481  PandaFileStreamBuf::_newline_mode = newline_mode;
482 #endif // USE_PANDAFILESTREAM
483 
484 #ifdef WIN32
485  // We don't necessarily want an error dialog when we fail to load a .dll
486  // file. But sometimes it is useful for debugging.
487  ConfigVariableBool show_dll_error_dialog
488  ("show-dll-error-dialog", false,
489  PRC_DESC("Set this true to enable the Windows system dialog that pops "
490  "up when a DLL fails to load, or false to disable it. It is "
491  "normally false, but it may be useful to set it true to debug "
492  "why a DLL is not loading. (Note that this actually disables "
493  "*all* critical error messages, and that it's a global setting "
494  "that some other libraries might un-set.)"));
495  if (show_dll_error_dialog) {
496  SetErrorMode(0);
497  } else {
498  SetErrorMode(SEM_FAILCRITICALERRORS);
499  }
500 #endif
501 
502 }
503 
504 /**
505  * Creates and returns a new, empty ConfigPage. This page will be stacked on
506  * top of any pages that were created before; it may shadow variable
507  * declarations that are defined in previous pages.
508  */
510 make_explicit_page(const string &name) {
511  ConfigPage *page = new ConfigPage(name, false, _next_page_seq);
512  ++_next_page_seq;
513  _explicit_pages.push_back(page);
514  _pages_sorted = false;
515  invalidate_cache();
516  return page;
517 }
518 
519 /**
520  * Removes a previously-constructed ConfigPage from the set of active pages,
521  * and deletes it. The ConfigPage object is no longer valid after this call.
522  * Returns true if the page is successfully deleted, or false if it was
523  * unknown (which should never happen if the page was legitimately
524  * constructed).
525  */
528  Pages::iterator pi;
529  for (pi = _explicit_pages.begin(); pi != _explicit_pages.end(); ++pi) {
530  if ((*pi) == page) {
531  _explicit_pages.erase(pi);
532  delete page;
533  invalidate_cache();
534  return true;
535  }
536  }
537  return false;
538 }
539 
540 /**
541  *
542  */
543 void ConfigPageManager::
544 output(std::ostream &out) const {
545  out << "ConfigPageManager, "
546  << _explicit_pages.size() + _implicit_pages.size()
547  << " pages.";
548 }
549 
550 /**
551  *
552  */
553 void ConfigPageManager::
554 write(std::ostream &out) const {
555  check_sort_pages();
556  out << _explicit_pages.size() << " explicit pages:\n";
557 
558  Pages::const_iterator pi;
559  for (pi = _explicit_pages.begin(); pi != _explicit_pages.end(); ++pi) {
560  const ConfigPage *page = (*pi);
561  out << " " << page->get_name();
562  if (page->get_trust_level() > 0) {
563  out << " (signed " << page->get_trust_level() << ": ";
564  page->output_brief_signature(out);
565  out << ")\n";
566  } else if (!page->get_signature().empty()) {
567  out << " (invalid signature: ";
568  page->output_brief_signature(out);
569  out << ")\n";
570  } else {
571  out << "\n";
572  }
573  }
574 
575  out << "\n" << _implicit_pages.size() << " implicit pages:\n";
576  for (pi = _implicit_pages.begin(); pi != _implicit_pages.end(); ++pi) {
577  const ConfigPage *page = (*pi);
578  out << " " << page->get_name();
579  if (page->get_trust_level() > 0) {
580  out << " (signed " << page->get_trust_level() << ": ";
581  page->output_brief_signature(out);
582  out << ")\n";
583  } else if (!page->get_signature().empty()) {
584  out << " (invalid signature: ";
585  page->output_brief_signature(out);
586  out << ")\n";
587  } else {
588  out << "\n";
589  }
590  }
591 }
592 
593 /**
594  *
595  */
596 ConfigPageManager *ConfigPageManager::
597 get_global_ptr() {
598  if (_global_ptr == nullptr) {
599  _global_ptr = new ConfigPageManager;
600  }
601  return _global_ptr;
602 }
603 
604 // This class is used in sort_pages, below.
605 class CompareConfigPages {
606 public:
607  bool operator () (const ConfigPage *a, const ConfigPage *b) const {
608  return (*a) < (*b);
609  }
610 };
611 
612 /**
613  * Sorts the list of pages into priority order, so that the page at the front
614  * of the list is the one that shadows all following pages.
615  */
616 void ConfigPageManager::
617 sort_pages() {
618  sort(_implicit_pages.begin(), _implicit_pages.end(), CompareConfigPages());
619  sort(_explicit_pages.begin(), _explicit_pages.end(), CompareConfigPages());
620 
621  _pages_sorted = true;
622 }
623 
624 /**
625  * Checks for the prefix "<auto>" in the value of the $PRC_DIR environment
626  * variable (or in the compiled-in DEFAULT_PRC_DIR value). If it is found,
627  * then the actual directory is determined by searching upward from the
628  * executable's starting directory, or from the current working directory,
629  * until at least one .prc file is found.
630  *
631  * Returns true if the prc_dir has been filled with a valid directory name,
632  * false if no good directory name was found.
633  */
634 bool ConfigPageManager::
635 scan_auto_prc_dir(Filename &prc_dir) const {
636  string prc_dir_string = prc_dir;
637  if (prc_dir_string.substr(0, 6) == "<auto>") {
638  Filename suffix = prc_dir_string.substr(6);
639 
640  // Start at the dtool directory.
642  Filename dir = dtool.get_dirname();
643 
644  if (scan_up_from(prc_dir, dir, suffix)) {
645  return true;
646  }
647 
648  // Try the program's directory.
650  if (scan_up_from(prc_dir, dir, suffix)) {
651  return true;
652  }
653 
654  // Didn't find it; too bad.
655  std::cerr << "Warning: unable to auto-locate config files in directory named by \""
656  << prc_dir << "\".\n";
657  return false;
658  }
659 
660  // The filename did not begin with "<auto>", so it stands unchanged.
661  return true;
662 }
663 
664 /**
665  * Used to implement scan_auto_prc_dir(), above, this scans upward from the
666  * indicated directory name until a directory is found that includes at least
667  * one .prc file, or the root directory is reached.
668  *
669  * If a match is found, puts it result and returns true; otherwise, returns
670  * false.
671  */
672 bool ConfigPageManager::
673 scan_up_from(Filename &result, const Filename &dir,
674  const Filename &suffix) const {
675  Filename consider(dir, suffix);
676 
677  vector_string files;
678  if (consider.is_directory()) {
679  if (consider.scan_directory(files)) {
680  vector_string::const_iterator fi;
681  for (fi = files.begin(); fi != files.end(); ++fi) {
682  Globs::const_iterator gi;
683  for (gi = _prc_patterns.begin();
684  gi != _prc_patterns.end();
685  ++gi) {
686  if ((*gi).matches(*fi)) {
687  result = consider;
688  return true;
689  }
690  }
691 
692  for (gi = _prc_executable_patterns.begin();
693  gi != _prc_executable_patterns.end();
694  ++gi) {
695  if ((*gi).matches(*fi)) {
696  result = consider;
697  return true;
698  }
699  }
700  }
701  }
702  }
703 
704  Filename parent = dir.get_dirname();
705 
706  if (dir == parent) {
707  // Too bad; couldn't find a match.
708  return false;
709  }
710 
711  // Recursively try again on the parent.
712  return scan_up_from(result, parent, suffix);
713 }
714 
715 /**
716  * This is called once, at startup, the first time that the config system has
717  * been initialized and is ready to read config variables. It's intended to
718  * be a place to initialize values that are defined at a lower level than the
719  * config system itself.
720  */
721 void ConfigPageManager::
722 config_initialized() {
724 
725 #ifndef NDEBUG
726  ConfigVariableString panda_package_version
727  ("panda-package-version", "local_dev",
728  PRC_DESC("This can be used to specify the value returned by "
729  "PandaSystem::get_package_version_str(), in development mode only, "
730  "and only if another value has not already been compiled in. This "
731  "is intended for developer convenience, to masquerade a development "
732  "build of Panda as a different runtime version. Use with caution."));
733  ConfigVariableString panda_package_host_url
734  ("panda-package-host-url", "",
735  PRC_DESC("This can be used to specify the value returned by "
736  "PandaSystem::get_package_host_url(), in development mode only, "
737  "and only if another value has not already been compiled in. This "
738  "is intended for developer convenience, to masquerade a development "
739  "build of Panda as a different runtime version. Use with caution."));
740 
742  panda_sys->set_package_version_string(panda_package_version);
743  panda_sys->set_package_host_url(panda_package_host_url);
744 #endif // NDEBUG
745 
746  // Also set up some other low-level things.
748  ("text-encoding", TextEncoder::E_utf8,
749  PRC_DESC("Specifies how international characters are represented in strings "
750  "of 8-byte characters presented to Panda. See TextEncoder::set_encoding()."));
751  TextEncoder::set_default_encoding(text_encoding);
752 
754  ("filesystem-encoding", TextEncoder::E_utf8,
755  PRC_DESC("Specifies the default encoding used for wide-character filenames."));
756  Filename::set_filesystem_encoding(filesystem_encoding);
757 
759 }
static size_t extract_words(const std::string &str, vector_string &words)
Divides the string into a number of words according to whitespace.
A global object that maintains the set of ConfigPages everywhere in the world, and keeps them in sort...
bool delete_explicit_page(ConfigPage *page)
Removes a previously-constructed ConfigPage from the set of active pages, and deletes it.
ConfigPage * make_explicit_page(const std::string &name)
Creates and returns a new, empty ConfigPage.
void reload_implicit_pages()
Searches the PRC_DIR and/or PRC_PATH directories for *.prc files and loads them in as pages.
A page of ConfigDeclarations that may be loaded or unloaded.
Definition: configPage.h:30
get_signature
Returns the raw binary signature that was found in the prc file, if any.
Definition: configPage.h:60
bool read_encrypted_prc(std::istream &in, const std::string &password)
Automatically decrypts and reads the stream, given the indicated password.
Definition: configPage.cxx:224
get_trust_level
Returns the trust level associated with this page.
Definition: configPage.h:59
bool read_prc(std::istream &in)
Reads the contents of a complete prc file, as returned by the indicated istream, into the current pag...
Definition: configPage.cxx:124
void output_brief_signature(std::ostream &out) const
Outputs the first few hex digits of the signature.
Definition: configPage.cxx:346
get_name
Returns the name of the page.
Definition: configPage.h:43
This is a convenience class to specialize ConfigVariable as a boolean type.
This class specializes ConfigVariable as an enumerated type.
This is a convenience class to specialize ConfigVariable as a string type.
bool is_empty() const
Returns true if the search list is empty, false otherwise.
get_num_directories
Returns the number of directories on the search list.
Definition: dSearchPath.h:76
void clear()
Removes all the directories from the search list.
void append_directory(const Filename &directory)
Adds a new directory to the end of the search list.
get_directory
Returns the nth directory on the search list.
Definition: dSearchPath.h:76
get_dtool_name
Returns the name of the libdtool DLL that is used in this program, if it can be determined.
set_environment_variable
Changes the definition of the indicated environment variable.
get_environment_variable
Returns the definition of the indicated environment variable, or the empty string if the variable is ...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
bool scan_directory(vector_string &contents) const
Attempts to open the named filename as if it were a directory and looks for the non-hidden files with...
Definition: filename.cxx:1718
bool is_executable() const
Returns true if the filename exists and is executable.
Definition: filename.cxx:1387
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1123
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:1863
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:414
bool make_true_case()
On a case-insensitive operating system (e.g.
Definition: filename.cxx:1053
static void set_filesystem_encoding(TextEncoder::Encoding encoding)
Specifies the default encoding to be used for all subsequent Filenames.
Definition: filename.I:630
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:424
bool make_canonical()
Converts this filename to a canonical name by replacing the directory part with the fully-qualified d...
Definition: filename.cxx:1011
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
Definition: filename.cxx:1359
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
set_case_sensitive
Sets whether the match is case sensitive (true) or case insensitive (false).
Definition: globPattern.h:48
static std::ostream & out()
A convenient way to get the ostream that should be written to for a Notify- type message.
Definition: notify.cxx:265
void config_initialized()
Intended to be called only by Config, this is a callback that indicates to Notify when Config has don...
Definition: notify.cxx:428
static Notify * ptr()
Returns the pointer to the global Notify object.
Definition: notify.cxx:293
This class is used as a namespace to group several global properties of Panda.
Definition: pandaSystem.h:26
static PandaSystem * get_global_ptr()
Returns the global PandaSystem object.
static void set_notify_ptr(std::ostream *ptr)
Sets the ostream that is used to write error messages to.
set_default_encoding
Specifies the default encoding to be used for all subsequently created TextEncoder objects.
Definition: textEncoder.h:54
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.