]> git.kernelconcepts.de Git - karo-tx-redboot.git/blobdiff - tools/src/libcdl/cdlcore.hxx
Initial revision
[karo-tx-redboot.git] / tools / src / libcdl / cdlcore.hxx
diff --git a/tools/src/libcdl/cdlcore.hxx b/tools/src/libcdl/cdlcore.hxx
new file mode 100644 (file)
index 0000000..c64e2c3
--- /dev/null
@@ -0,0 +1,5761 @@
+#ifndef __CDLCORE_HXX
+# define __CDLCORE_HXX
+
+//{{{  Banner                                           
+
+//==========================================================================
+//
+//      cdlcore.hxx
+//
+//      The core parts of the library. This header defines aspects of
+//      CDL that are shared between software cdl, hcdl, scdl, and any
+//      future languages based on the same core technology.
+//
+//==========================================================================
+//####COPYRIGHTBEGIN####
+//                                                                          
+// ----------------------------------------------------------------------------
+// Copyright (C) 2002 Bart Veer
+// Copyright (C) 1999, 2000, 2001 Red Hat, Inc.
+//
+// This file is part of the eCos host tools.
+//
+// This program is free software; you can redistribute it and/or modify it 
+// under the terms of the GNU General Public License as published by the Free 
+// Software Foundation; either version 2 of the License, or (at your option) 
+// any later version.
+// 
+// This program is distributed in the hope that it will be useful, but WITHOUT 
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+// more details.
+// 
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// ----------------------------------------------------------------------------
+//                                                                          
+//####COPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####                                             
+//
+// Author(s):           bartv
+// Contributors:        bartv
+// Date:                1999-04-15
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+//}}}
+//{{{  Platform dependencies                            
+
+// ----------------------------------------------------------------------------
+// Visual C++ has the delightful feature that the source browser will generate
+// warnings if there are any identifiers of length >= 256 characters, while at
+// the same time use of templates in the standard C++ library can easily
+// generate functions that long. It appears that the only way to disable the
+// warnings is by use of a %$#@(%*&%! #pragma.
+//
+// Similarly, VC++ gives spurious warnings when it comes to multiple virtual
+// inheritance.
+#ifdef _MSC_VER
+# pragma warning( disable: 4786 )
+# pragma warning( disable: 4250 )
+#endif
+
+//}}}
+//{{{  nested #include's                                
+
+// ----------------------------------------------------------------------------
+// The libcdl API is defined using parts of the standard C++ library,
+// including strings and various bits of STL. Therefore these headers must
+// be #include'd here for the header file to work.
+#include <vector>
+#include <list>
+#include <map>
+#include <set>
+#include <deque>
+#include <string>
+#include <functional>
+#include <algorithm>
+
+// <cctype> is needed in various places in the implementation.
+// This #include should be moved to an implementation-specific
+// header.
+#include <cctype>
+
+// Now for some eCos host-side infrastructure headers.
+//
+// Get the cyg_int64 data type and CYG_UNUSED_PARAM() macro.
+#include <cyg/infra/cyg_type.h>
+
+// Some of the classes need to reference the cyg_assert_class_zeal enum.
+// Also inline functions may perform assertions.
+#include <cyg/infra/cyg_ass.h>
+
+// This header file also depends on having a suitable Tcl installation
+// Unfortunately <tcl.h> does some ugly things in the interests of
+// portability, including defining symbols such as EXTERN when
+// necessary, and this has to be patched up here as cleanly as possible.
+#ifndef CONST
+# define __CDL_CONST_UNDEFINED
+#endif
+#ifndef EXTERN
+# define __CDL_EXTERN_UNDEFINED
+#endif
+#ifndef VOID
+# define __CDL_VOID_UNDEFINED
+#endif
+#ifndef CHAR
+# define __CDL_CHAR_UNDEFINED
+#endif
+#ifndef SHORT
+# define __CDL_SHORT_UNDEFINED
+#endif
+#ifndef LONG
+# define __CDL_LONG_UNDEFINED
+#endif
+
+extern "C" {
+#include <tcl.h>
+}
+
+#ifdef __CDL_CONST_UNDEFINED
+# undef CONST
+# undef __CDL_CONST_UNDEFINED
+#endif
+#ifdef __CDL_EXTERN_UNDEFINED
+# undef EXTERN
+# undef __CDL_EXTERN_UNDEFINED
+#endif
+#ifdef __CDL_VOID_UNDEFINED
+# undef VOID
+# undef __CDL_VOID_UNDEFINED
+#endif
+#ifdef __CDL_CHAR_UNDEFINED
+# undef CHAR
+# undef __CDL_CHAR_UNDEFINED
+#endif
+#ifdef __CDL_SHORT_UNDEFINED
+# undef SHORT
+# undef __CDL_SHORT_UNDEFINED
+#endif
+#ifdef __CDL_LONG_UNDEFINED
+# undef LONG
+# undef __CDL_LONG_UNDEFINED
+#endif
+
+//}}}
+
+//{{{  Primitive types, constants:, enums, etc.         
+
+// ----------------------------------------------------------------------------
+// The CDL languages are defined in terms of arbitrary precision
+// arithmetic. This is necessary to allow e.g. pointers to be
+// manipulated at the CDL level on 64 bit target processors.
+//
+// Temporarily it is not necessary to provide this precision, so it is
+// convenient to stick to 64 bit integers as provided by the
+// underlying infrastructure. However the API is defined in terms of
+// the type cdl_int, so that it will be easier in future to make the
+// change to the correct datatype. At that point cdl_int can be
+// redefined to be a class which supports the appropriate operators.
+
+typedef cyg_int64 cdl_int;
+
+// ---------------------------------------------------------------------------
+// A common concept in the CDL language is a small amount of TCL code.
+// This is currently stored as a simple string. Conceivably it could
+// be byte-compiled and stored accordingly.
+
+typedef std::string  cdl_tcl_code;
+
+// ----------------------------------------------------------------------------
+// CDL values.
+//
+// CDL is a declarative programming language. It does involve the
+// manipulation of values, but such values do not necessarily
+// correspond to hardware-level entities such as integers or double
+// precision numbers. Hence the term "type" is avoided, "flavor"
+// is used instead. CDL understands four different flavors.
+//
+//    None  |  Bool
+//  --------+--------
+//    Data  |BoolData
+//
+//
+// The flavor "none" is used for entities that serve only as
+// placeholders in the hierarchy, allowing other entities to be
+// grouped more easily.
+//
+// Boolean entities can be either enabled or disabled. This is the
+// most common flavor for software configuration options, the user can
+// either enable or disable some unit of functionality. For software
+// packages implemented in C or C++ the implementation is obvious: iff
+// the entity is enabled then there will be a #define, and code will
+// check the setting using e.g. #ifdef.
+//
+// The flavor "data" implies some arbitrary data. Internally this will
+// be held as a string. Other properties such as legal_values,
+// check_proc and entry_proc can be used to constrain the
+// actual values, for example to an integer value within a certain
+// range.
+//
+// The flavor "booldata" combines the previous two: it means that
+// the option can be either enabled or disabled, and if it is
+// enabled then it must have a value as per legal_values etc.
+// One example of this is a software package: this may be either
+// enabled or disabled, and if it is enabled then it has a value
+// corresponding to the version string. Another example is a hardware
+// pin: this may or may not be connected, and if it is connected
+// then its value identifies some other pin.
+//
+// An entity's flavor is not always sufficient by itself to specify
+// how the user can manipulate it in a graphical tool. Obviously an
+// entity of flavor "none" cannot be manipulated at all. Flavor "bool"
+// normally implies a checkbutton, but occasionally a radiobutton will
+// be more appropriate. "Data" says very little about the user
+// interaction, it will be necessary to examine other properties such
+// as legal_values to determine a sensible representation. The same
+// goes for "BoolData", with the additional possibility that the
+// entity may be disabled.
+//
+// It can be argued that three of the flavors are redundant: both Bool
+// and BoolData could be implemented as cases of "Data" with a special
+// legal value "disabled" (or false, or whatever); "None" could be
+// implemented as constant "Data"; effectively CDL would manipulate
+// all data as strings, just like e.g. all variables in Tcl, or just
+// like all scalars in Perl. This approach is certainly tempting and
+// might well make it easier to document the language, but in practice
+// it would result in more verbose CDL: boolean entities really are a
+// different beast from data entities.
+//
+// It can also be argued that there should be more flavors. For
+// example there could be separate flavors for integer data, floating
+// point data, string data, and so on. There are a number of good
+// reasons for not doing so:
+//
+// 1) applying separate constraints such as legal_values allows much
+//    finer control over the actual values, for example numbers within a
+//    given range. As likely as not, a value will be constrained to
+//    something smaller than the range MININT to MAXINT (whatever those
+//    happen to be for the current target).
+//
+// 2) where do you stop? Do you provide separate flavors for signed
+//    vs. unsigned? Char, wchar_t, short, int, long, long long? How about
+//    the eCos data types cyg_ucount8, cyg_uint8, ... Is there support
+//    for enums? Arrays? Bitfields? Structures? Unions? C++ classes?
+//    How about other programming languages such as Ada or Java?
+//
+//    Any attempt to implement a grand union of all data types in CDL 
+//    is doomed to failure and should not be attempted. Treating
+//    everything as a string instead has proven successful in a number
+//    of languages, including Tcl and Perl.
+//
+// 3) for some variants of CDL, for example hardware CDL, it may not
+//    make much sense to display a value directly and allow it to be
+//    manipulated directly. The value associated with a pin entity
+//    identifies the pin to which it is connected, and typically
+//    this value will be manipulated by drag and drop rather than by
+//    typing some characters. Such a value certainly does not correspond
+//    to any machine data type.
+//
+// Another reason for extending the number of flavors is to provide
+// more information. For example there could be a specialized version
+// of the boolean flavor called "radio". This would imply a specific
+// representation in the user interface, and it would also impose
+// a constraint that it implicitly precludes any other radio entities
+// within the same group. However the same information can be specified
+// by other more general means such as requires statements.
+
+enum CdlValueFlavor {
+    CdlValueFlavor_Invalid      =  0,
+    CdlValueFlavor_None         =  1,
+    CdlValueFlavor_Bool         =  2,
+    CdlValueFlavor_BoolData     =  3,
+    CdlValueFlavor_Data         =  4
+};
+
+
+// Another important aspect of a value is where it came from. There
+// are a number of possible sources: the default value, calculated
+// from a default_value property; a value inferred by the inference
+// engine; a value set by a wizard; and a value set explicitly by
+// the user. These sources have different priorities, so for example
+// the inference engine can safely replace a calculated default
+// value without prompting the user, but changing a user-set value
+// automatically is undesirable.
+//
+// Wizard-generated values are considered more valuable than default
+// or inferred values (there is some user input involved), but less
+// valuable than values set explicitly by the user: the idea is that
+// a wizard asks fairly generic questions and makes a best guess at
+// the correct values, which may not be precise enough for the
+// user's needs.
+//
+// Arguably dialogs provide a level between wizards and users, in that
+// a dialog can theoretically manipulate several entities in one go so
+// it is a less precise way of setting values. At this stage it does
+// not seem worthwhile to add this distinction.
+//
+// The library actually maintains separate values for each source,
+// as well as the current source which is what actually gets used.
+// In theory it is possible for the user interface code to let
+// the user switch between these. It is not yet clear whether this
+// makes sense from an end user's perspective.
+
+enum CdlValueSource {
+    CdlValueSource_Invalid              = -1, // 0 is needed for array indexing
+    CdlValueSource_Default              =  0,
+    CdlValueSource_Inferred             =  1,
+    CdlValueSource_Wizard               =  2,
+    CdlValueSource_User                 =  3,
+    CdlValueSource_Current              =  4
+};        
+
+// ----------------------------------------------------------------------------
+// Update support.
+//
+// When there is a change to a node and there are references to that node,
+// the referencing properties will want to be informed about this. There
+// are various different kinds of changes, not all of which are always
+// relevant. For example, if a CDL entity gets destroyed or unloaded then
+// all referencing entities are likely to want to know about this, but
+// if a container's value changes then this has no effect on a reference
+// in e.g. a "parent" property. In some cases it is also useful to apply
+// updates to nodes rather than properties, e.g. when a node becomes
+// active or inactive.
+//
+// The generic update code is also used for initialization and finalization,
+// i.e. when the source object itself has just been loaded or is
+// being unloaded.
+//
+// For any particular update at most one bit set, but it is often
+// appropriate to treat several different kinds of update with
+// common code. Hence the enum values can be or'ed and and'ed.
+
+enum CdlUpdate {
+    CdlUpdate_Loaded            = 0x0001,       // The source has just been loaded
+    CdlUpdate_Init              = 0x0002,       // Second-phase of a load operation
+    CdlUpdate_Unloading         = 0x0004,       // The source is being unloaded
+    CdlUpdate_Created           = 0x0008,       // The destination has just been created
+    CdlUpdate_Destroyed         = 0x0010,       // The destination is being destroyed
+    CdlUpdate_ValueChange       = 0x0020,       // The destination's value has changed.
+                                                // This gets applied to nodes as well                   
+    CdlUpdate_ActiveChange      = 0x0040        // The node has become active or inactive
+};
+
+// ----------------------------------------------------------------------------
+// Inference engine callback.
+//
+// During a transaction there may be one or more invocations of the inference
+// engine, followed by a callback which should display the current transaction
+// status to the user and allow one or more recommended fixes to be accepted.
+// The callback's return code indicates what should happen next. "Cancel"
+// is pretty obvious. "Continue" may result in a commit, or it may result in
+// another iteration.
+
+enum CdlInferenceCallbackResult {
+    CdlInferenceCallbackResult_Continue = 0x01,
+    CdlInferenceCallbackResult_Cancel   = 0x02
+};
+
+// ----------------------------------------------------------------------------
+// Widget hints.
+//
+// The library can provide a hint to the GUI code as to a sensible
+// widget to use for displaying a particular valuable. There are separate
+// hints for the bool and data parts.
+
+enum CdlBoolWidget {
+    CdlBoolWidget_None                  = 0,    // The boolean part is not applicable
+    CdlBoolWidget_CustomDialog          = 1,    // There is a valid custom dialog property
+    CdlBoolWidget_CheckButton           = 2,    // For simple booleans
+    CdlBoolWidget_Radio                 = 3,    // For several mutual exclusive options,
+                                                // the data structure will provide a string identifier
+};
+
+enum CdlValueWidget {
+    CdlValueWidget_None                 = 0,    // The value part is not applicable
+    CdlValueWidget_CustomDialog         = 1,    // There is a valid custom dialog property
+    CdlValueWidget_Loadable             = 2,    // Use package/version dialog
+    CdlValueWidget_EntryBox             = 3,    // Fallback
+    CdlValueWidget_MultilineString      = 4,    // For complicated strings
+    CdlValueWidget_DecimalRange         = 5,    // e.g. 1 to 16
+                                                // Could be implemented as scale, radio buttons, entry, pull-down menu,
+                                                // combo box, ... depending on GUI conventions and number of entries
+    CdlValueWidget_HexRange             = 6,    // e.g. 0x01 to 0x10
+    CdlValueWidget_OctalRange           = 7,    // e.g. 01 to 020
+    CdlValueWidget_DoubleRange          = 8,    // e.g. 0.1 to 0.2
+    CdlValueWidget_NumericSet           = 9,    // e.g. 1 2 4 8 16
+                                                // The exact nature of the numbers is irrelevant, they will only
+                                                // get displayed, not edited
+                                                // Could be implemented as radio buttons, entry widget, pull-down menu,
+                                                // combo box, ... depending on GUI conventions and number of entries
+                                                // Each entry can have its own representation
+    CdlValueWidget_StringSet            = 10    // e.g. "ram", "rom"
+
+    // More to be added, e.g. for compiler flag handling
+};
+
+// ----------------------------------------------------------------------------
+// Value formats.
+//
+// The CDL input data can accept numbers in a variety of formats,
+// for example hexadecimal as well as decimal. It is desirable to try
+// to keep track of this formatting information where possible, so
+// that what the user sees and what ends up in header files corresponds
+// more closely to what is in the raw CDL data. For example, it is
+// much easier to understand 0x7fffffff than its decimal equivalent.
+//
+// The information kept here is very imprecise, it provides only
+// minimal formatting information. It is not clear yet whether this
+// will suffice or whether something more exact is going to be needed.
+enum CdlValueFormat
+{
+    CdlValueFormat_Default              = 0,
+    CdlValueFormat_Hex                  = 1,
+    CdlValueFormat_Octal                = 2
+};
+
+//}}}
+//{{{  Exception classes                                
+
+// ----------------------------------------------------------------------------
+// Some parts of the library make use of C++ exception handling. A number
+// of exception classes related to this library are useful. In addition
+// just about every part of the library can throw std::bad_alloc, but this
+// is not checked for explicitly anywhere.
+
+// This class is used for all exceptions where an error message should
+// be displayed to the user. There is a single string message associated
+// with the exception.
+
+class CdlStringException {
+    friend class CdlTest;
+
+  public:
+    CdlStringException(std::string message_arg) {
+        message = message_arg;
+    }
+    CdlStringException(const CdlStringException& original) {
+        message = original.message;
+    }
+    CdlStringException& operator=(const CdlStringException& original) {
+        message = original.message;
+        return *this;
+    }
+    ~CdlStringException() {
+        message = "";
+    }
+    const std::string& get_message() const {
+        return message;
+    }
+  private:
+    std::string message;
+    CdlStringException();
+};
+
+// CdlInputOutputException: this gets thrown when something goes wrong during
+// file I/O operations, e.g. a file exists but cannot be opened. The
+// exception contains a simple string explaining the error. This string
+// may contain multiple lines, it is intended to be written to stderr
+// or displayed in either a text widget or a dialog box.
+//
+// A separate class rather than a typedef is used to avoid any possible
+// error message confusion. Everything gets inlined so there should be
+// no performance issues.
+
+class CdlInputOutputException : public CdlStringException {
+    friend class CdlTest;
+  public:
+    CdlInputOutputException(std::string message_arg) :
+        CdlStringException(message_arg) {
+    }
+    CdlInputOutputException(const CdlInputOutputException& original) :
+        CdlStringException(original) {
+    }
+    CdlInputOutputException& operator=(const CdlInputOutputException& original) {
+        (void) CdlStringException::operator=(original);
+        return *this;
+    }
+};
+
+// This class is used when any parsing happens at the C++ level rather
+// than at the Tcl level. The exception should be caught before it
+// propagates through the Tcl interpreter, or the latter will end up
+// in an inconsistent state.
+
+class CdlParseException : public CdlStringException {
+    friend class CdlTest;
+  public:
+    CdlParseException(std::string message_arg) :
+        CdlStringException(message_arg) {
+    }
+    CdlParseException(const CdlParseException& original) :
+        CdlStringException(original) {
+    }
+    CdlParseException& operator=(const CdlParseException& original) {
+        (void) CdlStringException::operator=(original);
+        return *this;
+    }
+};
+
+// Evaluating an expression may fail for a variety of reasons, e.g. because
+// some referenced entity has not been loaded into the configuration.
+// This exception can be thrown in such cases.
+
+class CdlEvalException : public CdlStringException {
+    friend class CdlTest;
+  public:
+    CdlEvalException(std::string message_arg) :
+        CdlStringException(message_arg) {
+    }
+    CdlEvalException(const CdlEvalException& original) :
+        CdlStringException(original) {
+    }
+    CdlEvalException& operator=(const CdlEvalException& original) {
+        (void) CdlStringException::operator=(original);
+        return *this;
+    }
+};
+
+//}}}
+//{{{  Forward declarations of the body classes         
+
+// ----------------------------------------------------------------------------
+// This section provides forward declarations of the main classes in
+// the core of the library. Each variant of CDL will define additional
+// classes, e.g. cdl_option, but these will usually be derived from
+// the core ones.
+
+// There are three types of expression in CDL:
+// 1) ordinary expressions evaluate to a single value. The most common
+//    use is for the legal_values property.
+// 2) list expressions evaluate to a range of values, e.g. 1 to 10,
+//    and the most common use is for the legal_values property.
+// 3) goal expressions evaluate to either true or false and are used
+//    for e.g. requires and active_if properties.
+class CdlExpressionBody;
+class CdlListExpressionBody;
+class CdlGoalExpressionBody;
+
+// There are also objects for simple values, values and list values.
+// These are expanded classes, there are no associated pointer
+// types. It is quite likely that values need to be copied around
+// on the stack.
+class CdlSimpleValue;
+class CdlValue;
+class CdlListValue;
+
+// Properties. The base class is CdlProperty, and there are a number
+// of derived classes provided as standard. Additional derived classes
+// may be added in future.
+class CdlPropertyBody;
+class CdlProperty_MinimalBody;
+class CdlProperty_StringBody;
+class CdlProperty_TclCodeBody;
+class CdlProperty_ReferenceBody;
+class CdlProperty_StringVectorBody;
+class CdlProperty_ExpressionBody;
+class CdlProperty_ListExpressionBody;
+class CdlProperty_GoalExpressionBody;
+
+// Base classes. CDL entities such as options and components derive
+// from one or more of these, using virtual inheritance.
+// 
+// The lowest-level class is CdlNodeBody.
+//
+// 1) a node usually lives in a hierarchy, below a toplevel
+//    and with a container object as the parent. However nodes
+//    can live outside a container on a temporary basis,
+//    and toplevel objects have no parent.
+//
+// 2) a node has a name that is unique within the hierarchy.
+//
+// 3) a node has a vector of properties. Actually some entities
+//    will have an empty vector, e.g. the orphans container
+//    that is internal to the library. However it is too
+//    inconvenient to have separate base classes for these.
+//
+// 4) nodes can be referred to by properties in other nodes.
+class CdlNodeBody;
+
+// A container is a node that can contain other nodes.
+class CdlContainerBody;
+
+// A loadable object is a container whose data has come out of a CDL
+// script of some sort. It also stores details about all entities that
+// were loaded via this script (even if some of them were reparented)
+// thus supporting unload operations.
+class CdlLoadableBody;
+
+// A toplevel object is a container that acts as the toplevel of
+// a hierarchy, in other words its parent is always 0. In addition
+// a toplevel keeps track of all the names used in the hierarchy,
+// thus facilitating navigation.
+class CdlToplevelBody;
+
+// The remaining classes all add functionality to CdlNode, directly or
+// indirectly.
+//
+// A user-visible object is likely to appear in the user interface.
+// This means it may have an alias string, a description, a
+// documentation URL, and a gui_hint field.
+class CdlUserVisibleBody;
+
+// A valuable object has a value that can be retrieved but not
+// necessarily modified by the user. For example the value of an
+// interface is always calculated and users can never change it.
+// Valuable objects have a whole bunch of associated properties
+// including dependencies.
+class CdlValuableBody;
+
+// A parentable object has the parent property, i.e. it can
+// be reparented to anywhere in the hierarchy
+class CdlParentableBody;
+
+// A buildable object is a valuable object that may result in
+// something being built, typically a library in the case of
+// software packages.
+class CdlBuildableBody;
+
+// A loadable that contains buildables
+class CdlBuildLoadableBody;
+
+// A definable object is a valuable object whose value can result
+// in #define statements in a header file
+class CdlDefinableBody;
+
+// A loadable which can contain definables
+class CdlDefineLoadableBody;
+
+// TODO: add instantiation support
+
+// Custom dialogs and wizards are provided by the core.
+class CdlDialogBody;
+class CdlWizardBody;
+class CdlInterfaceBody;
+
+// Support for Tcl interpreters is also in the core, since it is
+// difficult to do anything CDL-related without at least one Tcl
+// interpreter lying around.
+class CdlInterpreterBody;
+
+// The basic conflict class is part of the core library, as are a
+// number of common derived classes for specific types of conflict.
+class CdlConflictBody;
+class CdlConflict_UnresolvedBody;
+class CdlConflict_IllegalValueBody;
+class CdlConflict_EvalExceptionBody;
+class CdlConflict_RequiresBody;
+class CdlConflict_DataBody;
+
+// Many operations happen (or may happen) in the context of a
+// transaction. This is necessary to keep track of the various
+// changes that can happen: for example, changing a component's
+// value may require other entities' default values to be
+// recalculated; it may change some legal_values list expressions,
+// causing current values to become invalid; it may affect
+// "requires" properties, causing goals to become satisfied or
+// not-satisfied; it may change the "active" state of everything
+// below the component, not to mention any entity with an
+// "active_if" properties, and when an entity becomes active or
+// inactive that may in turn affect other entities.
+//
+// Keeping track of all of this via recursion is possible, but there
+// are problems. If an entity is updated multiple times, no
+// optimizations are possible. It becomes much more difficult to
+// detect cycles. During an unload operation things can get very
+// messy. There is no easy way to track all of the changes and report
+// them to higher level code via a callback. There is no support
+// for any kind of rollback. A transaction model potentially
+// provides support for all of this, at the cost of a more
+// complex API.
+class CdlTransactionBody;
+
+// This class is used to pass information back to the application
+// about what has actually changed in a transaction.
+class CdlTransactionCallback;
+
+
+// Build info class. This is always an expanded object, but is
+// needed here to break a circular dependency.
+class CdlBuildInfo;
+
+// ----------------------------------------------------------------------------
+// Typedefs for the pointers. There are separate typedefs to cope with
+// const vs. non-const objects. Otherwise you end up with the problem
+// that "const CdlNode x" means that the pointer is const, not the
+// object pointed at.
+
+typedef CdlExpressionBody*              CdlExpression;
+typedef CdlListExpressionBody*          CdlListExpression;
+typedef CdlGoalExpressionBody*          CdlGoalExpression;
+
+typedef CdlPropertyBody*                CdlProperty;
+typedef CdlProperty_MinimalBody*        CdlProperty_Minimal;
+typedef CdlProperty_StringBody*         CdlProperty_String;
+typedef CdlProperty_TclCodeBody*        CdlProperty_TclCode;
+typedef CdlProperty_ReferenceBody*      CdlProperty_Reference;
+typedef CdlProperty_StringVectorBody*   CdlProperty_StringVector;
+typedef CdlProperty_ExpressionBody*     CdlProperty_Expression;
+typedef CdlProperty_ListExpressionBody* CdlProperty_ListExpression;
+typedef CdlProperty_GoalExpressionBody* CdlProperty_GoalExpression;
+
+typedef CdlNodeBody*                    CdlNode;
+typedef CdlContainerBody*               CdlContainer;
+typedef CdlLoadableBody*                CdlLoadable;
+typedef CdlToplevelBody*                CdlToplevel;
+typedef CdlUserVisibleBody*             CdlUserVisible;
+typedef CdlValuableBody*                CdlValuable;
+typedef CdlParentableBody*              CdlParentable;
+typedef CdlBuildableBody*               CdlBuildable;
+typedef CdlBuildLoadableBody*           CdlBuildLoadable;
+typedef CdlDefinableBody*               CdlDefinable;
+typedef CdlDefineLoadableBody*          CdlDefineLoadable;
+
+typedef CdlDialogBody*                  CdlDialog;
+typedef CdlWizardBody*                  CdlWizard;
+typedef CdlInterfaceBody*               CdlInterface;
+
+typedef CdlInterpreterBody*             CdlInterpreter;
+
+typedef CdlConflictBody*                CdlConflict;
+typedef CdlConflict_UnresolvedBody*     CdlConflict_Unresolved;
+typedef CdlConflict_IllegalValueBody*   CdlConflict_IllegalValue;
+typedef CdlConflict_EvalExceptionBody*  CdlConflict_EvalException;
+typedef CdlConflict_RequiresBody*       CdlConflict_Requires;
+typedef CdlConflict_DataBody*           CdlConflict_Data;
+
+typedef CdlTransactionBody*             CdlTransaction;
+
+// ----------------------------------------------------------------------------
+
+typedef const CdlExpressionBody*              CdlConstExpression;
+typedef const CdlListExpressionBody*          CdlConstListExpression;
+typedef const CdlGoalExpressionBody*          CdlConstGoalExpression;
+
+typedef const CdlPropertyBody*                CdlConstProperty;
+typedef const CdlProperty_MinimalBody*        CdlConstProperty_Minimal;
+typedef const CdlProperty_StringBody*         CdlConstProperty_String;
+typedef const CdlProperty_TclCodeBody*        CdlConstProperty_TclCode;
+typedef const CdlProperty_ReferenceBody*      CdlConstProperty_Reference;
+typedef const CdlProperty_StringVectorBody*   CdlConstProperty_StringVector;
+typedef const CdlProperty_ExpressionBody*     CdlConstProperty_Expression;
+typedef const CdlProperty_ListExpressionBody* CdlConstProperty_ListExpression;
+typedef const CdlProperty_GoalExpressionBody* CdlConstProperty_GoalExpression;
+
+typedef const CdlNodeBody*                    CdlConstNode;
+typedef const CdlContainerBody*               CdlConstContainer;
+typedef const CdlLoadableBody*                CdlConstLoadable;
+typedef const CdlToplevelBody*                CdlConstToplevel;
+typedef const CdlUserVisibleBody*             CdlConstUserVisible;
+typedef const CdlValuableBody*                CdlConstValuable;
+typedef const CdlParentableBody*              CdlConstParentable;
+typedef const CdlBuildableBody*               CdlConstBuildable;
+typedef const CdlBuildLoadableBody*           CdlConstBuildLoadable;
+typedef const CdlDefinableBody*               CdlConstDefinable;
+typedef const CdlDefineLoadableBody*          CdlConstDefineLoadable;
+
+typedef const CdlDialogBody*                  CdlConstDialog;
+typedef const CdlWizardBody*                  CdlConstWizard;
+typedef const CdlInterfaceBody*               CdlConstInterface;
+
+typedef const CdlInterpreterBody*             CdlConstInterpreter;
+
+typedef const CdlConflictBody*                CdlConstConflict;
+typedef const CdlConflict_UnresolvedBody*     CdlConstConflict_Unresolved;
+typedef const CdlConflict_IllegalValueBody*   CdlConstConflict_IllegalValue;
+typedef const CdlConflict_EvalExceptionBody*  CdlConstConflict_EvalException;
+typedef const CdlConflict_RequiresBody*       CdlConstConflict_Requires;
+typedef const CdlConflict_DataBody*           CdlConstConflict_Data;
+
+typedef const CdlTransactionBody*             CdlConstTransaction;
+
+//}}}
+//{{{  Miscellaneous types etc.                         
+
+// ----------------------------------------------------------------------------
+// This section is used for data types, function prototypes, etc. which could
+// not be defined until after the main CDL classes and handles.
+
+// This typedef is used for error and warning reporting functions.
+// Typically such a function pointer will be passed when the library
+// is asked to perform any non-trivial parsing operation, e.g. loading
+// a package.
+//
+// If the error is fatal then this callback function should raise
+// a CdlParseException.
+typedef void (*CdlDiagnosticFnPtr)(std::string);
+
+// ----------------------------------------------------------------------------
+// This function is used for update handler. Whenever there is a change
+// to CDL entity (it has just been loaded, or its value has changed, or
+// whatever) this can affect other CDL entities that reference it.
+// All such references occur via properties, and there should be
+// update handlers associated with those properties.
+//
+// Update handlers are also invoked for initialization and finalization
+// operations, i.e. when the source object itself has just been loaded
+// or is in the process of being unloaded.
+//
+// The arguments to an update handler are:
+// 1) the transaction in which the operation takes place
+// 2) the source object containing the reference
+// 3) the source property containing the reference
+// 4) the destination object. This may be 0 for some update
+//    operations.
+// 5) an indication of the change that has happened. This should
+//    be a CdlUpdate value.
+typedef void (*CdlUpdateHandler)(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+
+// ----------------------------------------------------------------------------
+// This function is also used for transactions. Typically during a
+// transaction there will be one or more invocations of the inference engine,
+// with callbacks in between to allow one or more of the recommended
+// changes to be undone.
+typedef CdlInferenceCallbackResult (*CdlInferenceCallback)(CdlTransaction);
+
+// ----------------------------------------------------------------------------
+// The TCL API and C++ do not always mesh cleanly, for example a lot
+// happens in terms of ClientData which is a void* pointer. To avoid
+// too many casts all over the place libcdl provides a CdlInterpreter
+// class and the following alternative to Tcl_CmdProc*. A single
+// function will be used for the TCL command: its ClientData will be
+// the CdlInterpreterCommand, and the CdlInterpreter is accessible via
+// AssocData. This does result in some overheads, but none of these
+// should be in performance-critical code.
+typedef int (*CdlInterpreterCommand)(CdlInterpreter, int, const char*[]);
+
+// ----------------------------------------------------------------------------
+// In the libcdl world it is often convenient to swap whole sets of
+// commands in and out. For example when executing the body of a
+// cdl_component it is desirable to swap in commands for all the
+// properties that make sense in a component and swap out all the
+// commands that made sense in a higher level. It is assumed that none
+// of the commands being swapped in or out are built-ins. Achieving
+// this involves a vector of this simple utility structure.
+class CdlInterpreterCommandEntry {
+  public:
+    std::string                 name;
+    CdlInterpreterCommand       command;
+
+    CdlInterpreterCommandEntry() : name(""), command(0) {}
+    CdlInterpreterCommandEntry(const char *name_arg, CdlInterpreterCommand command_arg)
+        : name(name_arg), command(command_arg)
+    {
+    }
+    CdlInterpreterCommandEntry(std::string name_arg, CdlInterpreterCommand command_arg)
+        : name(name_arg), command(command_arg)
+    {
+    }
+    ~CdlInterpreterCommandEntry()
+    {
+        name = "";
+        command = 0;
+    }
+};
+
+// ----------------------------------------------------------------------------
+// Persistence support.
+// Some applications want to be able to store additional information
+// in savefiles, and essentially this involves extra commands that
+// get executed when the savefile is executed. It is possible that
+// the application reading back the savefile does not understand
+// the same set of commands as the application that wrote back the
+// data, so the library tries hard not to lose data.
+//
+// The CdlSaveCallback function typedef is used when installing
+// an application-specific savefile command. The first argument
+// indicates the node for which the callback is being invoked:
+// this may be the entire toplevel, or just an option, or whatever.
+//
+// The CdlSavefileCommand structure keeps track of the command,
+// the save callback if any (non-zero only for application-specific
+// data, zero implies that the command is handled by the lirary).
+// The load command is invoked when reading in a savefile and the
+// appropriate command is executed: unrecognised commands will be
+// processed by CdlToplevelBody::savefile_handle_unknown().
+
+typedef void (*CdlSaveCallback)(CdlNode, CdlInterpreter, Tcl_Channel, int);
+
+struct CdlSavefileCommand {
+    std::string           name;
+    CdlSaveCallback       save_callback;
+    CdlInterpreterCommand load_command;
+};
+
+// ----------------------------------------------------------------------------
+// Widget hint.
+// This structure provides widget hint information for a CdlValuable.
+// There are separate hints for the bool and data parts, and possibly
+// some additional data such as a string identifying the set of
+// items in a radio button.
+struct CdlWidgetHint {
+    CdlBoolWidget       bool_widget;
+    CdlValueWidget      value_widget;
+    std::string         radio_button_interface;
+};
+
+//}}}
+//{{{  Memory leak detection                            
+
+// ----------------------------------------------------------------------------
+// Provide some macros that are useful for detecting memory leaks. Basically
+// there is a static counter for every class, which gets incremented by the
+// constructor(s) and decremented by the destructor. Memory leak detection
+// is currently enabled if tracing is enabled. It would be possible to use
+// another configure-time option, but the overheads of tracing are likely
+// to dwarf the overheads of memory leak detection.
+//
+// For now the memleak counters are always present, even in non-debug
+// versions. The overhead is sufficiently small that it can be
+// ignored.There is control over whether or not the counters get
+// updated in the constructor or destructor. Otherwise there would be problems
+// with whether or not there should be a semicolon at the end of the
+// CYGDBG_DECLARE_MEMLEAK_COUNTER() macro definition.
+
+#define CYGDBG_DECLARE_MEMLEAK_COUNTER()        static int memleak_counter
+#define CYGDBG_DEFINE_MEMLEAK_COUNTER(class)    int class::memleak_counter = 0
+#define CYGDBG_GET_MEMLEAK_COUNTER(class)       class::memleak_counter
+
+#ifdef CYGDBG_USE_TRACING
+
+#define CYGDBG_MEMLEAK_CONSTRUCTOR()            this->memleak_counter++;
+#define CYGDBG_MEMLEAK_DESTRUCTOR()             this->memleak_counter--;
+#define CYGDBG_MEMLEAK_CHECKTHIS()              if (this->memleak_counter < 0) { return false; }
+    
+#else
+
+#define CYGDBG_MEMLEAK_CONSTRUCTOR()
+#define CYGDBG_MEMLEAK_DESTRUCTOR()
+#define CYGDBG_MEMLEAK_CHECKTHIS()
+
+#endif
+
+//}}}
+
+//{{{  Cdl class                                        
+
+// ---------------------------------------------------------------------------
+// The sole purpose of this class is to provide some utility functions with
+// reasonable namespace protection, without requiring that the compiler
+// implements namespaces.
+
+class Cdl {
+    
+  public:
+
+    static bool         is_valid_value_flavor(CdlValueFlavor);
+    static bool         is_valid_value_source(CdlValueSource);
+    
+    static bool         is_valid_cdl_name(const std::string&);
+    static bool         is_valid_c_preprocessor_symbol(const std::string&);
+    
+    static bool         string_to_integer(std::string, cdl_int&);
+    static bool         string_to_double(std::string, double&);
+    static bool         string_to_bool(std::string, bool&);
+    static void         integer_to_string(cdl_int, std::string&, CdlValueFormat = CdlValueFormat_Default);
+    static std::string  integer_to_string(cdl_int, CdlValueFormat = CdlValueFormat_Default);
+    static void         double_to_string(double, std::string&, CdlValueFormat = CdlValueFormat_Default);
+    static std::string  double_to_string(double, CdlValueFormat = CdlValueFormat_Default);
+    static void         bool_to_string(bool, std::string&);
+    static std::string  bool_to_string(bool);
+    static void         integer_to_double(cdl_int, double&);
+    static double       integer_to_double(cdl_int);
+    static bool         double_to_integer(double, cdl_int&);
+    
+    static bool         string_to_flavor(std::string, CdlValueFlavor&);
+    static bool         flavor_to_string(CdlValueFlavor, std::string&);
+    static bool         string_to_source(std::string, CdlValueSource&);
+    static bool         source_to_string(CdlValueSource, std::string&);
+                                         
+    static std::string  get_library_version();
+    static void         set_interactive(bool = true);
+    static bool         is_interactive();
+
+    static bool         truth() { return true; }
+    static bool         falsehood() { return false; }
+    
+    // return values are -1,0,1 just like strcmp(). The most recent
+    // version is the smallest.
+    static int          compare_versions(std::string, std::string);
+
+    // Also provide an STL-friendly comparison class
+    class version_cmp {
+      public:
+        bool operator()(const std::string& v1, const std::string& v2) const {
+            return Cdl::compare_versions(v1,v2) < 0;
+        }
+    };
+
+    // Split a version string into major, minor and release numbers.
+    static void         split_version_string(const std::string&, std::string& /* major */,
+                                             std::string& /* minor */, std::string& /* release */);
+    
+    // It is occasionally useful to take a full CDL name such as CYGPKG_KERNEL
+    // and turn it into a short form, i.e. kernel.
+    static std::string  get_short_form(const std::string&);
+    
+  private:
+    static bool         interactive;
+};
+
+//}}}
+//{{{  CdlInterpreter class                             
+
+// ----------------------------------------------------------------------------
+// libcdl requires access to a Tcl interpreter. For now the standard
+// interpreter is used. In the long run it may be better to use a
+// custom parser in places, if only to improve the diagnostics messages
+// that users see.
+//
+// Consider the case of software CDL (other CDL variants will have
+// similar requirements). A Tcl interpreter is needed to read in the
+// data for a given package. It will also be needed at various stages
+// when the data is being manipulated, e.g. to display a custom dialog
+// or to execute e.g. a check_proc or a define_proc. Each package
+// should run in its own safe interpreter with limited capabilities:
+// file I/O is limited to read-only, but read-write in the build and
+// install trees; network I/O is out of the question, at least until
+// appropriate security support is added to the CDL language itself.
+// However the interpreter should be extended with additional commands
+// like cdl_get and cdl_set to access the configuration data.
+//
+// For security and robustness reasons it is desirable to have
+// separate interpreters for the various packages. This leads to the
+// concept of a master interpreter for the entire configuration, and a
+// group of slave interpreters, one per package. In this model it
+// is convenient to have the configuration and package entities
+// associated directly with the interpreter. Note that a single
+// application may have several configurations loaded in memory,
+// so there may be several master interpreters.
+//
+// Some applications will want to support the graphical side of CDL,
+// i.e. custom dialogs and wizards. This means linking in Tk, not to
+// mention X11 (or the Windows equivalents), and making some/all of
+// the Tk commands available to the safe interpreter. Arguably
+// commands like toplevel should always be disabled. Not all clients
+// of libcdl will want the overheads of linking with Tk and X, so this
+// has to be made optional.
+//
+// The approach taken is as follows:
+//
+// 1) there is a class CdlInterpreter which provides access to Tcl
+//    interpreters. Amongst other things it takes care of converting
+//    between C and C++ strings.
+//
+// 2) every toplevel needs its own CdlInterpreter. The application
+//    code should supply this interpreter itself when the toplevel
+//    is instantiated, allowing it to decide whether or not Tk should
+//    be available.
+//
+// 3) each loadable gets its own safe slave interpreter, derived from
+//    the toplevel's interpreter.
+//    NOTE: initially the slave interpreters are not actually safe. It
+//    is not clear in the long term to what extent per-loadable
+//    interpreters need to be sandboxes, there are issues such as
+//    doing the equivalent of autoconf tests.
+
+// Tcl 8.4 involved various incompatible API changes related to
+// const vs. non-const data. #define'ing USE_NON_CONST or
+// USE_COMPAT_CONST avoids some of the problems, but does not
+// help much for C++.
+#if (TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))
+# define CDL_TCL_CONST_CAST(type,var) (var)
+#else
+# define CDL_TCL_CONST_CAST(type,var) const_cast<type>(var)
+#endif
+
+class CdlInterpreterBody
+{
+    friend class        CdlTest;
+
+  public:
+
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+    // This is how a top-level (i.e. per-toplevel) interpreter
+    // should get created.
+    static CdlInterpreter       make(Tcl_Interp* = 0);
+
+    // Create a slave interpreter for reading in the data in e.g. a
+    // cdl_package
+    CdlInterpreter create_slave(CdlLoadable, bool /* safe */ = true);
+    
+    // Make the interpreter safe, a one-way operation.
+    void                make_safe();
+    
+    // The destructor is public.
+    virtual ~CdlInterpreterBody();
+
+    // Add or remove commands from an interpreter. This provides
+    // a more C++-friendly implementation of Tcl's
+    // CreateCommand() and DeleteCommand().
+    void add_command(std::string, CdlInterpreterCommand);
+    void remove_command(std::string);
+
+    // In the libcdl world it is also convenient to swap whole sets of
+    // commands in and out. This is achieved by push and pop operations.
+    // push returns the old set (0 at the toplevel). pop restores
+    // the old set.
+    std::vector<CdlInterpreterCommandEntry>* push_commands(std::vector<CdlInterpreterCommandEntry>&);
+    void pop_commands(std::vector<CdlInterpreterCommandEntry>*);
+    std::vector<CdlInterpreterCommandEntry>* get_pushed_commands() const;
+    
+    // Similarly, allow variables to be set, unset and queried
+    void        set_variable(std::string, std::string);
+    void        unset_variable(std::string);
+    std::string get_variable(std::string);
+    
+    // FIXME: add support for variable traces. These are needed
+    // for cdl_value and similar utilities.
+    
+    // Provide hooks into the AssocData() facilities associated with
+    // Tcl interpreters. This makes it possible to store arbitrary
+    // data with an interpreter, e.g. to keep track of current state.
+    void       set_assoc_data(const char*, ClientData, Tcl_InterpDeleteProc* =0);
+    void       delete_assoc_data(const char*);
+    ClientData get_assoc_data(const char*);
+
+    // Evaluate a string as Tcl code. The return value comes from Tcl, e.g.
+    // TCL_OK or TCL_ERROR. There are variants depending on whether or not
+    // the result string is of interest.
+    int eval(std::string);
+    int eval(std::string, std::string&);
+
+    // Ditto for any Tcl code that comes from CDL files
+    int eval_cdl_code(const cdl_tcl_code);
+    int eval_cdl_code(const cdl_tcl_code, std::string&);
+
+    // And support for evaluating an entire file
+    int eval_file(std::string);
+    int eval_file(std::string, std::string&);
+    
+    // For use by commands implemented in C++, a way of setting the result
+    void set_result(std::string);
+
+    // And a utility to get the result as well.
+    std::string get_result();
+
+    // Was the result set by the Tcl interpreter or by libcdl?
+    bool result_set_by_cdl();
+    
+    // A utility to quote data that is going to end up in a TCL script.
+    static std::string quote(std::string);
+
+    // Turn some multiline data into a comment.
+    static std::string multiline_comment(const std::string&, int, int = 0);
+
+    // Add some data to a comment, allowing for newlines if necessary
+    static std::string extend_comment(const std::string&, int, int = 0);
+    
+    // Write some data to a savefile, throwing an exception on error
+    void write_data(Tcl_Channel, std::string);
+
+    // File-related utilities.
+    void locate_subdirs(std::string, std::vector<std::string>&);
+    void locate_all_subdirs(std::string, std::vector<std::string>&);
+    void locate_files(std::string, std::vector<std::string>&);
+    void locate_all_files(std::string, std::vector<std::string>&);
+    bool is_directory(std::string);
+    bool is_file(std::string);
+
+    // When parsing a CDL script it is convenient to keep track of
+    // a number of items:
+    //
+    // 1) the toplevel, e.g. the entire configuration
+    // 2) the loadable, e.g. the current package
+    // 3) the parent of whatever is being processed at the moment
+    // 4) the entity, i.e. the thingamajig that is being processed.
+    // 5) the current file
+    // 6) an error reporting function
+    //
+    // This gives the various commands embedded in the Tcl interpreter
+    // enough information to do their job. Additional information can
+    // be provided via assoc_data()
+    //
+    // There should be only one call to set_toplevel(), for the
+    // master interpreter. All slaves inherit this, and the toplevel
+    // cannot be changed again.
+    //
+    // The loadable field is filled in via make_slave()
+    //
+    // For some members push and pop functions are more appropriate
+    // than set.
+    CdlToplevel         get_toplevel() const;
+    CdlLoadable         get_loadable() const;
+    CdlContainer        get_container() const;
+    CdlNode             get_node() const;
+    std::string         get_context() const;
+    CdlDiagnosticFnPtr  get_error_fn_ptr() const;
+    CdlDiagnosticFnPtr  get_warning_fn_ptr() const;
+    CdlTransaction      get_transaction() const;
+    void                set_toplevel(CdlToplevel);
+    void                set_transaction(CdlTransaction);
+    CdlContainer        push_container(CdlContainer);
+    void                pop_container(CdlContainer);
+    CdlNode             push_node(CdlNode);
+    void                pop_node(CdlNode);
+    std::string         push_context(std::string);
+    void                pop_context(std::string);
+    CdlDiagnosticFnPtr  push_error_fn_ptr(CdlDiagnosticFnPtr);
+    void                pop_error_fn_ptr(CdlDiagnosticFnPtr);
+    CdlDiagnosticFnPtr  push_warning_fn_ptr(CdlDiagnosticFnPtr);
+    void                pop_warning_fn_ptr(CdlDiagnosticFnPtr);
+
+    // Provide utility classes for common push/pop combinations. The
+    // push happens during the constructor, the pop during the
+    // destructor. This can simplify some code, especially when
+    // exceptions may get thrown.
+    class DiagSupport {
+      public:
+        DiagSupport(CdlInterpreter interp_arg, CdlDiagnosticFnPtr error_fn_arg, CdlDiagnosticFnPtr warn_fn_arg) {
+            interp         = interp_arg;
+            saved_error_fn = interp->push_error_fn_ptr(error_fn_arg);
+            saved_warn_fn  = interp->push_warning_fn_ptr(warn_fn_arg);
+        }
+        ~DiagSupport() {
+            interp->pop_error_fn_ptr(saved_error_fn);
+            interp->pop_warning_fn_ptr(saved_warn_fn);
+        }
+    private:
+        DiagSupport();
+
+        CdlInterpreter     interp;
+        CdlDiagnosticFnPtr saved_error_fn;
+        CdlDiagnosticFnPtr saved_warn_fn;
+    };
+    class ContextSupport {
+      public:
+        ContextSupport(CdlInterpreter interp_arg, std::string context) {
+            interp = interp_arg;
+            saved_context = interp->push_context(context);
+        }
+        ~ContextSupport() {
+            interp->pop_context(saved_context);
+        }
+      private:
+        ContextSupport();
+        CdlInterpreter interp;
+        std::string    saved_context;
+    };
+    class ContainerSupport {
+      public:
+        ContainerSupport(CdlInterpreter interp_arg, CdlContainer container) {
+            interp = interp_arg;
+            saved_container = interp->push_container(container);
+        }
+        ~ContainerSupport() {
+            interp->pop_container(saved_container);
+        }
+      private:
+        ContainerSupport();
+        CdlInterpreter interp;
+        CdlContainer   saved_container;
+    };
+    class NodeSupport {
+      public:
+        NodeSupport(CdlInterpreter interp_arg, CdlNode node) {
+            interp = interp_arg;
+            saved_node = interp->push_node(node);
+        }
+        ~NodeSupport() {
+            interp->pop_node(saved_node);
+        }
+      private:
+        NodeSupport();
+        CdlInterpreter interp;
+        CdlNode        saved_node;
+    };
+    class CommandSupport {
+      public:
+        CommandSupport(CdlInterpreter interp_arg, std::vector<CdlInterpreterCommandEntry>& commands) {
+            interp = interp_arg;
+            saved_commands = interp->push_commands(commands);
+        }
+        CommandSupport(CdlInterpreter interp_arg, CdlInterpreterCommandEntry* commands) {
+            unsigned int i;
+            for (i = 0; 0 != commands[i].command; i++) {
+                new_commands.push_back(commands[i]);
+            }
+            interp = interp_arg;
+            saved_commands = interp->push_commands(new_commands);
+        }
+        ~CommandSupport() {
+            interp->pop_commands(saved_commands);
+        }
+
+      private:
+        CommandSupport();
+        CdlInterpreter interp;
+        std::vector<CdlInterpreterCommandEntry>* saved_commands;
+        std::vector<CdlInterpreterCommandEntry> new_commands;
+    };
+
+    // Similar utility classes for variables and assoc data.
+    class VariableSupport {
+      public:
+        VariableSupport(CdlInterpreter interp_arg, std::string varname_arg, std::string data) {
+            interp  = interp_arg;
+            varname = varname_arg;
+            interp->set_variable(varname, data);
+        }
+        ~VariableSupport() {
+            interp->unset_variable(varname);
+        }
+      private:
+        VariableSupport();
+        CdlInterpreter interp;
+        std::string    varname;
+    };
+    class AssocSupport {
+      public:
+        AssocSupport(CdlInterpreter interp_arg, const char* name_arg, ClientData data, Tcl_InterpDeleteProc* del_proc = 0) {
+            interp = interp_arg;
+            name   = name_arg;
+            interp->set_assoc_data(name, data, del_proc);
+        }
+        ~AssocSupport() {
+            interp->delete_assoc_data(name);
+        }
+      private:
+        AssocSupport();
+        CdlInterpreter interp;
+        const char*    name;
+    };
+    
+    // Some command implementations may want to access other Tcl library
+    // routines such as Tcl_SplitList(). This requires convenient access
+    // to the underlying Tcl interpreter.
+    Tcl_Interp*         get_tcl_interpreter() const;
+    
+    // For use by the assertion macros.
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+
+  private:
+    // This is the Tcl command proc that gets registered for all
+    // CdlInterpreterCommand instances.
+    static int          tcl_command_proc(ClientData, Tcl_Interp*, int, const char*[]);
+
+    // This key is used to access the CdlInterpreter assoc data.
+    static char*        cdlinterpreter_assoc_data_key;
+    
+    // Do not allow static instances of a Cdl interpreter. There are too
+    // many possible failure conditions. Cdl interpreters can only be
+    // created dynamically via make(), which will invoke this.
+    CdlInterpreterBody(Tcl_Interp*);
+
+    // Default constructor, copy constructor and assignment are illegal
+    CdlInterpreterBody();
+    CdlInterpreterBody(const CdlInterpreterBody&);
+    CdlInterpreterBody& operator=(const CdlInterpreterBody&);
+
+    
+    Tcl_Interp*                 tcl_interp;     // The underlying Tcl interpreter
+    bool                        owns_interp;    // Was the Tcl interpreter created by the library?
+    std::vector<CdlInterpreter> slaves;         // All slave interpreters
+    CdlInterpreter              parent;         // Or else the parent
+    CdlToplevel                 toplevel;       // Data that gets used during the parsing process
+    CdlTransaction              transaction;
+    CdlLoadable                 loadable;
+    CdlContainer                container;
+    CdlNode                     node;
+    std::string                 context;
+    CdlDiagnosticFnPtr          error_fn_ptr;
+    CdlDiagnosticFnPtr          warning_fn_ptr;
+    bool                        cdl_result;
+    
+    std::vector<CdlInterpreterCommandEntry>* current_commands; // for push() and pop()
+    
+    enum {
+        CdlInterpreterBody_Invalid = 0,
+        CdlInterpreterBody_Magic   = 0x0be67689
+    } cdlinterpreterbody_cookie;
+};
+
+//}}}
+//{{{  CdlReference/Referrer classes                    
+
+// ---------------------------------------------------------------------------
+// CDL objects are organised primarily in a tree hierarchy. For
+// example a package contains components, components contain options,
+// and so on. The tree hierarchy tends to change rather infrequently,
+// so it makes sense to have a quick way of navigating between
+// entities without continuously having to do hash-table lookups. In
+// addition it is very desirable to make the connectivity
+// bidirectional: if a "requires" property in option A references
+// option B then it would be useful to have a link back to A from B;
+// that way, if the value of B changes it is a lot easier to keep
+// things up to date.
+//
+// Terminology: the entity which contains the reference, e.g. a
+// "requires" property, is the source. The relevant property is the
+// "source property". The entity pointed at is the destination.
+//
+// Unfortunately there may be connections between CDL entities outside
+// the tree hierarchy. In particular any property can contain one or
+// more references to packages, components, options, wizards, or
+// whatever. Often these references will be to options etc. within the
+// same package, but some references will go to other packages. There
+// may even be references to other configurations: for example a board
+// may contain both an ordinary processor and a DSP; these two need
+// their own configurations; however a package running on the DSP may
+// need to interact with a package running on the processor, and vice
+// versa.
+//
+// Also, a reference may occur inside an object that is not in the
+// hierarchy. For example CDL expressions may get evaluated inside Tcl
+// code rather than as part of a property. Such expressions may still
+// contain references to entities in the current configuration.
+//
+// References may not be resolved. When reading in a CDL script there
+// may be forward references. A reference may involve another package
+// that has not yet been loaded, which is a conflict.
+//
+// Using simple pointers to store these connections is a bad idea. It
+// makes it a lot harder to figure out what is connected to what, and
+// it introduces horrible consistency problems when packages get
+// loaded and unloaded. Instead libCDL provides a CdlReference class.
+// Whenever a CdlProperty contains a reference to some other CDL
+// entity there should be a CdlReference object corresponding to this.
+// The reverse direction is handled via a CdlReferrer object.
+//
+// A CdlReference object can be either bound or unbound. By default it
+// is unbound, containing only a string. It can then be bound via a
+// member function, examined, and unbound again as required. Creating
+// a binding automatically creates a CdlReferrer entry in the target
+// object, thus avoiding any risk of inconsistencies.
+//
+// The CdlReference class should not be used outside the hierarchy,
+// since every bound reference must have a referrer object pointing
+// back, and this link back can only be valid within the hierarchy.
+// Temporary CdlReference objects are useful during the construction
+// of properties.
+//
+// It is possible that a given property (e.g. a complicated "requires"
+// expression) has multiple references to another entity. Each of
+// these involves a separate CdlReference/CdlReferrer pair.
+
+// ----------------------------------------------------------------------------
+// The actual CdlReference class.
+
+class CdlReference {
+
+    friend class        CdlTest;
+
+    // CdlReferrer must be a friend so that when a package gets unloaded
+    // it can clean up all references to it.
+    friend class        CdlReferrer;
+    
+  public:
+
+    // The default constructor should not normally be used, instead
+    // a string should be supplied. However there are vectors of
+    // reference objects...
+    CdlReference();
+    
+    // The main constructor supplies the name of the referenced
+    // entity. The resulting object will be unbound.
+    CdlReference(const std::string);
+
+    // The copy constructor is legal for unbound objects only.
+    CdlReference(const CdlReference&);
+
+    // The assignment operator is needed for STL operations.
+    // Again it only makes sense of unbound objects.
+    CdlReference& operator=(const CdlReference&);
+    
+    // The destructor is only valid for unbound objects. All references
+    // should be unbound before an entity can be destroyed.
+    ~CdlReference();
+    
+    // Access the various fields.
+    void               set_destination_name(const std::string);
+    const std::string& get_destination_name() const;
+    CdlNode            get_destination() const;
+
+    // Binding a reference. Obviously this can only be used when the
+    // reference is still unbound. When doing the binding it is
+    // necessary to know:
+    //   (1) the object containing the reference.
+    //   (2) the specific property that contains the reference.
+    //   (3) the object being referred to.
+    // Binding a reference results in a new referrer entry in the
+    // destination.
+    void bind(CdlNode, CdlProperty, CdlNode);
+
+    // Unbinding a reference. Typically this only happens when the
+    // destination is unloaded. The arguments provide the source and
+    // the source property.
+    void unbind(CdlNode, CdlProperty);
+
+    // This is used by the ASSERT_CLASS() and ASSERT_THIS() macros.
+    bool check_this(cyg_assert_class_zeal cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+
+  private:
+    
+    // The data fields. The name is usually filled in by the
+    // constructor. The destination defaults to zero for an unbound
+    // object and gets filled in by the bind() operation.
+    std::string dest_name;
+    CdlNode     dest;
+
+    enum {
+        CdlReference_Invalid = 0,
+        CdlReference_Magic   = 0x3f908608
+    } cdlreference_cookie;
+};
+
+// ----------------------------------------------------------------------------
+// The CdlNode class (and hence just about everything) contains a
+// vector of CdlReferrer objects. This keeps track of all entities
+// that refer to this one, so if the value associated with this
+// changes it is possible to work out the impact of this on all
+// entities that rely on this value.
+//
+// Arguably this should work in terms of CdlValuable objects rather
+// than CdlNode objects. However it is convenient to use references
+// for the connection between e.g. an option and a dialog, where
+// there is no value involved. The reverse connection is of little
+// use in this circumstance.
+//
+// CdlReferrer objects are rarely accessed directly. Instead they will
+// be filled in during a CdlReference::bind() operation and erased
+// during a CdlReference::unbind() operation. The only operations that
+// should be public allow access to the contained data.
+
+class CdlReferrer {
+
+    friend class        CdlTest;
+
+    // CdlReference::bind() and unbind() have direct access to the
+    // members, since these two functions are really responsible for
+    // creating and destroying referrer objects.
+    friend class        CdlReference;
+    
+  public:
+
+    // The default constructor, copy constructor and assignment
+    // operator are all public to avoid problems with having vectors
+    // of referrer objects. Similarly the destructor is public.
+    // In practice updates actually happen as a consequence of
+    // CdlReference::bind() and CdlReference::unbind().
+    CdlReferrer();
+    CdlReferrer(const CdlReferrer&);
+    CdlReferrer& operator=(const CdlReferrer&);
+    ~CdlReferrer();
+
+    CdlNode     get_source() const;
+    CdlProperty get_source_property() const;
+    void        update(CdlTransaction, CdlNode, CdlUpdate);
+    bool        check_this(cyg_assert_class_zeal=cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  private:
+
+    CdlNode     source;
+    CdlProperty source_property;
+    
+    enum {
+        CdlReferrer_Invalid = 0,
+        CdlReferrer_Magic   = 0x70e1fc37
+    } cdlreferrer_cookie;
+};
+
+//}}}
+//{{{  Value and Expression  classes                    
+
+//{{{  CdlEvalContext                   
+
+// ----------------------------------------------------------------------------
+// Expression evaluation always happens within a certain context.
+// This may involve a transaction. Usually it involves a node and
+// a property within that node, although it is possible to evaluate
+// expressions from inside Tcl code.
+//
+// To avoid passing too many arguments around the various
+// evaluation-related routines, a utility class is provided.
+
+class CdlEvalContext {
+    
+    friend class CdlTest;
+    
+  public:
+
+    CdlTransaction      transaction;
+    CdlNode             node;
+    CdlProperty         property;
+    CdlToplevel         toplevel;
+
+    CdlEvalContext(CdlTransaction, CdlNode = 0, CdlProperty = 0, CdlToplevel = 0);
+    ~CdlEvalContext();
+
+    // Given a reference inside an expression, try to resolve this to either
+    // a node or, more specifically, a valuable.
+    CdlNode             resolve_reference(CdlExpression, int);
+    CdlValuable         resolve_valuable_reference(CdlExpression, int);
+    
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+
+  private:
+    // Illegal operation, the three fields must always be supplied,
+    // although they may be zero.
+    CdlEvalContext();
+
+    enum {
+        CdlEvalContext_Invalid  = 0,
+        CdlEvalContext_Magic    = 0x03434be9
+    } cdlevalcontext_cookie;
+    
+};
+
+//}}}
+//{{{  CdlSimpleValue                   
+
+// ----------------------------------------------------------------------------
+// Expression evaluation happens in terms of CdlSimpleValue objects.
+// In CDL all values are strings, but for the purposes of arithmetic
+// these strings sometimes have to be interpreted as integers or as
+// double precision numbers. Sometimes there is a choice, for example
+// the equality operator == can mean numerical or string comparison.
+// The basic rules that get applied are:
+//
+//    1) if the current value has an integer representation then
+//       use this by preference. This means that an expression
+//       of the form (CYGNUM_XXX != 0x100) will do a integer
+//       comparison if possible.
+//
+//    2) otherwise if the current value can be interpreted as a
+//       double precision number, use that representation.
+//       All integers can be interpreted as doubles (at the risk
+//       of some loss of precision), so the representation as
+//       a double should only be used if the integer representation
+//       is inappropriate.
+//
+//    3) otherwise interpret the value as a string.
+//
+// The default value is 0.
+
+class CdlSimpleValue {
+    
+    friend class CdlTest;
+
+  public:
+
+    CdlSimpleValue();
+    CdlSimpleValue(std::string);
+    CdlSimpleValue(cdl_int);
+    CdlSimpleValue(double);
+    CdlSimpleValue(const CdlSimpleValue&);
+    CdlSimpleValue(bool);
+    ~CdlSimpleValue();
+
+    CdlSimpleValue&     operator=(const CdlSimpleValue&);
+    CdlSimpleValue&     operator=(std::string);
+    CdlSimpleValue&     operator=(cdl_int);
+    CdlSimpleValue&     operator=(double);
+    
+    CdlSimpleValue&     operator=(bool);
+    
+    bool                operator==(const CdlSimpleValue&) const;
+    bool                operator!=(const CdlSimpleValue&) const;
+    bool                operator==(std::string arg) const
+    {
+        CdlSimpleValue val(arg);
+        return *this == val;
+    }
+    bool                operator==(cdl_int arg) const
+    {
+        CdlSimpleValue val(arg);
+        return *this == val;
+    }
+    bool                operator==(double arg) const
+    {
+        CdlSimpleValue val(arg);
+        return *this == val;
+    }
+    bool                operator!=(std::string arg) const
+    {
+        CdlSimpleValue val(arg);
+        return *this != val;
+    }
+    bool                operator!=(cdl_int arg) const
+    {
+        CdlSimpleValue val(arg);
+        return *this != val;
+    }
+    bool                operator!=(double arg) const
+    {
+        CdlSimpleValue val(arg);
+        return *this != val;
+    }
+    
+    void                set_value(std::string, CdlValueFormat = CdlValueFormat_Default);
+    std::string         get_value() const;
+    
+    bool                has_integer_value() const;
+    void                set_integer_value(cdl_int, CdlValueFormat = CdlValueFormat_Default);
+    cdl_int             get_integer_value() const;
+    
+    bool                has_double_value() const;
+    void                set_double_value(double, CdlValueFormat = CdlValueFormat_Default);
+    double              get_double_value() const;
+
+    CdlValueFormat      get_value_format() const;
+    void                set_value_format(CdlValueFormat);
+    void                set_value_format(CdlSimpleValue&);
+    void                set_value_format(CdlSimpleValue&, CdlSimpleValue&);
+
+    static void         eval_valuable(CdlEvalContext&, CdlValuable, CdlSimpleValue&);
+    
+    // For expression evaluation, it is often convenient to get hold
+    // of a boolean as well. This may indicate a non-empty string
+    // or a non-zero value.
+    bool                get_bool_value() const;
+    
+    // This class is too simple to warrant even a cookie validation.
+    bool check_this(cyg_assert_class_zeal zeal = cyg_quick) const {
+        return true;
+    }
+    
+  protected:
+
+  private:
+    enum {
+        int_valid       = 0x01,
+        double_valid    = 0x02,
+        string_valid    = 0x04,
+        int_invalid     = 0x08,
+        double_invalid  = 0x10
+    };
+    mutable int         valid_flags;
+    mutable std::string value;
+    mutable cdl_int     int_value;
+    mutable double      double_value;
+    CdlValueFormat      format;
+};
+
+//}}}
+//{{{  CdlListValue                     
+
+// ----------------------------------------------------------------------------
+// Evaluating a list expression results in a set of possible values, but
+// unlike the original list expression these values are now constant and
+// can have no dependencies on CDL entities. As with list expressions the
+// main operation on a list value is to detect membership, but using
+// list values allows multiple potential members to be tested without
+// repeated expression evaluation. The main use of list values is implicit
+// in libcdl, each list expression contains a mutable cached list value.
+//
+// A list value contains five sets of data:
+//
+// 1) separate vectors of strings, integers, and floating point constants.
+//    Having separate vectors of integers and floating points avoids
+//    problems when numbers can be represented in different formats.
+// 2) a vector of cdl_int pairs for ranges of integer data
+// 3) a vector of double pairs for ranges of floating point data
+//
+// Any of these vectors may be empty, but at least one of the vectors should
+// contain useful data. Possibly there should also be tables for cdl_int and
+// double to avoid unnecessary string conversions.
+
+class CdlListValue {
+    
+    friend class        CdlTest;
+
+    // A list value will only be filled in when a list expression is evaluated.
+    // The members cannot be updated by other means.
+    friend class        CdlListExpressionBody;
+    
+  public:
+
+    CdlListValue();
+    ~CdlListValue();
+    CdlListValue(const CdlListValue&);
+    CdlListValue& operator=(const CdlListValue&);
+
+    bool        is_member(CdlSimpleValue&) const;
+    bool        is_member(std::string, bool = true) const;
+    bool        is_member(cdl_int, bool = true) const;
+    bool        is_member(double, bool = true) const;
+
+    // These provide access to the raw data, for example if it is
+    // necessary to suggest a legal value to the user.
+    const std::vector<CdlSimpleValue>&                get_table() const;
+    const std::vector<std::pair<cdl_int, cdl_int> >&  get_integer_ranges() const;
+    const std::vector<std::pair<double, double> >&    get_double_ranges() const;
+
+    bool check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  private:
+    std::vector<CdlSimpleValue>                 table;
+    std::vector<std::pair<cdl_int, cdl_int> >   integer_ranges;
+    std::vector<std::pair<double, double> >     double_ranges;
+
+    enum {
+        CdlListValue_Invalid  = 0,
+        CdlListValue_Magic    = 0x2183a943
+    } cdllistvalue_cookie;
+};
+
+//}}}
+//{{{  CdlValue                         
+
+// ----------------------------------------------------------------------------
+// Values in CDL are non-trivial compared with some other languages.
+// Even though CDL is not a fully-typed language, it does still have
+// four different flavors to consider. There is also the problem that
+// an entity may have up to four different values which should be
+// stored (default, inferred, wizard, user), with the ability to
+// switch between them. The CdlValue class provides support for this.
+//
+// CdlValue objects are not normally updated explicitly. Instead
+// higher level code deals with CdlValuable objects, which inherit
+// privately from CdlValue. Modifications to CdlValuables happen in
+// the context of a transaction.
+//
+// The first concept to take into account is the flavor. There
+// are four flavors, None, Bool, BoolData and Data. The member
+// function get_flavor() can be used to obtain the current flavor.
+//
+//     CdlValueFlavor CdlValue::get_flavor() const;
+//
+// Values may be enabled or disabled. Values of flavor None
+// and Data are always enabled. Values of flavor Bool or BoolData
+// may or may not be enabled, by default they are disabled.
+//
+//     bool CdlValue::is_enabled(...) const;
+//
+// (The optional argument to is_enabled() is discussed later).
+//
+// Values of flavor BoolData and Data also have a string value,
+// which can be interpreted as an integer or a double under
+// the right circumstances.
+//
+//     std::string CdlValue::get_value(...) const;
+//     bool        CdlValue::has_integer_value(...) const;
+//     bool        CdlValue::has_double_value(...) const;
+//     cdl_int     CdlValue::get_integer_value(...) const;
+//     double      CdlValue::get_double_value(...) const;
+//
+// This is equivalent to a CdlSimpleValue object, and in fact
+// that is the internal representation. It is possible to
+// get hold of the CdlSimpleValue object directly:
+//
+//     CdlSimpleValue CdlValue::get_simple_value(...) const;
+//
+// The get_integer_value() and get_double_value() members should
+// only be used if you are confident that the current value has
+// an integer or double representation. Otherwise the result is
+// undefined.
+//
+// The optional argument to these member functions represents
+// the source. A value can be set from four different sources:
+// the default value (usually either 0 or the result of
+// evaluating a default_value property); an inferred value,
+// determined by the inference engine; a wizard value, i.e.
+// what a CDL wizard believes the correct value to be based
+// on user input; and a user value, something explicitly
+// set by the end user. These have different priorities:
+// the inference engine can override default values but not
+// user values. A CdlValue object keeps track of the current
+// source.
+//
+//    CdlValueSource CdlValue::get_source() const;
+//
+// If no argument is given to e.g. is_enabled() then the
+// current source is used. Otherwise it is possible to find
+// out whether or not the entity is enabled for each of the
+// sources.
+//
+// The default source is always defined, the others may or
+// may not be. It is possible to find out for each source
+// whether or not a value has been set.
+//
+//   bool CdlValue::has_source(CdlValueSource) const;
+//
+//
+// Updating values normally happens in the CdlValuable class,
+// but the member functions are the same. There is a member
+// function to change the flavor:
+//
+//   void CdlValue::set_flavor(CdlValueFlavor);
+//
+// However this member function is intended only for use by the
+// library itself. An entity's flavor is normally defined by CDL data,
+// and should not be updated explicitly by application code.
+//
+// There are two member functions to manipulate the value source:
+//
+//     void CdlValue::set_source(CdlValueSource);
+//     void CdlValue::invalidate_source(CdlValueSource);
+//
+// The first function can be used if e.g. the user wants to
+// change his or her mind and go back to the default value
+// rather than a user value. The user value is not forgotten
+// and can be reinstated. 
+//
+// invalidate_source() can be used to completely cancel a
+// value source. If that source happens to be the current one
+// then the current source will be adjusted appropriately.
+// It is illegal to attempt to invalidate the default source.
+//
+// For values with flavor Bool and BoolData, there are three
+// member functions that can be used to control the enabled
+// status:
+//
+//   void CdlValue::set_enabled(bool, CdlValueSource);
+//   void CdlValue::enable(CdlValueSource);
+//   void CdlValue::disable(CdlValueSource);
+//
+// Note that when updating a CdlValue object the source should
+// be known and must be specified. If the source has a higher
+// priority than the current one then it will automatically
+// become the new source. On the rare occasion that this is
+// not desired, set_source() will have to be used afterwards
+// to reset the current source.
+//
+// For values with flavor BoolData and Data the following
+// member functions are available to change the value string:
+//
+//   void CdlValue::set_value(std::string, CdlValueSource);
+//   void CdlValue::set_value(cdl_int, CdlValueSource);
+//   void CdlValue::set_value(double, CdlvalueSource);
+//   void CdlValue::set_value(CdlSimpleValue&, CdlValueSource);
+//
+// For values with flavor BoolData is is possible to
+// combine updating the enabled flag and the string value:
+//
+//   void CdlValue::set_enabled_and_value(bool, std::string, CdlValueSource);
+//   void CdlValue::set_enabled_and_value(bool, cdl_int, CdlValueSource);
+//   void CdlValue::set_enabled_and_value(bool, double, CdlValueSource);
+//   void CdlValue::set_enabled_and_value(bool, CdlSimpleValue&, CdlValueSource);
+//   void CdlValue::enable_and_set_value(std::string, CdlValueSource);
+//   void CdlValue::enable_and_set_value(cdl_int, CdlValueSource);
+//   void CdlValue::enable_and_set_value(double, CdlValueSource);
+//   void CdlValue::enable_and_set_value(CdlSimpleValue&, CdlValueSource);
+//   void CdlValue::disable_and_set_value(std::string, CdlValueSource);
+//   void CdlValue::disable_and_set_value(cdl_int, CdlValueSource);
+//   void CdlValue::disable_and_set_value(double, CdlValueSource);
+//   void CdlValue::disable_and_set_value(CdlSimpleValue&, CdlValueSource);
+//
+// Obviously many of these functions are just simple inlines.
+//
+// There is one final member function:
+//
+//   void CdlValue::set(CdlSimpleValue, CdlValueSource);
+//
+// This member function is defined to do the right thing,
+// whatever the flavor happens to be.
+
+class CdlValue {
+    
+    friend class CdlTest;
+
+  public:
+
+    CdlValue(CdlValueFlavor = CdlValueFlavor_Bool);
+    virtual ~CdlValue();
+    CdlValue(const CdlValue&);
+    CdlValue&           operator=(const CdlValue&);
+
+    CdlValueFlavor      get_flavor() const;
+    CdlValueSource      get_source() const;
+    bool                has_source(CdlValueSource) const;
+
+    bool                is_enabled(CdlValueSource = CdlValueSource_Current) const;
+    std::string         get_value(CdlValueSource = CdlValueSource_Current) const;
+    bool                has_integer_value(CdlValueSource = CdlValueSource_Current) const;
+    bool                has_double_value(CdlValueSource = CdlValueSource_Current) const;
+    cdl_int             get_integer_value(CdlValueSource = CdlValueSource_Current) const;
+    double              get_double_value(CdlValueSource = CdlValueSource_Current) const;
+    CdlSimpleValue      get_simple_value(CdlValueSource = CdlValueSource_Current) const;
+    
+    void set_source(CdlValueSource);
+    void invalidate_source(CdlValueSource);
+    
+    void set_enabled(bool, CdlValueSource);
+    void enable(CdlValueSource source)
+    {
+        set_enabled(true, source);
+    }
+    void disable(CdlValueSource source)
+    {
+        set_enabled(false, source);
+    }
+
+    void set_value(CdlSimpleValue&, CdlValueSource);
+    void set_value(std::string data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_value(val, source);
+    }
+    void set_integer_value(cdl_int data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_value(val, source);
+    }
+    void set_double_value(double data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_value(val, source);
+    }
+    void set_enabled_and_value(bool, CdlSimpleValue&, CdlValueSource);
+    void set_enabled_and_value(bool enabled, std::string data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_enabled_and_value(enabled, val, source);
+    }
+    void set_enabled_and_value(bool enabled, cdl_int data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_enabled_and_value(enabled, val, source);
+    }
+    void set_enabled_and_value(bool enabled, double data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_enabled_and_value(enabled, val, source);
+    }
+    void enable_and_set_value(CdlSimpleValue& val, CdlValueSource source)
+    {
+        set_enabled_and_value(true, val, source);
+    }
+    void enable_and_set_value(std::string data, CdlValueSource source)
+    {
+        set_enabled_and_value(true, data, source);
+    }
+    void enable_and_set_value(cdl_int data, CdlValueSource source)
+    {
+        set_enabled_and_value(true, data, source);
+    }
+    void enable_and_set_value(double data, CdlValueSource source)
+    {
+        set_enabled_and_value(true, data, source);
+    }
+    void disable_and_set_value(CdlSimpleValue& val, CdlValueSource source)
+    {
+        set_enabled_and_value(false, val, source);
+    }
+    void disable_and_set_value(std::string data, CdlValueSource source)
+    {
+        set_enabled_and_value(false, data, source);
+    }
+    void disable_and_set_value(cdl_int data, CdlValueSource source)
+    {
+        set_enabled_and_value(false, data, source);
+    }
+    void disable_and_set_value(double data, CdlValueSource source)
+    {
+        set_enabled_and_value(false, data, source);
+    }
+
+    void set(CdlSimpleValue&, CdlValueSource);
+    
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+    // This should only be used by the library itself.
+    void                set_flavor(CdlValueFlavor);
+    
+  protected:
+    
+  private:
+    
+    CdlValueFlavor      flavor;
+    CdlValueSource      current_source;
+
+    // FIXME: a static const member should be used for the array
+    // sizes, but VC++ does not support that part of the language.
+    // FIXME: std::bitset should be used here. Using lots of separate
+    // bools here is inefficient.
+    bool                source_valid[4];
+    bool                enabled[4];
+    CdlSimpleValue      values[4];
+    
+    enum {
+        CdlValue_Invalid = 0,
+        CdlValue_Magic   = 0x41837960
+    } cdlvalue_cookie;
+};
+
+//}}}
+//{{{  CdlSubexpression                 
+
+// ----------------------------------------------------------------------------
+// Expressions come into existence primarily as the result of reading
+// in certain properties like default_value in CDL data. It is also
+// possible for expressions to be generated and evaluated on the fly
+// inside Tcl code, but that is expected to be a comparatively rare
+// events. Expression objects always live on the heap, usually only
+// in derived classes.
+//
+// An ordinary expression evaluates to a single value. There are two
+// other types of expression in the CDL language, goal expressions and
+// list expression. A goal expression is essentially a set of ordinary
+// expressions with implicit &&'s between them. A list expression
+// is a set of expressions that can be evaluated to a constant vector,
+// plus pairs of expressions that constitute ranges. Again goal and
+// list expressions only live on the heap.
+//
+// Both parsing an evaluation involve tokens for the various
+// operators. The inference engine, conflict reporting code, and
+// other diagnostic code will also need to have ready access to
+// this information. Hence it makes a bit more sense to have
+// the enum outside the expression class.
+
+enum CdlExprOp {
+    CdlExprOp_Invalid           =  0,
+    CdlExprOp_EOD               =  1,   // End of data reached
+    CdlEXprOp_Command           =  2,   // [tcl code]
+    CdlExprOp_Variable          =  3,   // $tcl_variable
+    CdlExprOp_StringConstant    =  4,   // "hello"
+    CdlExprOp_IntegerConstant   =  5,   // 123
+    CdlExprOp_DoubleConstant    =  6,   // 3.1415
+    CdlExprOp_Reference         =  7,   // CYGPKG_INFRA
+    CdlExprOp_Range             =  8,   // x to y
+    CdlExprOp_Negate            =  9,   // -x
+    CdlExprOp_Plus              = 10,   // +x
+    CdlExprOp_LogicalNot        = 11,   // !x
+    CdlExprOp_BitNot            = 12,   // ~x
+    CdlExprOp_Indirect          = 13,   // *x
+    CdlExprOp_Active            = 14,   // ?x
+    CdlExprOp_Function          = 15,   // sin(x)
+    CdlExprOp_Multiply          = 16,   // x * y
+    CdlExprOp_Divide            = 17,   // x / y
+    CdlExprOp_Remainder         = 18,   // x % y
+    CdlExprOp_Add               = 19,   // x + y
+    CdlExprOp_Subtract          = 20,   // x - y
+    CdlExprOp_LeftShift         = 21,   // x << y
+    CdlExprOp_RightShift        = 22,   // x >> y
+    CdlExprOp_LessThan          = 23,   // x < y
+    CdlExprOp_LessEqual         = 24,   // x <= y
+    CdlExprOp_GreaterThan       = 25,   // x > y
+    CdlExprOp_GreaterEqual      = 26,   // x >= y
+    CdlExprOp_Equal             = 27,   // x == y
+    CdlExprOp_NotEqual          = 28,   // x != y
+    CdlExprOp_BitAnd            = 29,   // x & y
+    CdlExprOp_BitXor            = 30,   // x ^ y
+    CdlExprOp_BitOr             = 31,   // x | y
+    CdlExprOp_And               = 32,   // x && y
+    CdlExprOp_Or                = 33,   // x || y
+    CdlExprOp_Cond              = 34,   // x ? a : b
+    CdlExprOp_StringConcat      = 35,   // x . y
+    CdlExprOp_Implies           = 36,   // x implies y
+    CdlExprOp_Xor               = 37,   // x xor y
+    CdlExprOp_Eqv               = 38    // x eqv y
+};
+
+// ----------------------------------------------------------------------------
+// A subexpression consists of an operation, possibly some constant
+// data, and possibly indices into the subexpression vector.
+// Normally some unions would be used, but unions and objects such
+// as std::string do not mix, and the amount of memory involved is
+// not big enough to really worry about.
+
+#define CdlFunction_MaxArgs     3
+struct CdlSubexpression {
+
+    CdlExprOp           op;
+    CdlSimpleValue      constants;              // String, integer or double constant
+    int                 reference_index;        // iff CdlExprOp_Reference
+
+    int                 lhs_index;              // for all non-constant operators
+    int                 rhs_index;              // for binary and ternary operators only
+    int                 rrhs_index;             // only for ternary operators.
+    
+    int                 func;                   // iff CdlExprOp_Function
+    int                 args[CdlFunction_MaxArgs];
+};
+
+//}}}
+//{{{  CdlFunction                      
+
+// ----------------------------------------------------------------------------
+// Generic support for function parsing, evaluation, and inference. The
+// implementation is extensible so that functions can be added to the
+// core via static constructors.
+
+class CdlFunction {
+    
+    friend class CdlTest;
+    
+  public:
+    CdlFunction(const char* /* name */, int /* no_args */,
+                void (*)(CdlExpression, const CdlSubexpression&),
+                void (*)(CdlEvalContext&, CdlExpression, const CdlSubexpression&, CdlSimpleValue&),
+                bool (*)(CdlTransaction, CdlExpression, unsigned int, bool, int),
+                bool (*)(CdlTransaction, CdlExpression, unsigned int, CdlSimpleValue&, int)
+                );
+    ~CdlFunction();
+    
+    static bool         is_function(std::string, int&);
+    static std::string  get_name(int);
+    static int          get_args_count(int);
+
+    static void         check(CdlExpression, const CdlSubexpression&);
+    static void         eval(CdlEvalContext&, CdlExpression, const CdlSubexpression&, CdlSimpleValue&);
+    static bool         infer_bool(CdlTransaction, CdlExpression, unsigned int, bool, int);
+    static bool         infer_value(CdlTransaction, CdlExpression, unsigned int, CdlSimpleValue&, int);
+
+    static void         (*null_check)(CdlExpression, const CdlSubexpression&);
+    static bool         (*null_infer_bool)(CdlTransaction, CdlExpression, unsigned int, bool, int);
+    static bool         (*null_infer_value)(CdlTransaction, CdlExpression, unsigned int, CdlSimpleValue&, int);
+    
+  protected:
+
+  private:
+    // Keep track of all functions in the system
+    static std::vector<CdlFunction*>    all_functions;
+
+    // Each function object is given a unique id during initialization
+    static int          next_id;
+    int                 id;
+
+    // Provided by the constructor
+    const char*         name;
+    int                 number_args;
+    void                (*check_fn)(CdlExpression, const CdlSubexpression&);
+    void                (*eval_fn)(CdlEvalContext&, CdlExpression, const CdlSubexpression&, CdlSimpleValue&);
+    bool                (*infer_bool_fn)(CdlTransaction, CdlExpression, unsigned int, bool, int);
+    bool                (*infer_value_fn)(CdlTransaction, CdlExpression, unsigned int, CdlSimpleValue&, int);
+    
+    // The default constructor is illegal
+    CdlFunction();
+};
+
+//}}}
+//{{{  CdlExpression                    
+
+// ----------------------------------------------------------------------------
+// And now for the expression class itself.
+
+class CdlExpressionBody {
+
+    friend class CdlTest;
+    
+  public:
+
+    // The default constructor is basically a no-op, new expression
+    // objects only get created as a consequence of parsing. However
+    // it exists and is protected for the convenience of derived
+    // classes. The copy constructor is protected, allowing parsing
+    // code to first parse an expression and then copy the expression
+    // into a higher level object. The assignment operator is illegal.
+    // There is no reason to hide the destructor.
+    virtual ~CdlExpressionBody();
+
+    // An expression involves three pieces of data. There is a vector
+    // of subexpressions. It is also necessary to know where
+    // evaluation should being, in accordance with operator precedence
+    // rules. And there is a vector of CdlReference objects - this
+    // needs to be kept separate from the subexpression vector because
+    // CdlReference objects are comparatively tricky.
+    //
+    // All of this data is public and can be readily inspected by the
+    // inference engine, by conflict detection code, by diagnostic
+    // code, etc.
+    std::vector<CdlSubexpression>       sub_expressions;
+    int                                 first_subexpression;
+    std::vector<CdlReference>           references;
+    bool                                update(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+    
+    // There are a number of parsing functions. The first one is
+    // used by higher-level code to parse a single expression. Its
+    // argument is a single string (which may be the result of
+    // concatenating several Tcl arguments), and at the end of the
+    // parse operation there should be no further data. The result
+    // will either be a new expression object or a parsing exception
+    // to be caught by higher level code.
+    static CdlExpression        parse(std::string);
+
+    // This is used when parsing list expressions, which involve a
+    // sequence of ordinary expressions and possibly range operators.
+    // The whole list expression lives in a single string, and it is
+    // necessary to provide an index indicating where in the string
+    // parsing should begin. It is also useful to return details of
+    // the token that caused parsing to terminate (EOD, Range, or
+    // the start of something else).
+    static CdlExpression        parse(std::string, int&, CdlExprOp&, int&);
+
+    // A goal expression is derived from an ordinary expression but
+    // has somewhat different rules for evaluating. Parsing a goal
+    // expression involves parsing a number of ordinary expressions
+    // with implicit && operators between them, and it requires
+    // a parsing function that can be used to extend an existing
+    // expression.
+    //
+    // NOTE: possibly this should should be a protected member, since
+    // its main use is in parsing goal expressions. 
+    static void continue_parse(CdlExpression, std::string, int&, CdlExprOp&, int&);
+
+    // Evaluating expressions. Note that this may fail at run-time
+    // because of errors that cannot be caught sensibly when the
+    // expression is read in, for example arithmetic overflow or
+    // division by zero. Because such failures are a possibility
+    // anyway no special action is taken to prevent an expression
+    // with e.g. an unresolved reference from being evaluated.
+    //
+    // eval() is the public interface, and manages
+    // CdlConflict_EvalException objects. eval_internal() is for use
+    // by list and goal expressions.
+    void eval(CdlEvalContext&, CdlSimpleValue&);
+    void eval_internal(CdlEvalContext&, CdlSimpleValue&);
+    void eval_subexpression(CdlEvalContext&, int, CdlSimpleValue&);
+    
+    // The full original expression is useful for diagnostics purposes
+    std::string get_original_string() const;
+    
+    bool        check_this(cyg_assert_class_zeal cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+
+    // The default constructor does very little, the main work
+    // is done by the various parsing functions. However it is
+    // available to derived classes, especially goal expressions.
+    CdlExpressionBody();
+    
+    // The copy constructor has to be usable by derived classes,
+    // e.g. CdlExpressionProperty
+    CdlExpressionBody(const CdlExpressionBody&);
+    
+  private:
+
+    
+    // The assignment operator is illegal.
+    CdlExpressionBody&  operator=(const CdlExpressionBody&);
+
+    // The string that was parsed originally
+    std::string                 expression_string;
+    
+    enum {
+        CdlExpressionBody_Invalid       = 0,
+        CdlExpressionBody_Magic         = 0x760293a3
+    } cdlexpressionbody_cookie;
+};
+
+//}}}
+//{{{  CdlListExpression                
+
+// ----------------------------------------------------------------------------
+// The main use of list expressions is for the legal_values
+// properties. Essentially a list expression is just a vector of
+// ordinary expressions and ranges of expressions. 
+
+class CdlListExpressionBody {
+
+    friend class CdlTest;
+
+  public:
+
+    // Availability of constructors etc. is as per the ordinary
+    // expression class.
+    virtual ~CdlListExpressionBody();
+    
+    // The data associated with a list expression is a vector of
+    // expressions, plus a vector of expression pairs constituting
+    // ranges. As with ordinary expressions the data is fully public
+    // and can be readily examined by e.g. the inference engine.
+    std::vector<CdlExpression>                                  data;
+    std::vector<std::pair<CdlExpression,CdlExpression> >        ranges;
+
+    // Parsing. This involves taking a single string, typically from
+    // a CDL script, and parsing one or more ordinary expressions.
+    static CdlListExpression parse(std::string);
+    
+    // Evaluation support. A list expression evaluates to a list value.
+    void eval(CdlEvalContext&, CdlListValue&);
+
+    // More commonly client code is going to be interested in whether
+    // or not a particular value is a legal member. The result
+    // cache ensures that it is possible to 
+    bool is_member(CdlEvalContext&, CdlSimpleValue&);
+    bool is_member(CdlEvalContext&, std::string);
+    bool is_member(CdlEvalContext&, cdl_int);
+    bool is_member(CdlEvalContext&, double);
+
+    // The full original expression is useful for diagnostics purposes
+    std::string get_original_string() const;
+    
+    bool check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+    CdlListExpressionBody(const CdlListExpressionBody&);
+    bool        update(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+
+  private:
+
+    CdlListExpressionBody();
+    CdlListExpressionBody& operator=(const CdlListExpressionBody&);
+
+    void eval_internal(CdlEvalContext&, CdlListValue&);
+    std::string         expression_string;
+    
+    enum {
+        CdlListExpressionBody_Invalid   = 0,
+        CdlListExpressionBody_Magic     = 0x7da4bcc2
+    } cdllistexpressionbody_cookie;
+};
+
+//}}}
+//{{{  CdlGoalExpression                
+
+// ----------------------------------------------------------------------------
+// A goal expression inherits privately from ordinary expressions. Essentially
+// a goal expression is simply a set of ordinary expressions separated by &&,
+// but it can only be evaluated to a boolean. The parse() and eval() members
+// of the base class should not be exposed. There is a member to get hold of
+// the underlying ordinary expression, for use by e.g. the inference engine.
+
+class CdlGoalExpressionBody : private CdlExpressionBody {
+
+    friend class CdlTest;
+
+    typedef CdlExpressionBody inherited;
+
+  public:
+    virtual ~CdlGoalExpressionBody();
+
+    static CdlGoalExpression parse(std::string);
+
+    // A few variants of the eval() member, with a choice of returning
+    // by value or by reference. The latter provide consistency with the
+    // other expression classes.
+    bool eval(CdlEvalContext&);
+    void eval(CdlEvalContext&, bool&);
+
+    // Provide public access to the underlying expression object,
+    // useful for the inference engine
+    CdlExpression               get_expression();
+    
+    // The full original expression is useful for diagnostics purposes
+    std::string get_original_string() const;
+    
+    bool check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+    CdlGoalExpressionBody(const CdlGoalExpressionBody&);
+
+
+  private:
+
+    CdlGoalExpressionBody();
+    CdlGoalExpressionBody& operator=(const CdlGoalExpressionBody&);
+    void eval_internal(CdlEvalContext&, bool&);
+    
+    std::string expression_string;
+    
+    enum {
+        CdlGoalExpressionBody_Invalid = 0,
+        CdlGoalExpressionBody_Magic   = 0x5a58bb24
+    } cdlgoalexpressionbody_cookie;
+};
+
+//}}}
+//{{{  CdlInfer                         
+
+// ----------------------------------------------------------------------------
+// A utility class related to inference. This exports the main functions
+// needed, allowing e.g. per-function inference routines from func.cxx to
+// interact with the main inference engine.
+
+class CdlInfer {
+  public:
+    static bool make_active(CdlTransaction, CdlNode, int /* level */);
+    static bool make_inactive(CdlTransaction, CdlNode, int /* level */);
+    static bool set_valuable_value(CdlTransaction, CdlValuable, CdlSimpleValue&, int /* level */);
+    static bool set_valuable_bool(CdlTransaction, CdlValuable, bool, int /* level */);
+    static bool subexpr_value(CdlTransaction, CdlExpression, unsigned int /* index */, CdlSimpleValue& goal, int /* level */);
+    static bool subexpr_bool(CdlTransaction, CdlExpression, unsigned int /* index */, bool, int /* level */);
+
+  private:
+    CdlInfer();
+};
+
+//}}}
+
+//}}}
+//{{{  CdlConflict classes                              
+
+// ----------------------------------------------------------------------------
+// As a configuration is created and modified there will be times when
+// things are not completely consistent. There may be a reference to
+// some option that is not in any package in the current
+// configuration. An option may have an invalid value, possibly as a
+// side effect of a change to some other option. There may be a
+// dependency that is not satisfied. There may be other types of
+// conflict.
+//
+// The library provides a base class CdlConflict, and a number of
+// derived classes for common types of conflict such as unresolved
+// references. All conflicts are associated with a CdlNode and a
+// property within that. It is possible to use dynamic_cast<> to find
+// out the exact type of a conflict, or alternatively to use the
+// virtual member function get_explanation().
+//
+// Conflicts may be disabled by the user if they are not actually
+// important as far as application code is concerned. In other words
+// the end user is allowed to override the constraints specified in
+// the CDL. This information is saved with the configuration data.
+// Preferably the user should give an explanation for why the conflict
+// is disabled, to serve as a reminder some months later when the
+// configuration is reloaded.
+//
+//
+// Conflicts have a fairly complicated life cycle. First it is
+// necessary to distinguish between structural and normal conflicts. A
+// structural conflict is typically caused by a reference to a
+// non-existent valuable. These conflicts are generally created only
+// when something is loaded, and only go away when something is
+// unloaded. A normal conflict is typically related to a value, for
+// example a value outside the legal range, or a "requires" property
+// that is not satisfied.
+//
+// Conflicts are created and destroyed in the context of a
+// transaction, which in turn operates in the context of a toplevel.
+// If the transaction is committed then new conflicts get added to the
+// appropriate toplevel list, and destroyed conflicts get removed from
+// the toplevel list. The transaction field indicates whether the
+// conflict is currently per-transaction or global.
+//
+// Transactions may get nested, i.e. a conflict may get created as
+// part of a sub-transaction, and when that sub-transaction is committed
+// the conflict is moved to the parent transaction.
+//
+// For each toplevel, libcdl keeps track of all conflicts. This only
+// applies to committed conflicts, per-transaction conflicts are not
+// accessible in this way.
+//
+// As part of a transaction, libcdl may attempt to find solutions for
+// particular conflicts, and those solutions may get installed
+// automatically. No attempt is made to keep track of solutions
+// on a global basis, only on a per-transaction basis.
+
+class CdlConflictBody {
+    
+    friend class CdlTest;
+
+    // Transactions and conflicts are closely connected
+    friend class CdlTransactionBody;
+    
+  public:
+
+    // Creation happens only inside a derived class.
+    // Clearing a conflict only happens inside transactions.
+    // Destroying a conflict only happens from inside a
+    // per-transaction clear(), or during a transaction commit.
+    
+    // Is this conflict part of a transaction, or has it been committed to the toplevel.
+    CdlTransaction      get_transaction() const;
+
+    // Is inference implemented for this type of conflict?
+    virtual bool        resolution_implemented() const;
+
+    // Try to resolve an existing global conflict. A new transaction
+    // is created for this operation, the conflict is resolved within
+    // that transaction, and then CdlTransaction::body() is used to
+    // handle inference callbacks, commits, etc. See also
+    // CdlToplevel::resolve_conflicts() and
+    // CdlToplevel::resolve_all_conflicts(). The conflict may cease to
+    // exist as a side-effect of this call.
+    void                resolve();
+  
+    // Keep track of whether or not this conflict has a solution
+    // 1) a conflict may have a current solution. This gets invalidated
+    //    whenever there is a change to a value that was referenced
+    //    while identifying the solution.
+    //
+    //    A current solution is indicated by a non-empty solution vector.
+    //    
+    // 2) a conflict may not have a current solution. Again this gets
+    //    invalidated whenever a referred value changes. There is a boolean
+    //    to keep track of this.
+    //
+    // 3) a conflict may not have a current solution, but another run of
+    //    the inference engine may find one.
+    bool                has_known_solution() const;
+    bool                has_no_solution() const;
+    const std::vector<std::pair<CdlValuable, CdlValue> >& get_solution() const;
+    const std::set<CdlValuable>& get_solution_references() const;
+    void                clear_solution();
+    
+    // Provide a text message "explaining" the conflict.
+    // This only makes sense for derived classes.
+    virtual std::string get_explanation() const = 0;
+
+    // Basic information access.
+    CdlNode             get_node() const;
+    CdlProperty         get_property() const;
+    bool                is_structural() const;
+
+    // Enabling and disabling conflicts currently happens outside the
+    // context of any transaction.
+    // FIXME: these are not currently implemented. It would be necessary
+    // to store the information in the savefile, which requires an
+    // unambiguous way of identifying a conflict that is likely to
+    // survice package version changes.
+    void                disable(std::string);
+    void                enable();
+    bool                is_enabled() const;
+    std::string         get_disabled_reason() const;
+    
+    bool check_this(cyg_assert_class_zeal zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+    CdlConflictBody(CdlTransaction, CdlNode, CdlProperty, bool /* structural */);
+    
+    // The destructor gets accessed from inside the friend transaction class,
+    // either during a clear_conflict() or during a transaction commit.
+    virtual ~CdlConflictBody();
+    
+    // All conflicts are associated with a node and a property.
+    // This information will be useful to derived classes'
+    // implementations of get_explanation()
+    CdlNode             node;
+    CdlProperty         property;
+    
+  private:
+
+    // Attempt to resolve a conflict in a sub-transaction
+    // This is invoked from inside the transaction resolve code.
+    // There are additional exported interfaces inside and outside
+    // the transaction class.
+    virtual bool        inner_resolve(CdlTransaction, int);
+    
+    // Keep track of the transaction in which this conflict was created.
+    // The field is cleared at the end of a transaction.
+    CdlTransaction      transaction;
+
+    // Usually the derived class will decide whether or not
+    // this conflict is structural in nature, but the data
+    // needs to be available at base constructor time so
+    // a virtual function is not appropriate.
+    bool                structural;
+    
+    // Solution support
+    bool                                           no_solution;
+    std::vector<std::pair<CdlValuable, CdlValue> > solution;
+    std::set<CdlValuable>                          solution_references;
+    void update_solution_validity(CdlValuable);
+    
+    // Users may disable a conflict. Usually they will have to
+    // supply a reason for this.
+    bool                enabled;
+    std::string         reason;
+
+    enum {
+        CdlConflictBody_Invalid = 0,
+        CdlConflictBody_Magic   = 0x073e8853
+    } cdlconflictbody_cookie;
+
+    // Illegal operations. Conflicts always live on the heap.
+    CdlConflictBody();
+    CdlConflictBody(const CdlConflictBody&);
+    CdlConflictBody& operator=(const CdlConflictBody&);
+};
+
+// ----------------------------------------------------------------------------
+// An unresolved conflict means that there is a reference in some
+// property to an entity that is not yet in the current configuration.
+// The class provides convenient access to the name of the unresolved
+// entity. 
+
+class CdlConflict_UnresolvedBody : public CdlConflictBody {
+    
+    friend class CdlTest;
+  public:
+
+    static void         make(CdlTransaction, CdlNode, CdlProperty, std::string);
+
+    std::string         get_target_name() const;
+    std::string         get_explanation() const;
+    static bool         test(CdlConflict);
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+
+  private:
+    virtual ~CdlConflict_UnresolvedBody();
+    CdlConflict_UnresolvedBody(CdlTransaction, CdlNode, CdlProperty, std::string);
+    std::string         target_name;
+    enum {
+        CdlConflict_UnresolvedBody_Invalid      = 0,
+        CdlConflict_UnresolvedBody_Magic        = 0x1b24bb8a
+    } cdlconflict_unresolvedbody_cookie;
+    
+    CdlConflict_UnresolvedBody();
+    CdlConflict_UnresolvedBody(const CdlConflict_UnresolvedBody&);
+    CdlConflict_UnresolvedBody& operator=(const CdlConflict_UnresolvedBody&);
+};
+
+// ----------------------------------------------------------------------------
+// An illegal value can be caused because of a number of properties:
+// legal_values, check_proc, entry_proc, ... In the case of the latter
+// the Tcl code should provide text explaining why the value is
+// illegal.
+
+class CdlConflict_IllegalValueBody : public CdlConflictBody {
+
+    friend class CdlTest;
+
+  public:
+
+    static void         make(CdlTransaction, CdlNode, CdlProperty);
+
+    bool                resolution_implemented() const;
+    
+    std::string         get_explanation() const;
+    void                set_explanation(std::string);
+    static bool         test(CdlConflict);
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+
+  private:
+    virtual ~CdlConflict_IllegalValueBody();
+    bool    inner_resolve(CdlTransaction, int);
+    CdlConflict_IllegalValueBody(CdlTransaction, CdlNode, CdlProperty);
+    std::string explanation;
+    enum {
+        CdlConflict_IllegalValueBody_Invalid    = 0,
+        CdlConflict_IllegalValueBody_Magic      = 0x4fb27ed1
+    } cdlconflict_illegalvaluebody_cookie;
+
+    CdlConflict_IllegalValueBody();
+    CdlConflict_IllegalValueBody(const CdlConflict_IllegalValueBody&);
+    CdlConflict_IllegalValueBody& operator=(const CdlConflict_IllegalValueBody&);
+};
+
+// ----------------------------------------------------------------------------
+// There are times when expression evaluation will fail, e.g. because of
+// a division by zero. The explanation is supplied by the evaluation code.
+
+class CdlConflict_EvalExceptionBody : public CdlConflictBody {
+
+    friend class CdlTest;
+
+  public:
+
+    static void         make(CdlTransaction, CdlNode, CdlProperty, std::string);
+
+    std::string         get_explanation() const;
+    void                set_explanation(std::string);   // mainly for internal use
+    static bool         test(CdlConflict);
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+
+  private:
+    virtual ~CdlConflict_EvalExceptionBody();
+    CdlConflict_EvalExceptionBody(CdlTransaction, CdlNode, CdlProperty, std::string);
+    std::string explanation;
+    enum {
+        CdlConflict_EvalExceptionBody_Invalid   = 0,
+        CdlConflict_EvalExceptionBody_Magic     = 0x7e64bc41
+    } cdlconflict_evalexceptionbody_cookie;
+};
+
+// ----------------------------------------------------------------------------
+// A goal expression evaluates to false. Producing sensible diagnostics
+// depends on a detailed understanding of goal expressions, which will 
+// have to wait until the inference engine comes along.
+
+class CdlConflict_RequiresBody : public CdlConflictBody {
+
+    friend class CdlTest;
+
+  public:
+
+    static void         make(CdlTransaction, CdlNode, CdlProperty);
+    bool                resolution_implemented() const;
+    
+    std::string         get_explanation() const;
+    static bool         test(CdlConflict);
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+
+  private:
+    virtual ~CdlConflict_RequiresBody();
+    bool     inner_resolve(CdlTransaction, int);
+    CdlConflict_RequiresBody(CdlTransaction, CdlNode, CdlProperty);
+    enum {
+        CdlConflict_RequiresBody_Invalid        = 0,
+        CdlConflict_RequiresBody_Magic          = 0x78436331
+    } cdlconflict_requiresbody_cookie;
+};
+
+// ----------------------------------------------------------------------------
+// There is an unusual problem in the configuration data somewhere.
+// For example, a parent property can be resolved but the target is
+// not a container. There is not a lot that the user can do about
+// problems like this, apart from complaining to the component vendor,
+// but the problem should not be ignored either.
+
+class CdlConflict_DataBody : public CdlConflictBody {
+
+    friend class CdlTest;
+
+  public:
+
+    static void         make(CdlTransaction, CdlNode, CdlProperty, std::string);
+
+    std::string         get_explanation() const;
+    static bool         test(CdlConflict);
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+
+  private:
+    virtual ~CdlConflict_DataBody();
+    CdlConflict_DataBody(CdlTransaction, CdlNode, CdlProperty, std::string);
+    std::string message;
+    enum {
+        CdlConflict_DataBody_Invalid    = 0,
+        CdlConflict_DataBody_Magic      = 0x2cec7ad8
+    } cdlconflict_databody_cookie;
+};
+
+//}}}
+//{{{  CdlProperty class and derived classes            
+
+//{{{  Description                              
+
+// ---------------------------------------------------------------------------
+// There are many different kinds of property. An alias property contains
+// a simple string. A check_proc property contains a fragment of Tcl code
+// which can be represented internally as a string, as bytecodes, or both.
+// A requires property contains a goal expression. ...
+//
+// The implementation involves a base class CdlProperty and various
+// derived classes such as CdlProperty_StringBody and
+// CdlProperty_ExpressionBody.
+//
+// New CdlProperty objects get created only when reading in CDL scripts,
+// while executing commands like alias and requires. These commands are
+// implemented as C++ functions hooked into the TCL interpreter. The
+// property arguments are available as an argc/argv pair. Each command
+// will parse and validate the arguments and then invoke an appropriate
+// constructor.
+
+//}}}
+//{{{  CdlPropertyId_xxx                        
+
+// ----------------------------------------------------------------------------
+// Properties are identified by strings rather than by an enum or anything
+// like that. A string-based approach allows new properties to be added at
+// any time without invalidating an existing enum, complicating switch()
+// statements, etc. There are some performance issues but these are
+// manageable.
+//
+// A disadvantage of using strings is that there is a problem with
+// typos. Mistyping something like CdlPropertyId_Compile will generally
+// result in a compile-time failure. Mistyping "Complie" will cause
+// strange behaviour at run-time and is hard to track down.
+//
+// A compromise solution is to have #define'd string constants.
+
+#define CdlPropertyId_ActiveIf          "ActiveIf"
+#define CdlPropertyId_BuildProc         "BuildProc"
+#define CdlPropertyId_Calculated        "Calculated"
+#define CdlPropertyId_CancelProc        "CancelProc"
+#define CdlPropertyId_CheckProc         "CheckProc"
+#define CdlPropertyId_Compile           "Compile"
+#define CdlPropertyId_ConfirmProc       "ConfirmProc"
+#define CdlPropertyId_DecorationProc    "DecorationProc"
+#define CdlPropertyId_DefaultValue      "DefaultValue"
+#define CdlPropertyId_Define            "Define"
+#define CdlPropertyId_DefineHeader      "DefineHeader"
+#define CdlPropertyId_DefineProc        "DefineProc"
+#define CdlPropertyId_Description       "Description"
+#define CdlPropertyId_Dialog            "Dialog"
+#define CdlPropertyId_Display           "Display"
+#define CdlPropertyId_DisplayProc       "DisplayProc"
+#define CdlPropertyId_Doc               "Doc"
+#define CdlPropertyId_EntryProc         "EntryProc"
+#define CdlPropertyId_Flavor            "Flavor"
+#define CdlPropertyId_DefineFormat      "DefineFormat"
+#define CdlPropertyId_Group             "Group"
+#define CdlPropertyId_Hardware          "Hardware"
+#define CdlPropertyId_IfDefine          "IfDefine"
+#define CdlPropertyId_Implements        "Implements"
+#define CdlPropertyId_IncludeDir        "IncludeDir"
+#define CdlPropertyId_IncludeFiles      "IncludeFiles"
+#define CdlPropertyId_InitProc          "InitProc"
+#define CdlPropertyId_InstallProc       "InstallProc"
+#define CdlPropertyId_LegalValues       "LegalValues"
+#define CdlPropertyId_Library           "Library"
+#define CdlPropertyId_LicenseProc       "LicenseProc"
+#define CdlPropertyId_Make              "Make"
+#define CdlPropertyId_Makefile          "Makefile"
+#define CdlPropertyId_MakeObject        "MakeObject"
+#define CdlPropertyId_NoDefine          "NoDefine"
+#define CdlPropertyId_Object            "Object"
+#define CdlPropertyId_Parent            "Parent"
+#define CdlPropertyId_Requires          "Requires"
+#define CdlPropertyId_Screen            "Screen"
+#define CdlPropertyId_Script            "Script"
+#define CdlPropertyId_UpdateProc        "UpdateProc"
+#define CdlPropertyId_Wizard            "Wizard"
+
+//}}}
+//{{{  Base class                               
+
+// ----------------------------------------------------------------------------
+// The base class is never used directly. Instead the appropriate derived
+// objects are instantiated and when appropriate it will be necessary to
+// do a dynamic cast from a CdlProperty to e.g. a CdlProperty_String.
+
+class CdlPropertyBody {
+
+    friend class CdlTest;
+    
+  public:
+    // The destructor is public, to avoid possible problems with STL.
+    virtual ~CdlPropertyBody();
+    
+    // These routines provide access to the basic data.
+    std::string get_property_name() const;
+
+    // Get hold of the arguments that were present in the original data.
+    int         get_argc() const;
+    bool        has_option(std::string) const;
+    std::string get_option(std::string) const;
+    const std::vector<std::string>&     get_argv() const;
+    const std::vector<std::pair<std::string,std::string> >&     get_options() const;
+    
+    // Resolve any references, or generate/update appropriate conflict
+    // objects. The default implementation is a no-op because not all
+    // properties involve references.
+    virtual void update(CdlTransaction, CdlNode /* source */, CdlNode /* dest */, CdlUpdate);
+    
+    bool check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+
+    // The legal constructor can only get invoked from a derived class
+    // constructor. The first argument identifies the property, e.g.
+    // CdlPropertyId_Doc (which is just #define'd to the string
+    // "doc").
+    //
+    // The argc and argv fields provide access to the original
+    // data in the command that resulted in the property being
+    // constructed. Often but not always argv[0] will be the same as
+    // the property id. The argv information is stored mainly for
+    // diagnostics purposes, it may be removed in future to avoid
+    // wasting memory.
+    //
+    // The options field is the result of parsing options such
+    // as -library=libextras.a. It consists of a vector of
+    // <name/value> pairs, and is usually obtained via
+    // CdlParse::parse_options().
+    CdlPropertyBody(CdlNode, std::string, int argc, const char* argv[], std::vector<std::pair<std::string,std::string> >&);
+    
+  private:
+    // This string indicates the command used to define this property,
+    // e.g. "doc" or "define_proc". It is provided to the constructor.
+    std::string                 name;
+
+    // All property data comes out of a file and gets rid via a
+    // Tcl interpreter. The raw file data is stored with the property,
+    // mainly for diagnostics purposes.
+    std::vector<std::string>    argv;
+
+    std::vector<std::pair<std::string, std::string> >   options;
+
+    // The usual set of illegal operations.
+    CdlPropertyBody();
+    CdlPropertyBody(const CdlPropertyBody&);
+    CdlPropertyBody& operator=(const CdlPropertyBody&);
+    
+    enum {
+        CdlPropertyBody_Invalid = 0,
+        CdlPropertyBody_Magic   = 0x60dd58f4
+    } cdlpropertybody_cookie;
+};
+
+//}}}
+//{{{  CdlProperty_Minimal                      
+
+// ----------------------------------------------------------------------------
+// This class is used for properties that are simple flags, e.g. no_define.
+// There should be no additional data associated with such properties.
+
+class CdlProperty_MinimalBody : public CdlPropertyBody {
+
+    friend class CdlTest;
+    
+  public:
+    static CdlProperty_Minimal   make(CdlNode, std::string, int, const char*[], std::vector<std::pair<std::string,std::string> >&);
+    virtual ~CdlProperty_MinimalBody( );
+    bool                        check_this( cyg_assert_class_zeal = cyg_quick ) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+    
+  private:
+    typedef CdlPropertyBody     inherited;
+    
+    CdlProperty_MinimalBody(CdlNode, std::string, int, const char*[], std::vector<std::pair<std::string,std::string> >&);
+    enum {
+        CdlProperty_MinimalBody_Invalid = 0,
+        CdlProperty_MinimalBody_Magic   = 0x25625b8c
+    } cdlproperty_minimalbody_cookie;
+
+    CdlProperty_MinimalBody();
+    CdlProperty_MinimalBody(const CdlProperty_MinimalBody&);
+    CdlProperty_MinimalBody& operator=(const CdlProperty_MinimalBody&);
+};
+
+//}}}
+//{{{  CdlProperty_String                       
+
+// ----------------------------------------------------------------------------
+// A string property contains a single piece of additional data in the form
+// of a string.
+
+class CdlProperty_StringBody : public CdlPropertyBody {
+
+    friend class CdlTest;
+    
+  public:
+    static CdlProperty_String    make(CdlNode, std::string, std::string, int, const char*[],
+                                      std::vector<std::pair<std::string,std::string> >&);
+    virtual ~CdlProperty_StringBody();
+
+    std::string                 get_string(void) const;
+    bool                        check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+    
+  private:
+    typedef CdlPropertyBody     inherited;
+    
+    CdlProperty_StringBody(CdlNode, std::string /* id */, std::string /* data */, int, const char*[],
+                           std::vector<std::pair<std::string,std::string> >&);
+    std::string                 data;
+    enum {
+        CdlProperty_StringBody_Invalid = 0,
+        CdlProperty_StringBody_Magic   = 0x78d1ca94
+    } cdlproperty_stringbody_cookie;
+
+    // The only legal constructor supplies all the data.
+    CdlProperty_StringBody();
+    CdlProperty_StringBody(const CdlProperty_StringBody&);
+    CdlProperty_StringBody& operator=(const CdlProperty_StringBody&);
+};
+
+//}}}
+//{{{  CdlProperty_TclCode                      
+
+// ----------------------------------------------------------------------------
+// A TclCode property is currently equivalent to a string property. In
+// future this may change to allow the byte-compiled versions of the
+// script to be stored.
+//
+// One of the properties, "screen" inside a cdl_wizard, also takes
+// an integer. Rather than create yet another class, this is handled
+// by a separate constructor.
+
+
+class CdlProperty_TclCodeBody : public CdlPropertyBody {
+
+    friend class CdlTest;
+
+  public:
+    static CdlProperty_TclCode   make(CdlNode, std::string, cdl_tcl_code, int, const char*[],
+                                      std::vector<std::pair<std::string,std::string> >&);
+    static CdlProperty_TclCode   make(CdlNode, std::string, cdl_int, cdl_tcl_code, int, const char*[],
+                                      std::vector<std::pair<std::string,std::string> >&);
+    virtual ~CdlProperty_TclCodeBody();
+    
+    cdl_int                     get_number(void) const;
+    const cdl_tcl_code&         get_code(void)   const;
+    bool                        check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  private:
+    typedef CdlPropertyBody     inherited;
+    
+    CdlProperty_TclCodeBody(CdlNode, std::string, cdl_int, cdl_tcl_code, int, const char*[],
+                            std::vector<std::pair<std::string,std::string> >&);
+
+    cdl_int                     number;
+    cdl_tcl_code                code;
+    enum {
+        CdlProperty_TclCodeBody_Invalid = 0,
+        CdlProperty_TclCodeBody_Magic   = 0x7b14d4e5
+    } cdlproperty_tclcodebody_cookie;
+
+    CdlProperty_TclCodeBody();
+    CdlProperty_TclCodeBody(const CdlProperty_TclCodeBody&);
+    CdlProperty_TclCodeBody& operator=(const CdlProperty_TclCodeBody&);
+    
+};
+
+//}}}
+//{{{  CdlProperty_StringVector                 
+
+// ----------------------------------------------------------------------------
+// This is used for multiple constant strings, as opposed to a list
+// expression which requires evaluation. One example is a list
+// of aliases.
+
+class CdlProperty_StringVectorBody : public CdlPropertyBody {
+
+    friend class CdlTest;
+
+  public:
+    static CdlProperty_StringVector     make(CdlNode, std::string, const std::vector<std::string>&, int, const char*[],
+                                             std::vector<std::pair<std::string,std::string> >&);
+    virtual ~CdlProperty_StringVectorBody();
+    
+    const std::vector<std::string>&     get_strings() const;
+    std::string                         get_first_string() const;
+    unsigned int                        get_number_of_strings() const;
+    std::string                         get_string(unsigned int) const;                  
+    bool                                check_this(cyg_assert_class_zeal zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  private:
+    typedef CdlPropertyBody            inherited;
+    
+    CdlProperty_StringVectorBody(CdlNode, std::string, const std::vector<std::string>&, int, const char*[],
+                                 std::vector<std::pair<std::string,std::string> >&);
+
+    std::vector<std::string>            data;
+    enum {
+        CdlProperty_StringVectorBody_Invalid = 0,
+        CdlProperty_StringVectorBody_Magic   = 0x4ed039f3
+    } cdlproperty_stringvectorbody_cookie;
+
+    CdlProperty_StringVectorBody();
+    CdlProperty_StringVectorBody(const CdlProperty_StringVectorBody&);
+    CdlProperty_StringVectorBody& operator=(const CdlProperty_StringVectorBody&);
+};
+
+//}}}
+//{{{  CdlProperty_Reference                    
+
+// ----------------------------------------------------------------------------
+// This is used for properties such as wizard and dialog, where the data
+// identifies some other entity in the system. The class is both a property
+// and a reference object. Most of the desired functionality is provided by
+// inheritance from CdlReference.
+
+class CdlProperty_ReferenceBody : public CdlPropertyBody, public CdlReference {
+
+    friend class CdlTest;
+
+  public:
+    static CdlProperty_Reference make(CdlNode, std::string /* id */, std::string /* destination */,
+                                      CdlUpdateHandler, int, const char*[],
+                                      std::vector<std::pair<std::string,std::string> >&);
+    virtual ~CdlProperty_ReferenceBody();
+    
+    void update(CdlTransaction, CdlNode, CdlNode, CdlUpdate);
+    
+    bool check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  private:
+    typedef CdlPropertyBody     inherited_property;
+    typedef CdlReference        inherited_reference;
+
+    CdlUpdateHandler            update_handler;
+    
+    CdlProperty_ReferenceBody(CdlNode, std::string /* id */, std::string /* destination */, CdlUpdateHandler, int, const char*[],
+                              std::vector<std::pair<std::string,std::string> >&);
+    enum {
+        CdlProperty_ReferenceBody_Invalid = 0,
+        CdlProperty_ReferenceBody_Magic   = 0x78100339
+    } cdlproperty_referencebody_cookie;
+
+    CdlProperty_ReferenceBody();
+    CdlProperty_ReferenceBody(const CdlProperty_ReferenceBody&);
+    CdlProperty_ReferenceBody& operator=(const CdlProperty_Reference&);
+};
+
+//}}}
+//{{{  CdlProperty_Expression                   
+
+// ----------------------------------------------------------------------------
+// An expression property simply inherits its functionality from the basic
+// property class and from the expression class.
+
+class CdlProperty_ExpressionBody : public CdlPropertyBody, public CdlExpressionBody {
+
+    friend class CdlTest;
+    
+  public:
+    static CdlProperty_Expression       make(CdlNode, std::string, CdlExpression, CdlUpdateHandler, int, const char*[],
+                                             std::vector<std::pair<std::string,std::string> >&);
+    virtual ~CdlProperty_ExpressionBody();
+    void update(CdlTransaction, CdlNode, CdlNode, CdlUpdate);
+    bool check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  private:
+    typedef CdlPropertyBody     inherited_property;
+    typedef CdlExpressionBody   inherited_expression;
+
+    CdlProperty_ExpressionBody(CdlNode, std::string, CdlExpression, CdlUpdateHandler, int, const char*[],
+                               std::vector<std::pair<std::string,std::string> >&);
+    
+    CdlUpdateHandler update_handler;
+    enum {
+        CdlProperty_ExpressionBody_Invalid = 0,
+        CdlProperty_ExpressionBody_Magic   = 0x05fb4056
+    } cdlproperty_expressionbody_cookie;
+
+    CdlProperty_ExpressionBody();
+    CdlProperty_ExpressionBody(const CdlProperty_ExpressionBody&);
+    CdlProperty_ExpressionBody& operator=(const CdlProperty_ExpressionBody&);
+};
+
+//}}}
+//{{{  CdlProperty_ListExpression               
+
+// ----------------------------------------------------------------------------
+// Similarly a list property simply inherits from property and from
+// list expressions.
+
+class CdlProperty_ListExpressionBody : public CdlPropertyBody, public CdlListExpressionBody {
+
+    friend class CdlTest;
+    
+  public:
+    static CdlProperty_ListExpression   make(CdlNode, std::string, CdlListExpression, CdlUpdateHandler, int, const char*[],
+                                             std::vector<std::pair<std::string,std::string> >&);
+    virtual ~CdlProperty_ListExpressionBody();
+    void update(CdlTransaction, CdlNode, CdlNode, CdlUpdate);
+    bool check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  private:
+    typedef CdlPropertyBody         inherited_property;
+    typedef CdlListExpressionBody   inherited_expression;
+
+    CdlProperty_ListExpressionBody(CdlNode, std::string, CdlListExpression, CdlUpdateHandler, int, const char*[],
+                                   std::vector<std::pair<std::string,std::string> >&);
+
+    CdlUpdateHandler update_handler;
+    enum {
+        CdlProperty_ListExpressionBody_Invalid = 0,
+        CdlProperty_ListExpressionBody_Magic   = 0x6b0136f5
+    } cdlproperty_listexpressionbody_cookie;
+
+    CdlProperty_ListExpressionBody();
+    CdlProperty_ListExpressionBody(const CdlProperty_ListExpressionBody&);
+    CdlProperty_ListExpressionBody& operator=(const CdlProperty_ListExpressionBody&);
+};
+
+//}}}
+//{{{  CdlProperty_GoalExpression               
+
+// ----------------------------------------------------------------------------
+// And a goal property inherits from property and from goal expressions.
+
+class CdlProperty_GoalExpressionBody : public CdlPropertyBody, public CdlGoalExpressionBody {
+
+    friend class CdlTest;
+    
+  public:
+    static CdlProperty_GoalExpression   make(CdlNode, std::string, CdlGoalExpression, CdlUpdateHandler, int, const char*[],
+                                             std::vector<std::pair<std::string,std::string> >&);
+    virtual ~CdlProperty_GoalExpressionBody();
+    void update(CdlTransaction, CdlNode, CdlNode, CdlUpdate);
+    bool check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  private:
+    typedef CdlPropertyBody         inherited_property;
+    typedef CdlGoalExpressionBody   inherited_expression;
+
+    CdlProperty_GoalExpressionBody(CdlNode, std::string, CdlGoalExpression, CdlUpdateHandler, int, const char*[],
+                                   std::vector<std::pair<std::string,std::string> >&);
+    
+    CdlUpdateHandler update_handler;
+    enum {
+        CdlProperty_GoalExpressionBody_Invalid = 0,
+        CdlProperty_GoalExpressionBody_Magic   = 0x08b2b31e
+    } cdlproperty_goalexpressionbody_cookie;
+
+    CdlProperty_GoalExpressionBody();
+    CdlProperty_GoalExpressionBody(const CdlProperty_GoalExpressionBody&);
+    CdlProperty_GoalExpressionBody& operator=(const CdlProperty_GoalExpressionBody&);
+};
+
+//}}}
+
+//}}}
+//{{{  CdlParse class                                   
+
+// ----------------------------------------------------------------------------
+// This is another utility class for collecting together parsing-related
+// functions.
+//
+// Note that this is only a utility class. When libcdl is used for parsing
+// things not related to software configuration the new functionality
+// does not have to reside inside the CdlParse class, but it may be
+// possible to re-use some of the functionality in that class.
+
+class CdlParse {
+    
+  public:
+    // Utility routines.
+    static std::string  get_tcl_cmd_name(std::string);
+    static std::string  concatenate_argv(int, const char*[], int);
+    static int          parse_options(CdlInterpreter, std::string /* diag_prefix */, char** /* options */,
+                                               int /* argc */, const char*[] /* argv */, int /* start_index */,
+                                               std::vector<std::pair<std::string,std::string> >& /* result */);
+    static std::string  construct_diagnostic(CdlInterpreter, std::string /* classification */,
+                                             std::string /* sub-identifier */, std::string /* message */);
+                                       
+    static void         report_error(CdlInterpreter, std::string /* sub-identifier */, std::string /* message */);
+    static void         report_warning(CdlInterpreter, std::string /* sub-identifier */, std::string /* message */);
+    static void         clear_error_count(CdlInterpreter);
+    static int          get_error_count(CdlInterpreter);
+    static void         incr_error_count(CdlInterpreter, int=1);
+
+    static std::string  get_expression_error_location(void);
+    
+    // Support for Tcl's "unknown" command
+    static int          unknown_command(CdlInterpreter, int, const char*[]);
+    
+    // Property-related utilities
+    static void         report_property_parse_error(CdlInterpreter, std::string, std::string);
+    static void         report_property_parse_error(CdlInterpreter, CdlProperty, std::string);
+    static void         report_property_parse_warning(CdlInterpreter, std::string, std::string);
+    static void         report_property_parse_warning(CdlInterpreter, CdlProperty, std::string);
+    
+    // Utility parsing routines
+    static int  parse_minimal_property(CdlInterpreter, int, const char*[], std::string,
+                                       char**, void (*)(CdlInterpreter, CdlProperty_Minimal));
+    static int  parse_string_property(CdlInterpreter, int, const char*[], std::string,
+                                      char**, void (*)(CdlInterpreter, CdlProperty_String));
+    static int  parse_tclcode_property(CdlInterpreter, int, const char*[], std::string,
+                                       char**, void (*)(CdlInterpreter, CdlProperty_TclCode));
+    static int  parse_stringvector_property(CdlInterpreter, int, const char*[], std::string,
+                                            char**, void (*)(CdlInterpreter, CdlProperty_StringVector),
+                                            bool /* allow_empty */ = false);
+    static int  parse_reference_property(CdlInterpreter, int, const char*[], std::string,
+                                         char**, void (*)(CdlInterpreter, CdlProperty_Reference),
+                                         bool /* allow_empty */,
+                                         CdlUpdateHandler);
+    static int  parse_expression_property(CdlInterpreter, int, const char*[], std::string, 
+                                          char **, void (*)(CdlInterpreter, CdlProperty_Expression),
+                                          CdlUpdateHandler);
+    static int  parse_listexpression_property(CdlInterpreter, int, const char*[], std::string,
+                                              char **, void (*)(CdlInterpreter, CdlProperty_ListExpression),
+                                              CdlUpdateHandler);
+    static int  parse_goalexpression_property(CdlInterpreter, int, const char*[], std::string,
+                                              char **, void (*)(CdlInterpreter, CdlProperty_GoalExpression),
+                                              CdlUpdateHandler);
+};
+
+//}}}
+//{{{  CdlNode                                          
+
+// ----------------------------------------------------------------------------
+// A node object has a name and lives in a hierarchy. Each node keeps
+// track of the toplevel and owner. The memory overheads are
+// relatively small compared with the performance gains when such
+// information is needed.
+//
+// A node object also has a vector of properties, and can be referred to
+// by properties in other nodes. Some of the properties may result in
+// conflicts.
+
+class CdlNodeBody {
+
+    friend class CdlTest;
+
+    // Adding and removing nodes from the hierarchy is done
+    // by CdlToplevel members.
+    friend class CdlToplevelBody;
+
+    // CdlLoadable must be able to access the destructor
+    friend class CdlLoadableBody;
+    
+    // It is intended that CdlProperties will also add and remove themselves
+    friend class CdlPropertyBody;
+
+    // CdlReference bind and unbind operations need access to
+    // the referrers vector. So does CdlTransaction::commit()
+    friend class CdlReference;
+    friend class CdlTransactionBody;
+    
+  public:
+
+    // Basic information.
+    std::string         get_name() const;
+    CdlContainer        get_parent() const;
+    CdlLoadable         get_owner() const;
+    CdlToplevel         get_toplevel() const;
+
+    // Propagation support. Some updates such as active/inactive changes
+    // get applied to nodes as well as to properties. Note that because
+    // of multiple inheritance this virtual call can get confusing.
+    virtual void        update(CdlTransaction, CdlUpdate);
+    
+    // Is this node active or not? The is_active() call refers
+    // to the global state, things may be different inside a
+    // transaction.
+    bool                is_active() const;
+    bool                is_active(CdlTransaction transaction);
+
+    // Generally nodes become active when the parent becomes
+    // active and enabled. Some derived classes may impose
+    // additional restrictions, for example because of
+    // active_if constraints. This routine can be used
+    // to check whether or not a node should become active.
+    virtual bool        test_active(CdlTransaction);
+    
+    // Provide access to the various properties. Currently all this
+    // information is publicly available.
+    const std::vector<CdlProperty>&     get_properties() const;
+    CdlProperty                         get_property(std::string) const;
+    void                                get_properties(std::string, std::vector<CdlProperty>&) const;
+    std::vector<CdlProperty>            get_properties(std::string) const;
+    bool                                has_property(std::string) const;
+    int                                 count_properties(std::string) const;
+
+    // Provide access to the various global conflicts. More
+    // commonly conflicts are accessed on a per-transaction basis.
+    void get_conflicts(std::vector<CdlConflict>&) const;
+    void get_conflicts(bool (*)(CdlConflict), std::vector<CdlConflict>&) const;
+    void get_structural_conflicts(std::vector<CdlConflict>&) const;
+    void get_structural_conflicts(bool (*)(CdlConflict), std::vector<CdlConflict>&) const;
+    
+    // Provide access to all the referrers. This may not get used very
+    // much outside the library itself.
+    const std::vector<CdlReferrer>&     get_referrers() const;
+
+    // Add property parsers and validation code appropriate for a
+    // node. Currently this is a no-op, there are no properties
+    // associated with every node, but this may change in future e.g.
+    // for diagnostics purposes.
+    static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
+    void        check_properties(CdlInterpreter);
+
+    // Persistence support. The final classes such as cdl_option
+    // should provide implementations of these functions. The
+    // base function takes care of data that was present in an
+    // original save file but which was not recognised.
+    //
+    // Configuration save files are Tcl scripts, so it seems
+    // appropriate to handle the I/O via the Tcl library and
+    // to have a TCL interpreter available.
+    virtual void save(CdlInterpreter, Tcl_Channel, int, bool);
+    bool has_additional_savefile_information() const;
+    
+    // Mainly for diagnostics code, what is the actual name for this
+    // type of CDL object? This should be in terms of CDL data, e.g.
+    // "package" or "component", rather than in implementation terms
+    // such as "CdlPackageBody". 
+    virtual std::string get_class_name() const;
+
+    bool check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+    
+    // CdlNodeBodies are only instantiated by derived classes.
+    // They must always have a name. They need not be placed
+    // in the hierarchy immediately, that can wait until
+    // later.
+    CdlNodeBody(std::string);
+    // A dummy constructor is needed because of the virtual
+    // inheritance. 
+    CdlNodeBody();
+
+    // Nodes cannot be destroyed directly by application code,
+    // only by higher-level library functions such as unload_package()
+    virtual ~CdlNodeBody();
+
+    // Updating the name is rarely required, but is useful for savefiles.
+    void                set_name(std::string);
+    
+    // Is the node currently active? This applies to the global state
+    // only, not per-transaction state. Some derived classes may want
+    // to override the default value
+    bool                active;
+    
+  private:
+    
+    // The basic data. The name is known during construction.
+    // The other three fields get updated by e.g. CdlToplevel::add_node();
+    std::string         name;
+    CdlContainer        parent;
+    CdlLoadable         owner;
+    CdlToplevel         toplevel;
+
+    // This is used by remove_node_from_toplevel()/add_node_to_toplevel()
+    // to allow the latter to exactly reverse the former
+    int                 remove_node_container_position;
+    
+    // Properties normally only get added during the parsing process,
+    // and only get removed when the object itself is destroyed.
+    // A vector is the obvious implementation.
+    std::vector<CdlProperty> properties;
+
+    // Currently a vector of referrers is used. This vector is subject
+    // to change when packages get loaded and unloaded, possibly a
+    // list would be better.
+    std::vector<CdlReferrer> referrers;
+
+    // Savefiles may contain information that is not recognised by the
+    // current library, especially because of savefile hooks which
+    // allow e.g. the configuration tool to store its own information
+    // in save files. This information must not be lost, even if you are
+    // e.g. mixing command line and GUI tools. This vector holds
+    // the savefile information so that it can be put in the next
+    // savefile.
+    std::vector<std::string> unsupported_savefile_strings;
+    
+    enum {
+        CdlNodeBody_Invalid     = 0,
+        CdlNodeBody_Magic       = 0x309595b5
+    } cdlnodebody_cookie;
+    
+    // Illegal operations
+    CdlNodeBody(const CdlNodeBody&);
+    CdlNodeBody& operator=(const CdlNodeBody&);
+};
+
+//}}}
+//{{{  CdlContainer                                     
+
+// ----------------------------------------------------------------------------
+// A container is a node that can contain other nodes.
+
+class CdlContainerBody : virtual public CdlNodeBody {
+
+    friend class Cdltest;
+
+    // Allow CdlNode::check_this() access to the internals
+    friend class CdlNodeBody;
+    
+    // Adding a node to the hierarchy is done by a CdlToplevel member.
+    // Ditto for removing.
+    friend class CdlToplevelBody;
+
+    // Deleting a container can happen inside CdlToplevel and CdlLoadable
+    friend class CdlLoadableBody;
+    
+  public:
+
+    const std::vector<CdlNode>& get_contents() const;
+    bool                        contains(CdlConstNode, bool /* recurse */ = false) const;
+    bool                        contains(const std::string, bool /* recurse */ = false) const;
+    CdlNode                     find_node(const std::string, bool /* recurse */ = false) const;
+
+    // Propagation support. Some updates such as active/inactive changes
+    // get applied to nodes as well as to properties.
+    virtual void update(CdlTransaction, CdlUpdate);
+    
+    // Persistence support.
+    virtual void save(CdlInterpreter, Tcl_Channel, int, bool);
+    
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+
+    // Containers cannot be destroyed explicitly, only via higher-level
+    // code such as unload_package();
+    virtual ~CdlContainerBody();
+
+    CdlContainerBody();
+    // Special constructor needed for internal use.
+    CdlContainerBody(std::string);
+
+    // The CdlToplevel class needs access to its own contents.
+    std::vector<CdlNode>                contents;
+    
+  private:
+    enum {
+        CdlContainerBody_Invalid        = 0,
+        CdlContainerBody_Magic          = 0x543c5f1d
+    } cdlcontainerbody_cookie;
+
+    // Illegal operations
+    CdlContainerBody(const CdlContainerBody&);
+    CdlContainerBody& operator=(const CdlContainerBody&);
+};
+
+//}}}
+//{{{  CdlLoadable                                      
+
+// ----------------------------------------------------------------------------
+// A loadable object is a container that gets loaded or unloaded
+// atomically from a toplevel. The key difference is that a loadable
+// keeps track of all nodes that were loaded as part of this
+// operation, thus allowing unload operations to happen safely even if
+// nodes get re-parented all over the hierarchy. In addition, there is
+// a slave interpreter associated with every loadable.
+
+class CdlLoadableBody : virtual public CdlContainerBody {
+
+    friend class CdlTest;
+
+    // Allow CdlNode::check_this() access to the internals
+    friend class CdlNodeBody;
+    
+    // Adding nodes to the hierarchy is done by a toplevel member
+    friend class CdlToplevelBody;
+
+  public:
+    virtual ~CdlLoadableBody();
+
+    
+    const std::vector<CdlNode>&         get_owned() const;
+    bool                                owns(CdlConstNode) const;
+    CdlInterpreter                      get_interpreter() const;
+    std::string                         get_directory() const;
+
+    // Some properties such as doc and compile reference filenames.
+    // A search facility is useful.
+    virtual std::string find_relative_file(std::string /* filename */, std::string /* directory */ = "") const;
+    virtual std::string find_absolute_file(std::string, std::string, bool /* allow_urls */ = false) const;
+    virtual bool        has_subdirectory(std::string) const;
+
+    // These support load/unload operations inside transactions
+    // They are static members because some of them will want
+    // to delete the loadable.
+    static void         transaction_commit_load(CdlTransaction, CdlLoadable);
+    static void         transaction_cancel_load(CdlTransaction, CdlLoadable);
+    static void         transaction_commit_unload(CdlTransaction, CdlLoadable);
+    static void         transaction_cancel_unload(CdlTransaction, CdlLoadable);
+
+    // Binding and unbinding of properties. This involves processing
+    // the various properties, calculating default values, etc.
+    void                bind(CdlTransaction);
+    void                unbind(CdlTransaction);
+    
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+
+    CdlLoadableBody(CdlToplevel, std::string /* directory */);
+
+    // Needed by derived classes, but not actually used.
+    CdlLoadableBody();
+    
+  private:
+    
+    std::vector<CdlNode> owned;
+    CdlInterpreter       interp;
+    std::string          directory;
+
+    // Used by add/remove_node_from_toplevel()
+    int                  remove_node_loadables_position;
+    
+    enum {
+        CdlLoadableBody_Invalid = 0,
+        CdlLoadableBody_Magic   = 0x488d6127
+    } cdlloadablebody_cookie;
+
+    // Invalid operations
+    CdlLoadableBody(const CdlLoadableBody&);
+    CdlLoadableBody& operator=(const CdlLoadableBody&);
+};
+
+//}}}
+//{{{  CdlToplevel                                      
+
+// ----------------------------------------------------------------------------
+// Toplevels are containers that live at the top of a hierarchy
+// (surprise surprise). This means that they do not have a parent.
+// In addition a toplevel object keeps track of all the names
+// used, guaranteeing uniqueness and providing a quick lookup
+// facility.
+//
+// Every container is also a node, so every toplevel is a node.
+// Inheritance from CdlNode may seem wrong. However it achieves
+// consistency, everything in the hierarchy including the toplevel
+// is a node. The main disadvantage is that every toplevel now
+// needs a name.
+
+class CdlToplevelBody : virtual public CdlContainerBody {
+
+    friend class CdlTest;
+
+    // Allow CdlNode::check_this() access to the internals
+    friend class CdlNodeBody;
+    
+    // The CdlTransaction class needs direct access to the lists
+    // of conflicts.
+    friend class CdlTransactionBody;
+
+  public:
+    virtual ~CdlToplevelBody();
+
+    // Updating the hierarchy. This happens a node at a time. Adding a
+    // node involves updating the name->node map in the toplevel,
+    // setting the node's parent/owner/toplevel fields, and updating
+    // the parent and owner containers. The owner may be 0 for special
+    // nodes such as the orphans container. The parent must be known,
+    // although it may change later on during a change_parent() call.
+    //
+    // Removing a node is more complicated, and involves a two-stage
+    // process. First the node is removed from the toplevel, thus
+    // eliminating the name->node mapping. The owner and parent fields
+    // are preserved at this stage (except for the loadable itself),
+    // and the operation may be undone if the relevant transaction
+    // gets cancelled. If the transaction gets committed then the
+    // second remove operation handles the owner and parent fields,
+    // just prior to the node being deleted. For convenience there
+    // are also per-loadable variants for some of these.
+    //
+    // change_parent() is used to support parent-properties.
+    // A container of 0 indicates an orphan, i.e. a parent
+    // property that did not or does not correspond to a
+    // current container.
+    //
+    // There is also a clean-up call. This gets used for interfaces
+    // which may alternate between belonging to a loadable and
+    // being auto-created.
+    void add_node(CdlLoadable, CdlContainer, CdlNode);
+    void add_node_to_toplevel(CdlNode);
+    void remove_node_from_toplevel(CdlNode);
+    static void remove_node(CdlLoadable, CdlContainer, CdlNode);
+    void add_loadable_to_toplevel(CdlLoadable);
+    void remove_loadable_from_toplevel(CdlLoadable);
+    void change_parent(CdlLoadable, CdlContainer /* current */, CdlContainer /* new */, CdlNode, int /* pos */ = -1);
+    void cleanup_orphans();
+
+    // Toplevels keep track of all the loadables, in addition to
+    // inheriting tree behaviour from CdlContainer. This is convenient
+    // for some operations like determining build information
+    // which must operate on a per-loadable basis.
+    const std::vector<CdlLoadable>& get_loadables() const;
+    
+    // Name uniqueness is guaranteed. It is convenient to have an STL
+    // map as a lookup service.
+    CdlNode lookup(const std::string) const;
+
+    // There are two conflict lists associated with each toplevel. One
+    // is for "structural" conflicts, ones that can only be resolved
+    // by a fairly major change such as loading another package: a
+    // typical example is an unresolved parent reference. The other is
+    // for conflicts that can probably be resolved simply by changing
+    // some values. Both sets of conflicts are held as a simple list.
+    //
+    // The active vs. inactive state of a CDL entity affects the
+    // location of structural vs. non-structural conflicts. If an
+    // entity becomes inactive then structural conflicts are not
+    // affected, but non-structural conflicts are removed from the
+    // global list. If an entity's "requires" expression is not
+    // satisfied but the entity is inactive anyway then this is
+    // harmless.
+    const std::list<CdlConflict>& get_all_conflicts() const;
+    const std::list<CdlConflict>& get_all_structural_conflicts() const;
+
+    // Try to resolve some or all conflicts. Typically a new transaction
+    // will be created for this.
+    void        resolve_conflicts(const std::vector<CdlConflict>&);
+    void        resolve_all_conflicts();
+    
+    // Toplevels can have descriptions provided by the user. This is
+    // particularly important for pre-defined templates, target
+    // board descriptions, etc. where the user would like some
+    // extra information about the template before loading it in.
+    // The default value is an empty string.
+    std::string         get_description() const;
+    void                set_description(std::string);
+    
+    // Each toplevel must have an associated master Tcl interpreter.
+    CdlInterpreter      get_interpreter() const;
+
+    // Each toplevel should also have an associated directory for 
+    // the component repository. It is not required that all loadables
+    // are relative to this, but that is the default behaviour.
+    std::string         get_directory() const;
+
+    // Each toplevel may have a single active main transaction.
+    // For now there is no support for concurrent transactions
+    // operating on a single toplevel (although nested transactions
+    // are allowed)
+    CdlTransaction      get_active_transaction() const;
+    
+    // Build and define operations are available for all toplevels,
+    // even if they are not always applicable
+    void                get_build_info(CdlBuildInfo&);
+    void                get_all_build_info(CdlBuildInfo&);
+    void                generate_config_headers(std::string);
+    void                get_config_headers(std::vector<std::string>&);
+    void                generate_build_tree(std::string, std::string = "");
+    
+    // Values can be stored in limbo. This is useful when unloading 
+    // and reloading packages, e.g. when changing a version the
+    // current settings can be preserved as much as possible.
+    void                set_limbo_value(CdlValuable);
+    bool                has_limbo_value(std::string) const;
+    CdlValue            get_limbo_value(std::string) const;
+    CdlValue            get_and_remove_limbo_value(std::string);
+    void                clear_limbo();
+    
+    // Persistence support. These are commented in the source code.
+           void         initialize_savefile_support();
+    static bool         savefile_support_initialized();
+           void         add_savefile_command(std::string, CdlSaveCallback, CdlInterpreterCommand);
+           void         add_savefile_subcommand(std::string, std::string, CdlSaveCallback, CdlInterpreterCommand);
+           void         get_savefile_commands(std::vector<CdlInterpreterCommandEntry>&);
+           void         get_savefile_subcommands(std::string, std::vector<CdlInterpreterCommandEntry>&);
+           void         save_command_details(CdlInterpreter, Tcl_Channel, int, bool);
+    static int          savefile_handle_command(CdlInterpreter, int, const char*[]);
+    static int          savefile_handle_unsupported(CdlInterpreter, int, const char*[]);
+    static int          savefile_handle_unknown(CdlInterpreter, int, const char*[]);
+           void         save_unsupported_commands(CdlInterpreter, Tcl_Channel, int, bool);
+    static cdl_int      get_library_savefile_version();
+    static int          savefile_handle_version(CdlInterpreter, int, const char*[]);
+    static cdl_int      get_savefile_version(CdlInterpreter);
+           void         save_conflicts(CdlInterpreter, Tcl_Channel, int, bool);
+    static void         save_separator(CdlInterpreter, Tcl_Channel, std::string, bool);
+        
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  protected:
+    CdlToplevelBody(CdlInterpreter, std::string);
+
+  private:
+
+    std::map<std::string,CdlNode>       lookup_table;
+    std::vector<CdlLoadable>            loadables;
+    std::map<std::string,CdlValue>      limbo;
+    CdlInterpreter                      interp;
+    CdlContainer                        orphans;
+    std::string                         description;
+    std::string                         directory;
+    std::list<CdlConflict>              conflicts;
+    std::list<CdlConflict>              structural_conflicts;
+
+    // The savefile support corresponding to this application.
+    static cdl_int savefile_version;
+    static bool    savefile_commands_initialized;
+    static std::vector<CdlSavefileCommand> savefile_commands;
+    static std::map<std::string,std::vector<CdlSavefileCommand> > savefile_subcommands;
+
+    // Per-toplevel support. A savefile may contain unrecognised
+    // commands at the toplevel of a file, as well as unrecognised
+    // commands in e.g. the body of a cdl_configuration command.
+    // The latter is handled via the CdlNode base class.
+    std::vector<std::string> unsupported_savefile_toplevel_strings;
+    std::vector<std::string> unsupported_savefile_commands;
+    std::map<std::string, std::vector<std::string> > unsupported_savefile_subcommands;
+
+    // Keep track of the current active transaction for this toplevel (if any)
+    CdlTransaction      transaction;
+    
+    enum {
+        CdlToplevelBody_Invalid = 0,
+        CdlToplevelBody_Magic   = 0x0834666e
+    } cdltoplevelbody_cookie;
+    
+    // Invalid operations
+    CdlToplevelBody(const CdlToplevelBody&);
+    CdlToplevelBody& operator=(const CdlToplevelBody&);
+};
+
+//}}}
+//{{{  CdlUserVisible                                   
+
+// ----------------------------------------------------------------------------
+// A user-visible object is likely to have properties such as display,
+// description and doc. Many user-visible objects will have values but
+// not all, for example custom dialogs are likely to have a doc
+// property but they do not have a value.
+
+class CdlUserVisibleBody : virtual public CdlNodeBody {
+
+    friend class CdlTest;
+
+  public:
+    virtual ~CdlUserVisibleBody();
+
+    std::string         get_display() const;
+    std::string         get_description() const;
+    std::string         get_doc() const;
+
+    // NOTE: this will only work for absolute doc strings or for doc
+    // strings that are relative to the package.
+    std::string         get_doc_url() const;
+    
+    // Add property parsers and validation code appropriate for a
+    // user-visible object such as doc and description 
+    static void         add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
+    void                check_properties(CdlInterpreter);
+    static int          parse_description(CdlInterpreter, int, const char*[]);
+    static int          parse_display(CdlInterpreter, int, const char*[]);
+    static int          parse_doc(CdlInterpreter, int, const char*[]);
+
+    // Persistence support. The save code simply outputs some comments
+    // corresponding to the display, doc and description properties.
+    virtual void save(CdlInterpreter, Tcl_Channel, int, bool);
+    
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+    CdlUserVisibleBody();
+
+  private:
+
+    enum {
+        CdlUserVisibleBody_Invalid      = 0,
+        CdlUserVisibleBody_Magic        = 0x13bbc817
+    } cdluservisiblebody_cookie;
+
+    // Illegal operations
+    CdlUserVisibleBody(const CdlUserVisibleBody&);
+    CdlUserVisibleBody& operator=(const CdlUserVisibleBody&);
+};
+
+//}}}
+//{{{  CdlParentable                                    
+
+// ----------------------------------------------------------------------------
+// A parentable object may have the parent property, redefining its
+// position in the hierarchy.
+
+class CdlParentableBody : virtual public CdlNodeBody {
+
+    friend class CdlTest;
+
+  public:
+    virtual ~CdlParentableBody();
+
+    static void         add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
+    void                check_properties(CdlInterpreter);
+    static int          parse_parent(CdlInterpreter, int, const char*[]);
+    static void         update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+    CdlParentableBody();
+
+  private:
+
+    // Unloads may be cancelled. To restore the previous state exactly
+    // it is necessary to keep track of the old position.
+    int                 change_parent_save_position;
+    
+    enum {
+        CdlParentableBody_Invalid      = 0,
+        CdlParentableBody_Magic        = 0x40c6a077
+    } cdlparentablebody_cookie;
+
+    // Illegal operations
+    CdlParentableBody(const CdlParentableBody&);
+    CdlParentableBody& operator=(const CdlParentableBody&);
+};
+
+//}}}
+//{{{  CdlValuable                                      
+
+// ----------------------------------------------------------------------------
+// A valuable body has a value. Many valuables can be modified but not all.
+// Some properties make a valuable object read-only. In future there is
+// likely to be support for locked values as well. There is a member function
+// to check whether or not a valuable object is modifiable.
+//
+// Relevant properties for a valuable object are:
+//
+//  1) flavor           - readily available via CdlValue::get_flavor()
+//  2) default_value    - an expression
+//  3) legal_values     - a list expression
+//  4) entry_proc       - for validation purposes, in addition to legal_values
+//  5) check_proc       - ditto
+//  6) active_if        - goal expression
+//  7) requires         - goal expression
+//  8) dialog           - a custom dialog for editing this value
+//  9) calculated       - non-modifiable
+// 10) implements       - for interfaces
+//
+// A CdlValuable does not inherit directly from CdlValue, since it should
+// not be possible to modify a Valuable directly. Instead it contains a
+// CdlValue member, and provides essentially the same functions as
+// a CdlValue.
+
+class CdlValuableBody : virtual public CdlNodeBody {
+
+    friend class CdlTest;
+
+    // Transaction commit operations require direct access to the CdlValue
+    friend class CdlTransactionBody;
+    
+  private:
+    CdlValue value;
+    
+  public:
+    virtual ~CdlValuableBody();
+
+    // Accessing the current value. There are variants for the global state
+    // and for per-transaction operations.
+    const CdlValue&     get_whole_value() const;
+    
+    CdlValueFlavor      get_flavor() const;
+    CdlValueFlavor      get_flavor(CdlTransaction transaction) const
+    {   // The transaction is irrelevant, it cannot change the flavor
+        return this->get_flavor();
+    }
+
+    CdlValueSource      get_source() const;
+    bool                has_source(        CdlValueSource) const;
+    bool                is_enabled(        CdlValueSource = CdlValueSource_Current) const;
+    std::string         get_value(         CdlValueSource = CdlValueSource_Current) const;
+    bool                has_integer_value( CdlValueSource = CdlValueSource_Current) const;
+    cdl_int             get_integer_value( CdlValueSource = CdlValueSource_Current) const;
+    bool                has_double_value(  CdlValueSource = CdlValueSource_Current) const;
+    double              get_double_value(  CdlValueSource = CdlValueSource_Current) const;
+    CdlSimpleValue      get_simple_value(  CdlValueSource = CdlValueSource_Current) const;
+    
+    CdlValueSource      get_source(CdlTransaction) const;
+    bool                has_source(        CdlTransaction, CdlValueSource) const;
+    bool                is_enabled(        CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
+    std::string         get_value(         CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
+    bool                has_integer_value( CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
+    cdl_int             get_integer_value( CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
+    bool                has_double_value(  CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
+    double              get_double_value(  CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
+    CdlSimpleValue      get_simple_value(  CdlTransaction, CdlValueSource = CdlValueSource_Current) const;
+    
+    // -----------------------------------------------------------------
+    // Modify access. There are two variants of all the functions:
+    //
+    // 1) no transaction argument. A transaction will be created,
+    //    committed, and destroyed for the change in question.
+    //
+    // 2) a transaction argument. The existing transaction will be
+    //    updated but not committed. This allows multiple changes
+    //    to be grouped together.
+    //
+    // There are only a handful of exported functions, but lots
+    // of inline variants.
+    void set_source(CdlValueSource);
+    void invalidate_source(CdlValueSource);
+    void set_enabled(bool, CdlValueSource);
+    void set_value(CdlSimpleValue&, CdlValueSource);
+    void set_enabled_and_value(bool, CdlSimpleValue&, CdlValueSource);
+    void set(CdlSimpleValue&, CdlValueSource);
+    
+    void set_source(CdlTransaction, CdlValueSource);
+    void invalidate_source(CdlTransaction, CdlValueSource);
+    void set_enabled(CdlTransaction, bool, CdlValueSource);
+    void set_value(CdlTransaction, CdlSimpleValue&, CdlValueSource);
+    void set_enabled_and_value(CdlTransaction, bool, CdlSimpleValue&, CdlValueSource);
+    void set(CdlTransaction, CdlSimpleValue&, CdlValueSource);
+    void set(CdlTransaction, const CdlValue&);
+    
+    void enable(CdlValueSource source)
+    {
+        set_enabled(true, source);
+    }
+    void disable(CdlValueSource source)
+    {
+        set_enabled(false, source);
+    }
+    void set_value(std::string data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_value(val, source);
+    }
+    void set_integer_value(cdl_int data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_value(val, source);
+    }
+    void set_double_value(double data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_value(val, source);
+    }
+    void set_enabled_and_value(bool enabled, std::string data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_enabled_and_value(enabled, val, source);
+    }
+    void set_enabled_and_value(bool enabled, cdl_int data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_enabled_and_value(enabled, val, source);
+    }
+    void set_enabled_and_value(bool enabled, double data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_enabled_and_value(enabled, val, source);
+    }
+    void enable_and_set_value(CdlSimpleValue& val, CdlValueSource source)
+    {
+        set_enabled_and_value(true, val, source);
+    }
+    void enable_and_set_value(std::string data, CdlValueSource source)
+    {
+        set_enabled_and_value(true, data, source);
+    }
+    void enable_and_set_value(cdl_int data, CdlValueSource source)
+    {
+        set_enabled_and_value(true, data, source);
+    }
+    void enable_and_set_value(double data, CdlValueSource source)
+    {
+        set_enabled_and_value(true, data, source);
+    }
+    void disable_and_set_value(CdlSimpleValue& val, CdlValueSource source)
+    {
+        set_enabled_and_value(false, val, source);
+    }
+    void disable_and_set_value(std::string data, CdlValueSource source)
+    {
+        set_enabled_and_value(false, data, source);
+    }
+    void disable_and_set_value(cdl_int data, CdlValueSource source)
+    {
+        set_enabled_and_value(false, data, source);
+    }
+    void disable_and_set_value(double data, CdlValueSource source)
+    {
+        set_enabled_and_value(false, data, source);
+    }
+    void enable(CdlTransaction transaction, CdlValueSource source)
+    {
+        set_enabled(transaction, true, source);
+    }
+    void disable(CdlTransaction transaction, CdlValueSource source)
+    {
+        set_enabled(transaction, false, source);
+    }
+    void set_value(CdlTransaction transaction, std::string data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_value(transaction, val, source);
+    }
+    void set_integer_value(CdlTransaction transaction, cdl_int data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_value(transaction, val, source);
+    }
+    void set_double_value(CdlTransaction transaction, double data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_value(transaction, val, source);
+    }
+    void set_enabled_and_value(CdlTransaction transaction, bool enabled, std::string data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_enabled_and_value(transaction, enabled, val, source);
+    }
+    void set_enabled_and_value(CdlTransaction transaction, bool enabled, cdl_int data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_enabled_and_value(transaction, enabled, val, source);
+    }
+    void set_enabled_and_value(CdlTransaction transaction, bool enabled, double data, CdlValueSource source)
+    {
+        CdlSimpleValue val(data);
+        set_enabled_and_value(transaction, enabled, val, source);
+    }
+    void enable_and_set_value(CdlTransaction transaction, CdlSimpleValue& val, CdlValueSource source)
+    {
+        set_enabled_and_value(transaction, true, val, source);
+    }
+    void enable_and_set_value(CdlTransaction transaction, std::string data, CdlValueSource source)
+    {
+        set_enabled_and_value(transaction, true, data, source);
+    }
+    void enable_and_set_value(CdlTransaction transaction, cdl_int data, CdlValueSource source)
+    {
+        set_enabled_and_value(transaction, true, data, source);
+    }
+    void enable_and_set_value(CdlTransaction transaction, double data, CdlValueSource source)
+    {
+        set_enabled_and_value(transaction, true, data, source);
+    }
+    void disable_and_set_value(CdlTransaction transaction, CdlSimpleValue& val, CdlValueSource source)
+    {
+        set_enabled_and_value(transaction, false, val, source);
+    }
+    void disable_and_set_value(CdlTransaction transaction, std::string data, CdlValueSource source)
+    {
+        set_enabled_and_value(transaction, false, data, source);
+    }
+    void disable_and_set_value(CdlTransaction transaction, cdl_int data, CdlValueSource source)
+    {
+        set_enabled_and_value(transaction, false, data, source);
+    }
+    void disable_and_set_value(CdlTransaction transaction, double data, CdlValueSource source)
+    {
+        set_enabled_and_value(transaction, false, data, source);
+    }
+
+    // -----------------------------------------------------------------
+    virtual bool   is_modifiable() const;
+    void           get_widget_hint(CdlWidgetHint&);
+
+    // -----------------------------------------------------------------
+    // Propagation support. If a valuable becomes active or inactive
+    // because e.g. its parent is disabled then this may affect
+    // requires conflicts etc.
+    virtual void update(CdlTransaction, CdlUpdate);
+    
+    virtual bool test_active(CdlTransaction);
+    
+    // -----------------------------------------------------------------
+    // Property-related stuff.
+    bool                                has_calculated_expression() const;
+    bool                                has_default_value_expression() const;
+    bool                                has_legal_values() const;
+    bool                                has_entry_proc() const;
+    bool                                has_check_proc() const;
+    bool                                has_active_if_conditions() const;
+    bool                                has_requires_goals() const;
+    bool                                has_dialog() const;
+    bool                                has_wizard() const;
+    
+    CdlProperty_Expression              get_calculated_expression() const;
+    CdlProperty_Expression              get_default_value_expression() const;
+    CdlProperty_ListExpression          get_legal_values() const;
+    cdl_tcl_code                        get_entry_proc() const;
+    cdl_tcl_code                        get_check_proc() const;
+    void                                get_active_if_conditions(std::vector<CdlProperty_GoalExpression>&) const;
+    void                                get_requires_goals(std::vector<CdlProperty_GoalExpression>&) const;
+    CdlDialog                           get_dialog() const;
+    CdlWizard                           get_wizard() const;
+    void                                get_implemented_interfaces(std::vector<CdlInterface>&) const;
+    
+    // Add property parsers and validation code appropriate for a
+    // valuable object such as default_value and legal_values
+    static void         add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
+    void                check_properties(CdlInterpreter);
+    static int          parse_active_if(CdlInterpreter, int, const char*[]);
+    static void         active_if_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+    static int          parse_calculated(CdlInterpreter, int, const char*[]);
+    static void         calculated_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+    static int          parse_check_proc(CdlInterpreter, int, const char*[]);
+    static int          parse_default_value(CdlInterpreter, int, const char*[]);
+    static void         default_value_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+    static int          parse_dialog(CdlInterpreter, int, const char*[]);
+    static void         dialog_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+    static int          parse_entry_proc(CdlInterpreter, int, const char*[]);
+    static int          parse_flavor(CdlInterpreter, int, const char*[]);
+    static int          parse_group(CdlInterpreter, int, const char*[]);
+    static int          parse_implements(CdlInterpreter, int, const char*[]);
+    static void         implements_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+    static int          parse_legal_values(CdlInterpreter, int, const char*[]);
+    static void         legal_values_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+    static int          parse_requires(CdlInterpreter, int, const char*[]);
+    static void         requires_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+    static int          parse_wizard(CdlInterpreter, int, const char*[]);
+    static void         wizard_update_handler(CdlTransaction, CdlNode, CdlProperty, CdlNode, CdlUpdate);
+
+    // Persistence suppot
+    void save(CdlInterpreter, Tcl_Channel, int, bool /* modifiable */, bool /* minimal */);
+    bool value_savefile_entry_needed() const;
+    static void initialize_savefile_support(CdlToplevel, std::string);
+    static int  savefile_value_source_command(CdlInterpreter, int, const char*[]);
+    static int  savefile_user_value_command(CdlInterpreter, int, const char*[]);
+    static int  savefile_wizard_value_command(CdlInterpreter, int, const char*[]);
+    static int  savefile_inferred_value_command(CdlInterpreter, int, const char*[]);
+    static int  savefile_xxx_value_command(CdlInterpreter, int, const char*[], CdlValueSource);
+    
+    // Make sure that the current value is legal. This gets called automatically
+    // by all the members that modify values. It has to be a virtual function
+    // since some derived classes, e.g. hardware-related valuables, may impose
+    // constraints over and above legal_values etc.
+    virtual void check_value(CdlTransaction);
+
+    // Similarly check the requires properties
+    void check_requires(CdlTransaction, CdlProperty_GoalExpression);
+    void check_requires(CdlTransaction);
+
+    // Enabling or disabling a valuable may affect the active state of children
+    void check_children_active(CdlTransaction);
+
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+    CdlValuableBody(CdlValueFlavor = CdlValueFlavor_Bool);
+
+    
+  private:
+
+    
+    enum {
+        CdlValuableBody_Invalid = 0,
+        CdlValuableBody_Magic   = 0x2b2acc03
+    } cdlvaluablebody_cookie;
+
+    // Illegal operations
+    CdlValuableBody(const CdlValuableBody&);
+    CdlValuableBody& operator=(const CdlValuableBody&);
+};
+
+//}}}
+//{{{  CdlTransaction etc.                              
+
+//{{{  Description                      
+
+// ----------------------------------------------------------------------------
+// Transactions. These are used for all changes to a configuration. In some
+// cases a transaction is implicit:
+//
+//    valuable->set_value(...)
+//
+// The actual implementation of this is:
+//
+//    valuable->set_value(...)
+//        transact = CdlTransactionBody::make(valuable->get_toplevel())
+//        valuable->set_value(transact, ...)
+//        <complicated bits>
+//        transact->commit()
+//        delete transact
+//
+// Alternatively the use of transactions may be explicit. For implicit
+// uses the library will invoke an inference callback at the
+// appropriate time. For explicit transactions this is not necessary.
+//
+// The commit() operation invokes a transaction callback which should
+// not be confused with the inference callback. The former is intended
+// for display updates, it specifies everything that has changed
+// during the transaction. The latter is used for reporting new
+// conflicts to the user, suggesting fixes, etc.
+//
+// A whole bunch of information is associated with a transaction,
+// including: all value changes, details of new conflicts, and details
+// of existing conflicts that have gone away. The commit operation
+// takes care of updating the toplevel. Until the commit happens
+// the toplevel itself remains unchanged. It is also possible to cancel
+// a transaction.
+//
+// An important concept related to transactions is propagation.
+// Changing a value may have various effects, for example it may
+// change the result of a legal_values list expression, resulting in a
+// conflict object having to be created or destroyed. Changing one
+// value may result in other value changes, e.g. because of a
+// default_value property. All this is "propagation", and may
+// happen multiple times within a single transaction.
+//
+// Transaction objects are also used during load or unload operations,
+// but those are a little bit special. In particular it is not possible
+// to cancel such a transaction, there will have been updates to the
+// toplevel. Using a transaction is convenient because there is a 
+// need for propagation.
+//
+// Currently a transaction should be deleted immediately after a
+// commit or cancel. This may change in future, in that transaction
+// objects can be used to hold undo information.
+//
+//
+// The other big concept related to transactions is inference.
+// Changing a value may result in one or more new conflicts being
+// created. In some cases the library can figure out for itself how to
+// resolve these conflicts, using an inference engine. There are
+// parameters to control the operation of the inference engine,
+// including whether it runs at all, what changes it is allowed
+// to make automatically (usually default and inferred values can
+// be updated, but not wizard or user values), and how much
+// recursion will happen.
+//
+// Assuming a default setup in a GUI environment, a typical
+// sequence of events would be:
+//
+//     valuable->set_value(...)
+//         transact = CdlTransactionBody::make(valuable->get_toplevel())
+//         valuable->set_value(transact, ...)
+//             transact->set_whole_value(valuable, ...)
+//         transact->propagate()
+//         while (!finished)
+//             transact->resolve()
+//                 <inference>
+//             invoke inference callback
+//                 transact->apply_solution() (1 or more times)
+//                     transact->set_whole_value(valuable, ...) (1 or more times)
+//             transact->propagate()
+//         transact->commit() | transact->cancel()
+//         delete transact
+//
+// Note that the propagation steps have to be invoked explicitly,
+// allowing multiple changes to be processed in one go. There is
+// a utility function which combines the functionality from
+// the first propagate() call up to but not including the
+// transaction delete operator. 
+//
+//
+// The inference engine itself is a complicated beast. There are
+// a number of interfaces, but at the end of the day it ends up
+// creating a sub-transaction and trying to resolve a single
+// conflict in that sub-transaction. The conflict may belong to
+// the current transaction or it may be global.
+//
+//     <inference>
+//         for each conflict of interest
+//             make sure that there is not already a valid solution
+//             check that the inference engine can handle it
+//             create a sub-transaction, associated with the conflict
+//             apply the conflict resolution code
+//             if the solution is ok
+//                 install it
+//             else if the solution might e.g. overwrite a user value
+//                 keep it, the user can decide during the inference callback
+//
+// The conflict resolution typically works by attempting to change
+// one or more values in the sub-transaction, propagating them,
+// and seeing what new conflicts get created. If no new conflicts
+// get created and one or more existing conflicts go away, groovy.
+// Otherwise recursion can be used to try to resolve the new
+// conflicts, or other strategies can be explored.
+//
+// NOTE: what is really necessary is some way of keeping track of the
+// "best" solution to date, and allow exploration of alternatives.
+// Or possibly keep track of all solutions. That has to be left to
+// a future version.
+
+//}}}
+//{{{  CdlTransactionCommitCancelOp     
+
+// ----------------------------------------------------------------------------
+// The CdlTransaction class has built-in knowledge of how to handle values,
+// active state, and a few things like that. However there are also more
+// complicated operations such as loading and unloading, instantiating
+// items, etc. which also need to happen in the context of a transaction
+// but which the transaction class does not necessarily know about
+// itself - or at least, not in any detail. Since the libcdl core is
+// intended to be useful in various contexts, some sort of extensibility
+// is essential.
+//
+// This is achieved by an auxiliary class, CdlTransactionCommitCancelOp.
+// Clients of the transaction class can have their own utility class
+// which derives from this, and create suitable objects. The transaction
+// class maintains a vector of the pending commit/cancel operations.
+//
+// Each CdlTransactionCommitCancelOp object has two member functions,
+// one for when the transaction gets committed and one for when it
+// gets cancelled. If a sub-transaction gets committed then its
+// pending ops are transferred across to the parent, allowing the
+// parent to be cancelled sensibly: the commit ops only get run for
+// the toplevel transaction. If a sub-transaction gets cancelled then
+// the pending ops are invoked immediately.
+//
+// There is an assumption that commit/cancel ops get executed strictly
+// in FIFO order. Specifically, commit ops get run from first one to
+// the last one, allowing later operations in the transaction to
+// overwrite earlier ones. Cancel ops get run in reverse order.
+
+class CdlTransactionCommitCancelOp {
+    friend class CdlTest;
+
+  public:
+
+    CdlTransactionCommitCancelOp() { }
+    virtual ~CdlTransactionCommitCancelOp() { };
+
+    // The default implementations of both of these do nothing.
+    // Derived classes should override at least one of these
+    // functions.
+    virtual void commit(CdlTransaction transaction) {
+        CYG_UNUSED_PARAM(CdlTransaction, transaction);
+    }
+    virtual void cancel(CdlTransaction transaction) {
+        CYG_UNUSED_PARAM(CdlTransaction, transaction);
+    }
+    
+  protected:
+
+  private:
+};
+
+//}}}
+//{{{  CdlTransaction class             
+
+class CdlTransactionBody {
+
+    friend class CdlTest;
+
+    friend class CdlConflictBody;
+    friend class CdlValuableBody;
+    
+  public:
+
+    // Create a toplevel transaction
+    static CdlTransaction make(CdlToplevel);
+    virtual ~CdlTransactionBody();
+    CdlToplevel get_toplevel() const;
+    
+    // Or a sub-transaction. Usually these are created in the context of
+    // a conflict that is being resolved.
+    CdlTransaction make(CdlConflict = 0);
+    CdlTransaction get_parent() const;
+    CdlConflict    get_conflict() const;
+    
+    // Commit all the changes. Essentially this means transferring
+    // all of the per-transaction data to the toplevel, and then
+    // invoking the transaction callback. All propagation, inference,
+    // etc. should happen before the commit()
+    // This routine can also be used to transfer changes from a
+    // sub-transaction to the parent.
+    void        commit();
+
+    // A variant of the commit() operation can be used to
+    // store a sub-transaction in a conflict's solution vector,
+    // rather than updating the parent transaction. This is useful
+    // for inferred solutions which cannot be applied without
+    // user confirmation
+    void        save_solution();
+
+    // Can a solution held in a sub-transaction be applied without
+    // e.g. overwriting a user value with an inferred value?
+    bool        user_confirmation_required() const;
+
+    // If the user has explicitly changed a value in the current transaction
+    // then the inference engine should not undo this or suggest a solution
+    // that will undo the change.
+    bool        changed_by_user(CdlValuable) const;
+
+    // A variant which is used for checking the hierarchy when disabling
+    // a container
+    bool        subnode_changed_by_user(CdlContainer) const;
+    
+    // Is one transaction preferable to another?
+    bool        is_preferable_to(CdlTransaction) const;
+    
+    // Find out about per-transaction conflicts. This is particularly
+    // useful for the inference callback. The other containers can
+    // be accessed as well, for completeness.
+    const std::list<CdlConflict>&       get_new_conflicts() const;
+    const std::list<CdlConflict>&       get_new_structural_conflicts() const;
+    const std::vector<CdlConflict>&     get_deleted_conflicts() const;
+    const std::vector<CdlConflict>&     get_deleted_structural_conflicts() const;
+    const std::vector<CdlConflict>&     get_resolved_conflicts() const ;
+    const std::list<CdlConflict>&       get_global_conflicts_with_solutions() const;
+    const std::map<CdlValuable, CdlValue>& get_changes() const;
+    const std::set<CdlNode>&            get_activated() const;
+    const std::set<CdlNode>&            get_deactivated() const;
+    const std::set<CdlValuable>&        get_legal_values_changes() const;
+    
+    // Manipulate the current set of conflicts, allowing for nested
+    // transactions and toplevel conflicts as well.
+    void        clear_conflict(CdlConflict);
+    bool        has_conflict_been_cleared(CdlConflict);
+    
+    bool        has_conflict(CdlNode, bool (*)(CdlConflict));
+    CdlConflict get_conflict(CdlNode, bool (*)(CdlConflict));
+    void        get_conflicts(CdlNode, bool (*)(CdlConflict), std::vector<CdlConflict>&);
+    void        clear_conflicts(CdlNode, bool (*)(CdlConflict));
+    bool        has_conflict(CdlNode, CdlProperty, bool (*)(CdlConflict));
+    CdlConflict get_conflict(CdlNode, CdlProperty, bool (*)(CdlConflict));
+    void        get_conflicts(CdlNode, CdlProperty, bool (*)(CdlConflict), std::vector<CdlConflict>&);
+    void        clear_conflicts(CdlNode, CdlProperty, bool (*)(CdlConflict));
+
+    bool        has_structural_conflict(CdlNode, bool (*)(CdlConflict));
+    CdlConflict get_structural_conflict(CdlNode, bool (*)(CdlConflict));
+    void        get_structural_conflicts(CdlNode, bool (*)(CdlConflict), std::vector<CdlConflict>&);
+    void        clear_structural_conflicts(CdlNode, bool (*)(CdlConflict));
+    bool        has_structural_conflict(CdlNode, CdlProperty, bool (*)(CdlConflict));
+    CdlConflict get_structural_conflict(CdlNode, CdlProperty, bool (*)(CdlConflict));
+    void        get_structural_conflicts(CdlNode, CdlProperty, bool (*)(CdlConflict), std::vector<CdlConflict>&);
+    void        clear_structural_conflicts(CdlNode, CdlProperty, bool (*)(CdlConflict));
+
+    // During the inference callback the user may decide to
+    // apply one or more of the solutions.
+    void        apply_solution(CdlConflict);
+    void        apply_solutions(const std::vector<CdlConflict>&);
+    void        apply_all_solutions();
+    
+    // Cancel all the changes done in this transaction. Essentially
+    // this just involves clearing out all the STL containers.
+    void        cancel();
+
+    // Support for commit/cancel ops. These are used for
+    // e.g. load and unload operations.
+    void        add_commit_cancel_op(CdlTransactionCommitCancelOp *);
+    void        cancel_last_commit_cancel_op();
+    CdlTransactionCommitCancelOp* get_last_commit_cancel_op() const;
+    const std::vector<CdlTransactionCommitCancelOp*>& get_commit_cancel_ops() const;
+    
+    // Propagation support
+    void        add_active_change(CdlNode);
+    void        add_legal_values_change(CdlValuable);
+    void        propagate();
+    bool        is_propagation_required() const;
+    
+    // Inference engine support.
+    void        resolve(int = 0); // Process the new conflicts raised by this transaction
+    void        resolve(CdlConflict, int = 0);
+    void        resolve(const std::vector<CdlConflict>&, int = 0);
+    
+    // An auxiliary function called by the inference engine to perform recursion
+    bool        resolve_recursion(int);
+    
+    // This function combines propagation, inference, and commit
+    // in one easy-to-use package
+    void        body();
+    
+    // Changes.
+    // There is a call to get hold of a CdlValue reference. Modifications
+    // should happen via a sequence of the form:
+    //
+    //    valuable->set_value(transact, ...)
+    //        const CdlValue& old_value = transact->get_whole_value(CdlValuable);
+    //        CdlValue new_value = old_value;
+    //        <modify new_value>
+    //        transact->set_whole_value(CdlValuable, old_value, new_value);
+    //
+    // When appropriate the get_whole_value() call takes care of
+    // updating the current conflict's solution_references vector. The
+    // set_whole_value() call updated the per-transaction changes map,
+    // and also stores sufficient information to support propagation.
+    // set_whole_value() requires both the old and new values, so
+    // that propagation can be optimized.
+    const CdlValue&     get_whole_value(CdlConstValuable) const;
+    void                set_whole_value(CdlValuable, const CdlValue&, const CdlValue&);
+
+    // Control over active vs. inactive also needs to happen inside
+    // transactions
+    bool                is_active(CdlNode) const;
+    void                set_active(CdlNode, bool);
+    
+    // Callback and parameter settings
+    static void (*get_callback_fn())(const CdlTransactionCallback&);
+    static void                 set_callback_fn(void (*)(const CdlTransactionCallback&));
+    static void                 set_inference_callback_fn(CdlInferenceCallback);
+    static CdlInferenceCallback get_inference_callback_fn();
+    static void                 enable_automatic_inference();
+    static void                 disable_automatic_inference();
+    static bool                 is_automatic_inference_enabled();
+    static void                 set_inference_recursion_limit(int);
+    static int                  get_inference_recursion_limit();
+    // The override indicates the highest level of value source that the
+    // library can overwrite without needing user confirmation. The
+    // default value is CdlValueSource_Inferred, indicating that the
+    // library can overwrite default and inferred values but not
+    // wizard or user values.
+    static void                 set_inference_override(CdlValueSource);
+    static CdlValueSource       get_inference_override();
+    
+    bool        check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+
+  private:
+
+    CdlTransactionBody(CdlToplevel, CdlTransaction, CdlConflict);
+
+    // The associated toplevel and optionally the parent transaction
+    // and the conflict being worked on
+    CdlToplevel         toplevel;
+    CdlTransaction      parent;
+    CdlConflict         conflict;
+    
+    // Per-transaction information. All value changes, new conflicts
+    // etc. first live in the context of a transaction. The global
+    // configuration only gets updated if the transaction is commited.
+    // There is also a vector of the pending commit/cancel ops.
+    std::vector<CdlTransactionCommitCancelOp*> commit_cancel_ops;
+    std::map<CdlValuable, CdlValue> changes;
+    std::list<CdlConflict>          new_conflicts;
+    std::list<CdlConflict>          new_structural_conflicts;
+    std::vector<CdlConflict>        deleted_conflicts;  // Existing global ones
+    std::vector<CdlConflict>        deleted_structural_conflicts;
+    std::vector<CdlConflict>        resolved_conflicts; // New ones already fixed by the inference engine
+    std::list<CdlConflict>          global_conflicts_with_solutions;
+    std::set<CdlNode>               activated;
+    std::set<CdlNode>               deactivated;
+    std::set<CdlValuable>           legal_values_changes;
+    bool                            dirty;
+    
+    // Change propagation. It is necessary to keep track of all
+    // pending value changes, active changes, and of things being
+    // loaded or unloaded. The set_value() call is used to update the
+    // value_changes container.
+    std::deque<CdlValuable>     value_changes;
+    std::deque<CdlNode>         active_changes;
+
+
+    // Control over the inference engine etc.
+    static CdlInferenceCallback inference_callback;
+    static bool                 inference_enabled;
+    static int                  inference_recursion_limit;
+    static CdlValueSource       inference_override;
+    static void (*callback_fn)(const CdlTransactionCallback&);
+    
+    enum {
+        CdlTransactionBody_Invalid = 0,
+        CdlTransactionBody_Magic   = 0x3f91e4df
+    } cdltransactionbody_cookie;
+
+    // Illegal operations
+    CdlTransactionBody();
+    CdlTransactionBody(const CdlTransactionBody &);
+    CdlTransactionBody& operator=(const CdlTransactionBody&);
+};
+
+//}}}
+//{{{  CdlTransactionCallback           
+
+// ----------------------------------------------------------------------------
+// The callback class is used to inform applications about all the
+// changes that are happening, including side effects. Application
+// code can install a callback function which gets invoked at the
+// end of every transaction.
+//
+// NOTE: this implementation is preliminary. In particular it is
+// not extensible, it only deals with changes relevant to software
+// configurations.
+
+class CdlTransactionCallback {
+    
+    friend class CdlTest;
+    friend class CdlTransactionBody;
+    
+  public:
+    ~CdlTransactionCallback();
+    static void (*get_callback_fn())(const CdlTransactionCallback&);
+    static void set_callback_fn(void (*)(const CdlTransactionCallback&));
+
+    // Callback functions should be able to retrieve information
+    // about the current transaction and toplevel, to avoid the use
+    // of statics.
+    CdlTransaction              get_transaction() const;
+    CdlToplevel                 get_toplevel() const;
+    
+    // active_changes and legal_values_changes get updated as the
+    // transaction proceeds, so a set implementation is more
+    // efficient. The others get filled in during a commit operation.
+    // A transaction may result in multiple conflicts for a given node
+    // being eliminated, so again a set is appropriate. For the others
+    // there is no possibility of duplicates so a vector is better.
+    std::vector<CdlValuable>    value_changes;
+    std::vector<CdlNode>        active_changes;
+    std::vector<CdlValuable>    legal_values_changes;
+    std::vector<CdlValuable>    value_source_changes;
+    std::vector<CdlConflict>    new_conflicts;
+    std::vector<CdlConflict>    new_structural_conflicts;
+    std::vector<CdlNode>        nodes_with_resolved_conflicts;
+    std::vector<CdlNode>        nodes_with_resolved_structural_conflicts;
+    
+    bool check_this(cyg_assert_class_zeal = cyg_quick) const;
+    
+  protected:
+
+  private:
+    CdlTransactionCallback(CdlTransaction);
+    CdlTransaction      transact;
+
+    // Illegal operation.
+    CdlTransactionCallback();
+    
+    enum {
+        CdlTransactionCallback_Invalid     = 0,
+        CdlTransactionCallback_Magic       = 0x0cec3a95
+    } cdltransactioncallback_cookie;
+};
+
+//}}}
+//{{{  CdlLocalTransaction              
+
+// ----------------------------------------------------------------------------
+// A utility class to create a per-function transaction object which gets
+// cleaned up automatically should an exception happen.
+
+class CdlLocalTransaction {
+    
+    friend class CdlTrest;
+    
+  public:
+    CdlLocalTransaction(CdlToplevel toplevel) {
+        transaction = CdlTransactionBody::make(toplevel);
+    }
+    ~CdlLocalTransaction() {
+        // The destructor may get invoked during exception handling.
+        // It is assumed that cancelling the transaction would be a
+        // good thing when that happens. Normal operation should
+        // go through the body() or commit() members, which clear
+        // the transaction field.
+        // There is a slight consistency here. Normally after a
+        // transaction commit the transaction object is still
+        // around. Here the transaction object get deleted. This
+        // is unlikely to matter in practice.
+        if (0 != transaction) {
+            transaction->cancel();
+            delete transaction;
+        }
+    }
+    CdlTransaction get() {
+        return transaction;
+    }
+    void body() {
+        transaction->body();
+        delete transaction;
+        transaction = 0;
+    }
+    void commit() {
+        transaction->commit();
+        delete transaction;
+        transaction = 0;
+    }
+    void propagate() {
+        transaction->propagate();
+    }
+    void destroy() {
+        if (0 != transaction) {
+            transaction->cancel();
+            delete transaction;
+            transaction = 0;
+        }
+    }
+
+  private:
+    CdlTransaction transaction;
+    CdlLocalTransaction();
+};
+
+//}}}
+
+//}}}
+//{{{  Build and define information                     
+
+//{{{  Description                      
+
+// ----------------------------------------------------------------------------
+// There are two related concepts: buildable components, and
+// definable components. The former typically refers to compiling
+// sources files to produce libraries, although other types of build
+// are possible. The latter refers to generating header files
+// containing the current configuration data. Typically any loadable
+// that is buildable is also definable, so that the source files can
+// #include the appropriate generated headers and adapt to the
+// configuration data that way. The inverse is not true: for example
+// in HCDL it may be appropriate to generate a header file but there
+// is nothing to be compiled, device drivers are software packages.
+//
+// The relevant base classes are as follows:
+//
+// 1) CdlBuildable      - this object can have build-related properties.
+//                        All buildables are also valuables.
+// 2) CdlBuildLoadable  - this is a base class for loadables, providing
+//                        some extra properties that are relevant for
+//                        loadables that can involve builds.
+// 3) CdlDefinable      - this object can result in #define's in a
+//                        header file. All exportables are also
+//                        valuables.
+// 4) CdlDefineLoadable - this is a base class for any loadables that
+//                        can contain buildables.
+//
+// Support for both buildable and exportable components is part of the
+// core library for now. This may change in future, depending on how
+// many CDL variants get implemented.
+//
+// There are various properties related to building. First, the
+// ones applicable to the CdlBuildLoadable class.
+//
+// 1) library xyz.
+//    This specifies the default library for anything built in this
+//    loadable. If there is no library property then it defaults to
+//    libtarget.a (or rather to a class static that happens to be
+//    initialized to libtarget.a)
+//
+// 2) include_dir <dir>.
+//    This specifies where the loadable's exported header files should
+//    end up. The default value is the toplevel, but e.g. the eCos
+//    kernel specifies an include_dir of cyg/kernel. Note that fixed
+//    header files are associated with buildables, not definables,
+//    the latter deal with generated header files only.
+//
+// 3) include_files <hdr1 hdr2 ...>
+//    The recommended directory hierarchy for non-trivial packages
+//    involves separate subdirectories src, include, cdl, doc, and
+//    test. This is too heavyweight for very simple packages where it
+//    is better to keep everything in just one directory. However that
+//    introduces a potential conflict between public and private
+//    header files, which can be resolved by the include_files
+//    property. The actual rules are:
+//
+//    a) if there an include_files property, that lists all the
+//       headers that should be exported.
+//
+//    b) else if there is an include subdirectory, it is assumed that
+//       all files below that should be exported.
+//
+//    c) otherwise all files matching a suitable glob pattern should
+//       be exported. The default pattern is *.h *.hxx *.inl, but can
+//       be overwritten.
+//
+// 4) makefile <file>
+//    This allows component developers to provide a GNU makefile to be
+//    used for building, rather than specify the relevant information
+//    via properties.
+//    NOTE: this property is ignored for now. It is roughly
+//    equivalent to a custom build step where the command is
+//    "make -C <dir> -f <file>", but in addition it is necessary to
+//    worry about phony targets for default, clean, etc.
+//
+// A DefineLoadable adds the following property:
+//
+// 1) define_header <file>
+//    This specifies the header file that will be generated. If this
+//    property is absent then the library will generate a default one
+//    based on the loadable's name, by discarding everything up to and
+//    including the first underscore, lowercasing the rest, and
+//    appending .h. For example, CYGPKG_KERNEL would result in a
+//    header file kernel.h.
+//
+//    Hardware packages have an implicit "define_header hardware.h"
+//    property.
+//  
+// A buildable has the following properties:
+//
+// 1) compile [-library xyz] <file1> <file2> ...
+//    This specifies one or more files that need to be compiled.
+//    By default the resulting object files will go into the
+//    current library (set via a higher-level library or
+//    defaulting to libtarget.a).
+//
+//    Legitimate filename suffixes for compile statements are .c, .cxx
+//    and .S. Further suffixes may be supported in future. In the
+//    long term we will need some external data files defining how
+//    the various suffixes should be handled.
+//
+//    Associated with every compilation are details of the compiler to
+//    be used and the compiler flags. For now no attempt is made
+//    to do anything interesting in this area, although there is
+//    sufficient information in the database for the needs of
+//    command line tools.
+//
+//    Longer term there are complications. Packages may want some
+//    control over the compiler flags that should be used, e.g.
+//    "requires {!(flags ~= ".*-fno-rtti.*")}" to guarantee that the
+//    compiler flags do not include -fno-rtti, rather useful if the
+//    package's source code depends on that language feature. Mixed
+//    architecture systems (e.g. ARM/Thumb) will cause problems when
+//    it comes to selecting the compiler. The exact means by which
+//    all this will work is not yet clear.
+//
+// 2) object [-library xyz] <file1> <file2> ...
+//    This specifies one or more pre-built object files that should
+//    go into the appropriate library.
+//
+//    The problem here is coping with different architectures, and for
+//    many architectures it will also be necessary to worry about
+//    multilibs. Third party component vendors are unlikely to supply
+//    separate object files for every supported architecture and every
+//    valid multilib within those architectures, so there are
+//    constraints on the multilib-related compiler flags used for
+//    building other packages and the application itself.
+//
+//    NOTE: this property is ignored for now.
+//
+// 3) make_object [-library xyz] [-priority pri] <file> <makefile fragment>
+//
+//    For example:
+//
+//    make_object toyslock.o {
+//        toyslock.o : toyslock.y
+//                yacc toyslock.y
+//                $(CC) $(CFLAGS) -o toyslock.o y.tab.c
+//    }
+//
+//    This defines a custom build step for an object file that
+//    should go into a particular directory. A makefile syntax
+//    is used to define the rule simply because it is likely
+//    to be familiar to package developers, and does not
+//    imply that the builds will happen via a makefile.
+//
+//    The optional priority field indicates at which stage during
+//    the build the rule should trigger. The default value is
+//    100, which is the same as for all files specified in
+//    "compile" properties. A lower value means that the object
+//    will be generated earlier. Libraries are generated at
+//    priority 200, and "make" properties normally execute at
+//    priority 300.
+//    NOTE: it is not clear yet whether supporting priorities
+//    in this way is a good idea, or whether the dependencies
+//    information could be used instead.
+//
+//    Unresolved issues:
+//
+//    a) what commands can be used in the build rules? There
+//       should be a core set of supported commands, as per
+//       an eCos toolchain build. It should also be possible
+//       for packages to provide their own host tools.
+//
+//       For sourceware folks, moving away from a single toolchain
+//       tarball and expecting them to download and install
+//       egcs, binutils and gdb separately is actually a bad
+//       idea in this regard, it makes it much more likely that
+//       some users will have an incomplete tools installation and
+//       hence that builds will fail.
+//
+//    b) there is an obvious need for variable substitution in the
+//       rules, e.g. $(CC). At what stage do these variables get
+//       expanded, and where does the required information live?
+//
+//    c) who is responsible for header file dependency analysis?
+//       Should the rules be modified automatically to do this,
+//       or do we leave this to the package developer? It may be
+//       very hard to do the former, but the latter will cause
+//       problems for IDE integration.
+//
+//    d) in which directory will the rules get run? What prevents
+//       filename conflicts between different packages?
+//
+//    NOTE: make_object is not actually required just yet, but the
+//    issues are much the same as for the "make" property which is
+//    required.
+//
+// 4) make [-priority pri] <target> <makefile fragment>
+//
+//    For example:
+//
+//    make target.ld {
+//    target.ld : arm.ld
+//            $(CC) -E -P -xc $(CFLAGS) -o $@ $<
+//    }
+//
+//    This defines a custom build step for a target that is not going
+//    to end up in a library. The main such targets at the moment are
+//    the linker script, vectors.o, and extras.o, but there may well
+//    be others in future.
+//
+//    The default priority for "make" properties is 300, which means
+//    that the build rules trigger after all normal compilations and
+//    after the libraries are generated. It is possible to specify
+//    custom build steps that should run before any compilations
+//    using a priority < 100.
+//
+//    Unresolved issues:
+//
+//    a) what commands can be used?
+//
+//    b) variable substitution?
+//
+//    c) header file dependency analysis?
+//
+//    d) directories and filenames?
+//
+//    e) where should the resulting files end up? Currently they can
+//       all go into $(PREFIX)/lib, but in the long term we may
+//       need to be a bit more flexible.
+//
+// 5) build_proc <tcl code>
+//
+//    This defines some Tcl code that should be run prior to any
+//    build, for example to generate a source file. It must run
+//    within the appropriate loadable's Tcl interpreter so that
+//    it can query the current configuration.
+//
+//    NOTE: this property is not implemented yet.
+//
+//
+// A definable has the following properties:
+//
+// 1) no_define
+//    Usually the library will generate either one or two #define's
+//    for every definable, inside the current header file. This can be
+//    suppressed by the no_define property, which is typically
+//    accompanied by some other #define-related property such as
+//    define_proc or define.
+//
+// 2) define [-file <filename>] [-format <format_string>] symbol
+//    This will result in an additional #define for the specified
+//    symbol in the specified file. The only filenames that are valid
+//    are the loadable's current filename (as per define_header), and
+//    the global header file system.h. Use of the latter should be
+//    avoided.
+//
+//    The optional format string behaves as per the define_format
+//    property below.
+//
+// 3) define_format <format_string>
+//    This is only relevant for booldata or data flavors. By default
+//    two #define's will be generated (assuming the valuable is active
+//    and enabled):
+//
+//        #define <symbol> value
+//        #define <symbol>_value
+//
+//    The latter will only be generated if the resulting symbol is
+//    a valid C preprocessor symbol, and is intended to allow the
+//    use of #ifdef as well as #ifdef (useful if the value is
+//    non-numerical). 
+//
+//    The define_format property provides control over the first of
+//    these two #defines. The net result is that the #define will be
+//    generated by evaluating the following Tcl fragment:
+//
+//        set result "#define <symbol> [<format> <value>]"
+//
+//    Command and variable substitution are available if desired,
+//    but for anything that complicated the define_proc property
+//    is normally more useful.
+//
+//    define_format is only applicable to the default definition,
+//    so it cannot be used in conjunction with no_define. The
+//    define property supports a -format option.
+//
+// 4) define_proc <tclcode>
+//    This specifies some Tcl code that should be run when header
+//    file generation takes place, in addition to any #define's
+//    generated by default or courtesy of define properties.
+//    The define_proc property is commonly used in conjunction with
+//    no_define, but this is not required.
+//
+//    There will be two channels already set up: cdl_header
+//    for the current loadable, and cdl_system_header for system.h.
+//    Writing data to system.h should be avoided.
+//
+// 5) if_define <condition> <symbol>
+
+//    This property provides direct support for a common programming
+//    paradigm. It allows direct generation of code like the
+//    following:
+//
+//    #ifdef CYGSRC_TOYS_BLOCKS
+//    # define CYGDBG_INFRA_USE_PRECONDITIONS 1
+//    #endif
+//
+//    In this case CYGSRC_TOYS_BLOCKS is the condition and
+//    CYGDBG_INFRA_USE_PRECONDITIONS is the symbol. The
+//    #ifdef/#define sequence will be generated in addition to
+//    any other #define's resulting from the default behaviour,
+//    the define property, or the define_proc property. It is
+//    not affected by no_define.
+
+//}}}
+//{{{  The build process                
+
+// ----------------------------------------------------------------------------
+// For command-line operation the steps involved in doing a build are:
+//
+// 1) work out what needs to be built.
+//
+// 2) generate a build and install tree. This involves making sure that
+//    the various directories exist and are accessible.
+//
+// 3) generate or update the toplevel makefile.
+//
+// 4) generate the configuration header files.
+//
+// For operation in an IDE steps (2) and (3) will be handled by
+// different code.
+//
+// There is a library call to get hold of all the build information:
+//
+//     config->get_build_info(CdlBuildInfo &info);
+//
+// This erases anything previously present in the build-info argument
+// and fills in the information appropriate to the current
+// configuration, essentially by walking down the list of loadables
+// and each loadable's list of nodes, checking for BuildLoadables
+// and Buildables along the way. The BuildInfo class is defined
+// further down.
+//
+// An alternative library call can be used to find out about all
+// possible files that need to be compiled etc., irrespective of the
+// current configuration settings. This could be useful when it
+// comes to letting the user control compiler flags etc.
+//
+//    config->get_all_build_info(CdlBuildInfo& info);
+//
+// There is another library call for step (4):
+//
+//    config->generate_config_headers(std::string directory)
+//
+// This will create or update the header files appropriate to
+// the current configuration. Temporary files will be generated,
+// diff'ed with the current version, and existing files will
+// only be modified if necessary. The directory argument
+// indicates where the header files should go, i.e. it should
+// be the equivalent of $(PREFIX)/include/pkgconf
+//
+// This library call does not delete any files it does not
+// recognize, that is the responsibility of higher-level code.
+// It is possible to get or update a list of the files that
+// will be generated:
+//
+//    config->get_config_headers(std::vector<std::string>& headers)
+//
+// The argument will be cleared if necessary and then filled in with
+// the current set of header files. Higher level code can compare the
+// result with the current files in the directory and take or suggest
+// remedial action.
+//
+// There is also a library call which combines all four stages:
+//
+//    config->generate_build_tree(std::string build_tree, std::string prefix = $(BUILD)/install)
+//
+//
+// The order in which the various build steps happen is important.
+//
+// 1) non-configuration headers must be copied from the component
+//    repository into $(PREFIX)/include. No compiles can happen
+//    before this.
+//
+// 2) all compile properties can happen in parallel. These have an
+//    effective priority of 100.
+//
+// 3) all make_object priorities can happen in parallel with
+//    compiles. These have a default priority of 100, but the
+//    priority can be modified.
+//
+// 4) the generated objects and any pre-built objects should be
+//    incorporated into the appropriate library. This happens
+//    at priority 200.
+//
+// 5) custom build steps associated with "make" properties should
+//    now run. These have a default priority of 300, but it is
+//    possible to override this.
+//
+// Usually all source files will come from the component repository,
+// which means that they are read-only. Ideally it should also be
+// possible for a source file to be copied into the build tree and
+// edited there, and subsequent builds should pick up the copy rather
+// than the original. The build data generated by libcdl will always
+// be in the form of relative pathnames to facilitate this.
+
+//}}}
+//{{{  CdlBuildInfo class               
+
+// ----------------------------------------------------------------------------
+// Extracting the build information.
+//
+// libcdl.a defines the following classes related to build information.
+//
+// CdlBuildInfo
+// CdlBuildInfo_Loadable
+// CdlBuildInfo_Header
+// CdlBuildInfo_Compile
+// CdlBuildInfo_Object
+// CdlBuildInfo_MakeObject
+// CdlBuildInfo_Make
+//
+// The build information is organized on a per-loadable basis.
+// Higher-level code may choose to flatten this or to keep the
+// distinction. A CdlBuildInfo object is primarily a vector of
+// CdlBuildInfo_Loadable objects. CdlBuildInfo objects can be created
+// statically.
+//
+// In turn, each CdlBuildInfo_Loadable object is primarily a
+// collection of five vectors, one each for Header, Compile, Object,
+// MakeObject and Make.
+//
+// All pathnames in these data structures will use forward slashes as
+// the directory separator, irrespective of the host platform. All
+// pathnames will be relative.
+
+struct CdlBuildInfo_Header {
+    std::string         source;         /* include/cyg_ass.h    */
+    std::string         destination;    /* cyg/infra/cyg_ass.h  */
+};
+
+struct CdlBuildInfo_Compile {
+    std::string         library;        /* libtarget.a          */
+    std::string         source;         /* src/fancy.cxx        */
+    // Compiler and cflags data may be added in future.
+};
+
+struct CdlBuildInfo_Object {
+    std::string         library;        /* libtarget.a          */
+    std::string         object;         /* obj/hello.o          */
+};
+
+struct CdlBuildInfo_MakeObject {
+    cdl_int             priority;       /* 100                  */
+    std::string         library;        /* libtarget.a          */
+    std::string         object;         /* toyslock.o           */
+    std::string         deps;           /* toyslock.y           */
+    /*
+      It is not clear whether the deps field is actually useful in the
+      context of IDE integration, but see the note about arm.inc
+      above.
+    */
+    std::string         rules;
+    /*
+      A typical value for "rules" might be:
+
+        yacc toyslock.y
+        $(CC) $(CFLAGS) -o toyslock.o y.tab.c
+        
+      Leading white space is not significant. Newlines are significant.
+      Backslash escapes in the text will not have been processed yet.
+    */  
+};
+
+struct CdlBuildInfo_Make {
+    cdl_int             priority;       /* 300                  */
+    std::string         target;         /* extras.o             */
+    std::string         deps;           /* libextras.a          */
+    std::string         rules;
+    /*
+      Something like:
+      
+  $(CC) $(ARCHFLAGS) $(LDARCHFLAGS) -nostdlib -Wl,-r -Wl,--whole-archive $(PREFIX)/lib/libextras.a -o $(PREFIX)/lib/extras.o
+  
+    */
+};
+
+class CdlBuildInfo_Loadable {
+    
+    friend class CdlTest;
+
+  public:
+    std::string         name;           /* CYGPKG_INFRA         */
+    std::string         directory;      /* infra/current        */
+    std::vector<CdlBuildInfo_Header>            headers;
+    std::vector<CdlBuildInfo_Compile>           compiles;
+    std::vector<CdlBuildInfo_Object>            objects;
+    std::vector<CdlBuildInfo_MakeObject>        make_objects;
+    std::vector<CdlBuildInfo_Make>              makes;
+    
+  protected:
+
+  private:
+};
+
+class CdlBuildInfo {
+
+    friend class CdlTest;
+
+  public:
+
+    std::vector<CdlBuildInfo_Loadable>  entries;
+
+  protected:
+
+  private:
+};
+
+//}}}
+//{{{  CdlBuildLoadable                 
+
+// ----------------------------------------------------------------------------
+// BuildLoadables are derived from Loadables and are appropriate for
+// any loadables that can contain build information. There are a
+// number of properties applicable at this level: makefile,
+// include_dir, include_files and library. The main interface of
+// interest is update_build_info().
+//
+// It is likely that all BuildLoadables are also Buildables, but this
+// is not required.
+
+class CdlBuildLoadableBody : virtual public CdlLoadableBody
+{
+    friend class CdlTest;
+
+  public:
+    virtual ~CdlBuildLoadableBody();
+
+    // This is the main way to extract information about what should
+    // get built. It takes into account the active and enabled states,
+    // as appropriate.
+    void        update_build_info(CdlBuildInfo&) const;
+
+    // An alternative which ignores the active and enabled states.
+    void        update_all_build_info(CdlBuildInfo&) const;
+    
+    // Property parsers and validation code appropriate for a
+    // build-loadable object such as makefile
+    static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
+    void        check_properties(CdlInterpreter);
+    static int  parse_library(CdlInterpreter, int, const char*[]);
+    static int  parse_makefile(CdlInterpreter, int, const char*[]);
+    static int  parse_include_dir(CdlInterpreter, int, const char*[]);
+    static int  parse_include_files(CdlInterpreter, int, const char*[]);
+    
+    // By default any compiled files will go into libtarget.a, which
+    // is the default value for this variable. Individual applications may
+    // specify an alternative default library.
+    static char*        default_library_name;
+
+    // When filling in a build_info structure the library needs to know
+    // what constitutes a header file. A glob pattern can be used for this.
+    // NOTE: in the long term this should come out of a data file.
+    static char*        default_headers_glob_pattern;
+    
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+    CdlBuildLoadableBody();
+
+  private:
+
+    enum {
+        CdlBuildLoadableBody_Invalid    = 0,
+        CdlBuildLoadableBody_Magic      = 0x55776643
+    } cdlbuildloadablebody_cookie;
+
+    // Illegal operations
+    CdlBuildLoadableBody(const CdlBuildLoadableBody&);
+    CdlBuildLoadableBody& operator=(const CdlBuildLoadableBody&);
+};
+
+//}}}
+//{{{  CdlBuildable                     
+
+// ----------------------------------------------------------------------------
+// Buildable objects can have properties such as compile and
+// make_object. These properties are not normally accessed
+// directly. Instead there is a member function to update a
+// CdlBuildInfo_Loadable object.
+//
+// The build properties for a given buildable have an effect iff
+// that buildable is active, and in addition if the buildable is also
+// a valuable then it must be enabled.
+
+class CdlBuildableBody : virtual public CdlNodeBody
+{
+
+    friend class CdlTest;
+
+  public:
+    virtual ~CdlBuildableBody();
+
+    // This is the main way to extract information about what should
+    // get built. It takes into account the active and enabled states,
+    // as appropriate. The second argument indicates the default
+    // library for the current loadable.
+    void        update_build_info(CdlBuildInfo_Loadable&, std::string) const;
+
+    // An alternative which ignores the active and enabled states.
+    void        update_all_build_info(CdlBuildInfo_Loadable&, std::string) const;
+    
+    // Add property parsers and validation code appropriate for a
+    // buildable object such as compile and make_object
+    static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
+    void        check_properties(CdlInterpreter);
+
+    static int  parse_build_proc(CdlInterpreter, int, const char*[]);
+    static int  parse_compile(CdlInterpreter, int, const char*[]);
+    static int  parse_make(CdlInterpreter, int, const char*[]);
+    static int  parse_make_object(CdlInterpreter, int, const char*[]);
+    static int  parse_object(CdlInterpreter, int, const char*[]);
+    static bool split_custom_build_step(std::string /* data */, std::string& /* target */, std::string& /* deps */,
+                                        std::string& /* rules*/, std::string& /* error_msg */);
+
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+    CdlBuildableBody();
+
+  private:
+
+    enum {
+        CdlBuildableBody_Invalid        = 0,
+        CdlBuildableBody_Magic          = 0x16eb1c04
+    } cdlbuildablebody_cookie;
+
+    // Illegal operations
+    CdlBuildableBody(const CdlBuildableBody&);
+    CdlBuildableBody& operator=(const CdlBuildableBody&);
+};
+
+//}}}
+//{{{  CdlDefineLoadable                
+
+// ----------------------------------------------------------------------------
+// DefineLoadables are derived from Loadables and are appropriate for
+// any loadables that can result in generated header files containing
+// configuration data. There is one applicable property,
+// define_header. The main interface of interest is
+// generate_config_headers().
+
+class CdlDefineLoadableBody : virtual public CdlLoadableBody
+{
+
+    friend class CdlTest;
+
+  public:
+    virtual ~CdlDefineLoadableBody();
+
+    // Update the header file for this loadable. The first argument
+    // is a channel to the loadable-specific header file. The second
+    // argument is a channel to the global header file.
+    void        generate_config_header(Tcl_Channel, Tcl_Channel) const;
+
+    // What header file should be generated for this loadable?
+    virtual std::string get_config_header() const;
+    
+    // Add property parsers and validation code.
+    static void         add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
+    void                check_properties(CdlInterpreter);
+    static int          parse_define_header(CdlInterpreter, int, const char*[]);
+
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+    CdlDefineLoadableBody();
+
+  private:
+
+    enum {
+        CdlDefineLoadableBody_Invalid   = 0,
+        CdlDefineLoadableBody_Magic     = 0x7e211709
+    } cdldefineloadablebody_cookie;
+
+    // Illegal operations
+    CdlDefineLoadableBody(const CdlDefineLoadableBody&);
+    CdlDefineLoadableBody& operator=(const CdlDefineLoadableBody&);
+};
+
+//}}}
+//{{{  CdlDefinable                     
+
+// ----------------------------------------------------------------------------
+// Definables are derived from Valuables and provide support for
+// outputting a configuration header file.
+
+class CdlDefinableBody : virtual public CdlValuableBody
+{
+
+    friend class CdlTest;
+
+  public:
+    virtual ~CdlDefinableBody();
+
+    // Update the header file for this definable. The loadable's Tcl
+    // interpreter will already have channels cdl_header and
+    // cdl_system_header set up appropriately.
+    void        generate_config_header( Tcl_Channel, Tcl_Channel) const;
+
+    // Add property parsers and validation code.
+    static void add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers);
+    void        check_properties(CdlInterpreter);
+    static int  parse_define(CdlInterpreter, int, const char*[]);
+    static int  parse_define_format(CdlInterpreter, int, const char*[]);
+    static int  parse_define_proc(CdlInterpreter, int, const char*[]);
+    static int  parse_if_define(CdlInterpreter, int, const char*[]);
+    static int  parse_no_define(CdlInterpreter, int, const char*[]);
+
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+    
+  protected:
+    CdlDefinableBody();
+
+  private:
+
+    enum {
+        CdlDefinableBody_Invalid        = 0,
+        CdlDefinableBody_Magic          = 0x65a2c95a
+    } cdldefinablebody_cookie;
+
+    // Illegal operations
+    CdlDefinableBody(const CdlDefinableBody&);
+    CdlDefinableBody& operator=(const CdlDefinableBody&);
+};
+
+//}}}
+
+//}}}
+//{{{  CdlDialog                                        
+
+// ----------------------------------------------------------------------------
+// A dialog simply inherits from CdlUserVisible and provides convenient
+// access to several dialog-specific properties.
+
+class CdlDialogBody :
+    public virtual CdlUserVisibleBody,
+    public virtual CdlParentableBody
+{
+    friend class CdlTest;
+
+  public:
+
+    virtual ~CdlDialogBody();
+
+    // Dialogs may be enabled or disabled globally. This affects
+    // CdlValuable::get_widget_hint() if the valuable has an associated
+    // custom dialog.
+    static void         disable_dialogs();
+    static void         enable_dialogs();
+    static bool         dialogs_are_enabled();
+
+    bool                has_init_proc() const;
+    bool                has_update_proc() const;
+    const cdl_tcl_code& get_init_proc() const;
+    const cdl_tcl_code& get_update_proc() const;
+    const cdl_tcl_code& get_display_proc() const;
+    const cdl_tcl_code& get_confirm_proc() const;
+    const cdl_tcl_code& get_cancel_proc() const;
+    
+    static int          parse_dialog(CdlInterpreter, int, const char*[]);
+    static int          parse_display_proc(CdlInterpreter, int, const char*[]);
+    static int          parse_update_proc(CdlInterpreter, int, const char*[]);
+    
+    // Persistence support. Dialogs should just be ignored when it
+    // comes to saving and restoring files.
+    virtual void        save(CdlInterpreter, Tcl_Channel, int, bool);
+                                                
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  private:
+    // The constructor only gets invoked from inside parse_dialog()
+    CdlDialogBody(std::string);
+
+    static bool         dialogs_enabled;
+    
+    enum {
+        CdlDialogBody_Invalid   = 0,
+        CdlDialogBody_Magic     = 0x3f4df391
+    } cdldialogbody_cookie;
+    
+    // Illegal operations. The dialog name must be known at the time
+    // that the object is constructed.
+    CdlDialogBody();
+    CdlDialogBody(const CdlDialogBody&);
+    CdlDialogBody& operator=(const CdlDialogBody&);
+};
+
+//}}}
+//{{{  CdlWizard                                        
+
+// ----------------------------------------------------------------------------
+// A wizard is very much like a dialog, just a different set of properties.
+
+class CdlWizardBody :
+    public virtual CdlUserVisibleBody,
+    public virtual CdlParentableBody
+{
+    friend class CdlTest;
+
+  public:
+
+    virtual ~CdlWizardBody();
+
+    bool                has_init_proc() const;
+    bool                has_decoration_proc() const;
+    const cdl_tcl_code& get_init_proc() const;
+    const cdl_tcl_code& get_decoration_proc() const;
+    const cdl_tcl_code& get_confirm_proc() const;
+    const cdl_tcl_code& get_cancel_proc() const;
+    bool                has_screen(cdl_int) const;
+    cdl_int             get_first_screen_number() const;
+    const cdl_tcl_code& get_first_screen() const;
+    const cdl_tcl_code& get_screen(cdl_int) const;
+    static int          parse_wizard(CdlInterpreter, int, const char*[]);
+    static int          parse_cancel_proc(CdlInterpreter, int, const char*[]);
+    static int          parse_confirm_proc(CdlInterpreter, int, const char*[]);
+    static int          parse_decoration_proc(CdlInterpreter, int, const char*[]);
+    static int          parse_init_proc(CdlInterpreter, int, const char*[]);
+    static int          parse_screen(CdlInterpreter, int, const char*[]);
+    
+    // Persistence support. Wizards should just be ignored when it
+    // comes to saving and restoring files.
+    virtual void        save(CdlInterpreter, Tcl_Channel, int, bool);
+    
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  private:
+    // The constructor only gets invoked from inside parse_wizard().
+    CdlWizardBody(std::string);
+
+    // Illegal operations.
+    CdlWizardBody();
+    CdlWizardBody(const CdlWizardBody&);
+    CdlWizardBody& operator=(const CdlWizardBody&);
+    
+    enum {
+        CdlWizardBody_Invalid   = 0,
+        CdlWizardBody_Magic     = 0x4ec1c39a
+    } cdlwizardbody_cookie;
+};
+
+//}}}
+//{{{  CdlInterface class                               
+
+// ----------------------------------------------------------------------------
+// Similarly for interfaces.
+
+class CdlInterfaceBody : public virtual CdlNodeBody,
+                         public virtual CdlUserVisibleBody,
+                         public virtual CdlValuableBody,
+                         public virtual CdlParentableBody,
+                         public virtual CdlBuildableBody,
+                         public virtual CdlDefinableBody
+{
+    friend class CdlTest;
+
+  public:
+
+    ~CdlInterfaceBody();
+
+    void                get_implementers(std::vector<CdlValuable>&) const;
+    void                recalculate(CdlTransaction);
+    
+    static int          parse_interface(CdlInterpreter, int, const char*[]);
+    
+    // Persistence support. The interface data cannot sensibly be modified
+    // by users, it is all calculated. However it is useful to have the
+    // interface data present in the saved file so that users can examine
+    // dependencies etc.
+    virtual void        save(CdlInterpreter, Tcl_Channel, int, bool);
+    static void         initialize_savefile_support(CdlToplevel);
+    static int          savefile_interface_command(CdlInterpreter, int, const char*[]);
+
+    bool                was_generated() const;
+    virtual bool        is_modifiable() const;
+    virtual std::string get_class_name() const;
+    bool                check_this(cyg_assert_class_zeal = cyg_quick) const;
+    CYGDBG_DECLARE_MEMLEAK_COUNTER();
+
+  private:
+    CdlInterfaceBody(std::string, bool /* generated */);
+    bool        generated;
+    
+    enum {
+        CdlInterfaceBody_Invalid   = 0,
+        CdlInterfaceBody_Magic     = 0x67f7fbe5
+    } cdlinterfacebody_cookie;
+    CdlInterfaceBody();
+    CdlInterfaceBody(const CdlInterfaceBody&);
+    CdlInterfaceBody& operator=(const CdlInterfaceBody&);
+};
+
+//}}}
+
+#endif  /* !__CDLCORE_HXX */
+// EOF cdlcore.hxx