2
0
Эх сурвалжийг харах

first pass at new config interface

David Rose 21 жил өмнө
parent
commit
378645efbe
71 өөрчлөгдсөн 6509 нэмэгдсэн , 248 устгасан
  1. 12 16
      direct/src/dcparser/dcFile.cxx
  2. 18 0
      direct/src/extensions/ConfigVariableList-extensions.py
  3. 87 11
      dtool/Config.pp
  4. 37 2
      dtool/LocalSetup.pp
  5. 1 1
      dtool/metalibs/dtoolconfig/Sources.pp
  6. 5 15
      dtool/src/dconfig/Sources.pp
  7. 0 1
      dtool/src/dconfig/configTable.cxx
  8. 0 47
      dtool/src/dconfig/config_notify.cxx
  9. 24 98
      dtool/src/dconfig/dconfig.h
  10. 0 5
      dtool/src/dconfig/dconfig_composite1.cxx
  11. 2 2
      dtool/src/dtoolutil/dSearchPath.I
  12. 32 14
      dtool/src/dtoolutil/dSearchPath.cxx
  13. 4 4
      dtool/src/dtoolutil/dSearchPath.h
  14. 47 0
      dtool/src/prc/Sources.pp
  15. 249 0
      dtool/src/prc/configDeclaration.I
  16. 338 0
      dtool/src/prc/configDeclaration.cxx
  17. 119 0
      dtool/src/prc/configDeclaration.h
  18. 143 0
      dtool/src/prc/configPage.I
  19. 427 0
      dtool/src/prc/configPage.cxx
  20. 105 0
      dtool/src/prc/configPage.h
  21. 187 0
      dtool/src/prc/configPageManager.I
  22. 407 0
      dtool/src/prc/configPageManager.cxx
  23. 108 0
      dtool/src/prc/configPageManager.h
  24. 365 0
      dtool/src/prc/configVariable.I
  25. 43 0
      dtool/src/prc/configVariable.cxx
  26. 92 0
      dtool/src/prc/configVariable.h
  27. 141 0
      dtool/src/prc/configVariableBool.I
  28. 19 0
      dtool/src/prc/configVariableBool.cxx
  29. 54 0
      dtool/src/prc/configVariableBool.h
  30. 251 0
      dtool/src/prc/configVariableCore.I
  31. 467 0
      dtool/src/prc/configVariableCore.cxx
  32. 125 0
      dtool/src/prc/configVariableCore.h
  33. 126 0
      dtool/src/prc/configVariableDouble.I
  34. 37 0
      dtool/src/prc/configVariableDouble.cxx
  35. 54 0
      dtool/src/prc/configVariableDouble.h
  36. 127 0
      dtool/src/prc/configVariableInt.I
  37. 37 0
      dtool/src/prc/configVariableInt.cxx
  38. 54 0
      dtool/src/prc/configVariableInt.h
  39. 188 0
      dtool/src/prc/configVariableList.I
  40. 50 0
      dtool/src/prc/configVariableList.cxx
  41. 77 0
      dtool/src/prc/configVariableList.h
  42. 46 0
      dtool/src/prc/configVariableManager.I
  43. 262 0
      dtool/src/prc/configVariableManager.cxx
  44. 71 0
      dtool/src/prc/configVariableManager.h
  45. 160 0
      dtool/src/prc/configVariableString.I
  46. 19 0
      dtool/src/prc/configVariableString.cxx
  47. 57 0
      dtool/src/prc/configVariableString.h
  48. 41 0
      dtool/src/prc/configVariableTempl.h
  49. 21 0
      dtool/src/prc/config_prc.cxx
  50. 7 9
      dtool/src/prc/config_prc.h
  51. 0 0
      dtool/src/prc/globPattern.I
  52. 0 0
      dtool/src/prc/globPattern.cxx
  53. 1 1
      dtool/src/prc/globPattern.h
  54. 0 0
      dtool/src/prc/notify.I
  55. 13 15
      dtool/src/prc/notify.cxx
  56. 0 0
      dtool/src/prc/notify.h
  57. 0 0
      dtool/src/prc/notifyCategory.I
  58. 12 7
      dtool/src/prc/notifyCategory.cxx
  59. 0 0
      dtool/src/prc/notifyCategory.h
  60. 0 0
      dtool/src/prc/notifyCategoryProxy.I
  61. 0 0
      dtool/src/prc/notifyCategoryProxy.h
  62. 0 0
      dtool/src/prc/notifySeverity.cxx
  63. 0 0
      dtool/src/prc/notifySeverity.h
  64. 18 0
      dtool/src/prc/prcKeyRegistry.I
  65. 188 0
      dtool/src/prc/prcKeyRegistry.cxx
  66. 89 0
      dtool/src/prc/prcKeyRegistry.h
  67. 26 0
      dtool/src/prckeys/Sources.pp
  68. 426 0
      dtool/src/prckeys/makePrcKey.cxx
  69. 369 0
      dtool/src/prckeys/signPrcFile_src.cxx
  70. 11 0
      panda/src/express/config_express.N
  71. 13 0
      panda/src/express/config_express.h

+ 12 - 16
direct/src/dcparser/dcFile.cxx

@@ -29,6 +29,7 @@
 #include "config_express.h"
 #include "config_express.h"
 #include "virtualFileSystem.h"
 #include "virtualFileSystem.h"
 #include "executionEnvironment.h"
 #include "executionEnvironment.h"
+#include "configVariableList.h"
 #endif
 #endif
 
 
 
 
@@ -90,28 +91,23 @@ clear() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool DCFile::
 bool DCFile::
 read_all() {
 read_all() {
-  Config::ConfigTable::Symbol dc_files;
-  config_express.GetAll("dc-file", dc_files);
+  ConfigVariableList dc_files("dc-file", 0,
+                              "The list of dc files to load.");
 
 
-  if (dc_files.empty()) {
+  if (dc_files.size() == 0) {
     cerr << "No files specified via dc-file Config.prc variable!\n";
     cerr << "No files specified via dc-file Config.prc variable!\n";
     return false;
     return false;
   }
   }
 
 
-  // When we use GetAll(), we might inadvertently read duplicate
-  // lines.  Filter them out with a set.
-  pset<string> already_read;
-  
-  Config::ConfigTable::Symbol::iterator si;
-  for (si = dc_files.begin(); si != dc_files.end(); ++si) {
-    string dc_file = ExecutionEnvironment::expand_string((*si).Val());
+  int size = dc_files.size();
+
+  // Load the DC files in opposite order, because we want to load the
+  // least-important (most fundamental) files first.
+  for (int i = size - 1; i >= 0; i--) {
+    string dc_file = ExecutionEnvironment::expand_string(dc_files[i]);
     Filename filename = Filename::from_os_specific(dc_file);
     Filename filename = Filename::from_os_specific(dc_file);
-    if (already_read.insert(filename).second) {
-      if (!read(filename)) {
-        return false;
-      }
-    } else {
-      cerr << "DCFile::read_all Already read " << filename << "\n";
+    if (!read(filename)) {
+      return false;
     }
     }
   }
   }
 
 

+ 18 - 0
direct/src/extensions/ConfigVariableList-extensions.py

@@ -0,0 +1,18 @@
+
+    def __hash__(self):
+        raise AttributeError, "ConfigVariables are not immutable."
+
+    def ls(self):
+        from pandac.Notify import Notify
+        self.write(Notify.out())
+
+    def __cmp__(self, other):
+        return list(self).__cmp__(list(other))
+
+    def __len__(self):
+        return self.getNumValues()
+    
+    def __getitem__(self, n):
+        if n < 0 or n >= self.getNumUniqueValues():
+            raise IndexError
+        return self.getUniqueValue(n)

+ 87 - 11
dtool/Config.pp

@@ -104,17 +104,21 @@
 // #define INSTALL_LIB_DIR /usr/lib/python2.2/site-packages
 // #define INSTALL_LIB_DIR /usr/lib/python2.2/site-packages
 
 
 
 
-// The PRC files are used by Panda for runtime configuration.  Panda
-// will load up all files named *.prc in the directory specified by
-// the PRC_DIR environment variable, or in the directory named here if
-// that environment variable is undefined.  Config files are loaded up
-// in alphabetical order (sorted by ASCII value), and the
-// alphabetically last files have precedence.
-
-// By default, we specify the install/etc dir, which is where the
-// system-provided PRC files get copied to.
-#defer DEFAULT_PRC_DIR $[INSTALL_DIR]/etc
-
+// The character used to separate components of an OS-specific
+// directory name depends on the platform (it is '/' on Unix, '\' on
+// Windows).  That character selection is hardcoded into Panda and
+// cannot be changed here.  (Note that an internal Panda filename
+// always uses the forward slash, '/', to separate the components of a
+// directory name.)
+
+// There's a different character used to separate the complete
+// directory names in a search path specification.  On Unix, the
+// normal convention is ':', on Windows, it has to be ';', because the
+// colon is already used to mark the drive letter.  This character is
+// selectable here.  Most users won't want to change this.  If
+// multiple characters are placed in this string, any one of them may
+// be used as a separator character.
+#define DEFAULT_PATHSEP $[if $[WINDOWS_PLATFORM],;,:]
 
 
 // What level of compiler optimization/debug symbols should we build?
 // What level of compiler optimization/debug symbols should we build?
 // The various optimize levels are defined as follows:
 // The various optimize levels are defined as follows:
@@ -127,6 +131,78 @@
 //
 //
 #define OPTIMIZE 3
 #define OPTIMIZE 3
 
 
+
+// Panda uses prc files for runtime configuration.  There are many
+// compiled-in options to customize the behavior of the prc config
+// system; most users won't need to change any of them.  Feel free to
+// skip over all of the PRC_* variables defined here.
+
+// The default behavior is to search for files names *.prc in the
+// directory specified by the PRC_DIR environment variable, and then
+// to search along all of the directories named by the PRC_PATH
+// environment variable.  Either of these variables might be
+// undefined; if both of them are undefined, the default is to search
+// in the directory named here by DEFAULT_PRC_DIR.
+
+// By default, we specify the install/etc dir, which is where the
+// system-provided PRC files get copied to.
+#defer DEFAULT_PRC_DIR $[INSTALL_DIR]/etc
+
+// You can specify the names of the environment variables that are
+// used to specify the search location(s) for prc files at runtime.
+// These are space-separated lists of environment variable names.
+// Specify empty string for either one of these to disable the
+// feature.  For instance, redefining PRC_DIR_ENVVARS here to
+// PANDA_PRC_DIR would cause the environment variable $PANDA_PRC_DIR
+// to be consulted at startup instead of the default value of
+// $PRC_DIR.
+#define PRC_DIR_ENVVARS PRC_DIR
+#define PRC_PATH_ENVVARS PRC_PATH
+
+// You can specify the name of the file(s) to search for in the above
+// paths to be considered a config file.  This should be a
+// space-separated list of filename patterns.  This is *.prc by
+// default; normally there's no reason to change this.
+#define PRC_PATTERNS *.prc
+
+// One unusual feature of config is the ability to execute one or more
+// of the files it discovers as if it were a program, and then treat
+// the output of this program as a prc file.  If you want to use this
+// feature, define this variable to the filename pattern or patterns
+// for such executable-style config programs (e.g. *prc.exe).  This
+// can be the same as the above if you like this sort of ambiguity; in
+// that case, config will execute the file if it appears to be
+// executable; otherwise, it will simply read it.
+#define PRC_EXECUTABLE_PATTERNS
+
+// If you do use the above feature, you'll need another environment
+// variable that specifies additional arguments to pass to the
+// executable programs.  The default definition, given here, makes
+// that variable be $PRC_EXECUTABLE_ARGS.  Sorry, the same arguments
+// must be supplied to all executables in a given runtime session.
+#define PRC_EXECUTABLE_ARGS_ENVVAR PRC_EXECUTABLE_ARGS
+
+// You can implement signed prc files, if you require this advanced
+// feature.  This allows certain config variables to be set only by a
+// prc file that has been provided by a trusted source.  To do this,
+// first install and compile Dtool with OpenSSL (below) and run the
+// program make-prc-key, and then specify here the output filename
+// generated by that program, and then recompile Dtool (ppremake; make
+// install).
+#define PRC_PUBLIC_KEYS_FILENAME
+
+// By default, the signed-prc feature, above, is enabled only for a
+// release build (OPTIMIZE = 4).  In a normal development environment
+// (OPTIMIZE < 4), any prc file can set any config variable, whether
+// or not it is signed.  Set this variable true (nonempty) or false
+// (empty) to explicitly enable or disable this feature.
+#defer PRC_RESPECT_TRUST_LEVEL $[= $[OPTIMIZE],4]
+
+// This is the end of the PRC variable customization section.  The
+// remaining variables are of general interest to everyone.
+
+
+
 // NOTE: In the following, to indicate "yes" to a yes/no question,
 // NOTE: In the following, to indicate "yes" to a yes/no question,
 // define the variable to be a nonempty string.  To indicate "no",
 // define the variable to be a nonempty string.  To indicate "no",
 // define the variable to be an empty string.
 // define the variable to be an empty string.

+ 37 - 2
dtool/LocalSetup.pp

@@ -237,12 +237,47 @@ $[cdefine LINK_IN_GL]
 /* Define if we are linking PANDAPHYSICS in with PANDA. */
 /* Define if we are linking PANDAPHYSICS in with PANDA. */
 $[cdefine LINK_IN_PHYSICS]
 $[cdefine LINK_IN_PHYSICS]
 
 
+/* The compiled-in character(s) to expect to separate different
+   components of a path list (e.g. $PRC_PATH). */
+# define DEFAULT_PATHSEP "$[DEFAULT_PATHSEP]"
+
 /* The compiled-in default directory to look for the Configrc file, in
 /* The compiled-in default directory to look for the Configrc file, in
    the absence of the PRC_DIR environment variable set, and in
    the absence of the PRC_DIR environment variable set, and in
    the absence of anything specified via the configpath directive. */
    the absence of anything specified via the configpath directive. */
-# define DEFAULT_PRC_DIR "$[DEFAULT_PRC_DIR]"
-
+# define DEFAULT_PRC_DIR "$[unixfilename $[DEFAULT_PRC_DIR]]"
+
+/* The compiled-in name of the environment variable(s) that contain
+   the name of a single directory in which to search for prc files. */
+# define PRC_DIR_ENVVARS "$[PRC_DIR_ENVVARS]"
+
+/* The compiled-in name of the environment variable(s) that contain
+   the name of multiple directories, separated by DEFAULT_PATHSEP, in
+   which to search for prc files. */
+# define PRC_PATH_ENVVARS "$[PRC_PATH_ENVVARS]"
+
+/* The filename(s) to search for in the above paths.  Normally this is
+   *.prc. */
+# define PRC_PATTERNS "$[PRC_PATTERNS]"
+
+/* The filename(s) to search for, and execute, in the above paths.
+   Normally this is empty. */
+# define PRC_EXECUTABLE_PATTERNS "$[PRC_EXECUTABLE_PATTERNS]"
+
+/* The environment variable that defines optional args to pass to
+   executables found that match one of the above patterns. */
+# define PRC_EXECUTABLE_ARGS_ENVVAR "$[PRC_EXECUTABLE_ARGS_ENVVAR]"
+
+/* The filename that specifies the public keys to import into
+   config. */
+# define PRC_PUBLIC_KEYS_FILENAME "$[unixfilename $[PRC_PUBLIC_KEYS_FILENAME]]"
+#if $[PRC_PUBLIC_KEYS_FILENAME]
+# define PRC_PUBLIC_KEYS_INCLUDE "$[osfilename $[PRC_PUBLIC_KEYS_FILENAME]]"
+#endif
 
 
+/* Define if we want to enable the "trust_level" feature of prc config
+   variables.  This requires OpenSSL and PRC_PUBLIC_KEYS_FILENAME,
+   above. */
+$[cdefine PRC_RESPECT_TRUST_LEVEL]
 
 
 
 
 /* Define if your processor stores words with the most significant
 /* Define if your processor stores words with the most significant

+ 1 - 1
dtool/metalibs/dtoolconfig/Sources.pp

@@ -7,7 +7,7 @@
 #define DIR_TYPE metalib
 #define DIR_TYPE metalib
 #define BUILDING_DLL BUILDING_DTOOLCONFIG
 #define BUILDING_DLL BUILDING_DTOOLCONFIG
 
 
-#define COMPONENT_LIBS interrogatedb dconfig
+#define COMPONENT_LIBS interrogatedb dconfig prc
 #define LOCAL_LIBS dtoolutil dtoolbase
 #define LOCAL_LIBS dtoolutil dtoolbase
 #define USE_PACKAGES python
 #define USE_PACKAGES python
 
 

+ 5 - 15
dtool/src/dconfig/Sources.pp

@@ -1,4 +1,4 @@
-#define LOCAL_LIBS dtoolutil dtoolbase
+#define LOCAL_LIBS dtoolutil dtoolbase prc
 
 
 #begin lib_target
 #begin lib_target
   #define TARGET dconfig
   #define TARGET dconfig
@@ -7,29 +7,19 @@
   
   
   #define SOURCES \
   #define SOURCES \
     configTable.I configTable.h \
     configTable.I configTable.h \
-    config_dconfig.h config_notify.h config_setup.h \
+    config_dconfig.h config_setup.h \
     dconfig.I dconfig.h \
     dconfig.I dconfig.h \
-    globPattern.I globPattern.h \
-    notify.I \
-    notify.h notifyCategory.I \
-    notifyCategory.h notifyCategoryProxy.I notifyCategoryProxy.h \
-    notifySeverity.h serialization.I serialization.h  \
+    serialization.I serialization.h  \
     symbolEnt.I  symbolEnt.h
     symbolEnt.I  symbolEnt.h
     
     
  #define INCLUDED_SOURCES \
  #define INCLUDED_SOURCES \
     configTable.cxx config_dconfig.cxx dconfig.cxx \
     configTable.cxx config_dconfig.cxx dconfig.cxx \
-    config_notify.cxx \
-    globPattern.cxx \
-    notify.cxx notifyCategory.cxx \
-    notifySeverity.cxx symbolEnt.cxx 
+    symbolEnt.cxx 
 
 
   #define INSTALL_HEADERS                                               \
   #define INSTALL_HEADERS                                               \
     configTable.I configTable.h config_dconfig.h config_setup.h         \
     configTable.I configTable.h config_dconfig.h config_setup.h         \
     dconfig.I dconfig.h \
     dconfig.I dconfig.h \
-    globPattern.I globPattern.h \
-    notify.I notify.h notifyCategory.I                \
-    notifyCategory.h notifyCategoryProxy.I notifyCategoryProxy.h        \
-    notifySeverity.h serialization.I serialization.h symbolEnt.I        \
+    serialization.I serialization.h symbolEnt.I        \
     symbolEnt.h
     symbolEnt.h
 
 
 #end lib_target
 #end lib_target

+ 0 - 1
dtool/src/dconfig/configTable.cxx

@@ -708,7 +708,6 @@ ConfigTable* ConfigTable::Instance() {
     _instance = new ConfigTable;
     _instance = new ConfigTable;
     _instance->GetData();
     _instance->GetData();
     _instance->_initializing = false;
     _instance->_initializing = false;
-    Notify::ptr()->config_initialized();
   }
   }
   return _instance;
   return _instance;
 }
 }

+ 0 - 47
dtool/src/dconfig/config_notify.cxx

@@ -1,47 +0,0 @@
-// Filename: config_notify.cxx
-// Created by:  drose (29Feb00)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#include "config_notify.h"
-#include "dconfig.h"
-
-ConfigureDef(config_notify);
-
-ConfigureFn(config_notify) {
-}
-
-// We have to declare this as a function instead of a static const,
-// because its value might be needed at static init time.
-bool
-get_assert_abort() {
-  static bool *assert_abort = (bool *)NULL;
-  if (assert_abort == (bool *)NULL) {
-    assert_abort = new bool;
-    *assert_abort = config_notify.GetBool("assert-abort", false);
-  }
-  return *assert_abort;
-}
-
-bool
-get_notify_timestamp() {
-  static bool *notify_timestamp = (bool *)NULL;
-  if (notify_timestamp == (bool *)NULL) {
-    notify_timestamp = new bool;
-    *notify_timestamp = config_notify.GetBool("notify-timestamp", false);
-  }
-  return *notify_timestamp;
-}

+ 24 - 98
dtool/src/dconfig/dconfig.h

@@ -25,6 +25,11 @@
 #include "config_dconfig.h"
 #include "config_dconfig.h"
 #include "configTable.h"
 #include "configTable.h"
 #include "executionEnvironment.h"
 #include "executionEnvironment.h"
+#include "configVariableString.h"
+#include "configVariableBool.h"
+#include "configVariableInt.h"
+#include "configVariableDouble.h"
+#include "configVariableList.h"
 
 
 #include <vector>
 #include <vector>
 #include <map>
 #include <map>
@@ -181,15 +186,8 @@ template<class GetConfig>
 ConfigString Config<GetConfig>::Get(ConfigString sym)
 ConfigString Config<GetConfig>::Get(ConfigString sym)
 {
 {
    Init();
    Init();
-   ConfigTable* tab = ConfigTable::Instance();
-   if (tab->Defined(sym, Name()))
-      return (tab->Get(sym, Name())).Val();
-   else if (tab->Defined(sym, ExecutionEnvironment::get_binary_name()))
-      return (tab->Get(sym, ExecutionEnvironment::get_binary_name())).Val();
-   else if (tab->Defined(sym))
-      return (tab->Get(sym)).Val();
-   else
-      return "";
+   ConfigVariableString var(sym);
+   return var.get_value();
 }
 }
 
 
 template<class GetConfig>
 template<class GetConfig>
@@ -197,19 +195,14 @@ ConfigTable::Symbol& Config<GetConfig>::GetAll(const ConfigString sym,
                                                 ConfigTable::Symbol& s)
                                                 ConfigTable::Symbol& s)
 {
 {
    Init();
    Init();
-   ConfigTable* tab = ConfigTable::Instance();
-   if (tab->Defined(sym, Name())) {
-     const ConfigTable::Symbol& tsym = tab->GetSym(sym, Name());
-     s.insert(s.end(), tsym.begin(), tsym.end());
-   }
-   if (tab->Defined(sym, ExecutionEnvironment::get_binary_name())) {
-     const ConfigTable::Symbol& tsym = tab->GetSym(sym, ExecutionEnvironment::get_binary_name());
-     s.insert(s.end(), tsym.begin(), tsym.end());
-   }
-   if (tab->Defined(sym)) {
-     const ConfigTable::Symbol& tsym = tab->GetSym(sym);
-     s.insert(s.end(), tsym.begin(), tsym.end());
+   ConfigVariableList var(sym, 0, "DConfig");
+
+   int num_values = var.get_num_values();
+   for (int i = 0; i < num_values; i++) {
+     string value = var.get_string_value(i);
+     s.push_back(SymbolEnt(SymbolEnt::ConfigFile, value));
    }
    }
+
    return s;
    return s;
 }
 }
 
 
@@ -217,87 +210,32 @@ template<class GetConfig>
 bool Config<GetConfig>::GetBool(const ConfigString sym, bool def)
 bool Config<GetConfig>::GetBool(const ConfigString sym, bool def)
 {
 {
    Init();
    Init();
-   if (Defined(sym)) {
-      ConfigString s = Get(sym);
-      bool ret = ConfigTable::TrueOrFalse(s, def);
-      if (dconfig_cat->is_spam())
-         dconfig_cat->spam() << "symbol '" << sym << "' defined, returning: "
-                            << (ret?"true":"false") << endl;
-      return ret;
-   } else {
-      if (dconfig_cat->is_spam())
-         dconfig_cat->spam()
-            << "symbol '" << sym
-            << "' is not defined, returning provided default: "
-            << (def?"true":"false") << endl;
-      return def;
-   }
+   ConfigVariableBool var(sym, def, 0, "DConfig");
+   return var.get_value();
 }
 }
 
 
 template<class GetConfig>
 template<class GetConfig>
 int Config<GetConfig>::GetInt(const ConfigString sym, int def)
 int Config<GetConfig>::GetInt(const ConfigString sym, int def)
 {
 {
    Init();
    Init();
-   if (Defined(sym)) {
-      ConfigString v = Get(sym);
-      istringstream s(v);
-      int i;
-      s >> i;
-      if (dconfig_cat->is_spam())
-         dconfig_cat->spam() << "symbol '" << sym << "' defined, returning: "
-                            << i << endl;
-      return i;
-   } else {
-      if (dconfig_cat->is_spam())
-         dconfig_cat->spam()
-            << "symbol '" << sym
-            << "' is not defined, returning provided default: " << def << endl;
-      return def;
-   }
+   ConfigVariableInt var(sym, def, 0, "DConfig");
+   return var.get_value();
 }
 }
 
 
 template<class GetConfig>
 template<class GetConfig>
 float Config<GetConfig>::GetFloat(const ConfigString sym, float def)
 float Config<GetConfig>::GetFloat(const ConfigString sym, float def)
 {
 {
    Init();
    Init();
-   if (Defined(sym)) {
-      ConfigString v = Get(sym);
-      istringstream s(v);
-      float f;
-      s >> f;
-      if (dconfig_cat->is_spam())
-         dconfig_cat->spam() << "symbol '" << sym << "' defined, returning: "
-                            << f << endl;
-      return f;
-   } else {
-      if (dconfig_cat->is_spam())
-         dconfig_cat->spam()
-            << "symbol '" << sym
-            << "' is not defined, returning provided default: " << def << endl;
-      return def;
-   }
+   ConfigVariableDouble var(sym, def, 0, "DConfig");
+   return var.get_value();
 }
 }
 
 
 template<class GetConfig>
 template<class GetConfig>
 double Config<GetConfig>::GetDouble(const ConfigString sym, double def)
 double Config<GetConfig>::GetDouble(const ConfigString sym, double def)
 {
 {
    Init();
    Init();
-   if (Defined(sym)) {
-      ConfigString v = Get(sym);
-      istringstream s(v);
-      double d;
-      s >> d;
-      if (dconfig_cat->is_spam())
-         dconfig_cat->spam() << "symbol '" << sym << "' defined, returning: "
-                            << d << endl;
-      return d;
-   } else {
-      if (dconfig_cat->is_spam())
-         dconfig_cat->spam()
-            << "symbol '" << sym
-            << "' is not defined, returning provided default: " << def << endl;
-      return def;
-   }
+   ConfigVariableDouble var(sym, def, 0, "DConfig");
+   return var.get_value();
 }
 }
 
 
 template<class GetConfig>
 template<class GetConfig>
@@ -305,20 +243,8 @@ ConfigString Config<GetConfig>::GetString(const ConfigString sym,
                                            const ConfigString def)
                                            const ConfigString def)
 {
 {
    Init();
    Init();
-   if (Defined(sym)) {
-      ConfigString ret = Get(sym);
-      if (dconfig_cat->is_spam())
-         dconfig_cat->spam() << "symbol '" << sym << "' defined, returning: '"
-                            << ret << "'" << endl;
-      return ret;
-   } else {
-      if (dconfig_cat->is_spam())
-         dconfig_cat->spam()
-            << "symbol '" << sym
-            << "' is not defined, returning provided default: '" << def
-            << "'" << endl;
-      return def;
-   }
+   ConfigVariableString var(sym, def, 0, "DConfig");
+   return var.get_value();
 }
 }
 
 
 #include "dconfig.I"
 #include "dconfig.I"

+ 0 - 5
dtool/src/dconfig/dconfig_composite1.cxx

@@ -1,10 +1,5 @@
 
 
 #include "config_dconfig.cxx"
 #include "config_dconfig.cxx"
-#include "config_notify.cxx"
 #include "dconfig.cxx"
 #include "dconfig.cxx"
-#include "globPattern.cxx"
 #include "configTable.cxx"
 #include "configTable.cxx"
-#include "notify.cxx"
-#include "notifyCategory.cxx"             
-#include "notifySeverity.cxx"
 #include "symbolEnt.cxx"
 #include "symbolEnt.cxx"

+ 2 - 2
dtool/src/dtoolutil/dSearchPath.I

@@ -28,7 +28,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Filename DSearchPath::
 INLINE Filename DSearchPath::
 search_path(const Filename &filename, const string &path,
 search_path(const Filename &filename, const string &path,
-            const string &delimiters) {
-  DSearchPath search(path, delimiters);
+            const string &separator) {
+  DSearchPath search(path, separator);
   return search.find_file(filename);
   return search.find_file(filename);
 }
 }

+ 32 - 14
dtool/src/dtoolutil/dSearchPath.cxx

@@ -116,8 +116,8 @@ DSearchPath() {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 DSearchPath::
 DSearchPath::
-DSearchPath(const string &path, const string &delimiters) {
-  append_path(path, delimiters);
+DSearchPath(const string &path, const string &separator) {
+  append_path(path, separator);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -187,18 +187,28 @@ prepend_directory(const Filename &directory) {
 //               to the end of the search list.
 //               to the end of the search list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DSearchPath::
 void DSearchPath::
-append_path(const string &path, const string &delimiters) {
-  size_t p = 0;
-  while (p < path.length()) {
-    size_t q = path.find_first_of(delimiters, p);
-    if (q == string::npos) {
-      _directories.push_back(path.substr(p));
-      return;
-    }
-    if (q != p) {
-      _directories.push_back(path.substr(p, q - p));
+append_path(const string &path, const string &separator) {
+  string pathsep = separator;
+  if (pathsep.empty()) {
+    pathsep = DEFAULT_PATHSEP;
+  }
+
+  if (pathsep.empty()) {
+    append_directory(path);
+
+  } else {
+    size_t p = 0;
+    while (p < path.length()) {
+      size_t q = path.find_first_of(pathsep, p);
+      if (q == string::npos) {
+        _directories.push_back(Filename::from_os_specific(path.substr(p)));
+        return;
+      }
+      if (q != p) {
+        _directories.push_back(Filename::from_os_specific(path.substr(p, q - p)));
+      }
+      p = q + 1;
     }
     }
-    p = q + 1;
   }
   }
 }
 }
 
 
@@ -341,12 +351,20 @@ find_all_files(const Filename &filename,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DSearchPath::
 void DSearchPath::
 output(ostream &out, const string &separator) const {
 output(ostream &out, const string &separator) const {
+  string pathsep = separator;
+  if (pathsep.empty()) {
+    pathsep = DEFAULT_PATHSEP;
+    if (!pathsep.empty()) {
+      pathsep = pathsep[0];
+    }
+  }
+
   if (!_directories.empty()) {
   if (!_directories.empty()) {
     Directories::const_iterator di = _directories.begin();
     Directories::const_iterator di = _directories.begin();
     out << (*di);
     out << (*di);
     ++di;
     ++di;
     while (di != _directories.end()) {
     while (di != _directories.end()) {
-      out << separator << (*di);
+      out << pathsep << (*di);
       ++di;
       ++di;
     }
     }
   }
   }

+ 4 - 4
dtool/src/dtoolutil/dSearchPath.h

@@ -56,7 +56,7 @@ public:
 
 
 PUBLISHED:
 PUBLISHED:
   DSearchPath();
   DSearchPath();
-  DSearchPath(const string &path, const string &delimiters = ": \n\t");
+  DSearchPath(const string &path, const string &separator = string());
   DSearchPath(const DSearchPath &copy);
   DSearchPath(const DSearchPath &copy);
   void operator = (const DSearchPath &copy);
   void operator = (const DSearchPath &copy);
   ~DSearchPath();
   ~DSearchPath();
@@ -65,7 +65,7 @@ PUBLISHED:
   void append_directory(const Filename &directory);
   void append_directory(const Filename &directory);
   void prepend_directory(const Filename &directory);
   void prepend_directory(const Filename &directory);
   void append_path(const string &path,
   void append_path(const string &path,
-                   const string &delimiters = ": \n\t");
+                   const string &separator = string());
   void append_path(const DSearchPath &path);
   void append_path(const DSearchPath &path);
   void prepend_path(const DSearchPath &path);
   void prepend_path(const DSearchPath &path);
 
 
@@ -78,9 +78,9 @@ PUBLISHED:
 
 
   INLINE static Filename
   INLINE static Filename
   search_path(const Filename &filename, const string &path,
   search_path(const Filename &filename, const string &path,
-              const string &delimiters = ": \n\t");
+              const string &separator = string());
 
 
-  void output(ostream &out, const string &separator = ":") const;
+  void output(ostream &out, const string &separator = string()) const;
   void write(ostream &out, int indent_level = 0) const;
   void write(ostream &out, int indent_level = 0) const;
 
 
 private:
 private:

+ 47 - 0
dtool/src/prc/Sources.pp

@@ -0,0 +1,47 @@
+#define LOCAL_LIBS dtoolutil dtoolbase
+#define USE_PACKAGES ssl
+
+#begin lib_target
+  #define TARGET prc
+  
+  #define SOURCES \
+    config_prc.cxx config_prc.h \
+    configDeclaration.cxx configDeclaration.I configDeclaration.h \
+    configPage.cxx configPage.I configPage.h \
+    configPageManager.cxx configPageManager.I configPageManager.h \
+    configVariable.cxx configVariable.I configVariable.h \
+    configVariableBool.cxx configVariableBool.I configVariableBool.h \
+    configVariableCore.cxx configVariableCore.I configVariableCore.h \
+    configVariableDouble.cxx configVariableDouble.I configVariableDouble.h \
+    configVariableInt.cxx configVariableInt.I configVariableInt.h \
+    configVariableList.cxx configVariableList.I configVariableList.h \
+    configVariableManager.cxx configVariableManager.I configVariableManager.h \
+    configVariableString.cxx configVariableString.I configVariableString.h \
+    globPattern.cxx globPattern.I globPattern.h \
+    notify.cxx notify.I notify.h \
+    notifyCategory.cxx notifyCategory.I notifyCategory.h \
+    notifyCategoryProxy.I notifyCategoryProxy.h \
+    notifySeverity.cxx notifySeverity.h \
+    prcKeyRegistry.cxx prcKeyRegistry.h \
+  
+  #define INSTALL_HEADERS \
+    config_prc.h \
+    configDeclaration.I configDeclaration.h \
+    configPage.I configPage.h \
+    configPageManager.I configPageManager.h \
+    configVariable.I configVariable.h \
+    configVariableBool.I configVariableBool.h \
+    configVariableCore.I configVariableCore.h \
+    configVariableDouble.I configVariableDouble.h \
+    configVariableInt.I configVariableInt.h \
+    configVariableList.I configVariableList.h \
+    configVariableManager.I configVariableManager.h \
+    configVariableString.I configVariableString.h \
+    globPattern.I globPattern.h \
+    notify.I notify.h \
+    notifyCategory.I notifyCategory.h \
+    notifyCategoryProxy.I notifyCategoryProxy.h \
+    notifySeverity.h \
+    prcKeyRegistry.I prcKeyRegistry.h
+
+#end lib_target

+ 249 - 0
dtool/src/prc/configDeclaration.I

@@ -0,0 +1,249 @@
+// Filename: configDeclaration.I
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::operator <
+//       Access: Public
+//  Description: Sorts two declarations in order based on the order in
+//               which their respective pages were loaded, and the
+//               order in which they appear within the same page.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigDeclaration::
+operator < (const ConfigDeclaration &other) const {
+  if (get_page() == other.get_page()) {
+    // On the same page, we compare based on the decl_seq.
+    return get_decl_seq() < other.get_decl_seq();
+  }
+
+  // On different pages, we compare based on the page importance.
+  return *get_page() < *other.get_page();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::get_page
+//       Access: Public
+//  Description: Returns the page on which this declaration can be
+//               found.
+////////////////////////////////////////////////////////////////////
+INLINE ConfigPage *ConfigDeclaration::
+get_page() const {
+  return _page;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::get_variable
+//       Access: Public
+//  Description: Returns the variable that this declaration names.
+//               This variable may or may not have been defined by the
+//               time the declaration is read.
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableCore *ConfigDeclaration::
+get_variable() const {
+  return _variable;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::get_string_value
+//       Access: Public
+//  Description: Returns the value assigned to this variable.  This is
+//               the original one-line text defined for the variable
+//               in the .prc file (or passed to
+//               ConfigPage::make_declaration()).
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigDeclaration::
+get_string_value() const {
+  return _string_value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::set_string_value
+//       Access: Public
+//  Description: Changes the value assigned to this variable.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigDeclaration::
+set_string_value(const string &string_value) {
+  _string_value = string_value;
+  _got_words = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::get_num_words
+//       Access: Public
+//  Description: Returns the number of words in the declaration's
+//               value.  A word is defined as a sequence of
+//               non-whitespace characters delimited by whitespace.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigDeclaration::
+get_num_words() const {
+  if (!_got_words) {
+    ((ConfigDeclaration *)this)->get_words();
+  }
+  return _words.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::has_string_word
+//       Access: Public
+//  Description: Returns true if the declaration's value has a valid
+//               string value for the nth word.  This is really the
+//               same thing as asking if there are at least n words in
+//               the value.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigDeclaration::
+has_string_word(int n) const {
+  if (!_got_words) {
+    ((ConfigDeclaration *)this)->get_words();
+  }
+  return (n >= 0 && n < (int)_words.size());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::has_bool_word
+//       Access: Public
+//  Description: Returns true if the declaration's value has a valid
+//               boolean value for the nth word.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigDeclaration::
+has_bool_word(int n) const {
+  if (has_string_word(n)) {
+    ((ConfigDeclaration *)this)->check_bool_word(n);
+    return (_words[n]._flags & F_valid_bool) != 0;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::has_int_word
+//       Access: Public
+//  Description: Returns true if the declaration's value has a valid
+//               integer value for the nth word.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigDeclaration::
+has_int_word(int n) const {
+  if (has_string_word(n)) {
+    ((ConfigDeclaration *)this)->check_int_word(n);
+    return (_words[n]._flags & F_valid_int) != 0;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::has_double_word
+//       Access: Public
+//  Description: Returns true if the declaration's value has a valid
+//               integer value for the nth word.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigDeclaration::
+has_double_word(int n) const {
+  if (has_string_word(n)) {
+    ((ConfigDeclaration *)this)->check_double_word(n);
+    return (_words[n]._flags & F_valid_double) != 0;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::get_string_word
+//       Access: Public
+//  Description: Returns the string value of the nth word of the
+//               declaration's value, or empty string if there is no
+//               nth value.  See also has_string_word().
+////////////////////////////////////////////////////////////////////
+INLINE string ConfigDeclaration::
+get_string_word(int n) const {
+  if (has_string_word(n)) {
+    return _words[n]._str;
+  }
+  return string();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::get_bool_word
+//       Access: Public
+//  Description: Returns the boolean value of the nth word of the
+//               declaration's value, or false if there is no nth
+//               value.  See also has_bool_word().
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigDeclaration::
+get_bool_word(int n) const {
+  // We use has_string_word() instead of has_bool_word(), so we can
+  // return a partial answer if there was one.
+  if (has_string_word(n)) {
+    ((ConfigDeclaration *)this)->check_bool_word(n);
+    return _words[n]._bool;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::get_int_word
+//       Access: Public
+//  Description: Returns the integer value of the nth word of the
+//               declaration's value, or 0 if there is no nth value.
+//               See also has_int_word().
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigDeclaration::
+get_int_word(int n) const {
+  // We use has_string_word() instead of has_int_word(), so we can
+  // return a partial answer if there was one.
+  if (has_string_word(n)) {
+    ((ConfigDeclaration *)this)->check_int_word(n);
+    return _words[n]._int;
+  }
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::get_double_word
+//       Access: Public
+//  Description: Returns the integer value of the nth word of the
+//               declaration's value, or 0 if there is no nth value.
+//               See also has_double_word().
+////////////////////////////////////////////////////////////////////
+INLINE double ConfigDeclaration::
+get_double_word(int n) const {
+  // We use has_string_word() instead of has_double_word(), so we can
+  // return a partial answer if there was one.
+  if (has_string_word(n)) {
+    ((ConfigDeclaration *)this)->check_double_word(n);
+    return _words[n]._double;
+  }
+  return 0.0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::get_decl_seq
+//       Access: Public
+//  Description: Returns the sequence number of the declaration within
+//               the page.  Sequence numbers are assigned as each
+//               declaration is created; each declaration is given a
+//               higher sequence number than all the declarations
+//               created in the page before it.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigDeclaration::
+get_decl_seq() const {
+  return _decl_seq;
+}
+
+INLINE ostream &
+operator << (ostream &out, const ConfigDeclaration &decl) {
+  decl.output(out);
+  return out;
+}

+ 338 - 0
dtool/src/prc/configDeclaration.cxx

@@ -0,0 +1,338 @@
+// Filename: configDeclaration.cxx
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "configDeclaration.h"
+#include "configVariableCore.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::Constructor
+//       Access: Private
+//  Description: Use the ConfigPage::make_declaration() interface to
+//               create a new declaration.
+////////////////////////////////////////////////////////////////////
+ConfigDeclaration::
+ConfigDeclaration(ConfigPage *page, ConfigVariableCore *variable,
+                  const string &string_value, int decl_seq) :
+  _page(page),
+  _variable(variable),
+  _string_value(string_value),
+  _decl_seq(decl_seq),
+  _got_words(false)
+{
+  if (!_page->is_special()) {
+    _variable->add_declaration(this);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::Destructor
+//       Access: Private
+//  Description: Use the ConfigPage::delete_declaration() interface to
+//               delete a declaration.
+////////////////////////////////////////////////////////////////////
+ConfigDeclaration::
+~ConfigDeclaration() {
+  if (!_page->is_special()) {
+    _variable->remove_declaration(this);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::set_string_word
+//       Access: Public
+//  Description: Changes the nth word to the indicated value without
+//               affecting the other words.
+////////////////////////////////////////////////////////////////////
+void ConfigDeclaration::
+set_string_word(int n, const string &value) {
+  if (!_got_words) {
+    get_words();
+  }
+
+  while (n >= (int)_words.size()) {
+    Word w;
+    w._flags = 0;
+    _words.push_back(w);
+  }
+
+  _words[n]._str = value;
+  _words[n]._flags = 0;
+
+  // Now recompose the overall string value.
+  Words::const_iterator wi = _words.begin();
+  _string_value = (*wi)._str;
+  ++wi;
+
+  while (wi != _words.end()) {
+    _string_value += " ";
+    _string_value += (*wi)._str;
+    ++wi;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::set_bool_word
+//       Access: Public
+//  Description: Changes the nth word to the indicated value without
+//               affecting the other words.
+////////////////////////////////////////////////////////////////////
+void ConfigDeclaration::
+set_bool_word(int n, bool value) {
+  if (value) {
+    set_string_word(n, "1");
+  } else {
+    set_string_word(n, "0");
+  }
+
+  _words[n]._flags |= (F_checked_bool | F_valid_bool);
+  _words[n]._bool = value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::set_int_word
+//       Access: Public
+//  Description: Changes the nth word to the indicated value without
+//               affecting the other words.
+////////////////////////////////////////////////////////////////////
+void ConfigDeclaration::
+set_int_word(int n, int value) {
+  ostringstream strm;
+  strm << value;
+  set_string_word(n, strm.str());
+
+  _words[n]._flags |= (F_checked_int | F_valid_int);
+  _words[n]._int = value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::set_double_word
+//       Access: Public
+//  Description: Changes the nth word to the indicated value without
+//               affecting the other words.
+////////////////////////////////////////////////////////////////////
+void ConfigDeclaration::
+set_double_word(int n, double value) {
+  ostringstream strm;
+  strm << value;
+  set_string_word(n, strm.str());
+
+  _words[n]._flags |= (F_checked_double | F_valid_double);
+  _words[n]._double = value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::output
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConfigDeclaration::
+output(ostream &out) const {
+  out << get_variable()->get_name() << " " << get_string_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::write
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConfigDeclaration::
+write(ostream &out) const {
+  out << get_variable()->get_name() << " " << get_string_value();
+  if (!get_variable()->is_used()) {
+    out << "  (not used)";
+  }
+  out << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::get_words
+//       Access: Private
+//  Description: Separates the string value into words.
+////////////////////////////////////////////////////////////////////
+void ConfigDeclaration::
+get_words() {
+  if (!_got_words) {
+    _words.clear();
+    vector_string words;
+    extract_words(_string_value, words);
+    _words.reserve(words.size());
+
+    for (vector_string::const_iterator wi = words.begin();
+         wi != words.end();
+         ++wi) {
+      Word w;
+      w._str = (*wi);
+      w._flags = 0;
+      _words.push_back(w);
+    }
+
+    _got_words = true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::check_bool_word
+//       Access: Private
+//  Description: Checks whether the nth word can be interpreted as a
+//               boolean value.
+////////////////////////////////////////////////////////////////////
+void ConfigDeclaration::
+check_bool_word(int n) {
+  if (!_got_words) {
+    get_words();
+  }
+
+  if (n >= 0 && n < (int)_words.size()) {
+    Word &word = _words[n];
+    if ((word._flags & F_checked_bool) == 0) {
+      word._flags |= F_checked_bool;
+
+      string s = downcase(word._str);
+      if (s.empty()) {
+        word._bool = false;
+
+      } else if (s == "#t" || s == "1" || s[0] == 't') {
+        word._bool = true;
+
+      } else if (s == "#f" || s == "0" || s[0] == 'f') {
+        word._bool = false;
+
+      } else {
+        // Not a recognized bool value.
+        check_double_word(n);
+        if ((word._flags & F_checked_double) != 0) {
+          word._bool = (word._double != 0.0);
+        } else {
+          word._bool = false;
+        }
+        return;
+      }
+
+      word._flags |= F_valid_bool;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::check_int_word
+//       Access: Private
+//  Description: Checks whether the nth word can be interpreted as an
+//               integer value.
+////////////////////////////////////////////////////////////////////
+void ConfigDeclaration::
+check_int_word(int n) {
+  if (!_got_words) {
+    get_words();
+  }
+
+  if (n >= 0 && n < (int)_words.size()) {
+    Word &word = _words[n];
+    if ((word._flags & F_checked_int) == 0) {
+      word._flags |= F_checked_int;
+
+      const char *nptr = word._str.c_str();
+      char *endptr;
+      word._int = strtol(nptr, &endptr, 0);
+
+      if (*endptr == '\0') {
+        word._flags |= F_valid_int;
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::check_double_word
+//       Access: Private
+//  Description: Checks whether the nth word can be interpreted as a
+//               floating-point value.
+////////////////////////////////////////////////////////////////////
+void ConfigDeclaration::
+check_double_word(int n) {
+  if (!_got_words) {
+    get_words();
+  }
+
+  if (n >= 0 && n < (int)_words.size()) {
+    Word &word = _words[n];
+    if ((word._flags & F_checked_double) == 0) {
+      word._flags |= F_checked_double;
+
+      const char *nptr = word._str.c_str();
+      char *endptr;
+      word._double = strtod(nptr, &endptr);
+
+      if (*endptr == '\0') {
+        word._flags |= F_valid_double;
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::extract_words
+//       Access: Public, Static
+//  Description: Divides the string into a number of words according
+//               to whitespace.  The words vector should be cleared by
+//               the user before calling; otherwise, the list of words
+//               in the string will be appended to the end of whatever
+//               was there before.
+//
+//               The return value is the number of words extracted.
+////////////////////////////////////////////////////////////////////
+int ConfigDeclaration::
+extract_words(const string &str, vector_string &words) {
+  int num_words = 0;
+
+  size_t pos = 0;
+  while (pos < str.length() && isspace((unsigned int)str[pos])) {
+    pos++;
+  }
+  while (pos < str.length()) {
+    size_t word_start = pos;
+    while (pos < str.length() && !isspace((unsigned int)str[pos])) {
+      pos++;
+    }
+    words.push_back(str.substr(word_start, pos - word_start));
+    num_words++;
+
+    while (pos < str.length() && isspace((unsigned int)str[pos])) {
+      pos++;
+    }
+  }
+
+  return num_words;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigDeclaration::downcase
+//       Access: Public, Static
+//  Description: Returns the input string with all uppercase letters
+//               converted to lowercase.
+////////////////////////////////////////////////////////////////////
+string ConfigDeclaration::
+downcase(const string &s) {
+  string result;
+  result.reserve(s.size());
+  string::const_iterator p;
+  for (p = s.begin(); p != s.end(); ++p) {
+    result += tolower(*p);
+  }
+  return result;
+}

+ 119 - 0
dtool/src/prc/configDeclaration.h

@@ -0,0 +1,119 @@
+// Filename: configDeclaration.h
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGDECLARATION_H
+#define CONFIGDECLARATION_H
+
+#include "dtoolbase.h"
+#include "configPage.h"
+#include "vector_string.h"
+
+class ConfigVariableCore;
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigDeclaration
+// Description : A single declaration of a config variable, typically
+//               defined as one line in a .prc file,
+//               e.g. "show-frame-rate-meter 1".  This is really just
+//               a pairing of a string name (actually, a
+//               ConfigVariableCore pointer) to a string value.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG ConfigDeclaration {
+private:
+  ConfigDeclaration(ConfigPage *page, ConfigVariableCore *variable,
+                    const string &string_value, int decl_seq);
+  ~ConfigDeclaration();
+
+public:
+  INLINE bool operator < (const ConfigDeclaration &other) const;
+
+public:
+  INLINE ConfigPage *get_page() const;
+  INLINE ConfigVariableCore *get_variable() const;
+
+  INLINE const string &get_string_value() const;
+  INLINE void set_string_value(const string &value);
+
+  INLINE int get_num_words() const;
+
+  INLINE bool has_string_word(int n) const;
+  INLINE bool has_bool_word(int n) const;
+  INLINE bool has_int_word(int n) const;
+  INLINE bool has_double_word(int n) const;
+
+  INLINE string get_string_word(int n) const;
+  INLINE bool get_bool_word(int n) const;
+  INLINE int get_int_word(int n) const;
+  INLINE double get_double_word(int n) const;
+
+  void set_string_word(int n, const string &value);
+  void set_bool_word(int n, bool value);
+  void set_int_word(int n, int value);
+  void set_double_word(int n, double value);
+
+  INLINE int get_decl_seq() const;
+
+  void output(ostream &out) const;
+  void write(ostream &out) const;
+
+public:
+  static int extract_words(const string &str, vector_string &words);
+  static string downcase(const string &s);
+
+private:
+  void get_words();
+  void check_bool_word(int n);
+  void check_int_word(int n);
+  void check_double_word(int n);
+
+private:
+  ConfigPage *_page;
+  ConfigVariableCore *_variable;
+  string _string_value;
+  int _decl_seq;
+
+  enum WordFlags {
+    F_checked_bool   = 0x0001,
+    F_valid_bool     = 0x0002,
+    F_checked_int    = 0x0004,
+    F_valid_int      = 0x0008,
+    F_checked_double = 0x0010,
+    F_valid_double   = 0x0020,
+  };
+
+  class Word {
+  public:
+    string _str;
+    bool _bool;
+    int _int;
+    double _double;
+    short _flags;
+  };
+
+  typedef pvector<Word> Words;
+  Words _words;
+  bool _got_words;
+
+  friend class ConfigPage;
+};
+
+INLINE ostream &operator << (ostream &out, const ConfigDeclaration &decl);
+
+#include "configDeclaration.I"
+
+#endif

+ 143 - 0
dtool/src/prc/configPage.I

@@ -0,0 +1,143 @@
+// Filename: configPage.I
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::operator <
+//       Access: Public
+//  Description: Sorts two pages in order based on the order in
+//               which their respective pages were loaded, and the
+//               order in which they appear within the same page.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigPage::
+operator < (const ConfigPage &other) const {
+  if (is_implicit() != other.is_implicit()) {
+    // Explicitly-loaded pages are more important than
+    // implicitly-loaded pages, so put the implicit pages at the end
+    // of the list.
+    return (int)is_implicit() < (int)other.is_implicit();
+  }
+
+  // Within the implicit/explicit categorization, sort by the page
+  // sequence.  The higher page sequence is more important (since it
+  // was loaded later), so it gets sorted to the front of the list.
+  return get_page_seq() > other.get_page_seq();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::get_name
+//       Access: Published
+//  Description: Returns the name of the page.  If the page was loaded
+//               from a .prc file, this is usually the filename.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigPage::
+get_name() const {
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::is_special
+//       Access: Published
+//  Description: Returns true if this is the special "default" or
+//               "local" page, or false if it is an ordinary page,
+//               e.g. an implicit page loaded from a prc file at
+//               startup, or an explicit page created by
+//               ConfigPageManager::make_explicit_page().
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigPage::
+is_special() const {
+  return this == get_default_page() || this == get_local_page();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::is_implicit
+//       Access: Published
+//  Description: Returns true if the page was loaded by implicitly
+//               searching the config path on startup, or false if it
+//               was explicitly loaded by dynamic code after initial
+//               startup.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigPage::
+is_implicit() const {
+  return _implicit_load;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::get_page_seq
+//       Access: Published
+//  Description: Returns the sequence number of the page.  
+//
+//               Sequence numbers for a particular class (implicit
+//               vs. explicit) of pages are assigned as each page is
+//               loaded; each page is given a higher sequence number
+//               than all the pages loaded before it.
+//
+//               The implicit_load pages, which are discovered in the
+//               file system automatically, have a different set of
+//               sequence numbers than the explicit pages.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigPage::
+get_page_seq() const {
+  return _page_seq;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::get_trust_level
+//       Access: Published
+//  Description: Returns the trust level associated with this page.
+//               An untrusted page is trust level 0; if the page was
+//               loaded from a signed .prc file, its trust level is
+//               the index number of the certificate that signed it.
+//               Generally, a higher trust level value represents
+//               a greater level of trust.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigPage::
+get_trust_level() const {
+  return _trust_level;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::get_signature
+//       Access: Published
+//  Description: Returns the raw binary signature that was found in
+//               the prc file, if any.  This method is probably not
+//               terribly useful for most applications.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigPage::
+get_signature() const {
+  return _signature;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::make_dirty
+//       Access: Private
+//  Description: Called internally when the page is changed through
+//               some API operation, this is intended as a hook to
+//               mark the page untrusted.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigPage::
+make_dirty() {
+  _trust_level = 0;
+}
+
+INLINE ostream &
+operator << (ostream &out, const ConfigPage &page) {
+  page.output(out);
+  return out;
+}

+ 427 - 0
dtool/src/prc/configPage.cxx

@@ -0,0 +1,427 @@
+// Filename: configPage.cxx
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "configPage.h"
+#include "configDeclaration.h"
+#include "configVariableCore.h"
+#include "configVariableManager.h"
+#include "prcKeyRegistry.h"
+#include "config_prc.h"
+
+#include <ctype.h>
+
+#ifdef HAVE_SSL
+#include <openssl/evp.h>
+#endif
+
+ConfigPage *ConfigPage::_default_page = NULL;
+ConfigPage *ConfigPage::_local_page = NULL;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::Constructor
+//       Access: Private
+//  Description: The constructor is private because a ConfigPage
+//               should be constructed via the ConfigPageManager
+//               make_page() interface.
+////////////////////////////////////////////////////////////////////
+ConfigPage::
+ConfigPage(const string &name, bool implicit_load, int page_seq) :
+  _name(name),
+  _implicit_load(implicit_load),
+  _page_seq(page_seq),
+  _next_decl_seq(1),
+  _trust_level(0)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::Destructor
+//       Access: Private
+//  Description: The destructor is private because a ConfigPage
+//               should be deleted via the ConfigPageManager
+//               delete_page() interface.
+////////////////////////////////////////////////////////////////////
+ConfigPage::
+~ConfigPage() {
+  clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::get_default_page
+//       Access: Published, Static
+//  Description: Returns a pointer to the global "default page".  This
+//               is the ConfigPage that lists all variables' original
+//               default values.
+////////////////////////////////////////////////////////////////////
+ConfigPage *ConfigPage::
+get_default_page() {
+  if (_default_page == (ConfigPage *)NULL) {
+    _default_page = new ConfigPage("default", false, 0);
+  }
+  return _default_page;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::get_local_page
+//       Access: Published, Static
+//  Description: Returns a pointer to the global "local page".  This
+//               is the ConfigPage that lists the locally-assigned
+//               values for any variables in the world that have such
+//               a local assignment.
+////////////////////////////////////////////////////////////////////
+ConfigPage *ConfigPage::
+get_local_page() {
+  if (_local_page == (ConfigPage *)NULL) {
+    _local_page = new ConfigPage("local", false, 0);
+  }
+  return _local_page;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::clear
+//       Access: Published
+//  Description: Removes all of the declarations from the page.
+////////////////////////////////////////////////////////////////////
+void ConfigPage::
+clear() {
+  Declarations::iterator di;
+  for (di = _declarations.begin(); di != _declarations.end(); ++di) {
+    ConfigDeclaration *decl = (*di);
+    delete decl;
+  }
+  _declarations.clear();
+  _trust_level = 0;
+  _signature = string();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::read_prc
+//       Access: Published
+//  Description: Reads the contents of a complete prc file, as
+//               returned by the indicated istream, into the current
+//               page file.  Returns true on success, or false on some
+//               I/O error.
+////////////////////////////////////////////////////////////////////
+bool ConfigPage::
+read_prc(istream &in) {
+  // We must empty the page before we start to read it; otherwise
+  // trust level is meaningless.
+  clear();
+
+  // We avoid getline() here because of its notorious problem with
+  // last lines that lack a trailing newline character.
+  static const size_t buffer_size = 1024;
+  char buffer[buffer_size];
+
+#ifdef HAVE_SSL
+  // Set up the evp context for verifying the signature, if we find
+  // one.
+#ifdef SSL_097
+  _md_ctx = EVP_MD_CTX_create();
+#else
+  _md_ctx = new EVP_MD_CTX;
+#endif
+  EVP_VerifyInit((EVP_MD_CTX *)_md_ctx, EVP_sha1());
+#endif  // HAVE_SSL
+
+  string prev_line;
+
+  in.read(buffer, buffer_size);
+  size_t count = in.gcount();
+  while (count != 0) {
+    char *buffer_end = buffer + count;
+
+    // Look for the first line in the buffer..
+    char *newline = (char *)memchr((void *)buffer, '\n', count);
+    if (newline == NULL) {
+      // The buffer was one long line.  Huh.
+      prev_line += string(buffer, count);
+
+    } else {
+      // Process the first line in the buffer.
+      size_t length = newline - buffer;
+      read_prc_line(prev_line + string(buffer, length + 1));
+
+      // Now look for the next line, etc.
+      char *start = newline + 1;
+      newline = (char *)memchr((void *)start, '\n', buffer_end - start);
+      while (newline != NULL) {
+        length = newline - start;
+        read_prc_line(string(start, length + 1));
+        start = newline + 1;
+        newline = (char *)memchr((void *)start, '\n', buffer_end - start);
+      }
+
+      // The remaining text in the buffer is the start of the next
+      // line.
+      length = buffer_end - start;
+      prev_line = string(start, length);
+    }
+
+    in.read(buffer, buffer_size);
+    count = in.gcount();
+  }
+
+  if (!prev_line.empty()) {
+    read_prc_line(prev_line);
+  }
+
+#ifdef HAVE_SSL
+  // Now validate the signature and free the SSL structures.
+  if (!_signature.empty()) {
+    PrcKeyRegistry *pkr = PrcKeyRegistry::get_global_ptr();
+    int num_keys = pkr->get_num_keys();
+    for (int i = 1; i < num_keys && _trust_level == 0; i++) {
+      EVP_PKEY *pkey = pkr->get_key(i);
+      if (pkey != (EVP_PKEY *)NULL) {
+        int verify_result = 
+          EVP_VerifyFinal((EVP_MD_CTX *)_md_ctx, 
+                          (unsigned char *)_signature.data(), 
+                          _signature.size(), pkey);
+        if (verify_result == 1) {
+          _trust_level = i;
+        }
+      }
+    }
+    if (_trust_level == 0) {
+      prc_cat->info() 
+        << "invalid signature found in " << get_name() << "\n";
+    }
+  }
+#ifdef SSL_097
+  EVP_MD_CTX_destroy((EVP_MD_CTX *)_md_ctx);
+#else
+  delete (EVP_MD_CTX *)_md_ctx;
+#endif
+#endif  // HAVE_SSL
+
+  bool failed = (in.fail() && !in.eof());
+
+  return !failed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::make_declaration
+//       Access: Published
+//  Description: Adds the indicated variable/value pair as a new
+//               declaration on the page.
+////////////////////////////////////////////////////////////////////
+ConfigDeclaration *ConfigPage::
+make_declaration(const string &variable, const string &value) {
+  ConfigVariableManager *variable_mgr = ConfigVariableManager::get_global_ptr();
+  return make_declaration(variable_mgr->make_variable(variable), value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::make_declaration
+//       Access: Published
+//  Description: Adds the indicated variable/value pair as a new
+//               declaration on the page.
+////////////////////////////////////////////////////////////////////
+ConfigDeclaration *ConfigPage::
+make_declaration(ConfigVariableCore *variable, const string &value) {
+  ConfigDeclaration *decl = new ConfigDeclaration
+    (this, variable, value, _next_decl_seq);
+  _next_decl_seq++;
+
+  _declarations.push_back(decl);
+  make_dirty();
+
+  return decl;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::delete_declaration
+//       Access: Published
+//  Description: Removes the indicated declaration from the page and
+//               deletes it.  Returns true if the declaration is
+//               successfully removed, false if it was not on the
+//               page.
+////////////////////////////////////////////////////////////////////
+bool ConfigPage::
+delete_declaration(ConfigDeclaration *decl) {
+  Declarations::iterator di;
+  for (di = _declarations.begin(); di != _declarations.end(); ++di) {
+    if ((*di) == decl) {
+      _declarations.erase(di);
+      delete decl;
+      make_dirty();
+      return true;
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::get_num_declarations
+//       Access: Published
+//  Description: Returns the number of declarations on the page.
+////////////////////////////////////////////////////////////////////
+int ConfigPage::
+get_num_declarations() const {
+  return _declarations.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::get_declaration
+//       Access: Published
+//  Description: Returns the nth declaration on the page.
+////////////////////////////////////////////////////////////////////
+const ConfigDeclaration *ConfigPage::
+get_declaration(int n) const {
+  nassertr(n >= 0 && n < (int)_declarations.size(), NULL);
+  return _declarations[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::get_variable_name
+//       Access: Published
+//  Description: Returns the variable named by the nth declaration on
+//               the page.
+////////////////////////////////////////////////////////////////////
+string ConfigPage::
+get_variable_name(int n) const {
+  nassertr(n >= 0 && n < (int)_declarations.size(), string());
+  return _declarations[n]->get_variable()->get_name();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::get_string_value
+//       Access: Published
+//  Description: Returns the value assigned by the nth declaration on
+//               the page.
+////////////////////////////////////////////////////////////////////
+string ConfigPage::
+get_string_value(int n) const {
+  nassertr(n >= 0 && n < (int)_declarations.size(), string());
+  return _declarations[n]->get_string_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::is_variable_used
+//       Access: Published
+//  Description: Returns true if the nth active variable on
+//               the page has been used by code, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ConfigPage::
+is_variable_used(int n) const {
+  nassertr(n >= 0 && n < (int)_declarations.size(), false);
+  return _declarations[n]->get_variable()->is_used();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConfigPage::
+output(ostream &out) const {
+  out << "ConfigPage " << get_name() << ", " << get_num_declarations()
+      << " declarations.";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConfigPage::
+write(ostream &out) const {
+  Declarations::const_iterator di;
+  for (di = _declarations.begin(); di != _declarations.end(); ++di) {
+    (*di)->write(out);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::read_prc_line
+//       Access: Private
+//  Description: Handles reading in a single line from a .prc file.
+//               This is called internally by read_prc() for each
+//               line.
+////////////////////////////////////////////////////////////////////
+void ConfigPage::
+read_prc_line(const string &line) {
+  if (line.substr(0, 7) == "##!sig ") {
+    // This is a signature.  Accumulate it into the signature and
+    // return, and don't count it as contributing to the hash.
+    for (size_t p = 7; p < line.length() - 1; p += 2) {
+      unsigned char digit = (hex_digit(line[p]) << 4) | hex_digit(line[p + 1]);
+      _signature += digit;
+    }
+    return;
+  }
+
+#ifdef HAVE_SSL
+  // Accumulate any line that's not itself a signature into the hash,
+  // so we can validate the signature at the end.
+  EVP_VerifyUpdate((EVP_MD_CTX *)_md_ctx, line.data(), line.size());
+#endif  // HAVE_SSL
+
+  // Separate the line into a variable and a value.
+  size_t p = 0;
+  while (p < line.length() && isspace((unsigned char)line[p])) {
+    p++;
+  }
+
+  if (p == line.length() || line[p] == '#') {
+    // Blank line or comment; do nothing.
+    return;
+  }
+
+  size_t variable_begin = p;
+  while (p < line.length() && !isspace((unsigned char)line[p])) {
+    p++;
+  }
+  size_t variable_end = p;
+
+  while (p < line.length() && isspace((unsigned char)line[p])) {
+    p++;
+  }
+  size_t value_begin = p;
+
+  // The value extends from here to the end of the line, so trim
+  // whitespace backwards off from the end of the line.
+  p = line.length();
+  while (p > value_begin && isspace((unsigned char)line[p - 1])) {
+    p--;
+  }
+  size_t value_end = p;
+
+  string variable = line.substr(variable_begin, variable_end - variable_begin);
+  string value = line.substr(value_begin, value_end - value_begin);
+
+  make_declaration(variable, value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPage::hex_digit
+//       Access: Private, Static
+//  Description: Decodes a hex digit into its numeric value.
+////////////////////////////////////////////////////////////////////
+unsigned int ConfigPage::
+hex_digit(unsigned char digit) {
+  if (isalpha(digit)) {
+    return tolower(digit) - 'a' + 10;
+  } else if (isdigit(digit)) {
+    return digit - '0';
+  } else {
+    return 0;
+  }
+}

+ 105 - 0
dtool/src/prc/configPage.h

@@ -0,0 +1,105 @@
+// Filename: configPage.h
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGPAGE_H
+#define CONFIGPAGE_H
+
+#include "dtoolbase.h"
+#include "pvector.h"
+
+class ConfigDeclaration;
+class ConfigVariableCore;
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigPage
+// Description : A page of ConfigDeclarations that may be loaded or
+//               unloaded.  Typically this represents a single .prc
+//               file that is read from disk at runtime, but it may
+//               also represent a list of declarations built up
+//               by application code and explicitly loaded.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG ConfigPage {
+private:
+  ConfigPage(const string &name, bool implicit_load, int page_seq);
+  ~ConfigPage();
+
+public:
+  INLINE bool operator < (const ConfigPage &other) const;
+
+PUBLISHED:
+  static ConfigPage *get_default_page();
+  static ConfigPage *get_local_page();
+
+  INLINE const string &get_name() const;
+
+  INLINE bool is_special() const;
+  INLINE bool is_implicit() const;
+
+  INLINE int get_page_seq() const;
+  INLINE int get_trust_level() const;
+  INLINE const string &get_signature() const;
+
+  void clear();
+  bool read_prc(istream &in);
+
+  ConfigDeclaration *make_declaration(const string &variable, const string &value);
+  ConfigDeclaration *make_declaration(ConfigVariableCore *variable, const string &value);
+  bool delete_declaration(ConfigDeclaration *decl);
+
+  int get_num_declarations() const;
+  const ConfigDeclaration *get_declaration(int n) const;
+  string get_variable_name(int n) const;
+  string get_string_value(int n) const;
+  bool is_variable_used(int n) const;
+
+  void output(ostream &out) const;
+  void write(ostream &out) const;
+
+private:
+  INLINE void make_dirty();
+  void read_prc_line(const string &line);
+  static unsigned int hex_digit(unsigned char digit);
+
+  string _name;
+  bool _implicit_load;
+  int _page_seq;
+  int _next_decl_seq;
+  int _trust_level;
+
+  typedef pvector<ConfigDeclaration *> Declarations;
+  Declarations _declarations;
+
+  string _signature;
+
+#ifdef HAVE_SSL
+  // This maintains the hash of the prc file as we are scanning it, so
+  // we can compare its signature which we discover at the end.
+  void *_md_ctx;
+#endif  // HAVE_SSL
+
+  static ConfigPage *_default_page;
+  static ConfigPage *_local_page;
+
+  friend class ConfigPageManager;
+};
+
+INLINE ostream &operator << (ostream &out, const ConfigPage &page);
+
+#include "configPage.I"
+
+#endif

+ 187 - 0
dtool/src/prc/configPageManager.I

@@ -0,0 +1,187 @@
+// Filename: configPageManager.I
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::loaded_implicit_pages
+//       Access: Published
+//  Description: Returns true if the implicit *.prc files have already
+//               been loaded, false otherwise.  Normally this will
+//               only be false briefly before startup.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigPageManager::
+loaded_implicit_pages() const {
+  return _loaded_implicit;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::load_implicit_pages
+//       Access: Published
+//  Description: Searches the PRC_DIR and/or PRC_PATH directories for
+//               *.prc files and loads them in as pages.  This is
+//               normally called automatically at startup time, when
+//               the first variable's value is referenced.  See also
+//               reload_implicit_pages().
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigPageManager::
+load_implicit_pages() {
+  if (!_loaded_implicit) {
+    reload_implicit_pages();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::get_search_path
+//       Access: Published
+//  Description: Returns the search path used to locate implicit .prc
+//               files.  This is determined by the PRC_DIR and
+//               PRC_PATH environment variables.  The object returned
+//               by this method may be modified to change the path at
+//               runtime, and then reload_implicit_pages() called.
+////////////////////////////////////////////////////////////////////
+INLINE DSearchPath &ConfigPageManager::
+get_search_path() {
+  load_implicit_pages();
+  return _search_path;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::get_num_prc_patterns
+//       Access: Published
+//  Description: Returns the number of patterns, like "*.prc", that
+//               are compiled in that will be searched for as default
+//               config filenames.  Normally there is only one
+//               pattern, and it is "*.prc", but others may be
+//               specified with the PRC_FILENAME variable in
+//               Config.pp.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigPageManager::
+get_num_prc_patterns() const {
+  return _prc_patterns.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::get_prc_pattern
+//       Access: Published
+//  Description: Returns the nth filename pattern that will be
+//               considered a match as a valid config file.  See
+//               get_num_prc_patterns().
+////////////////////////////////////////////////////////////////////
+INLINE string ConfigPageManager::
+get_prc_pattern(int n) const {
+  nassertr(n >= 0 && n < (int)_prc_patterns.size(), string());
+  return _prc_patterns[n].get_pattern();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::get_num_prc_executable_patterns
+//       Access: Published
+//  Description: Returns the number of patterns, like "*.exe", that
+//               are compiled in that will be searched for as special
+//               config files that are to be executed as a program,
+//               and their output taken to be input.  This is normally
+//               empty.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigPageManager::
+get_num_prc_executable_patterns() const {
+  return _prc_executable_patterns.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::get_prc_executable_pattern
+//       Access: Published
+//  Description: Returns the nth filename pattern that will be
+//               considered a match as a valid executable-style config
+//               file.  See get_num_prc_executable_patterns().
+////////////////////////////////////////////////////////////////////
+INLINE string ConfigPageManager::
+get_prc_executable_pattern(int n) const {
+  nassertr(n >= 0 && n < (int)_prc_patterns.size(), string());
+  return _prc_executable_patterns[n].get_pattern();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::get_num_implicit_pages
+//       Access: Published
+//  Description: Returns the current number of implicitly-loaded
+//               ConfigPages in the world.  These represent files that
+//               were automatically discovered on the disk as .prc
+//               files.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigPageManager::
+get_num_implicit_pages() const {
+  return _implicit_pages.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::get_implicit_page
+//       Access: Published
+//  Description: Returns the nth implicit ConfigPage in the world.
+//               See get_num_implicit_pages().
+////////////////////////////////////////////////////////////////////
+INLINE ConfigPage *ConfigPageManager::
+get_implicit_page(int n) const {
+  check_sort_pages();
+  nassertr(n >= 0 && n < (int)_implicit_pages.size(), NULL);
+  return _implicit_pages[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::get_num_explicit_pages
+//       Access: Published
+//  Description: Returns the current number of explicitly-loaded
+//               ConfigPages in the world.  These represent pages that
+//               were loaded dynamically at runtime by explicit calls
+//               to ConfigPageManager::make_explicit_page().
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigPageManager::
+get_num_explicit_pages() const {
+  return _explicit_pages.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::get_explicit_page
+//       Access: Published
+//  Description: Returns the nth explicit ConfigPage in the world.
+//               See get_num_explicit_pages().
+////////////////////////////////////////////////////////////////////
+INLINE ConfigPage *ConfigPageManager::
+get_explicit_page(int n) const {
+  check_sort_pages();
+  nassertr(n >= 0 && n < (int)_explicit_pages.size(), NULL);
+  return _explicit_pages[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::check_sort_pages()
+//       Access: Private
+//  Description: Called internally to ensure that the list of
+//               pages is properly sorted.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigPageManager::
+check_sort_pages() const {
+  if (!_pages_sorted) {
+    ((ConfigPageManager *)this)->sort_pages();
+  }
+}
+
+INLINE ostream &
+operator << (ostream &out, const ConfigPageManager &pageMgr) {
+  pageMgr.output(out);
+  return out;
+}

+ 407 - 0
dtool/src/prc/configPageManager.cxx

@@ -0,0 +1,407 @@
+// Filename: configPageManager.cxx
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "configPageManager.h"
+#include "configDeclaration.h"
+#include "configPage.h"
+#include "prcKeyRegistry.h"
+#include "dSearchPath.h"
+#include "executionEnvironment.h"
+#include "pset.h"
+#include "config_prc.h"
+#include "pfstream.h"
+
+// Pick up the public key definitions.
+#ifdef PRC_PUBLIC_KEYS_INCLUDE
+#include PRC_PUBLIC_KEYS_INCLUDE
+#endif
+
+#include <algorithm>
+#include <ctype.h>
+
+ConfigPageManager *ConfigPageManager::_global_ptr = NULL;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::Constructor
+//       Access: Protected
+//  Description: The constructor is private (actually, just protected,
+//               but only to avoid a gcc compiler warning) because it
+//               should not be explicitly constructed.  There is only
+//               one ConfigPageManager, and it constructs itself.
+////////////////////////////////////////////////////////////////////
+ConfigPageManager::
+ConfigPageManager() {
+  _next_page_seq = 1;
+  _loaded_implicit = false;
+  _currently_loading = false;
+  _pages_sorted = true;
+
+#ifdef PRC_PUBLIC_KEYS_INCLUDE
+  // Record the public keys in the registry at startup time.
+  PrcKeyRegistry::get_global_ptr()->record_keys(prc_pubkeys, num_prc_pubkeys);
+#endif  // PRC_PUBLIC_KEYS_INCLUDE
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::Destructor
+//       Access: Protected
+//  Description: The ConfigPageManager destructor should never be
+//               called, because this is a global object that is never
+//               freed.
+////////////////////////////////////////////////////////////////////
+ConfigPageManager::
+~ConfigPageManager() {
+  prc_cat->error()
+    << "Internal error--ConfigPageManager destructor called!\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::reload_implicit_pages
+//       Access: Published
+//  Description: Searches the PRC_DIR and/or PRC_PATH directories for
+//               *.prc files and loads them in as pages.
+//
+//               This may be called after startup, to force the system
+//               to re-read all of the implicit prc files.
+////////////////////////////////////////////////////////////////////
+void ConfigPageManager::
+reload_implicit_pages() {
+  if (_currently_loading) {
+    // This is a recursion protector.  We can get recursion feedback
+    // between config and notify, as each tries to use the other at
+    // construction.
+    return;
+  }
+  _currently_loading = true;
+
+  // First, remove all the previously-loaded pages.
+  Pages::iterator pi;
+  for (pi = _implicit_pages.begin(); pi != _implicit_pages.end(); ++pi) {
+    delete (*pi);
+  }
+  _implicit_pages.clear();
+
+  // Build up the search path for .prc files.
+  _search_path.clear();
+
+  // PRC_DIR_ENVVARS lists one or more environment variables separated
+  // by spaces.  Pull them out, and each of those contains the name of
+  // a single directory to search.  Add it to the search path.
+  string prc_dir_envvars = PRC_DIR_ENVVARS;
+  if (!prc_dir_envvars.empty()) {
+    vector_string prc_dir_envvar_list;
+    ConfigDeclaration::extract_words(prc_dir_envvars, prc_dir_envvar_list);
+    for (size_t i = 0; i < prc_dir_envvar_list.size(); i++) {
+      string prc_dir = ExecutionEnvironment::get_environment_variable(prc_dir_envvar_list[i]);
+      _search_path.append_directory(Filename::from_os_specific(prc_dir));
+    }
+  }
+
+  // PRC_PATH_ENVVARS lists one or more environment variables separated
+  // by spaces.  Pull them out, and then each one of those contains a
+  // list of directories to search.  Add each of those to the search
+  // path.
+  string prc_path_envvars = PRC_PATH_ENVVARS;
+  if (!prc_path_envvars.empty()) {
+    vector_string prc_path_envvar_list;
+    ConfigDeclaration::extract_words(prc_path_envvars, prc_path_envvar_list);
+    for (size_t i = 0; i < prc_path_envvar_list.size(); i++) {
+      string prc_path = ExecutionEnvironment::get_environment_variable(prc_path_envvar_list[i]);
+      _search_path.append_path(prc_path);
+    }
+  }
+
+  if (_search_path.is_empty()) {
+    // If nothing's on the search path (PRC_DIR and PRC_PATH were not
+    // defined), then use the DEFAULT_PRC_DIR.
+    string default_prc_dir = DEFAULT_PRC_DIR;
+    if (!default_prc_dir.empty()) {
+      // It's already from-os-specific by ppremake.
+      _search_path.append_directory(default_prc_dir);
+    }
+  }
+
+  // PRC_PATTERNS lists one or more filename templates separated by
+  // spaces.  Pull them out too.
+  _prc_patterns.clear();
+
+  string prc_patterns = PRC_PATTERNS;
+  if (!prc_patterns.empty()) {
+    vector_string pat_list;
+    ConfigDeclaration::extract_words(prc_patterns, pat_list);
+    _prc_patterns.reserve(pat_list.size());
+    for (size_t i = 0; i < pat_list.size(); i++) {
+      GlobPattern glob(pat_list[i]);
+#ifdef WIN32
+      // On windows, the file system is case-insensitive, so the
+      // pattern should be too.
+      glob.set_case_sensitive(false);
+#endif  // WIN32
+      _prc_patterns.push_back(glob);
+    }
+  }
+
+  // Similarly for PRC_EXECUTABLE_PATTERNS.
+  _prc_executable_patterns.clear();
+
+  string prc_executable_patterns = PRC_EXECUTABLE_PATTERNS;
+  if (!prc_executable_patterns.empty()) {
+    vector_string pat_list;
+    ConfigDeclaration::extract_words(prc_executable_patterns, pat_list);
+    _prc_executable_patterns.reserve(pat_list.size());
+    for (size_t i = 0; i < pat_list.size(); i++) {
+      GlobPattern glob(pat_list[i]);
+#ifdef WIN32
+      glob.set_case_sensitive(false);
+#endif  // WIN32
+      _prc_executable_patterns.push_back(glob);
+    }
+  }
+
+  // Now find all of the *.prc files (or whatever matches
+  // PRC_PATTERNS) on the path.
+  ConfigFiles config_files;
+
+  // Use a pset to ensure that we only visit each directory once, even
+  // if it appears multiple times (under different aliases!) in the
+  // path.
+  pset<Filename> unique_dirnames;
+
+  // We walk through the list of directories in forward order, so that
+  // the most important directories are visited first.
+  for (int di = 0; di < _search_path.get_num_directories(); di++) {
+    const Filename &directory = _search_path.get_directory(di);
+    if (directory.is_directory()) {
+      Filename canonical(directory, ".");
+      canonical.make_canonical();
+      if (unique_dirnames.insert(canonical).second) {
+        vector_string files;
+        directory.scan_directory(files);
+
+        // We walk through the directory's list of files in reverse
+        // alphabetical order, because for historical reasons, the
+        // most important file within a directory is the
+        // alphabetically last file of that directory, and we still
+        // want to visit the most important files first.
+        vector_string::reverse_iterator fi;
+        for (fi = files.rbegin(); fi != files.rend(); ++fi) {
+          int file_flags = 0;
+          for (Globs::const_iterator gi = _prc_patterns.begin();
+               gi != _prc_patterns.end();
+               ++gi) {
+            if ((*gi).matches(*fi)) {
+              file_flags |= FF_read;
+              break;
+            }
+          }
+          for (Globs::const_iterator gi = _prc_executable_patterns.begin();
+               gi != _prc_executable_patterns.end();
+               ++gi) {
+            if ((*gi).matches(*fi)) {
+              file_flags |= FF_execute;
+              break;
+            }
+          }
+          if (file_flags != 0) {
+            ConfigFile file;
+            file._file_flags = file_flags;
+            file._filename = Filename(directory, (*fi));
+            config_files.push_back(file);
+          }
+        }
+      }
+    }
+  }
+
+  // Now we have a list of filenames in order from most important to
+  // least important.  Walk through the list in reverse order to load
+  // their contents, because we want the first file in the list (the
+  // most important) to be on the top of the stack.
+  ConfigFiles::reverse_iterator ci;
+  int i = 1;
+  for (ci = config_files.rbegin(); ci != config_files.rend(); ++ci) {
+    const ConfigFile &file = (*ci);
+    Filename filename = file._filename;
+
+    if ((file._file_flags & FF_execute) != 0 &&
+        filename.is_executable()) {
+      // Attempt to execute the file as a command.
+      string args = ExecutionEnvironment::get_environment_variable(PRC_EXECUTABLE_ARGS_ENVVAR);
+      string command = filename.to_os_specific() + " " + args;
+      IPipeStream ifs(command);
+
+      ConfigPage *page = new ConfigPage(filename, true, i);
+      i++;
+      _implicit_pages.push_back(page);
+      _pages_sorted = false;
+      
+      page->read_prc(ifs);
+
+    } else if ((file._file_flags & FF_read) != 0) {
+      // Just read the file.
+      filename.set_text();
+      
+      ifstream in;
+      if (!filename.open_read(in)) {
+        prc_cat.error()
+          << "Unable to read " << filename << "\n";
+        
+      } else {
+        ConfigPage *page = new ConfigPage(filename, true, i);
+        i++;
+        _implicit_pages.push_back(page);
+        _pages_sorted = false;
+        
+        page->read_prc(in);
+      }
+    }
+  }
+
+  if (!_loaded_implicit) {
+    Notify::ptr()->config_initialized();
+    _loaded_implicit = true;
+  }
+
+  _currently_loading = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::make_explicit_page
+//       Access: Published
+//  Description: Creates and returns a new, empty ConfigPage.  This
+//               page will be stacked on top of any pages that were
+//               created before; it may shadow variable declarations
+//               that are defined in previous pages.
+////////////////////////////////////////////////////////////////////
+ConfigPage *ConfigPageManager::
+make_explicit_page(const string &name) {
+  ConfigPage *page = new ConfigPage(name, false, _next_page_seq);
+  _next_page_seq++;
+  _explicit_pages.push_back(page);
+  _pages_sorted = false;
+  return page;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::delete_explicit_page
+//       Access: Published
+//  Description: Removes a previously-constructed ConfigPage from the
+//               set of active pages, and deletes it.  The ConfigPage
+//               object is no longer valid after this call.  Returns
+//               true if the page is successfully deleted, or false if
+//               it was unknown (which should never happen if the page
+//               was legitimately constructed).
+////////////////////////////////////////////////////////////////////
+bool ConfigPageManager::
+delete_explicit_page(ConfigPage *page) {
+  Pages::iterator pi;
+  for (pi = _explicit_pages.begin(); pi != _explicit_pages.end(); ++pi) {
+    if ((*pi) == page) {
+      _explicit_pages.erase(pi);
+      delete page;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConfigPageManager::
+output(ostream &out) const {
+  out << "ConfigPageManager, " 
+      << _explicit_pages.size() + _implicit_pages.size() 
+      << " pages.";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConfigPageManager::
+write(ostream &out) const {
+  check_sort_pages();
+  out << _explicit_pages.size() << " explicit pages:\n";
+
+  Pages::const_iterator pi;
+  for (pi = _explicit_pages.begin(); pi != _explicit_pages.end(); ++pi) {
+    const ConfigPage *page = (*pi);
+    out << "  " << page->get_name();
+    if (page->get_trust_level() > 0) {
+      out << "  (signed " << page->get_trust_level() << ")\n";
+    } else if (!page->get_signature().empty()) {
+      out << "  (invalid signature)\n";
+    } else {
+      out << "\n";
+    }
+  }
+
+  out << "\n" << _implicit_pages.size() << " implicit pages:\n";
+  for (pi = _implicit_pages.begin(); pi != _implicit_pages.end(); ++pi) {
+    const ConfigPage *page = (*pi);
+    out << "  " << page->get_name();
+    if (page->get_trust_level() > 0) {
+      out << "  (signed " << page->get_trust_level() << ")\n";
+    } else if (!page->get_signature().empty()) {
+      out << "  (invalid signature)\n";
+    } else {
+      out << "\n";
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::get_global_ptr
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ConfigPageManager *ConfigPageManager::
+get_global_ptr() {
+  if (_global_ptr == (ConfigPageManager *)NULL) {
+    _global_ptr = new ConfigPageManager;
+  }
+  return _global_ptr;
+}
+
+// This class is used in sort_pages, below.
+class CompareConfigPages {
+public:
+  bool operator () (const ConfigPage *a, const ConfigPage *b) const {
+    return (*a) < (*b);
+  }
+};
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigPageManager::sort_pages
+//       Access: Private
+//  Description: Sorts the list of pages into priority order,
+//               so that the page at the front of the list is
+//               the one that shadows all following pages.
+////////////////////////////////////////////////////////////////////
+void ConfigPageManager::
+sort_pages() {
+  sort(_implicit_pages.begin(), _implicit_pages.end(), CompareConfigPages());
+  sort(_explicit_pages.begin(), _explicit_pages.end(), CompareConfigPages());
+
+  _pages_sorted = true;
+}

+ 108 - 0
dtool/src/prc/configPageManager.h

@@ -0,0 +1,108 @@
+// Filename: configPageManager.h
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGPAGEMANAGER_H
+#define CONFIGPAGEMANAGER_H
+
+#include "dtoolbase.h"
+#include "pvector.h"
+#include "dSearchPath.h"
+#include "globPattern.h"
+#include "notify.h"
+
+class ConfigPage;
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigPageManager
+// Description : A global object that maintains the set of ConfigPages
+//               everywhere in the world, and keeps them in sorted
+//               order.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG ConfigPageManager {
+protected:
+  ConfigPageManager();
+  ~ConfigPageManager();
+
+PUBLISHED:
+  INLINE bool loaded_implicit_pages() const;
+  INLINE void load_implicit_pages();
+  void reload_implicit_pages();
+
+  INLINE DSearchPath &get_search_path();
+
+  INLINE int get_num_prc_patterns() const;
+  INLINE string get_prc_pattern(int n) const;
+
+  INLINE int get_num_prc_executable_patterns() const;
+  INLINE string get_prc_executable_pattern(int n) const;
+
+  ConfigPage *make_explicit_page(const string &name);
+  bool delete_explicit_page(ConfigPage *page);
+
+  INLINE int get_num_implicit_pages() const;
+  INLINE ConfigPage *get_implicit_page(int n) const;
+
+  INLINE int get_num_explicit_pages() const;
+  INLINE ConfigPage *get_explicit_page(int n) const;
+
+  void output(ostream &out) const;
+  void write(ostream &out) const;
+
+  static ConfigPageManager *get_global_ptr();
+
+private:
+  INLINE void check_sort_pages() const;
+  void sort_pages();
+
+  typedef pvector<ConfigPage *> Pages;
+  Pages _implicit_pages;
+  Pages _explicit_pages;
+  bool _pages_sorted;
+  int _next_page_seq;
+
+  bool _loaded_implicit;
+  bool _currently_loading;
+
+  DSearchPath _search_path;
+
+  typedef pvector<GlobPattern> Globs;
+  Globs _prc_patterns;
+  Globs _prc_executable_patterns;
+
+  // In load_implicit_pages(), we temporarily build up a list of
+  // potential config files to read and/or execute.  We'll need some
+  // data structures to store that information.
+  enum FileFlags {
+    FF_read     = 0x001,
+    FF_execute  = 0x002,
+  };
+  class ConfigFile {
+  public:
+    int _file_flags;
+    Filename _filename;
+  };
+  typedef pvector<ConfigFile> ConfigFiles;
+
+  static ConfigPageManager *_global_ptr;
+};
+
+INLINE ostream &operator << (ostream &out, const ConfigPageManager &pageMgr);
+
+#include "configPageManager.I"
+
+#endif

+ 365 - 0
dtool/src/prc/configVariable.I

@@ -0,0 +1,365 @@
+// Filename: configVariable.I
+// Created by:  drose (18Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::Constructor
+//       Access: Protected
+//  Description: This constructor is only intended to be called from a
+//               specialized ConfigVariableFoo derived class.
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariable::
+ConfigVariable(const string &name, ConfigVariableCore::ValueType value_type) :
+  _core(ConfigVariableManager::get_global_ptr()->make_variable(name))
+{
+  _core->set_value_type(value_type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariable::
+ConfigVariable(const string &name) :
+  _core(ConfigVariableManager::get_global_ptr()->make_variable(name))
+{
+  _core->set_used();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariable::
+~ConfigVariable() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_name
+//       Access: Published
+//  Description: Returns the name of the variable.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigVariable::
+get_name() const {
+  return _core->get_name();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_value_type
+//       Access: Published
+//  Description: Returns the stated type of this variable.  If the
+//               variable has not yet been defined, this will be
+//               VT_undefined.
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableCore::ValueType ConfigVariable::
+get_value_type() const {
+  return _core->get_value_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_trust_level
+//       Access: Published
+//  Description: Returns the minimum trust_level a prc file must
+//               demonstrate in order to redefine the value for this
+//               variable.  -1 indicates infinite trust: no prc file
+//               can redefine it.  Arguably, this should be called the
+//               "mistrust level", since the larger the value, the
+//               more suspicious we are of prc files.
+//
+//               This value only has effect in a release build.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariable::
+get_trust_level() const {
+  return _core->get_trust_level();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_description
+//       Access: Published
+//  Description: Returns the one-line description of this variable.
+//               If the variable has not yet been defined, this will
+//               be empty.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigVariable::
+get_description() const {
+  return _core->get_description();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_text
+//       Access: Published
+//  Description: Returns the paragraph help text describing the
+//               purpose of this variable in greater detail than
+//               get_description().  If the variable has not yet been
+//               defined, this will be empty.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigVariable::
+get_text() const {
+  return _core->get_text();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_default_value
+//       Access: Published
+//  Description: Returns the default variable specified for this
+//               variable.  If the variable has not yet been defined,
+//               this will return NULL.
+////////////////////////////////////////////////////////////////////
+INLINE const ConfigDeclaration *ConfigVariable::
+get_default_value() const {
+  return _core->get_default_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_string_value
+//       Access: Published
+//  Description: Returns the toplevel value of the variable, formatted
+//               as a string.
+////////////////////////////////////////////////////////////////////
+INLINE string ConfigVariable::
+get_string_value() const {
+  const ConfigDeclaration *decl = _core->get_declaration(0);
+  return decl->get_string_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::set_string_value
+//       Access: Published
+//  Description: Changes the value assigned to this variable.  This
+//               creates a local value that shadows any values defined
+//               in the .prc files, until clear_local_value() is
+//               called.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariable::
+set_string_value(const string &string_value) {
+  _core->make_local_value()->set_string_value(string_value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::clear_local_value
+//       Access: Published
+//  Description: Removes the local value defined for this variable,
+//               and allows its value to be once again retrieved from
+//               the .prc files.
+//
+//               Returns true if the value was successfully removed,
+//               false if it did not exist in the first place.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariable::
+clear_local_value() {
+  return _core->clear_local_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::has_local_value
+//       Access: Published
+//  Description: Returns true if this variable's value has been
+//               shadowed by a local assignment (as created via
+//               make_local_value()), or false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariable::
+has_local_value() const {
+  return _core->has_local_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_num_words
+//       Access: Published
+//  Description: Returns the number of words in the variable's
+//               value.  A word is defined as a sequence of
+//               non-whitespace characters delimited by whitespace.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariable::
+get_num_words() const {
+  const ConfigDeclaration *decl = _core->get_declaration(0);
+  return decl->get_num_words();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::has_string_word
+//       Access: Published
+//  Description: Returns true if the variable's value has a valid
+//               string value for the nth word.  This is really the
+//               same thing as asking if there are at least n words in
+//               the value.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariable::
+has_string_word(int n) const {
+  const ConfigDeclaration *decl = _core->get_declaration(0);
+  return decl->has_string_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::has_bool_word
+//       Access: Published
+//  Description: Returns true if the variable's value has a valid
+//               boolean value for the nth word.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariable::
+has_bool_word(int n) const {
+  const ConfigDeclaration *decl = _core->get_declaration(0);
+  return decl->has_bool_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::has_int_word
+//       Access: Published
+//  Description: Returns true if the variable's value has a valid
+//               integer value for the nth word.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariable::
+has_int_word(int n) const {
+  const ConfigDeclaration *decl = _core->get_declaration(0);
+  return decl->has_int_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::has_double_word
+//       Access: Published
+//  Description: Returns true if the variable's value has a valid
+//               integer value for the nth word.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariable::
+has_double_word(int n) const {
+  const ConfigDeclaration *decl = _core->get_declaration(0);
+  return decl->has_double_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_string_word
+//       Access: Published
+//  Description: Returns the string value of the nth word of the
+//               variable's value, or empty string if there is no
+//               nth value.  See also has_string_word().
+////////////////////////////////////////////////////////////////////
+INLINE string ConfigVariable::
+get_string_word(int n) const {
+  const ConfigDeclaration *decl = _core->get_declaration(0);
+  return decl->get_string_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_bool_word
+//       Access: Published
+//  Description: Returns the boolean value of the nth word of the
+//               variable's value, or false if there is no nth
+//               value.  See also has_bool_word().
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariable::
+get_bool_word(int n) const {
+  const ConfigDeclaration *decl = _core->get_declaration(0);
+  return decl->get_bool_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_int_word
+//       Access: Published
+//  Description: Returns the integer value of the nth word of the
+//               variable's value, or 0 if there is no nth value.
+//               See also has_int_word().
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariable::
+get_int_word(int n) const {
+  const ConfigDeclaration *decl = _core->get_declaration(0);
+  return decl->get_int_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::get_double_word
+//       Access: Published
+//  Description: Returns the integer value of the nth word of the
+//               variable's value, or 0 if there is no nth value.
+//               See also has_double_word().
+////////////////////////////////////////////////////////////////////
+INLINE double ConfigVariable::
+get_double_word(int n) const {
+  const ConfigDeclaration *decl = _core->get_declaration(0);
+  return decl->get_double_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::set_string_word
+//       Access: Published
+//  Description: Changes the nth word to the indicated value without
+//               affecting the other words.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariable::
+set_string_word(int n, const string &value) {
+  _core->make_local_value()->set_string_word(n, value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::set_bool_word
+//       Access: Published
+//  Description: Changes the nth word to the indicated value without
+//               affecting the other words.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariable::
+set_bool_word(int n, bool value) {
+  _core->make_local_value()->set_bool_word(n, value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::set_int_word
+//       Access: Published
+//  Description: Changes the nth word to the indicated value without
+//               affecting the other words.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariable::
+set_int_word(int n, int value) {
+  _core->make_local_value()->set_int_word(n, value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::set_double_word
+//       Access: Published
+//  Description: Changes the nth word to the indicated value without
+//               affecting the other words.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariable::
+set_double_word(int n, double value) {
+  _core->make_local_value()->set_double_word(n, value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariable::
+output(ostream &out) const {
+  _core->output(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariable::
+write(ostream &out) const {
+  _core->write(out);
+}
+
+INLINE ostream &
+operator << (ostream &out, const ConfigVariable &variable) {
+  variable.output(out);
+  return out;
+}

+ 43 - 0
dtool/src/prc/configVariable.cxx

@@ -0,0 +1,43 @@
+// Filename: configVariable.cxx
+// Created by:  drose (18Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "configVariable.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariable::Constructor
+//       Access: Protected
+//  Description: This constructor is only intended to be called from a
+//               specialized ConfigVariableFoo derived class.
+////////////////////////////////////////////////////////////////////
+ConfigVariable::
+ConfigVariable(const string &name, ConfigVariableCore::ValueType value_type,
+               int trust_level, const string &description, 
+               const string &text) :
+  _core(ConfigVariableManager::get_global_ptr()->make_variable(name))
+{
+  _core->set_value_type(value_type);
+  if (trust_level > -2) {
+    _core->set_trust_level(trust_level);
+  }
+  if (!description.empty()) {
+    _core->set_description(description);
+  }
+  if (!text.empty()) {
+    _core->set_text(text);
+  }
+}

+ 92 - 0
dtool/src/prc/configVariable.h

@@ -0,0 +1,92 @@
+// Filename: configVariable.h
+// Created by:  drose (18Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGVARIABLE_H
+#define CONFIGVARIABLE_H
+
+#include "dtoolbase.h"
+#include "configVariableCore.h"
+#include "configDeclaration.h"
+#include "configVariableManager.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigVariable
+// Description : This is a generic, untyped ConfigVariable.  It is
+//               also the base class for the typed ConfigVariables,
+//               and contains all of the code common to
+//               ConfigVariables of all types.
+//
+//               Mostly, this class serves as a thin wrapper around
+//               ConfigVariableCore and/or ConfigDeclaration, more or
+//               less duplicating the interface presented there.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG ConfigVariable {
+protected:
+  INLINE ConfigVariable(const string &name, 
+                        ConfigVariableCore::ValueType type);
+  ConfigVariable(const string &name, ConfigVariableCore::ValueType type,
+                 int trust_level, const string &description,
+                 const string &text);
+
+PUBLISHED:
+  INLINE ConfigVariable(const string &name);
+  INLINE ~ConfigVariable();
+
+  INLINE const string &get_name() const;
+
+  INLINE ConfigVariableCore::ValueType get_value_type() const;
+  INLINE int get_trust_level() const;
+  INLINE const string &get_description() const;
+  INLINE const string &get_text() const;
+  INLINE const ConfigDeclaration *get_default_value() const;
+
+  INLINE string get_string_value() const;
+  INLINE void set_string_value(const string &value);
+
+  INLINE bool clear_local_value();
+  INLINE bool has_local_value() const;
+
+  INLINE int get_num_words() const;
+
+  INLINE bool has_string_word(int n) const;
+  INLINE bool has_bool_word(int n) const;
+  INLINE bool has_int_word(int n) const;
+  INLINE bool has_double_word(int n) const;
+
+  INLINE string get_string_word(int n) const;
+  INLINE bool get_bool_word(int n) const;
+  INLINE int get_int_word(int n) const;
+  INLINE double get_double_word(int n) const;
+
+  INLINE void set_string_word(int n, const string &value);
+  INLINE void set_bool_word(int n, bool value);
+  INLINE void set_int_word(int n, int value);
+  INLINE void set_double_word(int n, double value);
+
+  INLINE void output(ostream &out) const;
+  INLINE void write(ostream &out) const;
+
+protected:
+  ConfigVariableCore *_core;
+};
+
+INLINE ostream &operator << (ostream &out, const ConfigVariable &variable);
+
+#include "configVariable.I"
+
+#endif

+ 141 - 0
dtool/src/prc/configVariableBool.I

@@ -0,0 +1,141 @@
+// Filename: configVariableBool.I
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableBool::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableBool::
+ConfigVariableBool(const string &name) :
+  ConfigVariable(name, ConfigVariableCore::VT_bool)
+{
+  _core->set_used();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableBool::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableBool::
+ConfigVariableBool(const string &name, bool default_value, int trust_level,
+                   const string &description, const string &text) :
+  ConfigVariable(name, ConfigVariableCore::VT_bool, trust_level, 
+                 description, text)
+{
+  _core->set_default_value(default_value ? "1" : "0");
+  _core->set_used();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableBool::operator =
+//       Access: Published
+//  Description: Reassigns the variable's local value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableBool::
+operator = (bool value) {
+  set_value(value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableBool::typecast operator
+//       Access: Published
+//  Description: Returns the variable's value.
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableBool::
+operator bool () const {
+  return get_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableBool::size()
+//       Access: Published
+//  Description: Returns the number of unique words in the variable.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableBool::
+size() const {
+  return get_num_words();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableBool::operator []
+//       Access: Published
+//  Description: Returns the value of the variable's nth word.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariableBool::
+operator [] (int n) const {
+  return get_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableBool::set_value
+//       Access: Published
+//  Description: Reassigns the variable's local value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableBool::
+set_value(bool value) {
+  set_string_value("");
+  set_bool_word(0, value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableBool::get_value
+//       Access: Published
+//  Description: Returns the variable's value.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariableBool::
+get_value() const {
+  return get_bool_word(0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableBool::get_default_value
+//       Access: Published
+//  Description: Returns the variable's default value.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariableBool::
+get_default_value() const {
+  const ConfigDeclaration *decl = ConfigVariable::get_default_value();
+  if (decl != (ConfigDeclaration *)NULL) {
+    return decl->get_bool_word(0);
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableBool::get_word
+//       Access: Published
+//  Description: Returns the variable's nth value.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariableBool::
+get_word(int n) const {
+  return get_bool_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableBool::set_word
+//       Access: Published
+//  Description: Reassigns the variable's nth value.  This makes a
+//               local copy of the variable's overall value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableBool::
+set_word(int n, bool value) {
+  set_bool_word(n, value);
+}

+ 19 - 0
dtool/src/prc/configVariableBool.cxx

@@ -0,0 +1,19 @@
+// Filename: configVariableBool.cxx
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "configVariableBool.h"

+ 54 - 0
dtool/src/prc/configVariableBool.h

@@ -0,0 +1,54 @@
+// Filename: configVariableBool.h
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGVARIABLEBOOL_H
+#define CONFIGVARIABLEBOOL_H
+
+#include "dtoolbase.h"
+#include "configVariable.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigVariableBool
+// Description : This is a convenience class to specialize
+//               ConfigVariable as a boolean type.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG ConfigVariableBool : public ConfigVariable {
+PUBLISHED:
+  INLINE ConfigVariableBool(const string &name);
+  INLINE ConfigVariableBool(const string &name, bool default_value,
+                            int trust_level = -2,
+                            const string &description = string(),
+                            const string &text = string());
+
+  INLINE void operator = (bool value);
+  INLINE operator bool () const;
+
+  INLINE int size() const;
+  INLINE bool operator [] (int n) const;
+
+  INLINE void set_value(bool value);
+  INLINE bool get_value() const;
+  INLINE bool get_default_value() const;
+
+  INLINE bool get_word(int n) const;
+  INLINE void set_word(int n, bool value);
+};
+
+#include "configVariableBool.I"
+
+#endif

+ 251 - 0
dtool/src/prc/configVariableCore.I

@@ -0,0 +1,251 @@
+// Filename: configVariableCore.I
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_name
+//       Access: Public
+//  Description: Returns the name of the variable.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigVariableCore::
+get_name() const {
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::is_used
+//       Access: Public
+//  Description: Returns true if the variable has been referenced by a
+//               ConfigVariable somewhere in code, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariableCore::
+is_used() const {
+  return _is_used;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_value_type
+//       Access: Public
+//  Description: Returns the stated type of this variable.  If the
+//               variable has not yet been defined, this will be
+//               VT_undefined.
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableCore::ValueType ConfigVariableCore::
+get_value_type() const {
+  return _value_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_trust_level
+//       Access: Public
+//  Description: Returns the minimum trust_level a prc file must
+//               demonstrate in order to redefine the value for this
+//               variable.  -1 indicates infinite trust: no prc file
+//               can redefine it.  Arguably, this should be called the
+//               "mistrust level", since the larger the value, the
+//               more suspicious we are of prc files.
+//
+//               This value only has effect in a release build.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableCore::
+get_trust_level() const {
+  return _trust_level;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_description
+//       Access: Public
+//  Description: Returns the one-line description of this variable.
+//               If the variable has not yet been defined, this will
+//               be empty.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigVariableCore::
+get_description() const {
+  return _description;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_text
+//       Access: Public
+//  Description: Returns the paragraph help text describing the
+//               purpose of this variable in greater detail than
+//               get_description().  If the variable has not yet been
+//               defined, this will be empty.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigVariableCore::
+get_text() const {
+  return _text;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_default_value
+//       Access: Public
+//  Description: Returns the default variable specified for this
+//               variable.  If the variable has not yet been defined,
+//               this will return NULL.
+////////////////////////////////////////////////////////////////////
+INLINE const ConfigDeclaration *ConfigVariableCore::
+get_default_value() const {
+  return _default_value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::set_used
+//       Access: Public
+//  Description: Marks that the variable has been "declared" by a
+//               ConfigVariable.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableCore::
+set_used() {
+  _is_used = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::has_local_value
+//       Access: Public
+//  Description: Returns true if this variable's value has been
+//               shadowed by a local assignment (as created via
+//               make_local_value()), or false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariableCore::
+has_local_value() const {
+  return _local_value != (ConfigDeclaration *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_num_references
+//       Access: Public
+//  Description: Returns the number of prc files that reference this
+//               variable.  This is not exactly the same as the number
+//               of declarations; see get_reference().
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableCore::
+get_num_references() const {
+  check_sort_declarations();
+  return _declarations.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_reference
+//       Access: Public
+//  Description: Returns the nth declaration in a prc file that
+//               references this variable.  This is similar, but not
+//               identical to, get_declaration().  The difference is
+//               that this will list *only* true references in a prc
+//               file, and will not list default values or
+//               locally-assigned values; it also will list even the
+//               untrusted files.
+////////////////////////////////////////////////////////////////////
+INLINE const ConfigDeclaration *ConfigVariableCore::
+get_reference(int n) const {
+  check_sort_declarations();
+  if (n >= 0 && n < (int)_declarations.size()) {
+    return _declarations[n];
+  }
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_num_trusted_references
+//       Access: Public
+//  Description: Returns the number of trusted prc files that
+//               reference this variable.  See also
+//               get_num_references().
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableCore::
+get_num_trusted_references() const {
+  check_sort_declarations();
+  return _trusted_declarations.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_trusted_reference
+//       Access: Public
+//  Description: Returns the nth declaration in a trusted prc file
+//               that references this variable.  This is similar, but
+//               not identical to, get_declaration().  The difference
+//               is that this will list *only* true references in a
+//               prc file, and will not list default values or
+//               locally-assigned values.
+//
+//               This is also similar to get_reference(), except that
+//               it only lists the trusted declarations, omitting the
+//               untrusted ones.
+////////////////////////////////////////////////////////////////////
+INLINE const ConfigDeclaration *ConfigVariableCore::
+get_trusted_reference(int n) const {
+  check_sort_declarations();
+  if (n >= 0 && n < (int)_trusted_declarations.size()) {
+    return _trusted_declarations[n];
+  }
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_num_unique_references
+//       Access: Public
+//  Description: Returns the number of trusted, unique (by string
+//               value) values there exist for this variable.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableCore::
+get_num_unique_references() const {
+  check_sort_declarations();
+  return _unique_declarations.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_unique_reference
+//       Access: Public
+//  Description: Returns the nth trusted, unique value for this
+//               variable.  This is similar to
+//               get_trusted_reference(), except that duplicate values
+//               are removed.
+////////////////////////////////////////////////////////////////////
+INLINE const ConfigDeclaration *ConfigVariableCore::
+get_unique_reference(int n) const {
+  check_sort_declarations();
+  if (n >= 0 && n < (int)_unique_declarations.size()) {
+    return _unique_declarations[n];
+  }
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::check_sort_declarations()
+//       Access: Private
+//  Description: Called internally to ensure that the list of
+//               declarations is properly sorted.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableCore::
+check_sort_declarations() const {
+  // First, make sure that all of the implicit .prc files have been
+  // loaded.  This may unsort the list by adding a bunch more
+  // declarations.
+  ConfigPageManager::get_global_ptr()->load_implicit_pages();
+  
+  // Then sort the list if it needs it.
+  if (!_declarations_sorted) {
+    ((ConfigVariableCore *)this)->sort_declarations();
+  }
+}
+
+INLINE ostream &
+operator << (ostream &out, const ConfigVariableCore &variable) {
+  variable.output(out);
+  return out;
+}

+ 467 - 0
dtool/src/prc/configVariableCore.cxx

@@ -0,0 +1,467 @@
+// Filename: configVariableCore.cxx
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "configVariableCore.h"
+#include "configDeclaration.h"
+#include "configPage.h"
+#include "pset.h"
+#include "notify.h"
+#include "config_prc.h"
+
+#include <algorithm>
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::Constructor
+//       Access: Private
+//  Description: Use the ConfigVariableManager::make_variable() 
+//               interface to create a new ConfigVariableCore.
+////////////////////////////////////////////////////////////////////
+ConfigVariableCore::
+ConfigVariableCore(const string &name) :
+  _name(name),
+  _is_used(false),
+  _value_type(VT_undefined),
+  _trust_level(0),
+  _default_value(NULL),
+  _local_value(NULL),
+  _declarations_sorted(true),
+  _value_queried(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::Destructor
+//       Access: Private
+//  Description: The destructor should never be called;
+//               ConfigVariableCore objects live forever and never get
+//               destructed.
+////////////////////////////////////////////////////////////////////
+ConfigVariableCore::
+~ConfigVariableCore() {
+  prc_cat->error()
+    << "Internal error--ConfigVariableCore destructor called!\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::set_value_type
+//       Access: Public
+//  Description: Specifies the type of this variable.  See
+//               get_value_type().  It is not an error to call this
+//               multiple times, but if the value changes once
+//               get_declaration() has been called, a warning is printed.
+////////////////////////////////////////////////////////////////////
+void ConfigVariableCore::
+set_value_type(ConfigVariableCore::ValueType value_type) {
+  if (_value_queried && _value_type != value_type) {
+    if (_description == "DConfig") {
+      // As a special exception, if the current description is
+      // "DConfig", we don't report a warning for changing the type,
+      // assuming the variable is being defined through the older
+      // DConfig interface.
+      
+    } else {
+      prc_cat->warning()
+        << "changing type for ConfigVariable " 
+        << get_name() << " from " << _value_type << " to " 
+        << value_type << ".\n";
+    }
+  }
+
+  _value_type = value_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::set_trust_level
+//       Access: Public
+//  Description: Specifies the trust level of this variable.  See
+//               get_trust_level().  It is not an error to call this
+//               multiple times, but if the value changes once
+//               get_declaration() has been called, a warning is printed.
+////////////////////////////////////////////////////////////////////
+void ConfigVariableCore::
+set_trust_level(int trust_level) {
+  if (_value_queried && _trust_level != trust_level) {
+    prc_cat->warning()
+      << "changing trust level for ConfigVariable " 
+      << get_name() << " from " << _trust_level << " to " 
+      << trust_level << ".\n";
+  }
+
+  _trust_level = trust_level;
+
+  // Changing the trust level will require re-sorting the
+  // declarations.
+  _declarations_sorted = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::set_description
+//       Access: Public
+//  Description: Specifies the one-line description of this variable.
+//               See get_description().  It is not an error to call
+//               this multiple times, but if the value changes once
+//               get_declaration() has been called, a warning is printed.
+////////////////////////////////////////////////////////////////////
+void ConfigVariableCore::
+set_description(const string &description) {
+  if (_value_queried && _description != description) {
+    if (description == "DConfig") {
+      // As a special exception, if the new description is "DConfig",
+      // we don't change it, since this is presumably coming from the
+      // older DConfig interface.
+      return;
+    }
+      
+    prc_cat->warning()
+      << "changing description for ConfigVariable " 
+      << get_name() << ".\n";
+  }
+
+  _description = description;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::set_text
+//       Access: Public
+//  Description: Specifies the long text description of this variable.
+//               See get_text().  It is not an error to call
+//               this multiple times, but if the value changes once
+//               get_declaration() has been called, a warning is printed.
+////////////////////////////////////////////////////////////////////
+void ConfigVariableCore::
+set_text(const string &text) {
+  if (_value_queried && _text != text) {
+    prc_cat->warning()
+      << "changing text for ConfigVariable " 
+      << get_name() << ".\n";
+  }
+
+  _text = text;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::set_default_value
+//       Access: Public
+//  Description: Specifies the default value for this variable if it
+//               is not defined in any prc file.
+////////////////////////////////////////////////////////////////////
+void ConfigVariableCore::
+set_default_value(const string &default_value) {
+  if (_default_value == (ConfigDeclaration *)NULL) {
+    // Defining the default value for the first time.
+    ConfigPage *default_page = ConfigPage::get_default_page();
+    _default_value = default_page->make_declaration(this, default_value);
+
+  } else {
+    // Modifying an existing default value.
+
+    if (_default_value->get_string_value() != default_value) {
+      if (_description == "DConfig") {
+        // As a special exception, if the current description is
+        // "DConfig", we don't report a warning for changing the
+        // default value, assuming the variable is being defined
+        // through the older DConfig interface.
+
+      } else {
+        prc_cat->warning()
+          << "changing default value for ConfigVariable " 
+          << get_name() << " from '" << _default_value->get_string_value()
+          << "' to '" << default_value << "'.\n";
+      }
+      _default_value->set_string_value(default_value);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::make_local_value
+//       Access: Public
+//  Description: Creates a new local value for this variable, if there
+//               is not already one specified.  This will shadow any
+//               values defined in the various .prc files.
+//
+//               If there is already a local value defined for this
+//               variable, simply returns that one.
+//
+//               Use clear_local_value() to remove the local value
+//               definition.
+////////////////////////////////////////////////////////////////////
+ConfigDeclaration *ConfigVariableCore::
+make_local_value() {
+  if (_local_value == (ConfigDeclaration *)NULL) {
+    ConfigPage *local_page = ConfigPage::get_local_page();
+    string string_value = get_declaration(0)->get_string_value();
+    _local_value = local_page->make_declaration(this, string_value);
+  }
+
+  return _local_value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::clear_local_value
+//       Access: Public
+//  Description: Removes the local value defined for this variable,
+//               and allows its value to be once again retrieved from
+//               the .prc files.
+//
+//               Returns true if the value was successfully removed,
+//               false if it did not exist in the first place.
+////////////////////////////////////////////////////////////////////
+bool ConfigVariableCore::
+clear_local_value() {
+  if (_local_value != (ConfigDeclaration *)NULL) {
+    ConfigPage::get_local_page()->delete_declaration(_local_value);
+    _local_value = NULL;
+    return true;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_num_declarations
+//       Access: Public
+//  Description: Returns the number of declarations that contribute to
+//               this variable's value.  If the variable has been
+//               defined, this will always be at least 1 (for the
+//               default value, at least).
+////////////////////////////////////////////////////////////////////
+int ConfigVariableCore::
+get_num_declarations() const {
+  if (has_local_value()) {
+    return 1;
+  }
+  check_sort_declarations();
+  if (!_trusted_declarations.empty()) {
+    return _trusted_declarations.size();
+  }
+
+  // We always have at least one: the default value.
+  return 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::get_declaration
+//       Access: Public
+//  Description: Returns the nth declarations that contributes to
+//               this variable's value.  The declarations are arranged
+//               in order such that earlier declarations shadow later
+//               declarations; thus, get_declaration(0) is always
+//               defined and always returns the current value of the
+//               variable.
+////////////////////////////////////////////////////////////////////
+const ConfigDeclaration *ConfigVariableCore::
+get_declaration(int n) const {
+  ((ConfigVariableCore *)this)->_value_queried = true;
+  if (_default_value == (ConfigDeclaration *)NULL) {
+    prc_cat->warning()
+      << "value queried before default value set for "
+      << get_name() << ".\n";
+    ((ConfigVariableCore *)this)->set_default_value("");
+  }
+
+  if (has_local_value()) {
+    return _local_value;
+  }
+  check_sort_declarations();
+  if (n >= 0 && n < (int)_trusted_declarations.size()) {
+    return _trusted_declarations[n];
+  }
+  return _default_value;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::output
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConfigVariableCore::
+output(ostream &out) const {
+  out << get_declaration(0)->get_string_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::write
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConfigVariableCore::
+write(ostream &out) const {
+  out << "ConfigVariable " << get_name() << ":\n";
+
+  check_sort_declarations();
+
+  if (has_local_value()) {
+    out << "  " << *_local_value << "  (defined locally)\n";
+  }
+
+  Declarations::const_iterator di;
+  for (di = _trusted_declarations.begin(); 
+       di != _trusted_declarations.end(); 
+       ++di) {
+    out << "  " << *(*di) 
+        << "  (from " << (*di)->get_page()->get_name() << ")\n";
+  }
+
+  if (_default_value != (ConfigDeclaration *)NULL) {
+    out << "  " << *_default_value << "  (default value)\n";
+  }
+
+  for (di = _untrusted_declarations.begin(); 
+       di != _untrusted_declarations.end(); 
+       ++di) {
+    out << "  " << *(*di) 
+        << "  (from " << (*di)->get_page()->get_name() << ", untrusted)\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::add_declaration
+//       Access: Private
+//  Description: Called only by the ConfigDeclaration constructor,
+//               this adds the indicated declaration to the list of
+//               declarations that reference this variable.
+////////////////////////////////////////////////////////////////////
+void ConfigVariableCore::
+add_declaration(ConfigDeclaration *decl) {
+  _declarations.push_back(decl);
+
+  _declarations_sorted = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::remove_declaration
+//       Access: Private
+//  Description: Called only by the ConfigDeclaration destructor,
+//               this removes the indicated declaration from the list
+//               of declarations that reference this variable.
+////////////////////////////////////////////////////////////////////
+void ConfigVariableCore::
+remove_declaration(ConfigDeclaration *decl) {
+  Declarations::iterator di;
+  for (di = _declarations.begin(); di != _declarations.end(); ++di) {
+    if ((*di) == decl) {
+      // Rather than deleting the declaration from the middle of the
+      // list, we maybe save a bit of time by swapping in the one at
+      // the end of the list (although this will unsort the list).
+      Declarations::iterator di2 = _declarations.end();
+      di2--;
+      (*di) = (*di2);
+      _declarations.erase(di2);
+      _declarations_sorted = false;
+      return;
+    }
+  }
+
+  // Hmm, it wasn't here.  Oh well.
+}
+
+// This class is used in sort_declarations, below.
+class CompareConfigDeclarations {
+public:
+  bool operator () (const ConfigDeclaration *a, const ConfigDeclaration *b) const {
+    return (*a) < (*b);
+  }
+};
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::sort_declarations
+//       Access: Private
+//  Description: Sorts the list of declarations into priority order,
+//               so that the declaration at the front of the list is
+//               the one that shadows all following declarations.
+////////////////////////////////////////////////////////////////////
+void ConfigVariableCore::
+sort_declarations() {
+  sort(_declarations.begin(), _declarations.end(), CompareConfigDeclarations());
+  Declarations::iterator di;
+
+  // Now that they're sorted, divide them into either trusted or
+  // untrusted declarations.
+#ifdef PRC_RESPECT_TRUST_LEVEL
+  // In this mode, normally for a release build, we sort the
+  // declarations honestly according to whether the prc file that
+  // defines them meets the required trust level.
+  _trusted_declarations.clear();
+  _untrusted_declarations.clear();
+  for (di = _declarations.begin(); di != _declarations.end(); ++di) {
+    const ConfigDeclaration *decl = (*di);
+    if (get_trust_level() >= 0 && 
+        get_trust_level() <= decl->get_page()->get_trust_level()) {
+      _trusted_declarations.push_back(decl);
+    } else {
+      _untrusted_declarations.push_back(decl);
+    }
+  }
+
+#else  // PRC_RESPECT_TRUST_LEVEL
+  // In this mode, normally for the development environment, all
+  // declarations are trusted, regardless of the trust level.
+  _trusted_declarations = _declarations;
+  _untrusted_declarations.clear();
+
+#endif  // PRC_RESPECT_TRUST_LEVEL
+
+  // Finally, determine the set of unique, trusted
+  // declarations--trusted declarations that have a unique string
+  // value.  This is usually unneeded, but what the heck, it doesn't
+  // need to be recomputed all that often.
+  _unique_declarations.clear();
+
+  pset<string> already_added;
+  for (di = _trusted_declarations.begin(); 
+       di != _trusted_declarations.end(); 
+       ++di) {
+    const ConfigDeclaration *decl = (*di);
+    if (already_added.insert(decl->get_string_value()).second) {
+      _unique_declarations.push_back(decl);
+    }
+  }
+
+  _declarations_sorted = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableCore::Type output operator
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ostream &
+operator << (ostream &out, ConfigVariableCore::ValueType type) {
+  switch (type) {
+  case ConfigVariableCore::VT_undefined:
+    return out << "undefined";
+
+  case ConfigVariableCore::VT_list:
+    return out << "list";
+
+  case ConfigVariableCore::VT_string:
+    return out << "string";
+
+  case ConfigVariableCore::VT_bool:
+    return out << "bool";
+
+  case ConfigVariableCore::VT_int:
+    return out << "int";
+
+  case ConfigVariableCore::VT_double:
+    return out << "double";
+  }
+
+  return out << "**invalid(" << (int)type << ")**";
+}

+ 125 - 0
dtool/src/prc/configVariableCore.h

@@ -0,0 +1,125 @@
+// Filename: configVariableCore.h
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGVARIABLECORE_H
+#define CONFIGVARIABLECORE_H
+
+#include "dtoolbase.h"
+#include "configPageManager.h"
+#include "pvector.h"
+#include "pmap.h"
+
+class ConfigDeclaration;
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigVariableCore
+// Description : The internal definition of a ConfigVariable.  This
+//               object is shared between all instances of a
+//               ConfigVariable that use the same variable name.
+//
+//               You cannot create a ConfigVariableCore instance
+//               directly; instead, use the make() method, which may
+//               return a shared instance.  Once created, these
+//               objects are never destructed.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG ConfigVariableCore {
+private:
+  ConfigVariableCore(const string &name);
+  ~ConfigVariableCore();
+
+public:
+  enum ValueType {
+    VT_undefined,
+    VT_list,
+    VT_string,
+    VT_bool,
+    VT_int,
+    VT_double,
+  };
+
+  INLINE const string &get_name() const;
+  INLINE bool is_used() const;
+
+  INLINE ValueType get_value_type() const;
+  INLINE int get_trust_level() const;
+  INLINE const string &get_description() const;
+  INLINE const string &get_text() const;
+  INLINE const ConfigDeclaration *get_default_value() const;
+
+  void set_value_type(ValueType value_type);
+  void set_trust_level(int trust_level);
+  void set_description(const string &description);
+  void set_text(const string &text);
+  void set_default_value(const string &default_value);
+  INLINE void set_used();
+
+  ConfigDeclaration *make_local_value();
+  bool clear_local_value();
+  INLINE bool has_local_value() const;
+
+  int get_num_declarations() const;
+  const ConfigDeclaration *get_declaration(int n) const;
+
+  INLINE int get_num_references() const;
+  INLINE const ConfigDeclaration *get_reference(int n) const;
+
+  INLINE int get_num_trusted_references() const;
+  INLINE const ConfigDeclaration *get_trusted_reference(int n) const;
+
+  INLINE int get_num_unique_references() const;
+  INLINE const ConfigDeclaration *get_unique_reference(int n) const;
+
+  void output(ostream &out) const;
+  void write(ostream &out) const;
+
+private:
+  void add_declaration(ConfigDeclaration *decl);
+  void remove_declaration(ConfigDeclaration *decl);
+
+  INLINE void check_sort_declarations() const;
+  void sort_declarations();
+
+private:
+  string _name;
+  bool _is_used;
+  ValueType _value_type;
+  int _trust_level;
+  string _description;
+  string _text;
+  ConfigDeclaration *_default_value;
+  ConfigDeclaration *_local_value;
+
+  typedef pvector<const ConfigDeclaration *> Declarations;
+  Declarations _declarations;
+  Declarations _trusted_declarations;
+  Declarations _untrusted_declarations;
+  Declarations _unique_declarations;
+  bool _declarations_sorted;
+  bool _value_queried;
+
+  friend class ConfigDeclaration;
+  friend class ConfigVariableManager;
+};
+
+INLINE ostream &operator << (ostream &out, const ConfigVariableCore &variable);
+
+ostream &operator << (ostream &out, ConfigVariableCore::ValueType type);
+
+#include "configVariableCore.I"
+
+#endif

+ 126 - 0
dtool/src/prc/configVariableDouble.I

@@ -0,0 +1,126 @@
+// Filename: configVariableDouble.I
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableDouble::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableDouble::
+ConfigVariableDouble(const string &name) :
+  ConfigVariable(name, ConfigVariableCore::VT_double)
+{
+  _core->set_used();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableDouble::operator =
+//       Access: Published
+//  Description: Reassigns the variable's local value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableDouble::
+operator = (double value) {
+  set_value(value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableDouble::typecast operator
+//       Access: Published
+//  Description: Returns the variable's value.
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableDouble::
+operator double () const {
+  return get_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableDouble::size()
+//       Access: Published
+//  Description: Returns the number of unique words in the variable.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableDouble::
+size() const {
+  return get_num_words();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableDouble::operator []
+//       Access: Published
+//  Description: Returns the value of the variable's nth word.
+////////////////////////////////////////////////////////////////////
+INLINE double ConfigVariableDouble::
+operator [] (int n) const {
+  return get_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableDouble::set_value
+//       Access: Published
+//  Description: Reassigns the variable's local value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableDouble::
+set_value(double value) {
+  set_string_value("");
+  set_double_word(0, value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableDouble::get_value
+//       Access: Published
+//  Description: Returns the variable's value.
+////////////////////////////////////////////////////////////////////
+INLINE double ConfigVariableDouble::
+get_value() const {
+  return get_double_word(0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableDouble::get_default_value
+//       Access: Published
+//  Description: Returns the variable's default value.
+////////////////////////////////////////////////////////////////////
+INLINE double ConfigVariableDouble::
+get_default_value() const {
+  const ConfigDeclaration *decl = ConfigVariable::get_default_value();
+  if (decl != (ConfigDeclaration *)NULL) {
+    return decl->get_double_word(0);
+  }
+  return 0.0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableDouble::get_word
+//       Access: Published
+//  Description: Returns the variable's nth value.
+////////////////////////////////////////////////////////////////////
+INLINE double ConfigVariableDouble::
+get_word(int n) const {
+  return get_double_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableDouble::set_word
+//       Access: Published
+//  Description: Reassigns the variable's nth value.  This makes a
+//               local copy of the variable's overall value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableDouble::
+set_word(int n, double value) {
+  set_double_word(n, value);
+}

+ 37 - 0
dtool/src/prc/configVariableDouble.cxx

@@ -0,0 +1,37 @@
+// Filename: configVariableDouble.cxx
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "configVariableDouble.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableDouble::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ConfigVariableDouble::
+ConfigVariableDouble(const string &name, double default_value, int trust_level,
+                     const string &description, const string &text) :
+  ConfigVariable(name, ConfigVariableCore::VT_double, trust_level, 
+                 description, text)
+{
+  ostringstream strm;
+  strm << default_value;
+
+  _core->set_default_value(strm.str());
+  _core->set_used();
+}

+ 54 - 0
dtool/src/prc/configVariableDouble.h

@@ -0,0 +1,54 @@
+// Filename: configVariableDouble.h
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGVARIABLEDOUBLE_H
+#define CONFIGVARIABLEDOUBLE_H
+
+#include "dtoolbase.h"
+#include "configVariable.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigVariableDouble
+// Description : This is a convenience class to specialize
+//               ConfigVariable as a floating-point type.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG ConfigVariableDouble : public ConfigVariable {
+PUBLISHED:
+  INLINE ConfigVariableDouble(const string &name);
+  ConfigVariableDouble(const string &name, double default_value,
+                       int trust_level = -2,
+                       const string &description = string(),
+                       const string &text = string());
+
+  INLINE void operator = (double value);
+  INLINE operator double () const;
+
+  INLINE int size() const;
+  INLINE double operator [] (int n) const;
+
+  INLINE void set_value(double value);
+  INLINE double get_value() const;
+  INLINE double get_default_value() const;
+
+  INLINE double get_word(int n) const;
+  INLINE void set_word(int n, double value);
+};
+
+#include "configVariableDouble.I"
+
+#endif

+ 127 - 0
dtool/src/prc/configVariableInt.I

@@ -0,0 +1,127 @@
+// Filename: configVariableInt.I
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableInt::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableInt::
+ConfigVariableInt(const string &name) :
+  ConfigVariable(name, ConfigVariableCore::VT_int)
+{
+  _core->set_used();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableInt::operator =
+//       Access: Published
+//  Description: Reassigns the variable's local value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableInt::
+operator = (int value) {
+  set_value(value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableInt::typecast operator
+//       Access: Published
+//  Description: Returns the variable's value.
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableInt::
+operator int () const {
+  return get_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableInt::size()
+//       Access: Published
+//  Description: Returns the number of unique words in the variable.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableInt::
+size() const {
+  return get_num_words();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableInt::operator []
+//       Access: Published
+//  Description: Returns the value of the variable's nth word.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableInt::
+operator [] (int n) const {
+  return get_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableInt::set_value
+//       Access: Published
+//  Description: Reassigns the variable's local value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableInt::
+set_value(int value) {
+  set_string_value("");
+  set_int_word(0, value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableInt::get_value
+//       Access: Published
+//  Description: Returns the variable's value.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableInt::
+get_value() const {
+  return get_int_word(0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableInt::get_default_value
+//       Access: Published
+//  Description: Returns the variable's default value.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableInt::
+get_default_value() const {
+  const ConfigDeclaration *decl = ConfigVariable::get_default_value();
+  if (decl != (ConfigDeclaration *)NULL) {
+    return decl->get_int_word(0);
+  }
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableInt::get_word
+//       Access: Published
+//  Description: Returns the variable's nth value.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableInt::
+get_word(int n) const {
+  return get_int_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableInt::set_word
+//       Access: Published
+//  Description: Reassigns the variable's nth value.  This makes a
+//               local copy of the variable's overall value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableInt::
+set_word(int n, int value) {
+  set_int_word(n, value);
+}
+

+ 37 - 0
dtool/src/prc/configVariableInt.cxx

@@ -0,0 +1,37 @@
+// Filename: configVariableInt.cxx
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "configVariableInt.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableInt::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ConfigVariableInt::
+ConfigVariableInt(const string &name, int default_value, int trust_level,
+                  const string &description, const string &text) :
+  ConfigVariable(name, ConfigVariableCore::VT_int, trust_level, 
+                 description, text)
+{
+  ostringstream strm;
+  strm << default_value;
+
+  _core->set_default_value(strm.str());
+  _core->set_used();
+}

+ 54 - 0
dtool/src/prc/configVariableInt.h

@@ -0,0 +1,54 @@
+// Filename: configVariableInt.h
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGVARIABLEINT_H
+#define CONFIGVARIABLEINT_H
+
+#include "dtoolbase.h"
+#include "configVariable.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigVariableInt
+// Description : This is a convenience class to specialize
+//               ConfigVariable as an integer type.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG ConfigVariableInt : public ConfigVariable {
+PUBLISHED:
+  INLINE ConfigVariableInt(const string &name);
+  ConfigVariableInt(const string &name, int default_value,
+                    int trust_level = -2,
+                    const string &description = string(),
+                    const string &text = string());
+
+  INLINE void operator = (int value);
+  INLINE operator int () const;
+
+  INLINE int size() const;
+  INLINE int operator [] (int n) const;
+
+  INLINE void set_value(int value);
+  INLINE int get_value() const;
+  INLINE int get_default_value() const;
+
+  INLINE int get_word(int n) const;
+  INLINE void set_word(int n, int value);
+};
+
+#include "configVariableInt.I"
+
+#endif

+ 188 - 0
dtool/src/prc/configVariableList.I

@@ -0,0 +1,188 @@
+// Filename: configVariableList.I
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableList::
+~ConfigVariableList() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::get_name
+//       Access: Published
+//  Description: Returns the name of the variable.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigVariableList::
+get_name() const {
+  return _core->get_name();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::get_value_type
+//       Access: Published
+//  Description: Returns the stated type of this variable.  This
+//               should be VT_list, unless a later variable
+//               declaration has changed it.
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableCore::ValueType ConfigVariableList::
+get_value_type() const {
+  return _core->get_value_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::get_trust_level
+//       Access: Published
+//  Description: Returns the minimum trust_level a prc file must
+//               demonstrate in order to redefine the value for this
+//               variable.  -1 indicates infinite trust: no prc file
+//               can redefine it.  Arguably, this should be called the
+//               "mistrust level", since the larger the value, the
+//               more suspicious we are of prc files.
+//
+//               This value only has effect in a release build.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableList::
+get_trust_level() const {
+  return _core->get_trust_level();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::get_description
+//       Access: Published
+//  Description: Returns the one-line description of this variable.
+//               If the variable has not yet been defined, this will
+//               be empty.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigVariableList::
+get_description() const {
+  return _core->get_description();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::get_text
+//       Access: Published
+//  Description: Returns the paragraph help text describing the
+//               purpose of this variable in greater detail than
+//               get_description().  If the variable has not yet been
+//               defined, this will be empty.
+////////////////////////////////////////////////////////////////////
+INLINE const string &ConfigVariableList::
+get_text() const {
+  return _core->get_text();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::get_num_values
+//       Access: Published
+//  Description: Returns the number of values in the variable.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableList::
+get_num_values() const {
+  return _core->get_num_trusted_references();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::get_string_value
+//       Access: Published
+//  Description: Returns the nth value of the variable.
+////////////////////////////////////////////////////////////////////
+INLINE string ConfigVariableList::
+get_string_value(int n) const {
+  const ConfigDeclaration *decl = _core->get_trusted_reference(n);
+  if (decl != (ConfigDeclaration *)NULL) {
+    return decl->get_string_value();
+  }
+  return string();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::get_num_unique_values
+//       Access: Published
+//  Description: Returns the number of unique values in the variable.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableList::
+get_num_unique_values() const {
+  return _core->get_num_unique_references();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::get_unique_value
+//       Access: Published
+//  Description: Returns the nth unique value of the variable.
+////////////////////////////////////////////////////////////////////
+INLINE string ConfigVariableList::
+get_unique_value(int n) const {
+  const ConfigDeclaration *decl = _core->get_unique_reference(n);
+  if (decl != (ConfigDeclaration *)NULL) {
+    return decl->get_string_value();
+  }
+  return string();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::size()
+//       Access: Published
+//  Description: Returns the number of unique values of the variable.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableList::
+size() const {
+  return get_num_unique_values();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::operator []
+//       Access: Published
+//  Description: Returns the nth unique value of the variable.  Note
+//               that the indexing operator returns the list of unique
+//               values, and so the maximum range is
+//               get_num_unique_values().
+////////////////////////////////////////////////////////////////////
+INLINE string ConfigVariableList::
+operator [] (int n) const {
+  return get_unique_value(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableList::
+output(ostream &out) const {
+  _core->output(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableList::
+write(ostream &out) const {
+  _core->write(out);
+}
+
+INLINE ostream &
+operator << (ostream &out, const ConfigVariableList &variable) {
+  variable.output(out);
+  return out;
+}

+ 50 - 0
dtool/src/prc/configVariableList.cxx

@@ -0,0 +1,50 @@
+// Filename: configVariableList.cxx
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "configVariableList.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableList::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ConfigVariableList::
+ConfigVariableList(const string &name, 
+                   int trust_level, const string &description, 
+                   const string &text) :
+  _core(ConfigVariableManager::get_global_ptr()->make_variable(name))
+{
+  _core->set_value_type(ConfigVariableCore::VT_list);
+  if (trust_level > -2) {
+    _core->set_trust_level(trust_level);
+  }
+  if (!description.empty()) {
+    _core->set_description(description);
+  }
+  if (!text.empty()) {
+    _core->set_text(text);
+  }
+
+  // A list variable implicitly defines a default value of the empty
+  // string.  This is just to prevent the core variable from
+  // complaining should anyone ask for its solitary value.
+  if (_core->get_default_value() == (ConfigDeclaration *)NULL) {
+    _core->set_default_value("");
+  }
+  _core->set_used();
+}

+ 77 - 0
dtool/src/prc/configVariableList.h

@@ -0,0 +1,77 @@
+// Filename: configVariableList.h
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGVARIABLELIST_H
+#define CONFIGVARIABLELIST_H
+
+#include "dtoolbase.h"
+#include "configVariableCore.h"
+#include "configDeclaration.h"
+#include "configVariableManager.h"
+#include "vector_string.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigVariableList
+// Description : This class is similar to ConfigVariable, but it
+//               reports its value as a list of strings.  In this
+//               special case, all of the declarations of the variable
+//               are returned as the elements of this list, in order.
+//
+//               Note that this is different from a normal
+//               ConfigVariableString, which just returns its topmost
+//               value, which can optionally be treated as a number of
+//               discrete words by dividing it at the spaces.
+//
+//               A ConfigVariableList cannot be modified locally.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG ConfigVariableList {
+PUBLISHED:
+  ConfigVariableList(const string &name, int trust_level = -2,
+                     const string &description = string(),
+                     const string &text = string());
+  INLINE ~ConfigVariableList();
+
+  INLINE const string &get_name() const;
+
+  INLINE ConfigVariableCore::ValueType get_value_type() const;
+  INLINE int get_trust_level() const;
+  INLINE const string &get_description() const;
+  INLINE const string &get_text() const;
+
+  INLINE int get_num_values() const;
+  INLINE string get_string_value(int n) const;
+
+  INLINE int get_num_unique_values() const;
+  INLINE string get_unique_value(int n) const;
+
+  INLINE int size() const;
+  INLINE string operator [] (int n) const;
+  
+  INLINE void output(ostream &out) const;
+  INLINE void write(ostream &out) const;
+
+protected:
+  ConfigVariableCore *_core;
+  vector_string _unique_values;
+};
+
+INLINE ostream &operator << (ostream &out, const ConfigVariableList &variable);
+
+#include "configVariableList.I"
+
+#endif

+ 46 - 0
dtool/src/prc/configVariableManager.I

@@ -0,0 +1,46 @@
+// Filename: configVariableManager.I
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::get_num_variables
+//       Access: Published
+//  Description: Returns the current number of active ConfigVariableCores in
+//               the world.
+////////////////////////////////////////////////////////////////////
+INLINE int ConfigVariableManager::
+get_num_variables() const {
+  return _variables.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::get_variable
+//       Access: Published
+//  Description: Returns the nth active ConfigVariableCore in the world.
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableCore *ConfigVariableManager::
+get_variable(int n) const {
+  nassertr(n >= 0 && n < (int)_variables.size(), NULL);
+  return _variables[n];
+}
+
+INLINE ostream &
+operator << (ostream &out, const ConfigVariableManager &variableMgr) {
+  variableMgr.output(out);
+  return out;
+}

+ 262 - 0
dtool/src/prc/configVariableManager.cxx

@@ -0,0 +1,262 @@
+// Filename: configVariableManager.cxx
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "configVariableManager.h"
+#include "configVariableCore.h"
+#include "configDeclaration.h"
+#include "configPage.h"
+#include "config_prc.h"
+
+ConfigVariableManager *ConfigVariableManager::_global_ptr = NULL;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::Constructor
+//       Access: Protected
+//  Description: The constructor is private (actually, just protected,
+//               but only to avoid a gcc compiler warning) because it
+//               should not be explicitly constructed.  There is only
+//               one ConfigVariableManager, and it constructs
+//               itself.
+////////////////////////////////////////////////////////////////////
+ConfigVariableManager::
+ConfigVariableManager() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::Destructor
+//       Access: Protected
+//  Description: The ConfigVariableManager destructor should never be
+//               called, because this is a global object that is never
+//               freed.
+////////////////////////////////////////////////////////////////////
+ConfigVariableManager::
+~ConfigVariableManager() {
+  prc_cat->error()
+    << "Internal error--ConfigVariableManager destructor called!\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::make_variable
+//       Access: Published
+//  Description: Creates and returns a new, undefined
+//               ConfigVariableCore with the indicated name; or if a
+//               variable with this name has already been created,
+//               returns that one instead.
+////////////////////////////////////////////////////////////////////
+ConfigVariableCore *ConfigVariableManager::
+make_variable(const string &name) {
+  VariablesByName::iterator ni;
+  ni = _variables_by_name.find(name);
+  if (ni != _variables_by_name.end()) {
+    return (*ni).second;
+  }
+
+  ConfigVariableCore *variable = new ConfigVariableCore(name);
+  _variables_by_name[name] = variable;
+  _variables.push_back(variable);
+  return variable;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::get_variable_name
+//       Access: Published
+//  Description: Returns the name of the nth active ConfigVariable in
+//               the list.
+////////////////////////////////////////////////////////////////////
+string ConfigVariableManager::
+get_variable_name(int n) const {
+  if (n >= 0 && n < (int)_variables.size()) {
+    return _variables[n]->get_name();
+  }
+  return string();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::is_variable_used
+//       Access: Published
+//  Description: Returns true if the nth active ConfigVariable in
+//               the list has been used by code, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ConfigVariableManager::
+is_variable_used(int n) const {
+  if (n >= 0 && n < (int)_variables.size()) {
+    return _variables[n]->is_used();
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConfigVariableManager::
+output(ostream &out) const {
+  out << "ConfigVariableManager, " << _variables.size() << " variables.";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ConfigVariableManager::
+write(ostream &out) const {
+  out << "ConfigVariableManager, " << _variables.size() << " variables:\n";
+  VariablesByName::const_iterator ni;
+  for (ni = _variables_by_name.begin();
+       ni != _variables_by_name.end();
+       ++ni) {
+    ConfigVariableCore *variable = (*ni).second;
+    out << "  " << variable->get_name();
+    if (!variable->is_used()) {
+      out << "  (not used)";
+    } else {
+      out << " " << variable->get_declaration(0)->get_string_value();
+    }
+    out << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::list_unused_variables
+//       Access: Published
+//  Description: Writes a list of all the variables that have been
+//               defined in a prc file without having been declared
+//               somewhere in code.
+////////////////////////////////////////////////////////////////////
+void ConfigVariableManager::
+list_unused_variables() const {
+  VariablesByName::const_iterator ni;
+  for (ni = _variables_by_name.begin();
+       ni != _variables_by_name.end();
+       ++ni) {
+    ConfigVariableCore *variable = (*ni).second;
+    if (!variable->is_used()) {
+      cout << variable->get_name() << "\n";
+      int num_references = variable->get_num_references();
+      for (int i = 0; i < num_references; i++) {
+        cout << "  " << variable->get_reference(i)->get_page()->get_name()
+             << "\n";
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::list_variables
+//       Access: Published
+//  Description: Writes a list of all the variables that have been
+//               declared somewhere in code, along with a brief
+//               description.
+////////////////////////////////////////////////////////////////////
+void ConfigVariableManager::
+list_variables() const {
+  VariablesByName::const_iterator ni;
+  for (ni = _variables_by_name.begin();
+       ni != _variables_by_name.end();
+       ++ni) {
+    ConfigVariableCore *variable = (*ni).second;
+    if (variable->is_used()) {
+      
+      cout << variable->get_name() << " " 
+           << variable->get_value_type() << "\n";
+
+      const ConfigDeclaration *decl;
+
+      if (variable->get_value_type() == ConfigVariableCore::VT_list) {
+        // We treat a "list" variable as a special case: list all of
+        // its values.
+        cout << "  current value =\n";
+        int num_references = variable->get_num_trusted_references();
+        for (int i = 0; i < num_references; i++) {
+          decl = variable->get_trusted_reference(i);
+          cout << "    " << decl->get_string_value()
+               << "  (from " << decl->get_page()->get_name() << ")\n";
+        }
+        
+      } else {
+        // An ordinary, non-list variable gets one line for its
+        // current value (if it has one) and another line for its
+        // default value.
+        decl = variable->get_declaration(0);
+        if (decl != variable->get_default_value()) {
+          cout << "  current value = " << decl->get_string_value();
+          if (!decl->get_page()->is_special()) {
+            cout << "  (from " << decl->get_page()->get_name() << ")\n";
+          } else {
+            cout << "  (defined locally)\n";
+          }
+        }
+
+        decl = variable->get_default_value();
+        if (decl != (ConfigDeclaration *)NULL) {
+          cout << "  default value = " << decl->get_string_value() << "\n";
+        }
+      }
+
+      if (!variable->get_description().empty()) {
+        cout << "  " << variable->get_description() << "\n";
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::describe_variables
+//       Access: Published
+//  Description: Writes a list of all the variables along with each
+//               variable's long description.
+////////////////////////////////////////////////////////////////////
+void ConfigVariableManager::
+describe_variables() const {
+  VariablesByName::const_iterator ni;
+  for (ni = _variables_by_name.begin();
+       ni != _variables_by_name.end();
+       ++ni) {
+    ConfigVariableCore *variable = (*ni).second;
+    if (variable->is_used()) {
+      cout << variable->get_name() << " "
+           << variable->get_value_type() << "\n";
+
+      const ConfigDeclaration *decl = variable->get_default_value();
+      if (decl != (ConfigDeclaration *)NULL) {
+        cout << "  default value = " << decl->get_string_value() << "\n";
+      }
+
+      if (!variable->get_text().empty()) {
+        cout << "  " << variable->get_text() << "\n";
+      } else if (!variable->get_description().empty()) {
+        cout << "  " << variable->get_description() << "\n";
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableManager::get_global_ptr
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ConfigVariableManager *ConfigVariableManager::
+get_global_ptr() {
+  if (_global_ptr == (ConfigVariableManager *)NULL) {
+    _global_ptr = new ConfigVariableManager;
+  }
+  return _global_ptr;
+}

+ 71 - 0
dtool/src/prc/configVariableManager.h

@@ -0,0 +1,71 @@
+// Filename: configVariableManager.h
+// Created by:  drose (15Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGVARIABLEMANAGER_H
+#define CONFIGVARIABLEMANAGER_H
+
+#include "dtoolbase.h"
+#include "notify.h"
+#include "pvector.h"
+#include "pmap.h"
+
+class ConfigVariableCore;
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigVariableManager
+// Description : A global object that maintains the set of ConfigVariableCores
+//               everywhere in the world, and keeps them in sorted
+//               order.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG ConfigVariableManager {
+protected:
+  ConfigVariableManager();
+  ~ConfigVariableManager();
+
+PUBLISHED:
+  ConfigVariableCore *make_variable(const string &name);
+
+  INLINE int get_num_variables() const;
+  INLINE ConfigVariableCore *get_variable(int n) const;
+  string get_variable_name(int n) const;
+  bool is_variable_used(int n) const;
+
+  void output(ostream &out) const;
+  void write(ostream &out) const;
+
+  void list_unused_variables() const;
+  void list_variables() const;
+  void describe_variables() const;
+
+  static ConfigVariableManager *get_global_ptr();
+
+private:
+  typedef pvector<ConfigVariableCore *> Variables;
+  Variables _variables;
+
+  typedef pmap<string, ConfigVariableCore *> VariablesByName;
+  VariablesByName _variables_by_name;
+
+  static ConfigVariableManager *_global_ptr;
+};
+
+INLINE ostream &operator << (ostream &out, const ConfigVariableManager &variableMgr);
+
+#include "configVariableManager.I"
+
+#endif

+ 160 - 0
dtool/src/prc/configVariableString.I

@@ -0,0 +1,160 @@
+// Filename: configVariableString.I
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableString::
+ConfigVariableString(const string &name) :
+  ConfigVariable(name, ConfigVariableCore::VT_string)
+{
+  _core->set_used();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableString::
+ConfigVariableString(const string &name, string default_value, int trust_level,
+                     const string &description, const string &text) :
+  ConfigVariable(name, ConfigVariableCore::VT_string, trust_level, 
+                 description, text)
+{
+  _core->set_default_value(default_value);
+  _core->set_used();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::operator =
+//       Access: Published
+//  Description: Reassigns the variable's local value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableString::
+operator = (const string &value) {
+  set_value(value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::typecast operator
+//       Access: Published
+//  Description: Returns the variable's value.
+////////////////////////////////////////////////////////////////////
+INLINE ConfigVariableString::
+operator string () const {
+  return get_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::empty
+//       Access: Published
+//  Description: Returns true if the string is empty, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariableString::
+empty() const {
+  return get_value().empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::Equality operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariableString::
+operator == (const string &other) const {
+  return get_value() == other;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::Inequality operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariableString::
+operator != (const string &other) const {
+  return get_value() != other;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::Ordering operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool ConfigVariableString::
+operator < (const string &other) const {
+  return get_value() < other;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::set_value
+//       Access: Published
+//  Description: Reassigns the variable's local value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableString::
+set_value(const string &value) {
+  set_string_value(value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::get_value
+//       Access: Published
+//  Description: Returns the variable's value.
+////////////////////////////////////////////////////////////////////
+INLINE string ConfigVariableString::
+get_value() const {
+  return get_string_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::get_default_value
+//       Access: Published
+//  Description: Returns the variable's default value.
+////////////////////////////////////////////////////////////////////
+INLINE string ConfigVariableString::
+get_default_value() const {
+  const ConfigDeclaration *decl = ConfigVariable::get_default_value();
+  if (decl != (ConfigDeclaration *)NULL) {
+    return decl->get_string_word(0);
+  }
+  return string();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::get_word
+//       Access: Published
+//  Description: Returns the variable's nth value.
+////////////////////////////////////////////////////////////////////
+INLINE string ConfigVariableString::
+get_word(int n) const {
+  return get_string_word(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConfigVariableString::set_word
+//       Access: Published
+//  Description: Reassigns the variable's nth value.  This makes a
+//               local copy of the variable's overall value.
+////////////////////////////////////////////////////////////////////
+INLINE void ConfigVariableString::
+set_word(int n, const string &value) {
+  set_string_word(n, value);
+}

+ 19 - 0
dtool/src/prc/configVariableString.cxx

@@ -0,0 +1,19 @@
+// Filename: configVariableString.cxx
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "configVariableString.h"

+ 57 - 0
dtool/src/prc/configVariableString.h

@@ -0,0 +1,57 @@
+// Filename: configVariableString.h
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGVARIABLESTRING_H
+#define CONFIGVARIABLESTRING_H
+
+#include "dtoolbase.h"
+#include "configVariable.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigVariableString
+// Description : This is a convenience class to specialize
+//               ConfigVariable as a string type.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG ConfigVariableString : public ConfigVariable {
+PUBLISHED:
+  INLINE ConfigVariableString(const string &name);
+  INLINE ConfigVariableString(const string &name, string default_value,
+                              int trust_level = -2,
+                              const string &description = string(),
+                              const string &text = string());
+
+  INLINE void operator = (const string &value);
+  INLINE operator string () const;
+  INLINE bool empty() const;
+
+  // Comparison operators are handy.
+  INLINE bool operator == (const string &other) const;
+  INLINE bool operator != (const string &other) const;
+  INLINE bool operator < (const string &other) const;
+
+  INLINE void set_value(const string &value);
+  INLINE string get_value() const;
+  INLINE string get_default_value() const;
+
+  INLINE string get_word(int n) const;
+  INLINE void set_word(int n, const string &value);
+};
+
+#include "configVariableString.I"
+
+#endif

+ 41 - 0
dtool/src/prc/configVariableTempl.h

@@ -0,0 +1,41 @@
+// Filename: configVariableTempl.h
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIGVARIABLETEMPL_H
+#define CONFIGVARIABLETEMPL_H
+
+#include "dtoolbase.h"
+#include "configVariable.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConfigVariableTempl
+// Description : This is a template class to define ConfigVaribleInt,
+//               ConfigVariableBool, etc.
+////////////////////////////////////////////////////////////////////
+template<class ValueType, int EnumValue>
+class ConfigVariableTempl {
+PUBLISHED:
+  INLINE ConfigVariableTempl(const string &name);
+  INLINE ConfigVariableTempl(const string &name, ValueType default_value,
+                             int trust_level = -1,
+                             const string &description = string(),
+                             const string &text = string());
+
+  INLINE operator ValueType () const;
+  INLINE void operator = (const ValueType &value);
+

+ 21 - 0
dtool/src/prc/config_prc.cxx

@@ -0,0 +1,21 @@
+// Filename: config_prc.cxx
+// Created by:  drose (20Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_prc.h"
+
+NotifyCategoryDef(prc, "");

+ 7 - 9
dtool/src/dconfig/config_notify.h → dtool/src/prc/config_prc.h

@@ -1,5 +1,5 @@
-// Filename: config_notify.h
-// Created by:  drose (29Feb00)
+// Filename: config_prc.h
+// Created by:  drose (20Oct04)
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //
 //
@@ -16,15 +16,13 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
-#ifndef CONFIG_NOTIFY_H
-#define CONFIG_NOTIFY_H
+#ifndef CONFIG_PRC_H
+#define CONFIG_PRC_H
 
 
 #include "dtoolbase.h"
 #include "dtoolbase.h"
-#include "dconfig.h"
+#include "notifyCategoryProxy.h"
 
 
-ConfigureDecl(config_notify, EXPCL_DTOOLCONFIG, EXPTP_DTOOLCONFIG);
+NotifyCategoryDecl(prc, EXPCL_DTOOLCONFIG, EXPTP_DTOOLCONFIG);
 
 
-bool get_assert_abort();
-bool get_notify_timestamp();
+#endif
 
 
-#endif /* __CONFIG_NOTIFY_H__ */

+ 0 - 0
dtool/src/dconfig/globPattern.I → dtool/src/prc/globPattern.I


+ 0 - 0
dtool/src/dconfig/globPattern.cxx → dtool/src/prc/globPattern.cxx


+ 1 - 1
dtool/src/dconfig/globPattern.h → dtool/src/prc/globPattern.h

@@ -39,7 +39,7 @@
 //               particular pattern.
 //               particular pattern.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_DTOOLCONFIG GlobPattern {
 class EXPCL_DTOOLCONFIG GlobPattern {
-public:
+PUBLISHED:
   INLINE GlobPattern(const string &pattern = string());
   INLINE GlobPattern(const string &pattern = string());
   INLINE GlobPattern(const GlobPattern &copy);
   INLINE GlobPattern(const GlobPattern &copy);
   INLINE void operator = (const GlobPattern &copy);
   INLINE void operator = (const GlobPattern &copy);

+ 0 - 0
dtool/src/dconfig/notify.I → dtool/src/prc/notify.I


+ 13 - 15
dtool/src/dconfig/notify.cxx → dtool/src/prc/notify.cxx

@@ -17,19 +17,19 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "notify.h"
 #include "notify.h"
-#include "config_notify.h"
-#include "dconfig.h"
-
+#include "configPageManager.h"
+#include "configVariableString.h"
+#include "configVariableBool.h"
 #include "filename.h"
 #include "filename.h"
 
 
 #include <ctype.h>
 #include <ctype.h>
 
 
-#ifdef WIN32
-#include <windows.h>   //for DebugBreak()
-#endif
-
 Notify *Notify::_global_ptr = (Notify *)NULL;
 Notify *Notify::_global_ptr = (Notify *)NULL;
 
 
+static ConfigVariableBool assert_abort
+("assert-abort", false, 0,
+ "Set this true to trigger a core dump and/or stack trace when the first assertion fails");
+
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Notify::Constructor
 //     Function: Notify::Constructor
@@ -251,11 +251,6 @@ get_top_category() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 NotifyCategory *Notify::
 NotifyCategory *Notify::
 get_category(const string &basename, NotifyCategory *parent_category) {
 get_category(const string &basename, NotifyCategory *parent_category) {
-  // We have to ensure that config_notify has been at least created
-  // before we try to create any NotifyCategories, or we'll get an
-  // infinite recursion problem.  Calling this function is sufficient.
-  config_notify.AmInitializing();
-
   // The string should not contain colons.
   // The string should not contain colons.
   nassertr(basename.find(':') == string::npos, NULL);
   nassertr(basename.find(':') == string::npos, NULL);
 
 
@@ -448,7 +443,7 @@ assert_failure(const char *expression, int line,
 
 
   nout << "Assertion failed: " << message << "\n";
   nout << "Assertion failed: " << message << "\n";
 
 
-  if (get_assert_abort()) {
+  if (assert_abort) {
 #ifdef WIN32
 #ifdef WIN32
     // How to trigger an exception in VC++ that offers to take us into
     // How to trigger an exception in VC++ that offers to take us into
     // the debugger?  abort() doesn't do it.  We used to be able to
     // the debugger?  abort() doesn't do it.  We used to be able to
@@ -516,7 +511,7 @@ string_severity(const string &str) {
 //     Function: Notify::config_initialized
 //     Function: Notify::config_initialized
 //       Access: Public
 //       Access: Public
 //  Description: Intended to be called only by Config, this is a
 //  Description: Intended to be called only by Config, this is a
-//               callback that indicated to Notify when Config has
+//               callback that indicates to Notify when Config has
 //               done initializing and Notify can safely set up some
 //               done initializing and Notify can safely set up some
 //               internal state variables that depend on Config
 //               internal state variables that depend on Config
 //               variables.
 //               variables.
@@ -531,7 +526,10 @@ config_initialized() {
   already_initialized = true;
   already_initialized = true;
 
 
   if (_ostream_ptr == &cerr) {
   if (_ostream_ptr == &cerr) {
-    string notify_output = config_notify.GetString("notify-output", "");
+    ConfigVariableString notify_output
+      ("notify-output", "", 0,
+       "The filename to which to write all the output of notify");
+
     if (!notify_output.empty()) {
     if (!notify_output.empty()) {
       if (notify_output == "stdout") {
       if (notify_output == "stdout") {
         cout.setf(ios::unitbuf);
         cout.setf(ios::unitbuf);

+ 0 - 0
dtool/src/dconfig/notify.h → dtool/src/prc/notify.h


+ 0 - 0
dtool/src/dconfig/notifyCategory.I → dtool/src/prc/notifyCategory.I


+ 12 - 7
dtool/src/dconfig/notifyCategory.cxx → dtool/src/prc/notifyCategory.cxx

@@ -18,13 +18,19 @@
 
 
 #include "notifyCategory.h"
 #include "notifyCategory.h"
 #include "notify.h"
 #include "notify.h"
-#include "config_notify.h"
+#include "configPageManager.h"
+#include "configVariableString.h"
+#include "configVariableBool.h"
 
 
 #include <time.h>  // for strftime().
 #include <time.h>  // for strftime().
 #include <assert.h>
 #include <assert.h>
 
 
 time_t NotifyCategory::_server_delta = 0;
 time_t NotifyCategory::_server_delta = 0;
 
 
+static ConfigVariableBool notify_timestamp
+("notify-timestamp", false, 0,
+ "Set true to output the date & time with each notify message.");
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NotifyCategory::Constructor
 //     Function: NotifyCategory::Constructor
 //       Access: Private
 //       Access: Private
@@ -55,15 +61,14 @@ NotifyCategory(const string &fullname, const string &basename,
   }
   }
 
 
   if (!config_name.empty()) {
   if (!config_name.empty()) {
-    string severity_name;
-    if (!config_notify.AmInitializing()) {
-      severity_name = config_notify.GetString(config_name, "");
-    }
+    ConfigVariableString severity_name
+      (config_name, "", 0,
+       "Indicates the verbosity level of notify messages for the given category.");
     if (!severity_name.empty()) {
     if (!severity_name.empty()) {
       // The user specified a particular severity for this category at
       // The user specified a particular severity for this category at
       // config time.  Use it.
       // config time.  Use it.
       _severity = Notify::string_severity(severity_name);
       _severity = Notify::string_severity(severity_name);
-
+      
       if (_severity == NS_unspecified) {
       if (_severity == NS_unspecified) {
         nout << "Invalid severity name for " << config_name << ": "
         nout << "Invalid severity name for " << config_name << ": "
              << severity_name << "\n";
              << severity_name << "\n";
@@ -100,7 +105,7 @@ ostream &NotifyCategory::
 out(NotifySeverity severity, bool prefix) const {
 out(NotifySeverity severity, bool prefix) const {
   if (is_on(severity)) {
   if (is_on(severity)) {
     if (prefix) {
     if (prefix) {
-      if (get_notify_timestamp()) {
+      if (notify_timestamp) {
         // Format a timestamp to include as a prefix as well.
         // Format a timestamp to include as a prefix as well.
         time_t now = time(NULL) + _server_delta;
         time_t now = time(NULL) + _server_delta;
         struct tm *ptm = localtime(&now);
         struct tm *ptm = localtime(&now);

+ 0 - 0
dtool/src/dconfig/notifyCategory.h → dtool/src/prc/notifyCategory.h


+ 0 - 0
dtool/src/dconfig/notifyCategoryProxy.I → dtool/src/prc/notifyCategoryProxy.I


+ 0 - 0
dtool/src/dconfig/notifyCategoryProxy.h → dtool/src/prc/notifyCategoryProxy.h


+ 0 - 0
dtool/src/dconfig/notifySeverity.cxx → dtool/src/prc/notifySeverity.cxx


+ 0 - 0
dtool/src/dconfig/notifySeverity.h → dtool/src/prc/notifySeverity.h


+ 18 - 0
dtool/src/prc/prcKeyRegistry.I

@@ -0,0 +1,18 @@
+// Filename: prcKeyRegistry.I
+// Created by:  drose (19Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 188 - 0
dtool/src/prc/prcKeyRegistry.cxx

@@ -0,0 +1,188 @@
+// Filename: prcKeyRegistry.cxx
+// Created by:  drose (19Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "prcKeyRegistry.h"
+
+// This file requires OpenSSL to compile, because we use routines in
+// the OpenSSL library to manage keys and to sign and validate
+// signatures.
+
+#ifdef HAVE_SSL
+
+#include <openssl/pem.h>
+
+PrcKeyRegistry *PrcKeyRegistry::_global_ptr = NULL;
+
+////////////////////////////////////////////////////////////////////
+//     Function: PrcKeyRegistry::Constructor
+//       Access: Protected
+//  Description: There is only one PrcKeyRegistry in the world; use
+//               get_global_ptr() to get it.
+////////////////////////////////////////////////////////////////////
+PrcKeyRegistry::
+PrcKeyRegistry() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PrcKeyRegistry::Destructor
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PrcKeyRegistry::
+~PrcKeyRegistry() {
+  cerr << "Internal error--PrcKeyRegistry destructor called!\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PrcKeyRegistry::record_keys
+//       Access: Public
+//  Description: Records the list of public keys that are compiled
+//               into this executable.  The pointer is assumed to be
+//               to an area of static memory that will not be
+//               destructed, so the data is not copied, but only the
+//               pointer is assigned.
+//
+//               This method is normally called after including the
+//               code generated by the make-prc-key utility.
+////////////////////////////////////////////////////////////////////
+void PrcKeyRegistry::
+record_keys(const KeyDef *key_def, int num_keys) {
+  for (int i = 0; i < num_keys; i++) {
+    const KeyDef *def = &key_def[i];
+    if (def->_data != NULL) {
+      // Clear the ith key.
+      while ((int)_keys.size() <= i) {
+        Key key;
+        key._def = NULL;
+        key._pkey = NULL;
+        key._generated_time = 0;
+        _keys.push_back(key);
+      }
+      if (_keys[i]._def != def) {
+        if (_keys[i]._pkey != (EVP_PKEY *)NULL) {
+          EVP_PKEY_free(_keys[i]._pkey);
+          _keys[i]._pkey = NULL;
+        }
+        _keys[i]._def = def;
+        _keys[i]._generated_time = def->_generated_time;
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PrcKeyRegistry::set_key
+//       Access: Public
+//  Description: Sets the nth public key in the registry to the given
+//               value.  The EVP_PKEY structure must have been
+//               properly allocated view EVP_PKEY_new(); its ownership
+//               is transferred to the registry and it will eventually
+//               be freed via EVP_PKEY_free().
+////////////////////////////////////////////////////////////////////
+void PrcKeyRegistry::
+set_key(int n, EVP_PKEY *pkey, time_t generated_time) {
+  // Clear the nth key.
+  while ((int)_keys.size() <= n) {
+    Key key;
+    key._def = NULL;
+    key._pkey = NULL;
+    key._generated_time = 0;
+    _keys.push_back(key);
+  }
+  _keys[n]._def = NULL;
+  if (_keys[n]._pkey != (EVP_PKEY *)NULL) {
+    EVP_PKEY_free(_keys[n]._pkey);
+    _keys[n]._pkey = NULL;
+  }
+  _keys[n]._pkey = pkey;
+  _keys[n]._generated_time = generated_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PrcKeyRegistry::get_num_keys
+//       Access: Public
+//  Description: Returns the number of public keys in the registry.
+//               This is actually the highest index number + 1, which
+//               might not strictly be the number of keys, since there
+//               may be holes in the list.
+////////////////////////////////////////////////////////////////////
+int PrcKeyRegistry::
+get_num_keys() const {
+  return _keys.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PrcKeyRegistry::get_key
+//       Access: Public
+//  Description: Returns the nth public key, or NULL if the nth key is
+//               not defined.
+////////////////////////////////////////////////////////////////////
+EVP_PKEY *PrcKeyRegistry::
+get_key(int n) const {
+  if (n < 0 || n >= (int)_keys.size()) {
+    return NULL;
+  }
+
+  if (_keys[n]._def != (KeyDef *)NULL) {
+    if (_keys[n]._pkey == (EVP_PKEY *)NULL) {
+      // Convert the def to a EVP_PKEY structure.
+      const KeyDef *def = _keys[n]._def;
+      BIO *mbio = BIO_new_mem_buf((void *)def->_data, def->_length);
+      EVP_PKEY *pkey = PEM_read_bio_PUBKEY(mbio, NULL, NULL, NULL);
+      ((PrcKeyRegistry *)this)->_keys[n]._pkey = pkey;
+      BIO_free(mbio);
+
+      if (pkey == (EVP_PKEY *)NULL) {
+        // Couldn't read the bio for some reason.
+        ((PrcKeyRegistry *)this)->_keys[n]._def = NULL;
+      }
+    }
+  }
+
+  return _keys[n]._pkey;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PrcKeyRegistry::get_generated_time
+//       Access: Public
+//  Description: Returns the timestamp at which the indicated key was
+//               generated, or 0 if the key is not defined.
+////////////////////////////////////////////////////////////////////
+time_t PrcKeyRegistry::
+get_generated_time(int n) const {
+  if (n < 0 || n >= (int)_keys.size()) {
+    return 0;
+  }
+
+  return _keys[n]._generated_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PrcKeyRegistry::get_global_ptr
+//       Access: Public, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PrcKeyRegistry *PrcKeyRegistry::
+get_global_ptr() {
+  if (_global_ptr == (PrcKeyRegistry *)NULL) {
+    _global_ptr = new PrcKeyRegistry;
+  }
+  return _global_ptr;
+}
+
+#endif  // HAVE_SSL

+ 89 - 0
dtool/src/prc/prcKeyRegistry.h

@@ -0,0 +1,89 @@
+// Filename: prcKeyRegistry.h
+// Created by:  drose (19Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PRCKEYREGISTRY_H
+#define PRCKEYREGISTRY_H
+
+#include "dtoolbase.h"
+
+// This file requires OpenSSL to compile, because we use routines in
+// the OpenSSL library to manage keys and to sign and validate
+// signatures.
+
+#ifdef HAVE_SSL
+
+#include "pvector.h"
+#include <openssl/evp.h>
+
+////////////////////////////////////////////////////////////////////
+//       Class : PrcKeyRegistry
+// Description : This class records the set of public keys used to
+//               verify the signature on a prc file.  The actual
+//               public keys themselves are generated by the
+//               make-prc-key utility; the output of this utility is a
+//               .c file which should be named by the
+//               PRC_PUBLIC_KEYS_FILENAME variable in Config.pp.
+//
+//               This class requires the OpenSSL library.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOLCONFIG PrcKeyRegistry {
+protected:
+  PrcKeyRegistry();
+  ~PrcKeyRegistry();
+
+public:
+  struct KeyDef {
+    const char *_data;
+    size_t _length;
+    time_t _generated_time;
+  };
+
+  void record_keys(const KeyDef *key_def, int num_keys);
+  void set_key(int n, EVP_PKEY *pkey, time_t generated_time);
+
+  int get_num_keys() const;
+  EVP_PKEY *get_key(int n) const;
+  time_t get_generated_time(int n) const;
+
+  static PrcKeyRegistry *get_global_ptr();
+
+private:
+
+  class Key {
+  public:
+    const KeyDef *_def;
+    EVP_PKEY *_pkey;
+    time_t _generated_time;
+  };
+
+  typedef pvector<Key> Keys;
+  Keys _keys;
+
+  static PrcKeyRegistry *_global_ptr;
+};
+
+#include "prcKeyRegistry.I"
+
+#endif  // HAVE_SSL
+
+#endif
+
+    
+  
+  
+

+ 26 - 0
dtool/src/prckeys/Sources.pp

@@ -0,0 +1,26 @@
+#define LOCAL_LIBS dtoolutil dtoolbase prc dconfig interrogatedb
+
+#begin bin_target
+  #define BUILD_TARGET $[HAVE_SSL]
+  #define USE_PACKAGES ssl
+
+  #define TARGET make-prc-key
+
+  #define SOURCES \
+    makePrcKey.cxx
+
+  #define INSTALL_HEADERS \
+    signPrcFile_src.cxx
+
+#end bin_target
+
+// #begin bin_target
+//   #define BUILD_TARGET $[HAVE_SSL]
+//   #define USE_PACKAGES ssl
+// 
+//   #define TARGET panda-sign1
+// 
+//   #define SOURCES \
+//     panda_sign1.cxx
+// 
+// #end bin_target

+ 426 - 0
dtool/src/prckeys/makePrcKey.cxx

@@ -0,0 +1,426 @@
+// Filename: makePrcKey.cxx
+// Created by:  drose (19Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dtoolbase.h"
+#include "prcKeyRegistry.h"
+#include "filename.h"
+#include "vector_int.h"
+#include <assert.h>
+
+// Pick up the public key definitions.
+#ifdef PRC_PUBLIC_KEYS_INCLUDE
+#include PRC_PUBLIC_KEYS_INCLUDE
+#endif
+
+#include <openssl/rsa.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+#include <openssl/bio.h>
+
+#ifndef HAVE_GETOPT
+  #include "gnu_getopt.h"
+#else
+  #ifdef HAVE_GETOPT_H
+    #include <getopt.h>
+  #endif
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: output_ssl_errors
+//  Description: A convenience function that is itself a wrapper
+//               around the OpenSSL convenience function to output the
+//               recent OpenSSL errors.  This function sends the error
+//               string to cerr.
+////////////////////////////////////////////////////////////////////
+void
+output_ssl_errors() {
+  cerr << "Error occurred in SSL routines.\n";
+
+  static bool strings_loaded = false;
+  if (!strings_loaded) {
+    ERR_load_crypto_strings();
+    strings_loaded = true;
+  }
+
+  unsigned long e = ERR_get_error();
+  while (e != 0) {
+    static const size_t buffer_len = 256;
+    char buffer[buffer_len];
+    ERR_error_string_n(e, buffer, buffer_len);
+    cerr << buffer << "\n";
+    e = ERR_get_error();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: output_c_string
+//  Description: Extracts the data written to the indicated memory bio
+//               and writes it to the indicated stream, formatting it
+//               to be compiled into a C or C++ program as a string.
+////////////////////////////////////////////////////////////////////
+void
+output_c_string(ostream &out, const string &string_name, 
+                int index, BIO *mbio) {
+  char *data_ptr;
+  size_t data_size = BIO_get_mem_data(mbio, &data_ptr);
+
+  out << "static const char * const " << string_name
+      << index << "_data =\n"
+      << "  \"";
+
+  bool last_nl = false;
+  for (size_t i = 0; i < data_size; i++) {
+    if (data_ptr[i] == '\n') {
+      out << "\\n";
+      last_nl = true;
+
+    } else {
+      if (last_nl) {
+        out << "\"\n  \"";
+        last_nl = false;
+      }
+
+      if (isprint(data_ptr[i])) {
+        out << data_ptr[i];
+
+      } else {
+        out << "\\x" << hex << setw(2) << setfill('0') 
+            << (unsigned int)(unsigned char)data_ptr[i] << dec;
+      }
+    }
+  }
+  out << "\";\nstatic const size_t " << string_name << index
+      << "_length = " << data_size << ";\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: generate_key
+//  Description: Generates a new public and private key pair.
+////////////////////////////////////////////////////////////////////
+EVP_PKEY *
+generate_key() {
+  RSA *rsa = RSA_generate_key(1024, 7, NULL, NULL);
+  
+  if (rsa == (RSA *)NULL) {
+    output_ssl_errors();
+    exit(1);
+  }
+
+  EVP_PKEY *pkey = EVP_PKEY_new();
+  EVP_PKEY_assign_RSA(pkey, rsa);
+
+  return pkey;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: write_public_keys
+//  Description: Writes the list of public keys stored in the
+//               PrcKeyRegistry to the indicated output filename as a
+//               compilable list of KeyDef entries, suitable for
+//               passing to PrcKeyRegistry::record_keys().
+////////////////////////////////////////////////////////////////////
+void
+write_public_keys(Filename outfile) {
+  outfile.set_text();
+  cerr << "Rewriting " << outfile << "\n";
+
+  ofstream out;
+  if (!outfile.open_write(out)) {
+    cerr << "Unable to open " << outfile << " for writing.\n";
+    exit(1);
+  }
+
+  out <<
+    "\n"
+    "// This file was generated by make-prc-key.  It defines the public keys\n"
+    "// that will be used to validate signed prc files.\n"
+    "\n"
+    "#include \"prcKeyRegistry.h\"\n"
+    "\n";
+  
+  PrcKeyRegistry *pkr = PrcKeyRegistry::get_global_ptr();
+
+  BIO *mbio = BIO_new(BIO_s_mem());
+
+  int num_keys = pkr->get_num_keys();
+  int i;
+  for (i = 0; i < num_keys; i++) {
+    EVP_PKEY *pkey = pkr->get_key(i);
+
+    if (pkey != (EVP_PKEY *)NULL) {
+      if (!PEM_write_bio_PUBKEY(mbio, pkey)) {
+        output_ssl_errors();
+        exit(1);
+      }
+
+      output_c_string(out, "prc_pubkey", i, mbio);
+      BIO_reset(mbio);
+      out << "\n";
+    }
+  }
+
+  BIO_free(mbio);
+
+  // Now output the table that indexes all of the above.
+  out << "static PrcKeyRegistry::KeyDef const prc_pubkeys[" << num_keys << "] = {\n";
+
+  for (i = 0; i < num_keys; i++) {
+    EVP_PKEY *pkey = pkr->get_key(i);
+    time_t generated_time = pkr->get_generated_time(i);
+
+    if (pkey != (EVP_PKEY *)NULL) {
+      out << "  { prc_pubkey" << i << "_data, prc_pubkey" << i 
+          << "_length, " << generated_time << " },\n";
+    } else {
+      out << "  { NULL, 0, 0 },\n";
+    }
+  };
+
+  out << "};\n"
+      << "static const int num_prc_pubkeys = " << num_keys << ";\n\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: write_private_key
+//  Description: Generates a C++ program that can be used to sign a
+//               prc file with the indicated private key into the
+//               given output filename.
+////////////////////////////////////////////////////////////////////
+void
+write_private_key(EVP_PKEY *pkey, Filename outfile, int n, time_t now,
+                  const char *pp) {
+  outfile.set_text();
+  cerr << "Rewriting " << outfile << "\n";
+
+  ofstream out;
+  if (!outfile.open_write(out)) {
+    cerr << "Unable to open " << outfile << " for writing.\n";
+    exit(1);
+  }
+
+  out <<
+    "\n"
+    "// This file was generated by make-prc-key.  It can be compiled against\n"
+    "// dtool to produce a program that will sign a prc file using key number " << n << ".\n\n";
+
+  BIO *mbio = BIO_new(BIO_s_mem());
+
+  int write_result;
+  if (pp != NULL && *pp == '\0') {
+    // The supplied password was the empty string.  This means not to
+    // encrypt the private key.
+    write_result =
+      PEM_write_bio_PKCS8PrivateKey(mbio, pkey, NULL, NULL, 0, NULL, NULL);
+
+  } else {
+    // Otherwise, the default is to encrypt it.
+    write_result =
+      PEM_write_bio_PKCS8PrivateKey(mbio, pkey, EVP_des_ede3_cbc(),
+                                    NULL, 0, NULL, (void *)pp);
+  }
+
+  if (!write_result) {
+    output_ssl_errors();
+    exit(1);
+  }
+
+  output_c_string(out, "prc_privkey", n, mbio);
+
+  BIO_free(mbio);
+
+  out << 
+    "\n\n"
+    "#define KEY_NUMBER " << n << "\n"
+    "#define KEY_DATA prc_privkey" << n << "_data\n"
+    "#define KEY_LENGTH prc_privkey" << n << "_length\n"
+    "#define PROGNAME \"" << outfile.get_basename_wo_extension() << "\"\n"
+    "#define GENERATED_TIME " << now << "\n\n"
+
+    "#include \"signPrcFile_src.cxx\"\n\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: usage
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void
+usage() {
+  cerr <<
+    "\nmake-prc-key [opts] 1 [2 3 ...]\n\n"
+
+    "This program generates one or more new private keys for signing\n"
+    "a prc file.  The key itself is a completely arbitrary random bit\n"
+    "sequence.  It is divided into a public and a private key; the public\n"
+    "key is not secret and will be compiled into Panda, while the private\n"
+    "key should be safeguarded and will be written into a .cxx file that\n"
+    "can be compiled as a standalone application.\n\n"
+
+    "The generated public keys are written to outfile.cxx, which can\n"
+    "then be named via the PRC_PUBLIC_KEYS_FILENAME Config.pp variable\n"
+    "so that they will be compiled into the config system and will be\n"
+    "available to verify signatures on prc files.  If -o is not\n"
+    "specified, the filename previously named by\n"
+    "PRC_PUBLIC_KEYS_FILENAME is used (and the previous contents as\n"
+    "compiled into this executable will be preserved).\n\n"
+    
+    "The private keys are written to one or more files named\n"
+    "outfile_sign1.cxx, outfile_sign2.cxx, etc., based on the key numbers\n"
+    "to be generated.  When compiled, these files will generate a program\n"
+    "that can be used to sign a prc file with the indicated key.\n\n"
+    
+    "The arguments on the command line list the individual key numbers to\n"
+    "generate.  For each integer specified, a different key will be\n"
+    "created.  There should be one key for each trust level required,\n"
+    "so typically you will only need one or two keys.\n\n"
+  
+    "Options:\n\n"
+    
+    "   -o outfile.cxx\n"
+    "       Specifies the name and location of the output file to generate.\n"
+    "       This directly specifies the name of the public key file, and\n"
+    "       also indirectly specifies the names of the private key programs\n"
+    "       that are to be generated (they will be named outfile_sign1.cxx,\n"
+    "       outfile_sign2.cxx, and so on).\n\n"
+
+    "   -p \"[pass phrase]\"\n"
+    "       Uses the indicated pass phrase to encrypt the private key.\n"
+    "       If this is not specified on the command line, you will be\n"
+    "       prompted interactively (and you may then specify a different\n"
+    "       pass phrase for each key).  Every user of the signing programs\n"
+    "       (outfile_sign1.cxx, etc.) will need to know the pass phrase\n"
+    "       in order to sign prc files.\n\n"
+
+    "       If this is specified as the empty string (\"\"), then the key\n"
+    "       will not be encrypted, and anyone can run the signing\n"
+    "       programs without having to supply a pass phrase.\n\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: main
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int
+main(int argc, char *argv[]) {
+  extern char *optarg;
+  extern int optind;
+  const char *optstr = "o:p:h";
+
+  Filename outfile;
+  bool got_outfile = false;
+  string pass_phrase;
+  bool got_pass_phrase = false;
+
+  int flag = getopt(argc, argv, optstr);
+
+  while (flag != EOF) {
+    switch (flag) {
+    case 'o':
+      outfile = optarg;
+      got_outfile = true;
+      break;
+
+    case 'p':
+      pass_phrase = optarg;
+      got_pass_phrase = true;
+      break;
+
+    case 'h':
+      usage();
+      exit(0);
+
+    default:
+      exit(1);
+    }
+    flag = getopt(argc, argv, optstr);
+  }
+
+  argc -= (optind-1);
+  argv += (optind-1);
+
+  if (argc < 2) {
+    usage();
+    exit(1);
+  }
+
+  if (got_outfile) {
+    if (outfile.get_extension() != "cxx") {
+      cerr << "Output file '" << outfile << "' should have a .cxx extension.\n";
+      exit(1);
+    }
+  } else {
+#ifdef PRC_PUBLIC_KEYS_INCLUDE
+    PrcKeyRegistry::get_global_ptr()->record_keys(prc_pubkeys, num_prc_pubkeys);
+    outfile = PRC_PUBLIC_KEYS_FILENAME;
+#endif
+
+    if (outfile.empty()) {
+      cerr << "No -o specified, and no PRC_PUBLIC_KEYS_FILENAME variable\n"
+           << "compiled in.\n\n";
+      exit(1);
+    }
+  }
+
+  int max_key_number = 0;
+
+  vector_int key_numbers;
+  for (int i = 1; i < argc; i++) {
+    char *endptr;
+    int number = strtol(argv[i], &endptr, 0);
+    if (*endptr) {
+      cerr << "Parameter '" << argv[i] << "' should be an integer.\n";
+      exit(1);
+    }
+    if (number <= 0) {
+      cerr << "Key numbers must be greater than 0; you specified " << number
+           << ".\n";
+      exit(1);
+    }
+    key_numbers.push_back(number);
+  }
+
+  // Seed the random number generator.
+  RAND_status();
+
+  // Load the OpenSSL algorithms.
+  OpenSSL_add_all_algorithms();
+
+  time_t now = time(NULL);
+
+  const char *pp = NULL;
+  if (got_pass_phrase) {
+    pp = pass_phrase.c_str();
+  }
+
+  vector_int::iterator ki;
+  for (ki = key_numbers.begin(); ki != key_numbers.end(); ++ki) {
+    int n = (*ki);
+    EVP_PKEY *pkey = generate_key();
+    PrcKeyRegistry::get_global_ptr()->set_key(n, pkey, now);
+
+    ostringstream strm;
+    strm << outfile.get_fullpath_wo_extension() << "_sign" << n
+         << ".cxx";
+
+    write_private_key(pkey, strm.str(), n, now, pp);
+  }
+
+  write_public_keys(outfile);
+
+  return (0);
+}

+ 369 - 0
dtool/src/prckeys/signPrcFile_src.cxx

@@ -0,0 +1,369 @@
+// Filename: signPrcFile_src.cxx
+// Created by:  drose (19Oct04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+// This file is intended to be #included by a generated *_sign?.cxx
+// file, one of the output files of make-prc-key.  This contains the
+// common code to sign a prc file with the given signature.
+
+#include "dtoolbase.h"
+
+#include "filename.h"
+#include "executionEnvironment.h"
+
+#include <time.h>
+
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+
+#ifndef HAVE_GETOPT
+  #include "gnu_getopt.h"
+#else
+  #ifdef HAVE_GETOPT_H
+    #include <getopt.h>
+  #endif
+#endif
+
+string progname = PROGNAME;
+
+////////////////////////////////////////////////////////////////////
+//     Function: output_ssl_errors
+//  Description: A convenience function that is itself a wrapper
+//               around the OpenSSL convenience function to output the
+//               recent OpenSSL errors.  This function sends the error
+//               string to cerr.
+////////////////////////////////////////////////////////////////////
+void
+output_ssl_errors() {
+  cerr << "Error occurred in SSL routines.\n";
+
+  static bool strings_loaded = false;
+  if (!strings_loaded) {
+    ERR_load_crypto_strings();
+    strings_loaded = true;
+  }
+
+  unsigned long e = ERR_get_error();
+  while (e != 0) {
+    static const size_t buffer_len = 256;
+    char buffer[buffer_len];
+    ERR_error_string_n(e, buffer, buffer_len);
+    cerr << buffer << "\n";
+    e = ERR_get_error();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: read_prc_line
+//  Description: Reads a single line of the prc file.
+////////////////////////////////////////////////////////////////////
+void
+read_prc_line(const string &line, string &data) {
+  // Strip out lines with this prefix.  These are from a previous
+  // signature.
+  if (line.substr(0, 3) == "##!") {
+    return;
+  }
+
+  // Other lines are counted.
+  data += line;
+  return;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: read_file
+//  Description: Reads the entire contents of the file, less any
+//               previous signatures, to the indicated string.
+////////////////////////////////////////////////////////////////////
+void
+read_file(istream &in, string &data) {
+  // We avoid getline() here because of its notorious problem with
+  // last lines that lack a trailing newline character.
+  static const size_t buffer_size = 1024;
+  char buffer[buffer_size];
+
+  string prev_line;
+
+  in.read(buffer, buffer_size);
+  size_t count = in.gcount();
+  while (count != 0) {
+    char *buffer_end = buffer + count;
+
+    // Look for the first line in the buffer..
+    char *newline = (char *)memchr((void *)buffer, '\n', count);
+    if (newline == NULL) {
+      // The buffer was one long line.  Huh.
+      prev_line += string(buffer, count);
+
+    } else {
+      // Process the first line in the buffer.
+      size_t length = newline - buffer;
+      read_prc_line(prev_line + string(buffer, length + 1), data);
+
+      // Now look for the next line, etc.
+      char *start = newline + 1;
+      newline = (char *)memchr((void *)start, '\n', buffer_end - start);
+      while (newline != NULL) {
+        length = newline - start;
+        read_prc_line(string(start, length + 1), data);
+        start = newline + 1;
+        newline = (char *)memchr((void *)start, '\n', buffer_end - start);
+      }
+
+      // The remaining text in the buffer is the start of the next
+      // line.
+      length = buffer_end - start;
+      prev_line = string(start, length);
+    }
+
+    in.read(buffer, buffer_size);
+    count = in.gcount();
+  }
+
+  if (!prev_line.empty()) {
+    read_prc_line(prev_line, data);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: output_hex
+//  Description: Outputs the indicated data stream as a series of hex
+//               digits.
+////////////////////////////////////////////////////////////////////
+void
+output_hex(ostream &out, const unsigned char *data, size_t size) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: sign_prc
+//  Description: Applies the signature to the named file.
+////////////////////////////////////////////////////////////////////
+void
+sign_prc(Filename filename, bool no_comments, EVP_PKEY *pkey) {
+  filename.set_text();
+
+  ifstream in;
+  if (!filename.open_read(in)) {
+    cerr << "Unable to read file " << filename << "\n";
+    exit(1);
+  }
+
+  string data;
+  read_file(in, data);
+  in.close();
+
+  // If the file didn't end with a newline, make it do so.
+  if (!data.empty() && data[data.length() - 1] != '\n') {
+    data += '\n';
+  }
+
+  // Append the comments before the signature (these get signed too).
+  ostringstream strm;
+  strm << "##!\n";
+  if (!no_comments) {
+    time_t now = time(NULL);
+    struct tm *t = localtime(&now);
+    char formatted[128];
+    strftime(formatted, 128, "%I:%M %p %B %d, %Y", t);
+
+    strm << "##! Signed on " << formatted;
+    if (ExecutionEnvironment::has_environment_variable("USER")) {
+      strm << " by " << ExecutionEnvironment::get_environment_variable("USER");
+    }
+
+    time_t generated_time = GENERATED_TIME;
+    t = localtime(&generated_time);
+    strftime(formatted, 128, "%I:%M %p %B %d, %Y", t);
+    
+    strm << "\n"
+         << "##! Signed with level " << KEY_NUMBER << " key generated on "
+         << formatted << "\n"
+         << "##!\n";
+  }
+  data += strm.str();
+
+  EVP_MD_CTX *md_ctx;
+
+#ifdef SSL_097
+  md_ctx = EVP_MD_CTX_create();
+#else
+  md_ctx = new EVP_MD_CTX;
+#endif
+
+  EVP_SignInit(md_ctx, EVP_sha1());
+  EVP_SignUpdate(md_ctx, data.data(), data.size());
+
+  unsigned int max_size = EVP_PKEY_size(pkey);
+  unsigned char *sig_data = new unsigned char[max_size];
+  unsigned int sig_size;
+  if (!EVP_SignFinal(md_ctx, sig_data, &sig_size, pkey)) {
+    output_ssl_errors();
+    exit(1);
+  }
+  assert(sig_size <= max_size);
+
+#ifdef SSL_097
+  EVP_MD_CTX_destroy(md_ctx);
+#else
+  delete md_ctx;
+#endif
+
+  // Now open the file in write mode and rewrite it with the new
+  // signature.
+  ofstream out;
+  if (!filename.open_write(out)) {
+    cerr << "Unable to rewrite file " << filename << "\n";
+    exit(1);
+  }
+  cerr << "Rewriting " << filename << "\n";
+
+  out << data << hex << setfill('0');
+  static const size_t row_width = 32;
+  for (size_t p = 0; p < sig_size; p += row_width) {
+    out << "##!sig ";
+    size_t end = min(sig_size, p + row_width);
+    for (size_t q = p; q < end; q++) {
+      out << setw(2) << (unsigned int)sig_data[q];
+    }
+    out << "\n";
+  }
+  out << dec;
+
+  delete[] sig_data;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: usage
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void
+usage() {
+  time_t generated_time = GENERATED_TIME;
+  struct tm *t = localtime(&generated_time);
+  char formatted[128];
+  strftime(formatted, 128, "%I:%M %p %B %d, %Y", t);
+
+  cerr <<
+    "\n" << progname << " [opts] file.prc [file.prc ...]\n\n"
+
+    "This program will sign the named prc file(s) with the trust level " << KEY_NUMBER << "\n"
+    "key that was generated on " << formatted << ".  This is necessary\n"
+    "for any prc file that defines one or more config variables that require a\n"
+    "trust level of " << KEY_NUMBER << ".\n\n"
+
+    "Once the prc file has been signed, it may not be changed without invalidating\n"
+    "the signature; if you change the file, you will have to sign it again.\n\n"
+
+    "Each prc file is modified in-place with the new signature.  Any previous\n"
+    "signatures in the file are removed.\n\n"
+
+    "Options:\n\n"
+
+    "   -n  Avoids writing a comment to the file that describes the date and\n"
+    "       time the signature was applied.\n\n"
+
+    "   -p \"[pass phrase]\"\n"
+    "       Uses the indicated pass phrase to decrypt the private key.\n"
+    "       If this is not specified on the command line, you will be\n"
+    "       prompted interactively.\n\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: main
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int
+main(int argc, char *argv[]) {
+  if (argv[0] != NULL && *argv[0]) {
+    // Get the program name from the command-line arguments, if the OS
+    // provides it.
+    Filename progfile = argv[0];
+    progname = progfile.get_basename_wo_extension();
+  }
+
+  extern char *optarg;
+  extern int optind;
+  const char *optstr = "np:h";
+
+  bool no_comments = false;
+  string pass_phrase;
+  bool got_pass_phrase = false;
+
+  int flag = getopt(argc, argv, optstr);
+
+  while (flag != EOF) {
+    switch (flag) {
+    case 'n':
+      no_comments = true;
+      break;
+
+    case 'p':
+      pass_phrase = optarg;
+      got_pass_phrase = true;
+      break;
+
+    case 'h':
+      usage();
+      exit(0);
+
+    default:
+      exit(1);
+    }
+    flag = getopt(argc, argv, optstr);
+  }
+
+  argc -= (optind-1);
+  argv += (optind-1);
+
+  if (argc < 2) {
+    usage();
+    exit(1);
+  }
+
+  // Seed the random number generator.
+  RAND_status();
+
+  // Load the OpenSSL algorithms.
+  OpenSSL_add_all_algorithms();
+
+  // Convert the compiled-in data to an EVP_PKEY.
+  const char *pp = NULL;
+  if (got_pass_phrase) {
+    pp = pass_phrase.c_str();
+  }
+
+  BIO *mbio = BIO_new_mem_buf((void *)KEY_DATA, KEY_LENGTH);
+  EVP_PKEY *pkey = PEM_read_bio_PrivateKey(mbio, NULL, NULL, (void *)pp);
+  BIO_free(mbio);
+
+  if (pkey == (EVP_PKEY *)NULL) {
+    // Actually, we're not 100% sure this was the problem, but we
+    // can't really tell why it failed, and we're 99% sure anyway.
+    cerr << "Invalid pass phrase.\n";
+    exit(1);
+  }
+
+  for (int i = 1; i < argc; i++) {
+    sign_prc(argv[1], no_comments, pkey);
+  }
+
+  return (0);
+}

+ 11 - 0
panda/src/express/config_express.N

@@ -1,5 +1,6 @@
 forcetype DSearchPath
 forcetype DSearchPath
 forcetype Filename
 forcetype Filename
+forcetype GlobPattern
 forcetype Notify
 forcetype Notify
 forcetype NotifyCategory
 forcetype NotifyCategory
 forcetype NotifySeverity
 forcetype NotifySeverity
@@ -7,6 +8,16 @@ forcetype NotifySeverity
 forcetype ConfigExpress
 forcetype ConfigExpress
 renametype ConfigExpress ConfigExpress
 renametype ConfigExpress ConfigExpress
 
 
+forcetype ConfigPage
+forcetype ConfigPageManager
+forcetype ConfigVariable
+forcetype ConfigVariableBool
+forcetype ConfigVariableDouble
+forcetype ConfigVariableInt
+forcetype ConfigVariableList
+forcetype ConfigVariableManager
+forcetype ConfigVariableString
+
 forcetype istream
 forcetype istream
 forcetype ostream
 forcetype ostream
 forcetype iostream
 forcetype iostream

+ 13 - 0
panda/src/express/config_express.h

@@ -24,6 +24,19 @@
 #include "clockObject.h"
 #include "clockObject.h"
 #include "dconfig.h"
 #include "dconfig.h"
 
 
+// We include these files to force them to be instrumented by
+// interrogate.
+#include "globPattern.h"
+#include "configPage.h"
+#include "configPageManager.h"
+#include "configVariable.h"
+#include "configVariableBool.h"
+#include "configVariableDouble.h"
+#include "configVariableInt.h"
+#include "configVariableList.h"
+#include "configVariableManager.h"
+#include "configVariableString.h"
+
 ConfigureDecl(config_express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
 ConfigureDecl(config_express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
 NotifyCategoryDecl(express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
 NotifyCategoryDecl(express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
 NotifyCategoryDecl(thread, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
 NotifyCategoryDecl(thread, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);