3 //============================================================================
7 // Implementations of the various base classes
9 //============================================================================
10 //####COPYRIGHTBEGIN####
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 2002 Bart Veer
14 // Copyright (C) 1999, 2000 Red Hat, Inc.
16 // This file is part of the eCos host tools.
18 // This program is free software; you can redistribute it and/or modify it
19 // under the terms of the GNU General Public License as published by the Free
20 // Software Foundation; either version 2 of the License, or (at your option)
23 // This program is distributed in the hope that it will be useful, but WITHOUT
24 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
25 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
28 // You should have received a copy of the GNU General Public License along with
29 // this program; if not, write to the Free Software Foundation, Inc.,
30 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 // ----------------------------------------------------------------------------
34 //####COPYRIGHTEND####
35 //============================================================================
36 //#####DESCRIPTIONBEGIN####
42 // Description: libcdl defines a hierarchy of base classes, used for
43 // constructing higher-level entities such as options
46 //####DESCRIPTIONEND####
47 //============================================================================
52 // ----------------------------------------------------------------------------
53 #include "cdlconfig.h"
55 // Get the infrastructure types, assertions, tracing and similar
57 #include <cyg/infra/cyg_ass.h>
58 #include <cyg/infra/cyg_trac.h>
60 // <cdl.hxx> defines everything implemented in this module.
61 // It implicitly supplies <string>, <vector> and <map> because
62 // the class definitions rely on these headers.
63 #include <cdlcore.hxx>
69 // ----------------------------------------------------------------------------
70 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlNodeBody);
71 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlContainerBody);
72 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlLoadableBody);
73 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlToplevelBody);
74 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlUserVisibleBody);
75 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlParentableBody);
82 // ----------------------------------------------------------------------------
83 // The real constructor takes a string argument and should get invoked first.
84 // Because of the way virtual inheritance is used it is also necessary to have
85 // a default constructor, but that need not do anything. A newly constructed
86 // object does not yet live in the hierarchy.
88 CdlNodeBody::CdlNodeBody(std::string name_arg)
90 CYG_REPORT_FUNCNAME("CdlNode:: constructor");
91 CYG_REPORT_FUNCARG1XV(this);
92 CYG_PRECONDITIONC("" != name_arg);
99 remove_node_container_position = -1;
101 // The STL containers will take care of themselves.
103 cdlnodebody_cookie = CdlNodeBody_Magic;
104 CYGDBG_MEMLEAK_CONSTRUCTOR();
106 CYG_POSTCONDITION_THISC();
110 CdlNodeBody::CdlNodeBody()
112 CYG_PRECONDITION_THISC();
118 // ----------------------------------------------------------------------------
119 // By the time the destructor gets invoked the node should already
120 // have been unbound and removed from the hierarchy.
122 CdlNodeBody::~CdlNodeBody()
124 CYG_REPORT_FUNCNAME("CdlNode:: destructor");
125 CYG_REPORT_FUNCARG1XV(this);
126 CYG_PRECONDITION_THISC();
128 // Make sure that the node is unbound: all references to and from
129 // this node should have been destroyed already inside a
131 CYG_PRECONDITIONC(0 == referrers.size());
133 // Make sure that the node has been removed from the hierarchy
134 CYG_PRECONDITIONC(0 == toplevel);
135 CYG_PRECONDITIONC(0 == owner);
136 CYG_PRECONDITIONC(0 == parent);
138 // Destroy all properties associated with this object.
139 std::vector<CdlProperty>::iterator prop_i;
140 for (prop_i= properties.begin(); prop_i != properties.end(); prop_i++) {
146 cdlnodebody_cookie = CdlNodeBody_Invalid;
149 unsupported_savefile_strings.clear();
151 CYGDBG_MEMLEAK_DESTRUCTOR();
157 //{{{ Trivial data access
159 // ----------------------------------------------------------------------------
162 CdlNodeBody::get_name() const
164 CYG_REPORT_FUNCNAME("CdlNode::get_name");
165 CYG_REPORT_FUNCARG1XV(this);
166 CYG_PRECONDITION_THISC();
173 CdlNodeBody::set_name(std::string name_arg)
175 CYG_REPORT_FUNCNAME("CdlNode::set_name");
176 CYG_REPORT_FUNCARG1XV(this);
177 CYG_PRECONDITION_THISC();
185 CdlNodeBody::get_parent() const
187 CYG_REPORT_FUNCNAMETYPE("CdlNode::get_parent", "parent %p");
188 CYG_REPORT_FUNCARG1XV(this);
189 CYG_PRECONDITION_THISC();
191 CdlContainer result = parent;
192 CYG_REPORT_RETVAL(result);
197 CdlNodeBody::get_owner() const
199 CYG_REPORT_FUNCNAMETYPE("CdlNode::get_owner", "owner %p");
200 CYG_REPORT_FUNCARG1XV(this);
201 CYG_PRECONDITION_THISC();
203 CdlLoadable result = owner;
204 CYG_REPORT_RETVAL(result);
209 CdlNodeBody::get_toplevel() const
211 CYG_REPORT_FUNCNAMETYPE("CdlNode::get_toplevel", "toplevel %p");
212 CYG_REPORT_FUNCARG1XV(this);
213 CYG_PRECONDITION_THISC();
215 CdlToplevel result = toplevel;
216 CYG_REPORT_RETVAL(result);
221 CdlNodeBody::get_class_name() const
223 CYG_REPORT_FUNCNAME("CdlNode::get_class_name");
224 CYG_PRECONDITION_THISC();
230 //{{{ The properties vector
232 // ----------------------------------------------------------------------------
233 // Trivial manipulation of the properties vector.
235 const std::vector<CdlProperty>&
236 CdlNodeBody::get_properties() const
238 CYG_REPORT_FUNCNAME("CdlNode::get_properties");
239 CYG_REPORT_FUNCARG1XV(this);
240 CYG_PRECONDITION_THISC();
247 CdlNodeBody::get_property(std::string id) const
249 CYG_REPORT_FUNCNAMETYPE("CdlNode::get_property", "result %p");
250 CYG_REPORT_FUNCARG1XV(this);
251 CYG_PRECONDITION_THISC();
253 CdlProperty result = 0;
254 std::vector<CdlProperty>::const_iterator prop_i;
255 for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
256 if ((*prop_i)->get_property_name() == id) {
262 CYG_REPORT_RETVAL(result);
267 CdlNodeBody::get_properties(std::string id, std::vector<CdlProperty>& result) const
269 CYG_REPORT_FUNCNAME("CdlNode::get_properties");
270 CYG_REPORT_FUNCARG1XV(this);
271 CYG_PRECONDITION_THISC();
273 std::vector<CdlProperty>::const_iterator prop_i;
274 for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
275 if ((*prop_i)->get_property_name() == id) {
276 result.push_back(*prop_i);
283 std::vector<CdlProperty>
284 CdlNodeBody::get_properties(std::string id) const
286 CYG_REPORT_FUNCNAME("CdlNode::get_properties");
287 CYG_REPORT_FUNCARG1XV(this);
288 CYG_PRECONDITION_THISC();
290 std::vector<CdlProperty> result;
291 std::vector<CdlProperty>::const_iterator prop_i;
292 for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
293 if ((*prop_i)->get_property_name() == id) {
294 result.push_back(*prop_i);
303 CdlNodeBody::has_property(std::string id) const
305 CYG_REPORT_FUNCNAMETYPE("CdlNode::has_property", "result %d");
306 CYG_REPORT_FUNCARG1XV(this);
307 CYG_PRECONDITION_THISC();
310 std::vector<CdlProperty>::const_iterator prop_i;
311 for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
312 if ((*prop_i)->get_property_name() == id) {
318 CYG_REPORT_RETVAL(result);
323 CdlNodeBody::count_properties(std::string id) const
325 CYG_REPORT_FUNCNAMETYPE("CdlNode::count_properties", "result %d");
326 CYG_REPORT_FUNCARG1XV(this);
327 CYG_PRECONDITION_THISC();
330 std::vector<CdlProperty>::const_iterator prop_i;
331 for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
332 if ((*prop_i)->get_property_name() == id) {
337 CYG_REPORT_RETVAL(result);
344 // ----------------------------------------------------------------------------
345 // Provide access to the current set of conflicts. This operates on the global
346 // state, more commonly these changes happen in the context of a transaction.
349 CdlNodeBody::get_conflicts(std::vector<CdlConflict>& result) const
351 CYG_REPORT_FUNCNAME("CdlNode::get_conflicts");
352 CYG_REPORT_FUNCARG1XV(this);
353 CYG_PRECONDITION_THISC();
355 const std::list<CdlConflict>& conflicts = toplevel->get_all_conflicts();
356 std::list<CdlConflict>::const_iterator conf_i;
357 for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
358 if ((*conf_i)->get_node() == this) {
359 result.push_back(*conf_i);
367 CdlNodeBody::get_conflicts(bool (*fn)(CdlConflict), std::vector<CdlConflict>& result) const
369 CYG_REPORT_FUNCNAME("CdlNode::get_conflicts");
370 CYG_REPORT_FUNCARG2XV(this, fn);
371 CYG_PRECONDITION_THISC();
372 CYG_CHECK_FUNC_PTRC(fn);
374 const std::list<CdlConflict>& conflicts = toplevel->get_all_conflicts();
375 std::list<CdlConflict>::const_iterator conf_i;
376 for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
377 if (((*conf_i)->get_node() == this) && ((*fn)(*conf_i))) {
378 result.push_back(*conf_i);
386 CdlNodeBody::get_structural_conflicts(std::vector<CdlConflict>& result) const
388 CYG_REPORT_FUNCNAME("CdlNode::get_structural_conflicts");
389 CYG_REPORT_FUNCARG1XV(this);
390 CYG_PRECONDITION_THISC();
392 const std::list<CdlConflict>& conflicts = toplevel->get_all_structural_conflicts();
393 std::list<CdlConflict>::const_iterator conf_i;
394 for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
395 if ((*conf_i)->get_node() == this) {
396 result.push_back(*conf_i);
404 CdlNodeBody::get_structural_conflicts(bool (*fn)(CdlConflict), std::vector<CdlConflict>& result) const
406 CYG_REPORT_FUNCNAME("CdlNode::get_conflicts");
407 CYG_REPORT_FUNCARG2XV(this, fn);
408 CYG_PRECONDITION_THISC();
409 CYG_CHECK_FUNC_PTRC(fn);
411 const std::list<CdlConflict>& conflicts = toplevel->get_all_structural_conflicts();
412 std::list<CdlConflict>::const_iterator conf_i;
413 for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
414 if (((*conf_i)->get_node() == this) && ((*fn)(*conf_i))) {
415 result.push_back(*conf_i);
425 // ----------------------------------------------------------------------------
426 // And access to the referrers vector.
427 const std::vector<CdlReferrer>&
428 CdlNodeBody::get_referrers() const
430 CYG_REPORT_FUNCNAME("CdlNode::get_referrers");
431 CYG_REPORT_FUNCARG1XV(this);
432 CYG_PRECONDITION_THISC();
439 //{{{ Property parsers
441 // ----------------------------------------------------------------------------
442 // Property parsing. For now there are now properties guaranteed to be
443 // associated with every node. This may change in future, e.g.
444 // internal debugging-related properties.
446 CdlNodeBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
448 CYG_REPORT_FUNCNAME("CdlNode::add_property_parsers");
453 CdlNodeBody::check_properties(CdlInterpreter interp)
455 CYG_REPORT_FUNCNAME("CdlNode::check_properties");
456 CYG_REPORT_FUNCARG2XV(this, interp);
457 CYG_PRECONDITION_THISC();
458 CYG_PRECONDITION_CLASSC(interp);
464 //{{{ is_active() etc.
466 // ----------------------------------------------------------------------------
467 // Associated with every node is a boolean that holds the current
468 // "active" state. Changes to this happen only at transaction
472 CdlNodeBody::is_active() const
474 CYG_REPORT_FUNCNAMETYPE("CdlNode::is_active", "result %d");
475 CYG_REPORT_FUNCARG1XV(this);
476 CYG_PRECONDITION_THISC();
478 bool result = active;
479 CYG_REPORT_RETVAL(result);
484 CdlNodeBody::is_active(CdlTransaction transaction)
486 CYG_REPORT_FUNCNAMETYPE("CdlNode::is_active", "result %d");
487 CYG_REPORT_FUNCARG2XV(this, transaction);
488 CYG_PRECONDITION_THISC();
489 CYG_PRECONDITION_ZERO_OR_CLASSC(transaction);
492 if (0 != transaction) {
493 result = transaction->is_active(this);
497 CYG_REPORT_RETVAL(result);
501 // This virtual member function allows nodes to check whether or not
502 // they should be active. Derived classes may impose additional
505 CdlNodeBody::test_active(CdlTransaction transaction)
507 CYG_REPORT_FUNCNAMETYPE("CdlNode::test_active", "result %d");
508 CYG_PRECONDITION_THISC();
509 CYG_PRECONDITION_CLASSC(transaction);
512 if ((0 != parent) && (transaction->is_active(parent))) {
513 CdlValuable valuable = dynamic_cast<CdlValuable>(parent);
516 } else if (valuable->is_enabled(transaction)) {
521 CYG_REPORT_RETVAL(result);
526 //{{{ Propagation support
528 // ----------------------------------------------------------------------------
529 // In the base class nothing needs doing for propagation.
531 CdlNodeBody::update(CdlTransaction transaction, CdlUpdate change)
533 CYG_REPORT_FUNCNAME("CdlNode::update");
534 CYG_REPORT_FUNCARG1XV(this);
535 CYG_PRECONDITION_THISC();
536 CYG_PRECONDITION_CLASSC(transaction);
542 //{{{ Persistence support
544 // ----------------------------------------------------------------------------
545 // The CdlNode::save() member should never get invoked directly, it should
546 // get invoked indirectly from e.g. CdlOption::save(). Normally there is
547 // no information associated with a node that ends up in a save file
548 // (the calling code will have take care of the name etc.). However there
549 // is support in the library for storing application-specific data in the
550 // save file, for example GUI information, and this information must be
551 // preserved even if it is not recognised. Savefiles are self-describing,
552 // they contain details of all the commands that are applicable.
553 // CdlNode::save() is responsible for outputting the unrecognised strings
557 CdlNodeBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
559 CYG_REPORT_FUNCNAME("CdlNode::save");
560 CYG_REPORT_FUNCARG5XV(this, interp, chan, indentation, minimal);
562 if (unsupported_savefile_strings.size() != 0) {
563 // We should already be inside the body of a suitable command,
564 // e.g. cdl_option xyz { ... }
565 // CdlToplevel::savefile_handle_unsupported() is responsible for
566 // putting suitably formatted strings into the
567 // unsupported_savefile_strings vector, so all that is needed here
568 // is to dump those strings to the channel.
569 std::string data = "\n";
570 std::vector<std::string>::const_iterator str_i;
571 for (str_i = unsupported_savefile_strings.begin(); str_i != unsupported_savefile_strings.end(); str_i++) {
572 data += std::string(indentation, ' ') + *str_i + " ;\n";
574 interp->write_data(chan, data);
577 CYG_UNUSED_PARAM(bool, minimal);
582 CdlNodeBody::has_additional_savefile_information() const
584 CYG_REPORT_FUNCNAMETYPE("CdlNode::has_additional_savefile_information", "result %d");
585 CYG_REPORT_FUNCARG1XV(this);
586 CYG_PRECONDITION_THISC();
588 bool result = (0 != unsupported_savefile_strings.size());
589 CYG_REPORT_RETVAL(result);
596 // ----------------------------------------------------------------------------
597 // Because of multiple and virtual inheritance, check_this() may
598 // get called rather a lot. Unfortunately all of the checks are
602 CdlNodeBody::check_this(cyg_assert_class_zeal zeal) const
604 if (CdlNodeBody_Magic != cdlnodebody_cookie) {
607 CYGDBG_MEMLEAK_CHECKTHIS();
613 // It is hard to validate the toplevel, owner, and parent
616 // 1) when a node is newly created all three fields will
618 // 2) the toplevel may be null if the node is in the process
619 // of being removed, e.g. during an unload operation.
620 // The node should still have a valid owner, and will
621 // have a parent unless the node is also the loadable.
622 // 3) some nodes are special, e.g. the orphans container,
623 // and do not have an owner.
625 // So the following combinations can occur:
626 // Toplevel Owner Parent
627 // 0 0 0 Creation & toplevel
628 // 0 Valid 0 Loadable being unloaded
629 // 0 Valid Valid Node being unloaded
630 // Valid 0 Valid Orphans container
631 // Valid Valid Valid Any node
639 case cyg_system_test :
642 if ((0 != toplevel) && (toplevel != this)) {
643 if (!toplevel->check_this(cyg_quick)) {
646 if (toplevel->lookup_table.find(name) == toplevel->lookup_table.end()) {
651 if (!parent->check_this(cyg_quick)) {
654 if (std::find(parent->contents.begin(), parent->contents.end(), this) == parent->contents.end()) {
659 if (!owner->check_this(cyg_quick)) {
662 if (std::find(owner->owned.begin(), owner->owned.end(), this) == owner->owned.end()) {
666 std::vector<CdlProperty>::const_iterator prop_i;
667 for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
668 if (!(*prop_i)->check_this(cyg_quick)) {
672 std::vector<CdlReferrer>::const_iterator ref_i;
673 for (ref_i = referrers.begin(); ref_i != referrers.end(); ref_i++) {
674 if (!ref_i->check_this(cyg_quick)) {
693 //{{{ CdlContainerBody
697 // ----------------------------------------------------------------------------
698 // A container simply holds other nodes in a hierarchy. Most
699 // containers correspond to data in CDL scripts, but there are
700 // exceptions. For example, if an option is reparented below some
701 // package or component that is not yet known then it can instead be
702 // reparented into an "orphans" container immediately below the
705 // Adding and removing entries to a container is done inside the
706 // CdlToplevel add_node() and remove_node() members. These will update
707 // all the fields needed to keep the hierarchy consistent. This
708 // means that the CdlContainer class itself provides only limited
711 CdlContainerBody::CdlContainerBody()
713 CYG_REPORT_FUNCNAME("CdlContainer:: default constructor");
714 CYG_REPORT_FUNCARG1XV(this);
716 cdlcontainerbody_cookie = CdlContainerBody_Magic;
717 CYGDBG_MEMLEAK_CONSTRUCTOR();
719 CYG_POSTCONDITION_THISC();
723 // This variant is for internal use, to allow the library to create
724 // containers that do not correspond to CDL entities such as
725 // the orphans container.
726 CdlContainerBody::CdlContainerBody(std::string name_arg)
727 : CdlNodeBody(name_arg)
729 CYG_REPORT_FUNCNAME("CdlContainerBody:: constructor (name)");
730 CYG_REPORT_FUNCARG1XV(this);
732 cdlcontainerbody_cookie = CdlContainerBody_Magic;
733 CYGDBG_MEMLEAK_CONSTRUCTOR();
735 CYG_POSTCONDITION_THISC();
742 // ----------------------------------------------------------------------------
744 CdlContainerBody::~CdlContainerBody()
746 CYG_REPORT_FUNCNAME("CdlContainer:: destructor");
747 CYG_REPORT_FUNCARG1XV(this);
748 CYG_PRECONDITION_THISC();
750 // Containers should always be empty by the time they
751 // get deleted. The toplevel and loadable destructors should
753 CYG_ASSERTC(0 == contents.size());
755 cdlcontainerbody_cookie = CdlContainerBody_Invalid;
756 CYGDBG_MEMLEAK_DESTRUCTOR();
762 //{{{ Accessing the contents
764 // ----------------------------------------------------------------------------
765 // Simple contents access facilities, including searching. Note that
766 // the toplevel class maintains a <name,ptr> map, which will usually
767 // be more efficient.
769 const std::vector<CdlNode>&
770 CdlContainerBody::get_contents() const
772 CYG_REPORT_FUNCNAME("CdlContainer::get_contents");
773 CYG_REPORT_FUNCARG1XV(this);
774 CYG_PRECONDITION_THISC();
781 CdlContainerBody::contains(CdlConstNode node, bool recurse) const
783 CYG_REPORT_FUNCNAMETYPE("CdlContainer::contains (node)", "result %d");
784 CYG_REPORT_FUNCARG3XV(this, node, recurse);
785 CYG_PRECONDITION_THISC();
786 CYG_PRECONDITION_CLASSC(node);
789 std::vector<CdlNode>::const_iterator node_i;
790 for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
791 if (node == *node_i) {
796 CdlConstContainer child = dynamic_cast<CdlConstContainer>(*node_i);
797 if ((0 != child) && child->contains(node, true)) {
804 CYG_REPORT_RETVAL(result);
809 CdlContainerBody::contains(const std::string name, bool recurse) const
811 CYG_REPORT_FUNCNAMETYPE("CdlContainer::contains (name)", "result %d");
812 CYG_REPORT_FUNCARG2XV(this, recurse);
813 CYG_PRECONDITION_THISC();
814 CYG_PRECONDITIONC("" != name);
817 std::vector<CdlNode>::const_iterator node_i;
818 for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
819 if ((*node_i)->get_name() == name) {
824 CdlConstContainer child = dynamic_cast<CdlConstContainer>(*node_i);
825 if ((0 != child) && child->contains(name, true)) {
832 CYG_REPORT_RETVAL(result);
837 CdlContainerBody::find_node(const std::string name, bool recurse) const
839 CYG_REPORT_FUNCNAMETYPE("CdlContainer::find_node", "result %p");
840 CYG_REPORT_FUNCARG2XV(this, recurse);
841 CYG_PRECONDITION_THISC();
842 CYG_PRECONDITIONC("" != name);
845 std::vector<CdlNode>::const_iterator node_i;
846 for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
847 if ((*node_i)->get_name() == name) {
852 CdlConstContainer child = dynamic_cast<CdlConstContainer>(*node_i);
854 result = child->find_node(name, true);
862 CYG_REPORT_RETVAL(result);
869 // ----------------------------------------------------------------------------
872 CdlContainerBody::get_class_name() const
874 CYG_REPORT_FUNCNAME("CdlContainer::get_class_name");
875 CYG_PRECONDITION_THISC();
883 // ----------------------------------------------------------------------------
884 // If a container becomes active and is enabled then it is necessary
885 // to check all the children in case they want to become active as well.
888 CdlContainerBody::update(CdlTransaction transaction, CdlUpdate change)
890 CYG_REPORT_FUNCNAME("CdlContainer::update");
891 CYG_REPORT_FUNCARG1XV(this);
892 CYG_PRECONDITION_THISC();
893 CYG_PRECONDITION_CLASSC(transaction);
895 if ((CdlUpdate_ActiveChange != change) && (CdlUpdate_ValueChange != change)) {
900 if (transaction->is_active(this)) {
901 // The container has become active. It is necessary to check
902 // all the children. If any of them should be active as well
903 // but are not then this needs to change.
904 std::vector<CdlNode>::iterator node_i;
906 for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
907 bool old_state = transaction->is_active(*node_i);
908 bool new_state = (*node_i)->test_active(transaction);
909 if (old_state != new_state) {
910 transaction->set_active(*node_i, new_state);
914 // The container has become inactive. Any children that were
915 // active should also become inactive.
916 std::vector<CdlNode>::iterator node_i;
917 for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
918 if (transaction->is_active(*node_i)) {
919 transaction->set_active(*node_i, false);
930 // ----------------------------------------------------------------------------
931 // This member function is invoked while traversing the hierarchy.
932 // The container itself will have been saved already, this member
933 // is responsible only for the contents. There are marker comments
934 // in the output file to indicate a new level in the hierarchy.
936 // Note that this member can also be invoked for the "orphans" container.
937 // That container will not appear in the save file, but its contents
941 CdlContainerBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
943 CYG_REPORT_FUNCNAME("CdlContainer::save");
944 CYG_REPORT_FUNCARG4XV(this, interp, chan, indentation);
945 CYG_PRECONDITION_THISC();
946 CYG_PRECONDITION_CLASSC(interp);
947 CYG_PRECONDITIONC(0 == indentation);
949 if (0 != contents.size()) {
951 interp->write_data(chan, "# >\n");
953 std::vector<CdlNode>::const_iterator node_i;
954 for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
955 (*node_i)->save(interp, chan, indentation, minimal);
958 interp->write_data(chan, "# <\n");
968 // ----------------------------------------------------------------------------
970 CdlContainerBody::check_this(cyg_assert_class_zeal zeal) const
972 if (CdlContainerBody_Magic != cdlcontainerbody_cookie) {
975 CYGDBG_MEMLEAK_CHECKTHIS();
977 if (cyg_extreme == zeal) {
978 std::vector<CdlNode>::const_iterator node_i;
979 for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
980 if (!((*node_i)->check_this(cyg_quick))) {
985 return CdlNodeBody::check_this(zeal);
991 //{{{ CdlLoadableBody
995 // ----------------------------------------------------------------------------
996 // A loadable object keeps track of all the nodes read in from a
997 // particular script, in an "owned" vector. Simply keeping things in a
998 // hierarchy is not enough because of possible re-parenting. Actual
999 // updates of the owned vector happen inside the CdlToplevel
1000 // add_node() and remove_node() family.
1002 CdlLoadableBody::CdlLoadableBody(CdlToplevel toplevel, std::string dir)
1003 : CdlContainerBody()
1005 CYG_REPORT_FUNCNAME("CdlLoadable:: constructor");
1006 CYG_REPORT_FUNCARG1XV(this);
1007 CYG_PRECONDITION_CLASSC(toplevel);
1009 // Initialize enough of the object to support check_this()
1012 remove_node_loadables_position = -1;
1013 cdlloadablebody_cookie = CdlLoadableBody_Magic;
1015 // The owned vector takes care of itself. It is necessary
1016 // to create a new slave interpreter, using the master
1017 // interpreter from the toplevel.
1018 CdlInterpreter master = toplevel->get_interpreter();
1019 CYG_ASSERTC(0 != master);
1020 interp = master->create_slave(this, false);
1021 interp->push_context(this->get_name());
1022 CYGDBG_MEMLEAK_CONSTRUCTOR();
1024 CYG_POSTCONDITION_THISC();
1025 CYG_REPORT_RETURN();
1028 // Needed by derived classes, but should never actually be used.
1029 CdlLoadableBody::CdlLoadableBody()
1031 CYG_FAIL("CdlLoadable default constructor should never get invoked");
1037 // ----------------------------------------------------------------------------
1038 // The loadable destructor. This gets invoked from two places: after an
1039 // unsuccessful load operation, and from inside the transaction commit
1040 // code. Either way most of the clean-up will have happened already:
1041 // all the nodes will have been removed from the toplevel's hierarchy,
1042 // and all property references to and from this loadable will have been
1045 // Since all nodes belonging to the loadable are also present in
1046 // the owned vector, they must be destroyed before this destructor
1047 // completes. Hence clearing out the contents cannot be left to
1048 // the base CdlContainer destructor.
1050 CdlLoadableBody::~CdlLoadableBody()
1052 CYG_REPORT_FUNCNAME("CdlLoadable:: destructor");
1053 CYG_REPORT_FUNCARG1XV(this);
1054 CYG_PRECONDITION_THISC();
1056 // Make sure that the loadable has already been removed from the
1057 // hierarchy: it should not have a toplevel or a parent.
1058 CYG_PRECONDITIONC(0 == toplevel);
1059 CYG_PRECONDITIONC(0 == parent);
1061 // Containers must have been created before any of their contents.
1062 // The only way to reverse this involves a parent property, but
1063 // all such properties will have been unbound already such that
1064 // the nodes can be safely deleted. The only worry is that
1065 // loadables own themselves.
1067 for (i = owned.size() - 1; i >= 0; i--) {
1068 CdlNode node = owned[i];
1069 CYG_LOOP_INVARIANT_CLASSC(node);
1072 CdlToplevelBody::remove_node(this, node->parent, node);
1077 // Now there should be exactly one entry in the owned vector,
1078 // the loadable itself. We already know that this is no longer
1079 // part of the toplevel and it does not have a parent, so
1080 // the only field we need to worry about is the owner.
1081 CYG_ASSERTC(1 == owned.size());
1082 CYG_ASSERTC(this == owned[0]);
1085 // Strictly speaking the owned vector should be clear by now,
1086 // but remove_node() does not actually bother to clear it.
1089 // The loadable should now be empty. It remains to clean up
1090 // a few odds and ends.
1091 cdlloadablebody_cookie = CdlLoadableBody_Invalid;
1092 CYG_ASSERTC(0 == owned.size());
1097 CYGDBG_MEMLEAK_DESTRUCTOR();
1099 CYG_REPORT_RETURN();
1103 //{{{ Simple information access
1105 // ----------------------------------------------------------------------------
1107 const std::vector<CdlNode>&
1108 CdlLoadableBody::get_owned() const
1110 CYG_REPORT_FUNCNAME("CdlLoadable::get_owned");
1111 CYG_REPORT_FUNCARG1XV(this);
1112 CYG_PRECONDITION_THISC();
1114 CYG_REPORT_RETURN();
1119 CdlLoadableBody::owns(CdlConstNode node) const
1121 CYG_REPORT_FUNCNAMETYPE("CdlLoadable::owns", "result %d");
1122 CYG_REPORT_FUNCARG2XV(this, node);
1123 CYG_PRECONDITION_THISC();
1124 CYG_PRECONDITION_CLASSC(node);
1126 bool result = false;
1127 std::vector<CdlNode>::const_iterator i = std::find(owned.begin(), owned.end(), node);
1128 if (i != owned.end()) {
1132 CYG_REPORT_RETVAL(result);
1137 CdlLoadableBody::get_interpreter() const
1139 CYG_REPORT_FUNCNAMETYPE("CdlLoadable::get_interpreter", "result %p");
1140 CYG_REPORT_FUNCARG1XV(this);
1141 CYG_PRECONDITION_THISC();
1143 CdlInterpreter result = interp;
1144 CYG_REPORT_RETVAL(result);
1149 CdlLoadableBody::get_directory() const
1151 CYG_REPORT_FUNCNAME("CdlLoadable::get_directory");
1152 CYG_REPORT_FUNCARG1XV(this);
1153 CYG_PRECONDITION_THISC();
1155 CYG_REPORT_RETURN();
1160 //{{{ Bind/unbind support
1162 // ----------------------------------------------------------------------------
1163 // Binding a loadable. This involves checking every property of every node
1164 // in the loadable, which the properties do themselves by a suitable
1165 // update() virtual function. Next, there may be properties in the
1166 // existing configuration which could not previously be bound: there
1167 // will be structural conflicts for all of these. Once all the pointers
1168 // go to the right places it is possible to calculate the default values
1169 // and generally process the properties. Finally each node's active
1170 // state is checked - the default inactive state will be inappropriate
1173 // FIXME: error recovery?
1176 CdlLoadableBody::bind(CdlTransaction transaction)
1178 CYG_REPORT_FUNCNAME("CdlLoadable::bind");
1179 CYG_REPORT_FUNCARG2XV(this, transaction);
1180 CYG_INVARIANT_THISC(CdlLoadableBody);
1181 CYG_INVARIANT_CLASSC(CdlTransactionBody, transaction);
1183 // The loadable must already be part of the hierarchy.
1184 CdlToplevel toplevel = this->get_toplevel();
1185 CYG_ASSERT_CLASSC(toplevel);
1187 // As a first step, bind all references in this loadable.
1188 // This is achieved via a Loaded update.
1189 const std::vector<CdlNode>& nodes = this->get_owned();
1190 std::vector<CdlNode>::const_iterator node_i;
1191 for (node_i = nodes.begin(); node_i != nodes.end(); node_i++) {
1192 const std::vector<CdlProperty>& properties = (*node_i)->get_properties();
1193 std::vector<CdlProperty>::const_iterator prop_i;
1194 for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
1195 (*prop_i)->update(transaction, *node_i, 0, CdlUpdate_Loaded);
1199 // Next, look for all structural conflicts which are unresolved
1200 // references and which can now be resolved. It is necessary
1201 // to check per-transaction structural conflicts, plus those
1202 // in any parent transactions, plus the global ones.
1203 std::list<CdlConflict>::const_iterator conf_i;
1204 CdlTransaction current_transaction = transaction;
1206 CYG_ASSERT_CLASSC(current_transaction);
1207 const std::list<CdlConflict>& new_structural_conflicts = current_transaction->get_new_structural_conflicts();
1209 for (conf_i = new_structural_conflicts.begin(); conf_i != new_structural_conflicts.end(); ) {
1211 CdlConflict conflict = *conf_i++;
1212 CYG_LOOP_INVARIANT_CLASSC(conflict);
1214 CdlConflict_Unresolved unresolved_conflict = dynamic_cast<CdlConflict_Unresolved>(conflict);
1215 if ((0 != unresolved_conflict) && !transaction->has_conflict_been_cleared(conflict)) {
1216 CdlNode dest = toplevel->lookup(unresolved_conflict->get_target_name());
1218 CdlNode node = unresolved_conflict->get_node();
1219 CdlProperty prop = unresolved_conflict->get_property();
1220 prop->update(transaction, node, dest, CdlUpdate_Created);
1224 current_transaction = current_transaction->get_parent();
1225 } while (0 != current_transaction);
1227 const std::list<CdlConflict>& structural_conflicts = toplevel->get_all_structural_conflicts();
1228 for (conf_i = structural_conflicts.begin(); conf_i != structural_conflicts.end(); ) {
1230 CdlConflict conflict = *conf_i++;
1231 CYG_LOOP_INVARIANT_CLASSC(conflict);
1233 CdlConflict_Unresolved this_conflict = dynamic_cast<CdlConflict_Unresolved>(conflict);
1234 if ((0 != this_conflict) && !transaction->has_conflict_been_cleared(conflict)) {
1235 CdlNode dest = toplevel->lookup(this_conflict->get_target_name());
1237 CdlNode node = this_conflict->get_node();
1238 CdlProperty prop = this_conflict->get_property();
1239 prop->update(transaction, node, dest, CdlUpdate_Created);
1244 // Conflict resolution has happened. Next it is time
1245 // to evaluate default_value expressions and the like
1246 // in the new loadable.
1247 for (node_i = nodes.begin(); node_i != nodes.end(); node_i++) {
1248 const std::vector<CdlProperty>& properties = (*node_i)->get_properties();
1249 std::vector<CdlProperty>::const_iterator prop_i;
1250 for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
1251 (*prop_i)->update(transaction, *node_i, 0, CdlUpdate_Init);
1255 // Nodes start off inactive. Check each one whether or not it
1256 // should be active.
1257 // NOTE: possibly this should be done via a per-node init
1259 for (node_i = nodes.begin(); node_i != nodes.end(); node_i++) {
1260 bool current_state = transaction->is_active(*node_i);
1261 bool new_state = (*node_i)->test_active(transaction);
1262 if (current_state != new_state) {
1263 transaction->set_active(*node_i, new_state);
1267 CYG_REPORT_RETURN();
1270 // ----------------------------------------------------------------------------
1273 CdlLoadableBody::unbind(CdlTransaction transaction)
1275 CYG_REPORT_FUNCNAME("CdlLoadable::unbind");
1276 CYG_REPORT_FUNCARG2XV(this, transaction);
1277 CYG_PRECONDITION_THISC();
1278 CYG_PRECONDITION_CLASSC(transaction);
1280 // First take care of all references to nodes in the loadable
1281 // that is disappearing. This involves a Destroyed update.
1282 const std::vector<CdlNode>& nodes = this->get_owned();
1283 std::vector<CdlNode>::const_iterator node_i;
1284 for (node_i = nodes.begin(); node_i != nodes.end(); node_i++) {
1285 // The update will remove referrer objects, so it is best
1286 // to work from the back.
1287 std::vector<CdlReferrer>& referrers = (*node_i)->referrers;
1288 std::vector<CdlReferrer>::reverse_iterator ref_i;
1289 for (ref_i = referrers.rbegin(); ref_i != referrers.rend(); ref_i = referrers.rbegin()) {
1290 ref_i->update(transaction, *node_i, CdlUpdate_Destroyed);
1291 CYG_LOOP_INVARIANT(ref_i != referrers.rbegin(), "the vector should have shrunk");
1295 // Now repeat the loop, but unbind references from the unloaded objects
1296 // to ones which are going to stay loaded. This will not cause
1297 // the properties to disappear.
1298 for (node_i = nodes.begin(); node_i != nodes.end(); node_i++) {
1299 const std::vector<CdlProperty>& properties = (*node_i)->get_properties();
1300 std::vector<CdlProperty>::const_iterator prop_i;
1301 for (prop_i = properties.begin(); prop_i != properties.end(); prop_i++) {
1302 (*prop_i)->update(transaction, *node_i, 0, CdlUpdate_Unloading);
1306 // Eliminate any conflicts that belong to this loadable.
1307 // FIXME: why is his necessary? Should these conflicts not get
1308 // eliminated by the above property iterations?
1309 std::list<CdlConflict>::const_iterator conf_i;
1310 const std::list<CdlConflict>& global_conflicts = toplevel->get_all_conflicts();
1311 for (conf_i = global_conflicts.begin(); conf_i != global_conflicts.end(); ) {
1312 CdlConflict conflict = *conf_i++;
1313 CYG_LOOP_INVARIANT_CLASSC(conflict);
1314 CdlNode node = conflict->get_node();
1315 if ((node->get_owner() == this) && !transaction->has_conflict_been_cleared(conflict)) {
1316 transaction->clear_conflict(conflict);
1319 const std::list<CdlConflict>& global_structural_conflicts = toplevel->get_all_structural_conflicts();
1320 for (conf_i = global_structural_conflicts.begin(); conf_i != global_structural_conflicts.end(); ) {
1321 CdlConflict conflict = *conf_i++;
1322 CYG_LOOP_INVARIANT_CLASSC(conflict);
1323 CdlNode node = conflict->get_node();
1324 if ((node->get_owner() == this) && !transaction->has_conflict_been_cleared(conflict)) {
1325 transaction->clear_conflict(conflict);
1328 const std::list<CdlConflict>& transaction_conflicts = transaction->get_new_conflicts();
1329 for (conf_i = transaction_conflicts.begin(); conf_i != transaction_conflicts.end(); ) {
1330 CdlConflict conflict = *conf_i++;
1331 CYG_LOOP_INVARIANT_CLASSC(conflict);
1332 CdlNode node = conflict->get_node();
1333 if (node->get_owner() == this) {
1334 transaction->clear_conflict(conflict);
1337 const std::list<CdlConflict>& transaction_structural_conflicts = transaction->get_new_structural_conflicts();
1338 for (conf_i = transaction_structural_conflicts.begin(); conf_i != transaction_structural_conflicts.end(); ) {
1339 CdlConflict conflict = *conf_i++;
1340 CYG_LOOP_INVARIANT_CLASSC(conflict);
1341 CdlNode node = conflict->get_node();
1342 if (node->get_owner() == this) {
1343 transaction->clear_conflict(conflict);
1347 // FIXME: how about cleanup_orphans()
1349 CYG_REPORT_RETURN();
1352 // ----------------------------------------------------------------------------
1353 // These members are invoked for load and unload operations.
1355 // Committing a load does not require anything, the loadable has
1356 // already been fully bound and all propagation has happened.
1359 CdlLoadableBody::transaction_commit_load(CdlTransaction transaction, CdlLoadable loadable)
1361 CYG_REPORT_FUNCNAME("CdlLoadable::transaction_commit_load");
1362 CYG_REPORT_FUNCARG2XV(transaction, loadable);
1363 CYG_PRECONDITION_CLASSC(transaction);
1364 CYG_PRECONDITION_CLASSC(loadable);
1366 CYG_UNUSED_PARAM(CdlTransaction, transaction);
1367 CYG_UNUSED_PARAM(CdlLoadable, loadable);
1369 CYG_REPORT_RETURN();
1372 // Cancelling a load is more difficult. The loadable has to be
1373 // unbound, removed from the toplevel, and deleted. If any of
1374 // this fails then we are in trouble, there is no easy way to
1377 CdlLoadableBody::transaction_cancel_load(CdlTransaction transaction, CdlLoadable loadable)
1379 CYG_REPORT_FUNCNAME("CdlLoadable::transaction_cancel_load");
1380 CYG_REPORT_FUNCARG2XV(transaction, loadable);
1381 CYG_PRECONDITION_CLASSC(transaction);
1382 CYG_PRECONDITION_CLASSC(loadable);
1384 CdlToplevel toplevel = transaction->get_toplevel();
1385 CYG_PRECONDITION_CLASSC(toplevel);
1386 CYG_ASSERTC(toplevel == loadable->get_toplevel());
1388 loadable->unbind(transaction);
1389 toplevel->remove_loadable_from_toplevel(loadable);
1392 CYG_REPORT_RETURN();
1395 // Committing an unload means that the loadable can now be deleted.
1396 // It should already be unbound and removed from the toplevel.
1398 CdlLoadableBody::transaction_commit_unload(CdlTransaction transaction, CdlLoadable loadable)
1400 CYG_REPORT_FUNCNAME("CdlLoadable::transaction_commit_unload");
1401 CYG_REPORT_FUNCARG2XV(transaction, loadable);
1402 CYG_PRECONDITION_CLASSC(transaction);
1403 CYG_PRECONDITION_CLASSC(loadable);
1405 CYG_UNUSED_PARAM(CdlTransaction, transaction);
1408 CYG_REPORT_RETURN();
1411 // Cancelling an unload means that the loadable has to be re-added
1412 // to the hierarchy and then rebound. This implies that value
1413 // propagation needs to happen. However, since all value changes
1414 // since the very start of the transaction are held inside the
1415 // transaction and will be eliminated, the original state will
1416 // be restored anyway so the propagation is not actually required.
1418 CdlLoadableBody::transaction_cancel_unload(CdlTransaction transaction, CdlLoadable loadable)
1420 CYG_REPORT_FUNCNAME("CdlLoadable::transaction_cancel_unload");
1421 CYG_REPORT_FUNCARG2XV(transaction, loadable);
1422 CYG_PRECONDITION_CLASSC(transaction);
1423 CYG_PRECONDITION_CLASSC(loadable);
1425 CdlToplevel toplevel = transaction->get_toplevel();
1426 CYG_PRECONDITION_CLASSC(toplevel);
1427 toplevel->add_loadable_to_toplevel(loadable);
1428 CYG_ASSERT_CLASSC(loadable);
1429 loadable->bind(transaction);
1431 CYG_REPORT_RETURN();
1435 //{{{ File search facilities
1437 // ----------------------------------------------------------------------------
1438 // File search facilities. Given a file name such as hello.cxx from a compile
1439 // property, or doc.html from a doc property, find the corresponding filename,
1440 // for example /usr/local/eCos/kernel/v1_3/doc/threads.html#create
1442 // The second argument (default value "") indicates a preferred directory
1443 // where searching should begin. This would be src for a source file,
1444 // doc for a URL, etc.
1446 // For some properties the data may refer to a URL rather than to a local
1447 // filename. This is controlled by the third argument, allow_urls.
1448 // If false then only local filenames will be considered. allow_urls
1449 // also controls whether or not anchor processing is performed.
1451 // RFC1807: a URL consists of <scheme>:<rest>, where <scheme> can be
1452 // any sequence of lower-case letters, digits, plus, dot or hyphen. It
1453 // is recommended that upper-case letters should be accepted as well.
1455 // RFC1807: an anchor is everything after the first # in the URL.
1457 static char find_absolute_file_script[] = " \n\
1458 set cdl_anchor \"\" \n\
1459 if {$::cdl_allow_urls} { \n\
1460 if { [regexp -- {^[a-zA-Z+.-]*:.*$} $::cdl_target] } { \n\
1461 return $::cdl_target \n\
1464 set non_anchor \"\" \n\
1465 if { [regexp -- {^([^#])(#.*$)} $::cdl_target tmp non_anchor cdl_anchor] } { \n\
1466 set ::cdl_target $non_anchor \n\
1469 if {$::cdl_prefdir != \"\"} { \n\
1470 set filename [file join $::cdl_topdir $::cdl_pkgdir $::cdl_prefdir $::cdl_target] \n\
1471 if {[file exists $filename]} { \n\
1472 return \"[set filename][set cdl_anchor]\" \n\
1475 set filename [file join $::cdl_topdir $::cdl_pkgdir $::cdl_target] \n\
1476 if {[file exists $filename]} { \n\
1477 return \"[set filename][set cdl_anchor]\" \n\
1483 CdlLoadableBody::find_absolute_file(std::string filename, std::string dirname, bool allow_urls) const
1485 CYG_REPORT_FUNCNAME("CdlLoadable::find_absolute_file");
1486 CYG_REPORT_FUNCARG1XV(this);
1487 CYG_PRECONDITION_THISC();
1488 CYG_PRECONDITIONC("" != filename);
1490 // These variable names should be kept in step with CdlBuildable::update_all_build_info()
1491 interp->set_variable("::cdl_topdir", get_toplevel()->get_directory());
1492 interp->set_variable("::cdl_pkgdir", directory);
1493 interp->set_variable("::cdl_prefdir", dirname);
1494 interp->set_variable("::cdl_target", filename);
1495 interp->set_variable("::cdl_allow_urls", allow_urls ? "1" : "0");
1498 int tmp = interp->eval(find_absolute_file_script, result);
1499 if (tmp != TCL_OK) {
1503 // Replace any backslashes in the repository with forward slashes.
1504 // The latter are used throughout the library
1505 // NOTE: this is not i18n-friendly.
1506 for (unsigned int i = 0; i < result.size(); i++) {
1507 if ('\\' == result[i]) {
1512 CYG_REPORT_RETURN();
1516 static char find_relative_file_script[] = " \n\
1517 if {$::cdl_prefdir != \"\"} { \n\
1518 set filename [file join $::cdl_prefdir $::cdl_target] \n\
1519 if {[file exists [file join $::cdl_topdir $::cdl_pkgdir $filename]]} { \n\
1520 return $filename \n\
1523 set filename $::cdl_target \n\
1524 if {[file exists [file join $::cdl_topdir $::cdl_pkgdir $filename]]} { \n\
1525 return \"[set filename][set cdl_anchor]\" \n\
1531 CdlLoadableBody::find_relative_file(std::string filename, std::string dirname) const
1533 CYG_REPORT_FUNCNAME("CdlLoadable::find_relative_file");
1534 CYG_REPORT_FUNCARG1XV(this);
1535 CYG_PRECONDITION_THISC();
1536 CYG_PRECONDITIONC("" != filename);
1538 // These variable names should be kept in step with CdlBuildable::update_all_build_info()
1539 interp->set_variable("::cdl_topdir", get_toplevel()->get_directory());
1540 interp->set_variable("::cdl_pkgdir", directory);
1541 interp->set_variable("::cdl_prefdir", dirname);
1542 interp->set_variable("::cdl_target", filename);
1545 int tmp = interp->eval(find_relative_file_script, result);
1546 if (tmp != TCL_OK) {
1550 // Replace any backslashes in the repository with forward slashes.
1551 // The latter are used throughout the library
1552 // NOTE: this is not i18n-friendly.
1553 for (unsigned int i = 0; i < result.size(); i++) {
1554 if ('\\' == result[i]) {
1559 CYG_REPORT_RETURN();
1563 static char has_subdirectory_script[] = " \n\
1564 set dirname [file join $::cdl_topdir $::cdl_pkgdir $::cdl_target] \n\
1565 if {[file isdirectory $dirname] == 0} { \n\
1572 CdlLoadableBody::has_subdirectory(std::string name) const
1574 CYG_REPORT_FUNCNAMETYPE("CdlLoadable::has_subdirectory", "result %d");
1575 CYG_REPORT_FUNCARG1XV(this);
1576 CYG_PRECONDITION_THISC();
1577 CYG_PRECONDITIONC("" != name);
1579 bool result = false;
1581 interp->set_variable("::cdl_topdir", get_toplevel()->get_directory());
1582 interp->set_variable("::cdl_pkgdir", directory);
1583 interp->set_variable("::cdl_target", name);
1585 std::string tcl_result;
1586 int tmp = interp->eval(has_subdirectory_script, tcl_result);
1587 if ((TCL_OK == tmp) && ("1" == tcl_result)) {
1591 CYG_REPORT_RETVAL(result);
1598 // ----------------------------------------------------------------------------
1601 CdlLoadableBody::get_class_name() const
1603 CYG_REPORT_FUNCNAME("CdlLoadable::get_class_name");
1604 CYG_PRECONDITION_THISC();
1605 CYG_REPORT_RETURN();
1612 // ----------------------------------------------------------------------------
1614 CdlLoadableBody::check_this(cyg_assert_class_zeal zeal) const
1616 if (CdlLoadableBody_Magic != cdlloadablebody_cookie) {
1619 CYGDBG_MEMLEAK_CHECKTHIS();
1621 if ((zeal == cyg_extreme) || (zeal == cyg_thorough)) {
1622 std::vector<CdlNode>::const_iterator node_i;
1623 for (node_i = owned.begin(); node_i != owned.end(); node_i++) {
1624 if ((!(*node_i)->check_this(cyg_quick)) || ((*node_i)->get_owner() != this)) {
1629 return CdlContainerBody::check_this(zeal);
1635 //{{{ CdlToplevelBody
1639 // ----------------------------------------------------------------------------
1640 // A toplevel is a container without a parent or owner. It keeps track
1641 // of all the names in the hierarchy, thus guaranteeing uniqueness and
1642 // providing a quick lookup facility.
1644 // The member functions add_node() and remove_node() are the only
1645 // way of modifying the hierarchy. Adding a node with a zero parent
1646 // means adding it to a special container, Orphans.
1648 // An interpreter object must be created explicitly, preventing
1649 // toplevel objects from being statically allocated (although
1650 // it is possible to play tricks with utility classes...)
1651 // There are too many possible error conditions when creating
1652 // an interpreter, so this should not happen until the world
1653 // is ready to deal with such errors.
1655 CdlToplevelBody::CdlToplevelBody(CdlInterpreter interp_arg, std::string directory_arg)
1656 : CdlContainerBody()
1658 CYG_REPORT_FUNCNAME("CdlToplevel:: constructor");
1659 CYG_REPORT_FUNCARG2XV(this, interp_arg);
1660 CYG_PRECONDITION_CLASSC(interp_arg);
1662 // The STL containers will take care of themselves.
1663 interp = interp_arg;
1664 directory = directory_arg;
1667 // A toplevel is always active, override the default setting for a node
1670 // Make the object valid before creating the orphans container.
1673 cdltoplevelbody_cookie = CdlToplevelBody_Magic;
1675 // Arguably creating the orphans container should be left until
1676 // it is actually needed. The advantage of creating it at the
1677 // start is that it will appear in a fixed location in the contents,
1678 // right at the start. Arguably the end would be better, but the
1679 // end can move as loadables get added and removed.
1681 // The GUI code will probably want to ignore any empty
1682 // containers that are not valuables and not user-visible.
1683 orphans = new CdlContainerBody("orphans");
1684 add_node(0, this, orphans);
1686 // Let the interpreter know about its owning toplevel, as well as
1688 interp->set_toplevel(this);
1690 // The orphans container needs to be active as well.
1691 orphans->active = true;
1693 CYGDBG_MEMLEAK_CONSTRUCTOR();
1695 CYG_POSTCONDITION_THISC();
1696 CYG_REPORT_RETURN();
1702 // ----------------------------------------------------------------------------
1703 // The toplevel should have been mostly cleared already, by the
1704 // appropriate derived class. Without any loadables there should not
1705 // be any conflicts. It is necessary to clean up the orphans
1706 // container, since that was created by the CdlToplevel constructor.
1707 // If there are any other special nodes then these should have been
1708 // cleared by higher level code.
1710 CdlToplevelBody::~CdlToplevelBody()
1712 CYG_REPORT_FUNCNAME("CdlToplevel:: destructor");
1713 CYG_REPORT_FUNCARG1XV(this);
1714 CYG_PRECONDITION_THISC();
1716 CYG_PRECONDITIONC(0 == loadables.size());
1717 CYG_PRECONDITIONC(0 == conflicts.size());
1718 CYG_PRECONDITIONC(0 == structural_conflicts.size());
1719 CYG_PRECONDITIONC(0 == transaction);
1721 CYG_PRECONDITIONC(0 != orphans);
1722 this->remove_node_from_toplevel(orphans);
1723 CdlToplevelBody::remove_node(0, this, orphans);
1727 CYG_PRECONDITIONC(0 == contents.size());
1729 cdltoplevelbody_cookie = CdlToplevelBody_Magic;
1732 unsupported_savefile_toplevel_strings.clear();
1733 unsupported_savefile_commands.clear();
1734 unsupported_savefile_subcommands.clear();
1736 // Since the interpreter is not created by the toplevel, it is
1737 // not destroyed with the toplevel either. This leaves a potential
1738 // big memory leak in application code.
1741 CYGDBG_MEMLEAK_DESTRUCTOR();
1743 CYG_REPORT_RETURN();
1747 //{{{ Adding and removing nodes
1749 // ----------------------------------------------------------------------------
1750 // Adding and removing a node, and changing a parent.
1752 // These routines allow the hierarchy to be manipulated. All nodes should
1753 // exist in a hierarchy below a toplevel, except for brief periods after
1754 // construction and during destruction.
1756 // Most nodes will belong to a loadable. An owner of 0 is allowed, for
1757 // objects internal to the library such as the orphans container.
1758 // Everything else must have an owner, and specifically a loadable owns
1762 CdlToplevelBody::add_node(CdlLoadable owner, CdlContainer parent, CdlNode node)
1764 CYG_REPORT_FUNCNAME("CdlToplevel::add_node");
1765 CYG_REPORT_FUNCARG4XV(this, owner, parent, node);
1766 CYG_PRECONDITION_THISC();
1767 CYG_PRECONDITION_ZERO_OR_CLASSC(owner);
1768 CYG_PRECONDITION_CLASSC(parent);
1769 CYG_PRECONDITION_CLASSC(node);
1771 // The node must not be in the hierarchy already.
1772 CYG_ASSERTC(0 == node->toplevel);
1773 CYG_ASSERTC(0 == node->owner);
1774 CYG_ASSERTC(0 == node->parent);
1776 // The node's name should be unique. Checks for that should have happened
1777 // in higher-level code.
1778 CYG_ASSERTC(lookup_table.find(node->name) == lookup_table.end());
1779 node->toplevel = this;
1780 lookup_table[node->name] = node;
1782 node->owner = owner;
1784 owner->owned.push_back(node);
1787 // If the node is in fact a loadable, it should own itself and
1788 // in addition the toplevel class keeps track of its loadables
1789 // in a separate vector.
1790 if (0 != dynamic_cast<CdlLoadable>(node)) {
1791 CYG_ASSERTC(owner == dynamic_cast<CdlLoadable>(node));
1792 this->loadables.push_back(owner);
1798 node->parent = parent;
1799 parent->contents.push_back(node);
1801 CYG_REPORT_RETURN();
1804 // Removing a node from a toplevel. This is the first step in deleting
1805 // a node: the step may be undone by a call to add_node_to_toplevel(),
1806 // or completed by a call to remove_node(). Removing a node from the
1807 // toplevel involves undoing the name->node mapping. In the case
1808 // of loadables, it also involves removing the node from the toplevel's
1809 // contents and loadables containers.
1812 CdlToplevelBody::remove_node_from_toplevel(CdlNode node)
1814 CYG_REPORT_FUNCNAME("CdlToplevel::remove_node_from_toplevel");
1815 CYG_REPORT_FUNCARG2XV(this, node);
1816 CYG_PRECONDITION_THISC();
1817 CYG_PRECONDITION_CLASSC(node);
1818 CYG_ASSERTC(this == node->toplevel);
1819 CYG_ASSERTC(lookup_table[node->name] == node);
1822 lookup_table.erase(node->name);
1824 CdlLoadable loadable = dynamic_cast<CdlLoadable>(node);
1825 if (0 != loadable) {
1826 CYG_ASSERTC(loadable == node->owner);
1827 CYG_ASSERTC(this == node->parent);
1829 // Because remove_node_from_toplevel() is reversible, the
1830 // loadable should reappear in its old position. Hence we
1831 // had better keep track of that position. Note that
1832 // this code assumed that the remove_node and add_node
1833 // calls are exactly reversed.
1835 for (i = 0; i < (int) this->contents.size(); i++) {
1836 if (this->contents[i] == node) {
1840 CYG_ASSERTC(i < (int) this->contents.size());
1841 node->remove_node_container_position = i;
1842 this->contents.erase(this->contents.begin() + i);
1845 // It is not clear that preserving the order of the loadables
1846 // in the toplevel is useful, but it is harmless.
1847 for (i = 0; i < (int) this->loadables.size(); i++) {
1848 if (this->loadables[i] == loadable) {
1852 CYG_ASSERTC(i < (int) this->loadables.size());
1853 loadable->remove_node_loadables_position = i;
1854 this->loadables.erase(this->loadables.begin() + i);
1857 CYG_REPORT_RETURN();
1861 CdlToplevelBody::remove_loadable_from_toplevel(CdlLoadable loadable)
1863 CYG_REPORT_FUNCNAME("CdlToplevel::remove_loadable_from_toplevel");
1864 CYG_REPORT_FUNCARG2XV(this, loadable);
1865 CYG_PRECONDITION_THISC();
1866 CYG_PRECONDITION_CLASSC(loadable);
1868 const std::vector<CdlNode>& contents = loadable->get_owned();
1869 for (int i = contents.size() - 1; i >= 0; i--) {
1870 CdlToplevel toplevel = contents[i]->get_toplevel();
1871 CYG_LOOP_INVARIANT_ZERO_OR_CLASSC(toplevel);
1872 if (0 != toplevel) {
1873 CYG_LOOP_INVARIANTC(this == toplevel);
1874 this->remove_node_from_toplevel(contents[i]);
1878 CYG_REPORT_RETURN();
1881 // Re-adding a node to a toplevel. This needs to undo all of the changes
1882 // that may have been done by remove_node_from_toplevel() above.
1884 CdlToplevelBody::add_node_to_toplevel(CdlNode node)
1886 CYG_REPORT_FUNCNAME("CdlToplevel::add_node_to_toplevel");
1887 CYG_REPORT_FUNCARG2XV(this, node);
1888 CYG_PRECONDITION_THISC();
1889 CYG_PRECONDITION_CLASSC(node);
1890 CYG_ASSERTC(0 == node->toplevel);
1891 CYG_ASSERTC(0 != node->owner);
1893 CYG_ASSERTC(lookup_table.find(node->name) == lookup_table.end());
1894 node->toplevel = this;
1895 lookup_table[node->name] = node;
1897 CdlLoadable loadable = dynamic_cast<CdlLoadable>(node);
1898 if (0 != loadable) {
1899 CYG_ASSERTC(loadable == node->owner);
1900 CYG_ASSERTC(0 == node->parent);
1901 CYG_ASSERTC(-1 != node->remove_node_container_position);
1902 CYG_ASSERTC(node->remove_node_container_position <= (int) this->contents.size());
1904 this->contents.insert(this->contents.begin() + node->remove_node_container_position, node);
1905 node->remove_node_container_position = -1;
1906 node->parent = this;
1908 CYG_ASSERTC(-1 != loadable->remove_node_loadables_position);
1909 this->loadables.insert(this->loadables.begin() + loadable->remove_node_loadables_position, loadable);
1910 loadable->remove_node_loadables_position = -1;
1913 CYG_REPORT_RETURN();
1917 CdlToplevelBody::add_loadable_to_toplevel(CdlLoadable loadable)
1919 CYG_REPORT_FUNCNAME("CdlToplevel::add_loadable_to_toplevel");
1920 CYG_REPORT_FUNCARG2XV(this, loadable);
1921 CYG_PRECONDITION_THISC();
1922 CYG_PRECONDITION_CLASSC(loadable);
1924 const std::vector<CdlNode>& contents = loadable->get_owned();
1925 for (int i = 0; i < (int) contents.size(); i++) {
1926 this->add_node_to_toplevel(contents[i]);
1929 CYG_REPORT_RETURN();
1932 // ----------------------------------------------------------------------------
1933 // The second stage remove operation. This cannot be undone, and
1934 // happens just before the node gets deleted and after a succesful
1935 // remove_node_from_toplevel().
1937 CdlToplevelBody::remove_node(CdlLoadable owner, CdlContainer parent, CdlNode node)
1939 CYG_REPORT_FUNCNAME("CdlToplevel::remove_node");
1940 CYG_REPORT_FUNCARG3XV(owner, parent, node);
1941 CYG_PRECONDITION_CLASSC(node);
1942 CYG_PRECONDITION_ZERO_OR_CLASSC(owner);
1943 CYG_PRECONDITION_ZERO_OR_CLASSC(parent);
1944 CYG_PRECONDITIONC(node->owner == owner);
1945 CYG_PRECONDITIONC(node->parent == parent);
1946 CYG_PRECONDITIONC(0 == node->toplevel);
1950 owner->owned.erase(std::find(owner->owned.begin(), owner->owned.end(), node));
1954 parent->contents.erase(std::find(parent->contents.begin(), parent->contents.end(), node));
1957 CYG_REPORT_RETURN();
1960 // Changing a parent does not affect the node's standing in terms of the
1961 // overall hierarchy or its owner, only the parent field.
1963 CdlToplevelBody::change_parent(CdlLoadable owner, CdlContainer old_parent, CdlContainer new_parent, CdlNode node, int pos)
1965 CYG_REPORT_FUNCNAME("CdlToplevel::change_parent");
1966 CYG_REPORT_FUNCARG6XV(this, owner, parent, new_parent, node, pos);
1967 CYG_PRECONDITION_THISC();
1968 CYG_PRECONDITION_CLASSC(old_parent);
1969 CYG_PRECONDITION_ZERO_OR_CLASSC(new_parent);
1970 CYG_PRECONDITION_CLASSC(node);
1971 CYG_PRECONDITIONC(node->owner == owner);
1972 CYG_PRECONDITIONC(node->parent == old_parent);
1973 CYG_PRECONDITIONC(this == node->toplevel);
1974 CYG_PRECONDITIONC(lookup_table[node->name] == node);
1976 if (0 == new_parent) {
1977 new_parent = orphans;
1979 old_parent->contents.erase(std::find(old_parent->contents.begin(), old_parent->contents.end(), node));
1983 new_parent->contents.push_back(node);
1985 CYG_ASSERTC(pos <= (int) new_parent->contents.size());
1986 new_parent->contents.insert(new_parent->contents.begin() + pos, node);
1988 node->parent = new_parent;
1990 CYG_REPORT_RETURN();
1993 // Cleaning up orphans.
1995 // Right now this is only relevant for interfaces. Consider the case
1996 // where a loadable is being removed and that loadable defines an
1997 // interface. There may be other loadables which still have
1998 // "implements" properties affecting that interface, so instead of
1999 // deleting the cdl_interface object it is necessary to turn it into
2000 // an auto-generated orphan. At some stage there may no longer be
2001 // any references to an interface, in which case it can be removed
2004 // In practice it is quite hard to do a clean-up purely on the basis
2005 // of implements properties, for example there may be an external
2006 // "requires" property as well which would need to have its references
2007 // cleaned up, then the expression needs to get re-evaluated, etc.
2008 // The transaction class does not currently provide a clean way
2009 // in which a single object can be destroyed. Instead the code below
2010 // checks for any references whose source is not the interface itself.
2013 CdlToplevelBody::cleanup_orphans()
2015 CYG_REPORT_FUNCNAME("CdlToplevel::cleanup_orphans");
2016 CYG_REPORT_FUNCARG1XV(this);
2017 CYG_PRECONDITION_THISC();
2019 // First figure out whether or not there are any interfaces along
2021 std::vector<CdlInterface> interfaces;
2022 const std::vector<CdlNode>& contents = orphans->get_contents();
2023 std::vector<CdlNode>::const_iterator node_i;
2025 for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
2026 CdlInterface intface = dynamic_cast<CdlInterface>(*node_i);
2030 const std::vector<CdlReferrer>& referrers = intface->get_referrers();
2031 std::vector<CdlReferrer>::const_iterator ref_i;
2032 for (ref_i = referrers.begin(); ref_i != referrers.end(); ref_i++) {
2033 if (ref_i->get_source() != intface) {
2037 if (ref_i == referrers.end()) {
2038 // None of the existing references involve an "implements" property, so
2039 // this interface can be deleted.
2040 interfaces.push_back(intface);
2044 if (0 != interfaces.size()) {
2045 CYG_FAIL("Not yet implemented");
2050 //{{{ Basic information
2052 // ----------------------------------------------------------------------------
2054 const std::vector<CdlLoadable>&
2055 CdlToplevelBody::get_loadables() const
2057 CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_loadables", "result %p");
2058 CYG_REPORT_FUNCARG1XV(this);
2059 CYG_PRECONDITION_THISC();
2061 const std::vector<CdlLoadable>& result = loadables;
2062 CYG_REPORT_RETVAL(&result);
2068 CdlToplevelBody::lookup(const std::string name) const
2070 CYG_REPORT_FUNCNAMETYPE("CdlToplevel::lookup", "result %p");
2071 CYG_REPORT_FUNCARG1XV(this);
2072 CYG_PRECONDITION_THISC();
2073 CYG_PRECONDITIONC("" != name);
2076 std::map<std::string,CdlNode>::const_iterator i = lookup_table.find(name);
2077 if (i != lookup_table.end()) {
2081 CYG_REPORT_RETVAL(result);
2086 CdlToplevelBody::get_interpreter() const
2088 CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_interpreter", "result %p");
2089 CYG_REPORT_FUNCARG1XV(this);
2090 CYG_PRECONDITION_THISC();
2092 CdlInterpreter result = interp;
2093 CYG_REPORT_RETVAL(result);
2098 CdlToplevelBody::get_description() const
2100 CYG_REPORT_FUNCNAME("CdlToplevel::get_description");
2101 CYG_REPORT_FUNCARG1XV(this);
2102 CYG_PRECONDITION_THISC();
2104 CYG_REPORT_RETURN();
2109 CdlToplevelBody::set_description(std::string new_description)
2111 CYG_REPORT_FUNCNAME("CdlToplevel::set_description");
2112 CYG_REPORT_FUNCARG1XV(this);
2113 CYG_PRECONDITION_THISC();
2115 description = new_description;
2117 CYG_REPORT_RETURN();
2121 CdlToplevelBody::get_directory() const
2123 CYG_REPORT_FUNCNAME("CdlToplevel::get_directory");
2124 CYG_REPORT_FUNCARG1XV(this);
2125 CYG_PRECONDITION_THISC();
2127 CYG_REPORT_RETURN();
2132 CdlToplevelBody::get_class_name() const
2134 CYG_REPORT_FUNCNAME("CdlToplevel::get_class_name");
2135 CYG_PRECONDITION_THISC();
2136 CYG_REPORT_RETURN();
2141 CdlToplevelBody::get_active_transaction() const
2143 CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_active_transaction", "result %p");
2144 CYG_REPORT_FUNCARG1XV(this);
2145 CYG_PRECONDITION_THISC();
2147 CdlTransaction result = transaction;
2148 CYG_REPORT_RETVAL(result);
2153 //{{{ Conflict support
2155 // ----------------------------------------------------------------------------
2156 const std::list<CdlConflict>&
2157 CdlToplevelBody::get_all_conflicts() const
2159 CYG_REPORT_FUNCNAME("CdlToplevel::get_all_conflicts");
2160 CYG_REPORT_FUNCARG1XV(this);
2161 CYG_PRECONDITION_THISC();
2163 const std::list<CdlConflict>& result = conflicts;
2165 CYG_REPORT_RETURN();
2169 const std::list<CdlConflict>&
2170 CdlToplevelBody::get_all_structural_conflicts() const
2172 CYG_REPORT_FUNCNAME("CdlToplevel::get_all_structural_conflicts");
2173 CYG_REPORT_FUNCARG1XV(this);
2174 CYG_PRECONDITION_THISC();
2176 const std::list<CdlConflict>& result = structural_conflicts;
2178 CYG_REPORT_RETURN();
2182 // ----------------------------------------------------------------------------
2183 // Resolve one or more conflicts. This involves creating a new transaction,
2184 // invoking the per-transaction resolve code, and then CdlTransaction::body()
2185 // takes care of everything else like propagation, further inference,
2186 // callbacks, committing, ...
2188 CdlToplevelBody::resolve_conflicts(const std::vector<CdlConflict>& conflicts_arg)
2190 CYG_REPORT_FUNCNAME("CdlToplevel::resolve_conflicts");
2191 CYG_REPORT_FUNCARG1XV(this);
2192 CYG_PRECONDITION_THISC();
2194 CdlTransaction transact = CdlTransactionBody::make(this);
2196 std::vector<CdlConflict>::const_iterator conf_i;
2197 for (conf_i = conflicts_arg.begin(); conf_i != conflicts_arg.end(); conf_i++) {
2198 CYG_LOOP_INVARIANT_CLASSC(*conf_i);
2199 CYG_LOOP_INVARIANTC(0 == (*conf_i)->get_transaction());
2201 if (((*conf_i)->resolution_implemented()) &&
2202 !transact->has_conflict_been_cleared(*conf_i) &&
2203 !(*conf_i)->has_known_solution() &&
2204 !(*conf_i)->has_no_solution() ) {
2205 transact->resolve(*conf_i);
2211 CYG_REPORT_RETURN();
2215 CdlToplevelBody::resolve_all_conflicts()
2217 CYG_REPORT_FUNCNAME("CdlToplevel::resolve_all_conflicts");
2218 CYG_REPORT_FUNCARG1XV(this);
2219 CYG_PRECONDITION_THISC();
2221 CdlTransaction transact = CdlTransactionBody::make(this);
2222 std::list<CdlConflict>::const_iterator conf_i;
2224 for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) {
2225 CYG_LOOP_INVARIANT_CLASSC(*conf_i);
2226 CYG_LOOP_INVARIANTC(0 == (*conf_i)->get_transaction());
2227 if ((*conf_i)->resolution_implemented() &&
2228 !transact->has_conflict_been_cleared(*conf_i) &&
2229 !(*conf_i)->has_known_solution() &&
2230 !(*conf_i)->has_no_solution() ) {
2231 transact->resolve(*conf_i);
2234 for (conf_i = structural_conflicts.begin(); conf_i != structural_conflicts.end(); conf_i++) {
2235 CYG_LOOP_INVARIANT_CLASSC(*conf_i);
2236 CYG_LOOP_INVARIANTC(0 == (*conf_i)->get_transaction());
2237 if (((*conf_i)->resolution_implemented()) &&
2238 !transact->has_conflict_been_cleared(*conf_i) &&
2239 !(*conf_i)->has_known_solution() &&
2240 !(*conf_i)->has_no_solution() ) {
2241 transact->resolve(*conf_i);
2248 CYG_REPORT_RETURN();
2254 // ----------------------------------------------------------------------------
2255 // Limbo support. This is basically trivial, an STL map does all the
2258 CdlToplevelBody::set_limbo_value(CdlValuable valuable)
2260 CYG_REPORT_FUNCNAME("CdlToplevel::set_limbo_value");
2261 CYG_REPORT_FUNCARG2XV(this, valuable);
2262 CYG_PRECONDITION_THISC();
2263 CYG_PRECONDITION_CLASSC(valuable);
2265 limbo[valuable->get_name()] = valuable->get_whole_value();
2267 CYG_REPORT_RETURN();
2271 CdlToplevelBody::has_limbo_value(std::string name) const
2273 CYG_REPORT_FUNCNAMETYPE("CdlToplevel::has_limbo_value", "result %d");
2274 CYG_REPORT_FUNCARG1XV(this);
2275 CYG_PRECONDITION_THISC();
2276 CYG_PRECONDITIONC("" != name);
2278 bool result = false;
2279 if (limbo.find(name) != limbo.end()) {
2283 CYG_REPORT_RETVAL(result);
2288 CdlToplevelBody::get_limbo_value(std::string name) const
2290 CYG_REPORT_FUNCNAME("CdlToplevel::get_limbo_value");
2291 CYG_REPORT_FUNCARG1XV(this);
2292 CYG_PRECONDITION_THISC();
2293 CYG_PRECONDITIONC("" != name);
2295 std::map<std::string,CdlValue>::const_iterator limbo_i = limbo.find(name);
2296 CYG_ASSERTC(limbo_i != limbo.end());
2298 CYG_REPORT_RETURN();
2299 return limbo_i->second;
2303 CdlToplevelBody::get_and_remove_limbo_value(std::string name)
2305 CYG_REPORT_FUNCNAME("CdlToplevel::get_and_remove_limbo_value");
2306 CYG_REPORT_FUNCARG1XV(this);
2307 CYG_PRECONDITION_THISC();
2308 CYG_PRECONDITIONC("" != name);
2310 std::map<std::string,CdlValue>::iterator limbo_i = limbo.find(name);
2311 CYG_ASSERTC(limbo_i != limbo.end());
2313 CdlValue local_copy = limbo_i->second;
2314 limbo.erase(limbo_i);
2316 CYG_REPORT_RETURN();
2321 CdlToplevelBody::clear_limbo()
2323 CYG_REPORT_FUNCNAME("CdlToplevel::clear_limbo");
2324 CYG_REPORT_FUNCARG1XV(this);
2325 CYG_PRECONDITION_THISC();
2329 CYG_REPORT_RETURN();
2333 //{{{ Persistence support
2337 // ----------------------------------------------------------------------------
2338 // Toplevels do not have any data specifically associated with them which
2339 // should go into savefiles (not quite true, there is a description field,
2340 // but that can be handled easily by the derived classes).
2342 // However there is a need in the library for some generic savefile support:
2344 // 1) it is an important goal that savefiles should be self-describing.
2345 // This is handled by having a header section at the start of each
2346 // savefile which describes what commands will appear in the savefile
2347 // (note that savefiles are actually just Tcl scripts). In addition
2348 // each savefile contains a version number so that individual commands
2349 // can detect and adapt to older versions of the library.
2351 // 2) savefiles should also be extensible, so for example a GUI tool should
2352 // be able to add its own information. This can be toplevel information,
2353 // i.e. a new command that gets executed at the savefile's toplevel,
2354 // or it can be a subcommand extending an existing command such as
2355 // cdl_option. Right now only one level of nesting is available, but
2356 // this should suffice.
2358 // 3) extensibility means that the application reading in a savefile may
2359 // not support the same set of commands as the application that generated
2360 // the savefile. Care is taken to avoid loss of data. However exact
2361 // ordering is not guaranteed to be preserved, and neither is formatting.
2363 // These needs are interrelated, and supported by the CdlToplevelBody
2364 // class. The functions of interest are:
2366 // virtual void initialize_savefile_support()
2367 // This should be called from higher-level code such as
2368 // CdlConfiguration::initialize_savefile_support() at the start of
2369 // any savefile-related operation.
2371 // The support operates on a per-application basis rather than a
2372 // per-toplevel basis, in spite of being a virtual member function
2373 // rather than a static. A virtual member function facilitates
2374 // automatic initialization. This causes some problems if you need
2375 // to load in toplevels with different application-specific
2376 // extensions, but it makes life a lot simpler for the application.
2378 // static bool savefile_support_initialized()
2379 // Has there been a call to initialize_savefile_support() yet?
2381 // virtual void add_savefile_command(std::string, CdlSaveCallback, CdlInterpreterCommand)
2382 // Register a new savefile toplevel command. The string must be a
2383 // valid command name. The callback function will be 0 for savedata
2384 // supported directly by the library, non-zero for application-specific
2385 // data, and is invoked during a save operation to allow application
2386 // code to add extra data to the savefile. The command procedure must
2387 // be provided and is registered with the Tcl interpreter.
2389 // virtual void add_savefile_subcommand(std::string cmd, std::string subcommand, CdlSaveCallback, CdlInterpreterCommand)
2390 // Typically savefile commands take the form <command> <name> <body>,
2391 // where <body> contains a set of subcommands. This function is used
2392 // to register a new subcommand.
2394 // void save_command_details(CdlInterpreter, Tcl_Channel, int)
2395 // This should be invoked early on when generating a savefile. Its
2396 // purpose is to store information about the current set of savefile
2397 // commands in the savefile, thus making savefiles self-describing.
2398 // This command acts on a per-toplevel basis, since each toplevel
2399 // may have been created via a load operation and hence may contain
2400 // unrecognised commands.
2402 // static void get_savefile_commands(std::vector<CdlInterpreterCommandEntry>&)
2403 // Work out the set of commands that should be supported by the
2404 // interpreter used to process a savefile. Note that this set gets
2405 // updated magically by savefile_handle_command().
2407 // static void get_savefile_subcommands(std::string, std::vector<CdlInterpreterCommandEntry>&)
2408 // Ditto for subcommands.
2410 // static int savefile_handle_command(CdlInterpreter, int, char**)
2411 // This implements cdl_savefile_command, and makes sure that
2412 // all of the commands that may be present in the savefile will
2415 // static int savefile_handle_unsupported(CdlInterpreter, int, char**)
2416 // This takes care of commands present in the savefile which are
2417 // not supported by the current application.
2419 // static int savefile_handle_unknown(CdlInterpreter, int, char**)
2420 // This is an implementation of "unknown" suitable for savefiles.
2421 // All commands that may get used in a savefile should be specified
2422 // via cdl_savefile_command, so an unknown command is an error.
2424 // cdl_int get_library_savefile_version()
2425 // Savefiles contain a format version number. This function can be used
2426 // to determine the current version used in the library.
2428 // int savefile_handle_version(CdlInterpreter, int, char**)
2429 // This is the implementation of the cdl_savefile_version command. It
2430 // stores the version information in the interpreter, allowing it
2431 // to be retrieved by other commands.
2433 // cdl_int get_savefile_version(CdlInterpreter)
2434 // This can be used to retrieve the version number that was present
2435 // in the current savefile.
2437 // The version number should not be used for application-specific
2438 // commands. It is possible for a savefile to be read in by a
2439 // different program and then updated: the updated savefile will
2440 // contain the unrecognised commands unchanged, but it will also
2441 // have the version number corresponding to that program rather
2442 // than to the original savefile.
2444 // void save_conflicts(CdlInterpreter, Tcl_Channel, int)
2445 // Output details of all the conflicts in the current configuration
2447 // void save_separator(CdlInterpreter, Tcl_Channel, int)
2448 // A utility to add a separator line to a savefile. This has to
2451 // FIXME: add limbo support
2454 //{{{ Statics and initialization
2456 // ----------------------------------------------------------------------------
2457 bool CdlToplevelBody::savefile_commands_initialized = false;
2458 std::vector<CdlSavefileCommand> CdlToplevelBody::savefile_commands;
2459 std::map<std::string,std::vector<CdlSavefileCommand> > CdlToplevelBody::savefile_subcommands;
2462 CdlToplevelBody::initialize_savefile_support()
2464 CYG_REPORT_FUNCNAME("CdlToplevel::initialize_savefile_support");
2465 CYG_REPORT_FUNCARG1XV(this);
2466 CYG_PRECONDITION_THISC();
2468 // This assignment avoids circular dependencies. It is not
2469 // completely accurate but close enough - the full set of
2470 // commands will be initialised shortly.
2471 savefile_commands_initialized = true;
2473 // The commands cdl_savefile_version and cdl_command are a core
2474 // part of the CDL savefile support.
2475 add_savefile_command("cdl_savefile_version", 0, &savefile_handle_version);
2476 add_savefile_command("cdl_savefile_command", 0, &savefile_handle_command);
2478 CYG_REPORT_RETURN();
2482 CdlToplevelBody::savefile_support_initialized()
2484 CYG_REPORT_FUNCNAMETYPE("CdlToplevel::check_savefile_support_initialized", "result %d");
2486 bool result = savefile_commands_initialized;
2487 CYG_REPORT_RETVAL(result);
2492 //{{{ Command details
2494 // ----------------------------------------------------------------------------
2495 // These routines are used to keep track of the savefile commands that
2496 // are understood by the current application. There may have been
2497 // additional per-toplevel commands when a savefile was read in, but
2498 // these are stored separately.
2500 // Currently there is only support for toplevel savefile commands
2501 // and for one level of subcommands. Multiple levels can probably
2502 // be accommodated by using the equivalent of a directory separator
2503 // in the savefile_subcommands map key.
2506 CdlToplevelBody::add_savefile_command(std::string name, CdlSaveCallback save_callback, CdlInterpreterCommand load_command)
2508 CYG_REPORT_FUNCNAME("CdlToplevel::add_savefile_command");
2509 CYG_REPORT_FUNCARG3XV(this, save_callback, load_command);
2510 CYG_PRECONDITION_THISC();
2511 CYG_PRECONDITIONC("" != name);
2513 if (!savefile_commands_initialized) {
2514 this->initialize_savefile_support();
2517 std::vector<CdlSavefileCommand>::const_iterator cmd_i;
2518 for (cmd_i = savefile_commands.begin(); cmd_i != savefile_commands.end(); cmd_i++) {
2519 if (cmd_i->name == name) {
2520 if ((cmd_i->save_callback != save_callback) || (cmd_i->load_command != load_command)) {
2521 CYG_FAIL("Internal error: attempt to define two toplevel savefile commands with the same name.");
2526 if (cmd_i == savefile_commands.end()) {
2527 CdlSavefileCommand cmd;
2529 cmd.save_callback = save_callback;
2530 cmd.load_command = load_command;
2531 savefile_commands.push_back(cmd);
2533 std::vector<CdlSavefileCommand> subcommands;
2534 savefile_subcommands[name] = subcommands;
2537 CYG_REPORT_RETURN();
2540 // Add a new subcommand for a given command. The command should have been
2543 CdlToplevelBody::add_savefile_subcommand(std::string cmd, std::string subcommand, CdlSaveCallback save_callback,
2544 CdlInterpreterCommand load_command)
2546 CYG_REPORT_FUNCNAME("CdlToplevel::add_savefile_subcommand");
2547 CYG_REPORT_FUNCARG3XV(this, save_callback, load_command);
2548 CYG_PRECONDITION_THISC();
2550 if (!savefile_commands_initialized) {
2551 this->initialize_savefile_support();
2554 std::vector<CdlSavefileCommand>::iterator cmd_i;
2555 for (cmd_i = savefile_commands.begin(); cmd_i != savefile_commands.end(); cmd_i++) {
2556 if (cmd_i->name == cmd) {
2560 CYG_ASSERTC(cmd_i != savefile_commands.end());
2562 for (cmd_i = savefile_subcommands[cmd].begin(); cmd_i != savefile_subcommands[cmd].end(); cmd_i++) {
2563 if (cmd_i->name == subcommand) {
2564 if ((cmd_i->save_callback != save_callback) || (cmd_i->load_command != load_command)) {
2565 CYG_FAIL("Internal error: attempt to define two subcommands with the same name.");
2569 if (cmd_i == savefile_subcommands[cmd].end()) {
2570 CdlSavefileCommand new_subcommand;
2571 new_subcommand.name = subcommand;
2572 new_subcommand.save_callback = save_callback;
2573 new_subcommand.load_command = load_command;
2574 savefile_subcommands[cmd].push_back(new_subcommand);
2577 CYG_REPORT_RETURN();
2580 // ----------------------------------------------------------------------------
2581 // This member function is invoked by e.g. CdlConfiguraton::save() to
2582 // take care of the generic savefile information, specifically the
2583 // savefile format version number and the various commands and subcommands
2584 // Note that it has to cope with per-toplevel commands from the original
2585 // savefile, as well as the global set.
2588 CdlToplevelBody::save_command_details(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
2590 CYG_REPORT_FUNCNAME("CdlToplevel::save_command_details");
2591 CYG_REPORT_FUNCARG4XV(this, interp, chan, indentation);
2592 CYG_PRECONDITION_THISC();
2593 CYG_PRECONDITION_CLASSC(interp);
2594 CYG_ASSERTC(0 == indentation);
2596 // The parent code should have provided the first couple of lines,
2597 // identifying whether this is an eCos configuration or some other
2598 // CDL-based entity.
2600 // Immediately after these lines we want a nice big comment
2601 // telling people that they can edit bits of this file, but
2602 // that other bits are automatically generated and will
2606 interp->write_data(chan,
2607 "# This section contains information about the savefile format.\n\
2608 # It should not be edited. Any modifications made to this section\n\
2609 # may make it impossible for the configuration tools to read\n\
2614 // Next output details of the savefile format version. This allows
2615 // all other code to adapt to the version.
2616 std::string savefile_data;
2617 Cdl::integer_to_string(savefile_version, savefile_data);
2618 savefile_data = "cdl_savefile_version " + savefile_data + ";\n";
2620 std::vector<CdlSavefileCommand>::const_iterator cmd_i, cmd_j;
2621 std::vector<std::string>::const_iterator cmd_k, cmd_l;
2623 for (cmd_i = savefile_commands.begin(); cmd_i != savefile_commands.end(); cmd_i++) {
2624 savefile_data += "cdl_savefile_command " + cmd_i->name + " ";
2626 if ((0 == savefile_subcommands[cmd_i->name].size()) &&
2627 (0 == this->unsupported_savefile_subcommands[cmd_i->name].size())) {
2629 savefile_data += "{};\n";
2633 savefile_data += "{";
2634 for (cmd_j = savefile_subcommands[cmd_i->name].begin();
2635 cmd_j != savefile_subcommands[cmd_i->name].end();
2638 savefile_data += " " + cmd_j->name;
2640 for (cmd_l = this->unsupported_savefile_subcommands[cmd_i->name].begin();
2641 cmd_l != this->unsupported_savefile_subcommands[cmd_i->name].end();
2644 savefile_data += " " + *cmd_l;
2646 savefile_data += " };\n";
2649 for (cmd_k = this->unsupported_savefile_commands.begin();
2650 cmd_k != this->unsupported_savefile_commands.end();
2652 savefile_data += "cdl_savefile_command " + *cmd_k + " ";
2653 if (0 == this->unsupported_savefile_subcommands[*cmd_k].size()) {
2655 savefile_data += "{};\n";
2659 savefile_data += "{";
2660 for (cmd_l = this->unsupported_savefile_subcommands[*cmd_k].begin();
2661 cmd_l != this->unsupported_savefile_subcommands[*cmd_k].end();
2664 savefile_data += " " + *cmd_l;
2666 savefile_data += " };\n";
2669 savefile_data += "\n";
2671 interp->write_data(chan, savefile_data);
2673 CYG_REPORT_RETURN();
2676 // ----------------------------------------------------------------------------
2677 // Get hold of the commands that should be added to the interpreter for
2678 // processing a savefile. Note that this will only deal with commands
2679 // supported by the library or the application, not any additional
2680 // unsupported commands specified in the savefile itself. The latter
2681 // will be taken care of magically by savefile_handle_command().
2684 CdlToplevelBody::get_savefile_commands(std::vector<CdlInterpreterCommandEntry>& cmds)
2686 CYG_REPORT_FUNCNAME("CdlToplevel::get_savefile_commands");
2687 CYG_REPORT_FUNCARG1XV(this);
2688 CYG_PRECONDITION_THISC();
2690 CdlInterpreterCommandEntry local_cmd;
2691 std::vector<CdlSavefileCommand>::const_iterator cmd_i;
2692 for (cmd_i = savefile_commands.begin(); cmd_i != savefile_commands.end(); cmd_i++) {
2693 // NOTE: this use of c_str() is somewhat dubious, but the string should not
2694 // change so the c_str() array should remain ok as well.
2695 local_cmd.name = cmd_i->name;
2696 local_cmd.command = cmd_i->load_command;
2697 cmds.push_back(local_cmd);
2700 // There is no point in iterating over this->unsupported_savefile_commands,
2701 // that vector should be empty since we have not actually started
2702 // processing the savefile yet.
2703 CYG_ASSERTC(0 == this->unsupported_savefile_commands.size());
2705 // Add an implementation of the "unknown" command.
2706 local_cmd.name = "unknown";
2707 local_cmd.command = &CdlToplevelBody::savefile_handle_unknown;
2708 cmds.push_back(local_cmd);
2710 CYG_REPORT_RETURN();
2713 // Having repeated calls of this for e.g. every cdl_option statement in
2714 // a savefile is expensive. Some sort of caching mechanism should be
2715 // used to avoid unnecessary overheads.
2717 CdlToplevelBody::get_savefile_subcommands(std::string main_command, std::vector<CdlInterpreterCommandEntry>& cmds)
2719 CYG_REPORT_FUNCNAME("CdlToplevel::get_savefile_subcommands");
2720 CYG_REPORT_FUNCARG1XV(this);
2721 CYG_PRECONDITION_THISC();
2723 CdlInterpreterCommandEntry local_cmd;
2724 std::vector<CdlSavefileCommand>::const_iterator cmd_i;
2725 for (cmd_i = savefile_subcommands[main_command].begin();
2726 cmd_i != savefile_subcommands[main_command].end();
2729 local_cmd.name = cmd_i->name.c_str();
2730 local_cmd.command = cmd_i->load_command;
2731 cmds.push_back(local_cmd);
2734 std::vector<std::string>::const_iterator cmd_j;
2735 for (cmd_j = this->unsupported_savefile_subcommands[main_command].begin();
2736 cmd_j != this->unsupported_savefile_subcommands[main_command].end();
2739 local_cmd.name = cmd_j->c_str();
2740 local_cmd.command = &savefile_handle_unsupported;
2741 cmds.push_back(local_cmd);
2744 CYG_REPORT_RETURN();
2747 // ----------------------------------------------------------------------------
2748 // This implements cdl_savefile_command which should appear near the
2749 // start of savefiles. The command takes two arguments, a primary
2750 // command name and a set of subcommand names.
2752 CdlToplevelBody::savefile_handle_command(CdlInterpreter interp, int argc, const char* argv[])
2754 CYG_REPORT_FUNCNAME("CdlToplevel::savefile_handle_command");
2755 CYG_REPORT_FUNCARG2XV(interp, argc);
2756 CYG_PRECONDITION_CLASSC(interp);
2758 CdlToplevel toplevel = interp->get_toplevel();
2759 CYG_ASSERT_CLASSC(toplevel);
2760 CYG_ASSERTC(toplevel->savefile_commands_initialized);
2763 CdlParse::report_error(interp, "", "Expecting at least one argument to cdl_savefile_command");
2764 } else if (2 == argc) {
2765 CdlParse::report_warning(interp, "",
2766 std::string("Missing third argument to `cdl_savefile_command ") + argv[1] +
2767 "'\n.Expecting an additional list of subcommands.");
2768 } else if (3 != argc) {
2769 CdlParse::report_warning(interp, "", std::string("Unexpected additional arguments to `cdl_savefile_command ") +
2770 argv[1] + " { " + argv[2] + " }");
2773 // Is the primary command one of the known ones?
2774 bool known_command = false;
2775 std::vector<CdlSavefileCommand>::const_iterator cmd_i;
2776 std::vector<std::string>::const_iterator cmd_j;
2779 // Make sure that the primary command is known.
2780 for (cmd_i = savefile_commands.begin(); cmd_i != savefile_commands.end(); cmd_i++) {
2781 if (cmd_i->name == argv[1]) {
2782 known_command = true;
2786 if (!known_command) {
2787 // Detect duplicate definitions, just in case.
2788 for (cmd_j = toplevel->unsupported_savefile_commands.begin();
2789 cmd_j != toplevel->unsupported_savefile_commands.end();
2791 if (*cmd_j == argv[1]) {
2795 if (cmd_j == toplevel->unsupported_savefile_commands.end()) {
2796 toplevel->unsupported_savefile_commands.push_back(argv[1]);
2801 // Now take care of all the subcommands.
2805 const char** list_entries = 0;
2808 Tcl_Interp* tcl_interp = interp->get_tcl_interpreter();
2809 if (TCL_OK != Tcl_SplitList(tcl_interp, CDL_TCL_CONST_CAST(char*, argv[2]), &list_count, CDL_TCL_CONST_CAST(char***, &list_entries))) {
2810 CdlParse::report_error(interp, "", std::string("Invalid subcommand list for `cdl_command ") + argv[1] + "'.");
2813 for (int i = 0; i < list_count; i++) {
2814 bool known_subcommand = false;
2815 if (known_command) {
2816 for (cmd_i = savefile_subcommands[argv[1]].begin();
2817 cmd_i != savefile_subcommands[argv[1]].end();
2820 if (cmd_i->name == list_entries[i]) {
2821 known_subcommand = true;
2825 if (!known_subcommand) {
2826 for (cmd_j = toplevel->unsupported_savefile_subcommands[argv[1]].begin();
2827 cmd_j != toplevel->unsupported_savefile_subcommands[argv[1]].end();
2830 if (*cmd_j == list_entries[i]) {
2831 known_subcommand = true;
2836 if (!known_subcommand) {
2837 toplevel->unsupported_savefile_subcommands[argv[1]].push_back(list_entries[i]);
2842 if (0 != list_entries) {
2843 Tcl_Free((char *)list_entries);
2847 if (0 != list_entries) {
2848 Tcl_Free((char *)list_entries);
2858 //{{{ handle_unsupported()
2860 // ----------------------------------------------------------------------------
2861 // This function is invoked when an unsupported command is detected in
2862 // a savefile. It turns the data back into a string which can go back
2863 // into the next savefile, thus avoiding loss of data.
2865 // It is possible that the savefile contents involved variable or
2866 // command substitution. If so then this information will have been
2867 // lost, there is no simple way of retrieving this from the interpreter.
2868 // Care has to be taken when generating the new command string to
2869 // perform appropriate quoting.
2871 // Ideally the original data could be extracted from the Tcl
2872 // interpreter somehow. Currently this data is not readily available,
2873 // and the resulting string may not match the original data exactly.
2875 CdlToplevelBody::savefile_handle_unsupported(CdlInterpreter interp, int argc, const char* argv[])
2877 CYG_REPORT_FUNCNAME("CdlNode::savefile_handle_unsupported");
2878 CYG_REPORT_FUNCARG2XV(interp, argc);
2879 CYG_ASSERT_CLASSC(interp);
2881 CdlToplevel toplevel = interp->get_toplevel();
2882 CYG_ASSERT_CLASSC(toplevel);
2883 CdlNode node = interp->get_node();
2884 CYG_ASSERT_ZERO_OR_CLASSC(node);
2886 std::string tmp = CdlInterpreterBody::quote(argv[0]);
2887 for (int i = 1; i < argc; i++) {
2888 tmp = tmp + " " + CdlInterpreterBody::quote(argv[i]);
2890 // Unknown commands may occur at the toplevel or inside
2891 // e.g. a cdl_option body. Toplevels are also nodes.
2893 toplevel->unsupported_savefile_toplevel_strings.push_back(tmp);
2895 node->unsupported_savefile_strings.push_back(tmp);
2902 //{{{ save_unsupported()
2904 // ----------------------------------------------------------------------------
2905 // This code deals with any toplevel data present in the original save
2906 // file that was not recognised.
2908 CdlToplevelBody::save_unsupported_commands(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
2910 CYG_REPORT_FUNCNAME("CdlToplevelBody::save_unsupported_commands");
2911 CYG_REPORT_FUNCARG3XV(this, interp, chan);
2912 CYG_PRECONDITION_THISC();
2913 CYG_PRECONDITION_CLASSC(interp);
2914 CYG_PRECONDITIONC(0 == indentation);
2916 std::string data = "\n";
2917 std::vector<std::string>::const_iterator str_i;
2918 for (str_i = unsupported_savefile_toplevel_strings.begin();
2919 str_i != unsupported_savefile_toplevel_strings.end();
2921 data += *str_i + " ;\n";
2923 interp->write_data(chan, data);
2925 CYG_UNUSED_PARAM(bool, minimal);
2926 CYG_REPORT_RETURN();
2930 //{{{ handle_unknown()
2932 // ----------------------------------------------------------------------------
2935 CdlToplevelBody::savefile_handle_unknown(CdlInterpreter interp, int argc, const char* argv[])
2937 CYG_REPORT_FUNCNAME("CdlToplevel::savefile_handle_unknown");
2938 CYG_REPORT_FUNCARG2XV(interp, argc);
2939 CYG_PRECONDITION_CLASSC(interp);
2941 CdlParse::report_error(interp, "", std::string("Unknown command `") + argv[1] + "'.");
2943 CYG_UNUSED_PARAM(int, argc);
2950 // ----------------------------------------------------------------------------
2951 // Savefiles include a version number that can be used by library
2952 // commands to cope with old and incompatible savefiles. This
2953 // version number should be changed only very rarely, hopefully never.
2954 cdl_int CdlToplevelBody::savefile_version = 1;
2957 CdlToplevelBody::get_library_savefile_version()
2959 CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_library_savefile_version", "result %ld");
2961 cdl_int result = savefile_version;
2962 CYG_REPORT_RETVAL((long) result);
2966 // This implements the cdl_savefile_version command. It stores the
2967 // version number with the interpreter, allowing it to be retrieved
2968 // by other commands.
2970 CdlToplevelBody::savefile_handle_version(CdlInterpreter interp, int argc, const char* argv[])
2972 CYG_REPORT_FUNCNAME("CdlToplevel::savefile_handle_version");
2973 CYG_REPORT_FUNCARG2XV(interp, argc);
2974 CYG_PRECONDITION_CLASSC(interp);
2977 CdlParse::report_warning(interp, "", "Expecting one argument to cdl_savefile_version");
2980 CdlParse::report_warning(interp, "",
2981 std::string("Unexpected number of arguments to cdl_savefile_version\n") +
2982 "There should be exactly one argument, the savefile format version number.");
2985 if (!Cdl::string_to_integer(argv[1], tmp)) {
2986 CdlParse::report_error(interp, "",
2987 std::string("Invalid version number `") + argv[1] + "' for cdl_savefile_version");
2989 // Store the data in a Tcl variable. This is at least as convenient
2991 interp->set_variable("cdl_savefile_version", argv[1]);
2999 CdlToplevelBody::get_savefile_version(CdlInterpreter interp)
3001 CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_savefile_version", "result %ld");
3002 CYG_REPORT_FUNCARG1XV(interp);
3003 CYG_PRECONDITION_CLASSC(interp);
3006 std::string version = interp->get_variable("cdl_savefile_version");
3007 if ("" != version) {
3008 if (!Cdl::string_to_integer(version, result)) {
3009 CdlParse::report_error(interp, "", std::string("Invalid cdl_savefile_version number `") + version + "'");
3013 CYG_REPORT_RETVAL((long) result);
3020 // ----------------------------------------------------------------------------
3022 CdlToplevelBody::save_conflicts(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
3024 CYG_REPORT_FUNCNAME("CdlToplevel::save_conflicts");
3025 CYG_REPORT_FUNCARG4XV(this, interp, chan, indentation);
3026 CYG_PRECONDITION_THISC();
3027 CYG_PRECONDITION_CLASSC(interp);
3028 CYG_PRECONDITIONC(0 == indentation);
3030 // For now only comments are generated here, so in a minimal save
3031 // there is no need for any of this data
3033 std::string data = "";
3034 if (0 == conflicts.size()) {
3035 data += "# There are no conflicts.\n";
3038 Cdl::integer_to_string((cdl_int) this->conflicts.size(), tmp);
3039 data += "# There are " + tmp + " conflicts.\n";
3041 std::list<CdlConflict>::const_iterator conf_i;
3042 for (conf_i = this->conflicts.begin(); conf_i != this->conflicts.end(); conf_i++) {
3045 CdlNode node = (*conf_i)->get_node();
3046 CdlProperty prop = (*conf_i)->get_property();
3047 std::string description = (*conf_i)->get_explanation();
3048 data += "# " + node->get_class_name() + " " + node->get_name() + "\n";
3049 data += "# Property " + prop->get_property_name() + "\n";
3050 data += CdlInterpreterBody::multiline_comment(description, 0, 2) + "\n";
3056 interp->write_data(chan, data);
3059 CYG_REPORT_RETURN();
3063 //{{{ save_separator()
3065 // ----------------------------------------------------------------------------
3067 CdlToplevelBody::save_separator(CdlInterpreter interp, Tcl_Channel chan, std::string msg, bool minimal)
3069 CYG_REPORT_FUNCNAME("CdlToplevel::save_separator");
3070 CYG_REPORT_FUNCARG1XV(interp);
3071 CYG_PRECONDITION_CLASSC(interp);
3074 std::string data = "# ---- " + msg + ' ';
3075 if (72 > data.size()) {
3076 data += std::string(72 - data.size(), '-');
3079 interp->write_data(chan, data);
3082 CYG_REPORT_RETURN();
3090 // ----------------------------------------------------------------------------
3092 CdlToplevelBody::check_this(cyg_assert_class_zeal zeal) const
3094 if (CdlToplevelBody_Magic != cdltoplevelbody_cookie) {
3097 CYGDBG_MEMLEAK_CHECKTHIS();
3099 if ((zeal == cyg_extreme) || (zeal == cyg_thorough)) {
3100 if (!interp->check_this(cyg_quick)) {
3103 if ((0 == orphans) || !orphans->check_this(cyg_quick)) {
3106 if (orphans != *contents.begin()) {
3109 if ((0 != transaction) && !transaction->check_this(cyg_quick)) {
3114 return CdlContainerBody::check_this(zeal);
3120 //{{{ CdlUserVisiblebody
3124 // ----------------------------------------------------------------------------
3125 // All user-visible object can have (and usually should have) three
3126 // properties: display (originally known as alias), description, and
3127 // doc. There is no additional data associated with a user-visible
3128 // object, everything is handled via the properties.
3130 CdlUserVisibleBody::CdlUserVisibleBody()
3132 CYG_REPORT_FUNCNAME("CdlUserVisible:: default constructor");
3133 CYG_REPORT_FUNCARG1XV(this);
3135 cdluservisiblebody_cookie = CdlUserVisibleBody_Magic;
3136 CYGDBG_MEMLEAK_CONSTRUCTOR();
3138 CYG_POSTCONDITION_THISC();
3139 CYG_REPORT_RETURN();
3142 CdlUserVisibleBody::~CdlUserVisibleBody()
3144 CYG_REPORT_FUNCNAME("CdlUserVisible:: destructor");
3145 CYG_REPORT_FUNCARG1XV(this);
3146 CYG_PRECONDITION_THISC();
3148 cdluservisiblebody_cookie = CdlUserVisibleBody_Invalid;
3149 CYGDBG_MEMLEAK_DESTRUCTOR();
3151 CYG_REPORT_RETURN();
3155 CdlUserVisibleBody::get_class_name() const
3157 CYG_REPORT_FUNCNAME("CdlUserVisible::get_class_name");
3158 CYG_PRECONDITION_THISC();
3159 CYG_REPORT_RETURN();
3160 return "uservisible";
3164 CdlUserVisibleBody::check_this(cyg_assert_class_zeal zeal) const
3166 if (CdlUserVisibleBody_Magic != cdluservisiblebody_cookie) {
3169 CYGDBG_MEMLEAK_CHECKTHIS();
3170 return CdlNodeBody::check_this(zeal);
3174 //{{{ Extracting information
3176 // ----------------------------------------------------------------------------
3177 // Extracting the information.
3180 CdlUserVisibleBody::get_display() const
3182 CYG_REPORT_FUNCNAME("CdlUserVisible::get_display");
3183 CYG_REPORT_FUNCARG1XV(this);
3184 CYG_PRECONDITION_THISC();
3186 std::string result = "";
3187 CdlProperty property = get_property(CdlPropertyId_Display);
3188 if (0 != property) {
3190 CdlProperty_String string_property = dynamic_cast<CdlProperty_String>(property);
3191 CYG_ASSERTC(0 != string_property);
3193 result = string_property->get_string();
3196 CYG_REPORT_RETURN();
3201 CdlUserVisibleBody::get_description() const
3203 CYG_REPORT_FUNCNAME("CdlUserVisible::get_description");
3204 CYG_REPORT_FUNCARG1XV(this);
3205 CYG_PRECONDITION_THISC();
3207 std::string result = "";
3208 CdlProperty property = get_property(CdlPropertyId_Description);
3209 if (0 != property) {
3211 CdlProperty_String string_property = dynamic_cast<CdlProperty_String>(property);
3212 CYG_ASSERTC(0 != string_property);
3214 result = string_property->get_string();
3217 CYG_REPORT_RETURN();
3222 CdlUserVisibleBody::get_doc() const
3224 CYG_REPORT_FUNCNAME("CdlUserVisible::get_doc");
3225 CYG_REPORT_FUNCARG1XV(this);
3226 CYG_PRECONDITION_THISC();
3228 std::string result = "";
3229 CdlProperty property = get_property(CdlPropertyId_Doc);
3230 if (0 != property) {
3232 CdlProperty_String string_property = dynamic_cast<CdlProperty_String>(property);
3233 CYG_ASSERTC(0 != string_property);
3235 result = string_property->get_string();
3238 CYG_REPORT_RETURN();
3243 CdlUserVisibleBody::get_doc_url() const
3245 CYG_REPORT_FUNCNAME("CdlUserVisible::get_doc_url");
3246 CYG_REPORT_FUNCARG1XV(this);
3247 CYG_PRECONDITION_THISC();
3249 std::string result = "";
3250 std::string doc_property = get_doc();
3251 if ("" != doc_property) {
3252 CdlLoadable owner = get_owner();
3253 CYG_ASSERTC(0 != owner);
3254 result = owner->find_absolute_file(doc_property, "doc", true);
3257 CYG_REPORT_RETURN();
3264 // ----------------------------------------------------------------------------
3265 // Parsing support. There are three property parsers to be added to
3266 // the current set. The checking code should make sure that at most
3267 // one of each property has been specified. In addition it is
3268 // necessary to recurse into the base class.
3271 CdlUserVisibleBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
3273 CYG_REPORT_FUNCNAME("CdlUserVisible::add_property_parsers");
3275 static CdlInterpreterCommandEntry commands[] =
3277 CdlInterpreterCommandEntry("display", &parse_display),
3278 CdlInterpreterCommandEntry("description", &parse_description),
3279 CdlInterpreterCommandEntry("doc", &parse_doc),
3280 CdlInterpreterCommandEntry("", 0)
3283 for (int i = 0; commands[i].command != 0; i++) {
3284 std::vector<CdlInterpreterCommandEntry>::const_iterator j;
3285 for (j = parsers.begin(); j != parsers.end(); j++) {
3286 if (commands[i].name == j->name) {
3287 if (commands[i].command != j->command) {
3288 CYG_FAIL("Property names are being re-used");
3293 if (j == parsers.end()) {
3294 parsers.push_back(commands[i]);
3297 CdlNodeBody::add_property_parsers(parsers);
3299 CYG_REPORT_RETURN();
3303 CdlUserVisibleBody::check_properties(CdlInterpreter interp)
3305 CYG_REPORT_FUNCNAME("CdlUserVisible::check_properties");
3306 CYG_REPORT_FUNCARG2XV(this, interp);
3307 CYG_PRECONDITION_THISC();
3308 CYG_PRECONDITION_CLASSC(interp);
3310 if (count_properties(CdlPropertyId_Display) > 1) {
3311 CdlParse::report_error(interp, "", "There should be at most one display property.");
3313 if (count_properties(CdlPropertyId_Description) > 1) {
3314 CdlParse::report_error(interp, "", "There should be at most one description property.");
3316 if (count_properties(CdlPropertyId_Doc) > 1) {
3317 CdlParse::report_error(interp, "", "There should be at most one doc property.");
3320 // FIXME: more validation of the doc property, in particular check that
3321 // the resulting URL would be either remote or to an existing file.
3323 CdlNodeBody::check_properties(interp);
3325 CYG_REPORT_RETURN();
3328 // ----------------------------------------------------------------------------
3329 // Syntax: description <string>
3332 CdlUserVisibleBody::parse_description(CdlInterpreter interp, int argc, const char* argv[])
3334 CYG_REPORT_FUNCNAMETYPE("parse_description", "result %d");
3336 int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Description, 0, 0);
3338 CYG_REPORT_RETVAL(result);
3343 // ----------------------------------------------------------------------------
3344 // Syntax: display <short description>
3347 CdlUserVisibleBody::parse_display(CdlInterpreter interp, int argc, const char* argv[])
3349 CYG_REPORT_FUNCNAMETYPE("parse_display", "result %d");
3351 int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Display, 0, 0);
3353 CYG_REPORT_RETVAL(result);
3357 // ----------------------------------------------------------------------------
3358 // Syntax: doc <url>
3361 CdlUserVisibleBody::parse_doc(CdlInterpreter interp, int argc, const char* argv[])
3363 CYG_REPORT_FUNCNAMETYPE("parse_doc", "result %d");
3365 int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Doc, 0, 0);
3367 CYG_REPORT_RETVAL(result);
3374 // ----------------------------------------------------------------------------
3375 // There is no data in a user visible object that users will want to edit,
3376 // but the display string, the documentation, and the description are all
3377 // useful and should be present in the savefile as comments.
3379 // The intention is that the UserVisible information appears immediately
3380 // above the option/component/whatever definition, e.g.:
3381 // # <display string.
3388 CdlUserVisibleBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
3390 CYG_REPORT_FUNCNAME("CdlUserVisible::save");
3391 CYG_REPORT_FUNCARG5XV(this, interp, chan, indentation, minimal);
3392 CYG_PRECONDITION_THISC();
3393 CYG_PRECONDITION_CLASSC(interp);
3394 CYG_ASSERTC(0 == indentation);
3397 std::string data = "";
3398 std::string display = get_display();
3399 if ("" != display) {
3400 data = std::string("# ") + display + "\n";
3402 // Note that this uses get_doc(), not get_doc_url(). The latter
3403 // would give an absolute pathname that is applicable to the
3404 // current user, but it would change if a different user loaded
3405 // and saved the file. This is a bad idea in terms of version
3407 std::string doc = get_doc();
3409 data += "# doc: " + doc + "\n";
3411 std::string description = get_description();
3412 if ("" != description) {
3414 while (i < description.size()) {
3416 while ((i < description.size()) && isspace(description[i])) {
3419 while ((i < description.size()) && ('\n' != description[i])) {
3420 data += description[i++];
3427 interp->write_data(chan, data);
3430 CYG_REPORT_RETURN();
3436 //{{{ CdlParentableBody
3438 // ----------------------------------------------------------------------------
3439 // A parentable object can have the parent property, i.e. it can be
3440 // positioned anywhere in the hierarchy. There is no data associated
3441 // with such an object.
3443 CdlParentableBody::CdlParentableBody()
3445 CYG_REPORT_FUNCNAME("CdlParentable:: default constructor");
3446 CYG_REPORT_FUNCARG1XV(this);
3448 change_parent_save_position = -1;
3449 cdlparentablebody_cookie = CdlParentableBody_Magic;
3450 CYGDBG_MEMLEAK_CONSTRUCTOR();
3452 CYG_POSTCONDITION_THISC();
3453 CYG_REPORT_RETURN();
3456 CdlParentableBody::~CdlParentableBody()
3458 CYG_REPORT_FUNCNAME("CdlParentable:: destructor");
3459 CYG_REPORT_FUNCARG1XV(this);
3460 CYG_PRECONDITION_THISC();
3462 cdlparentablebody_cookie = CdlParentableBody_Invalid;
3463 CYGDBG_MEMLEAK_DESTRUCTOR();
3465 CYG_REPORT_RETURN();
3468 // ----------------------------------------------------------------------------
3471 CdlParentableBody::get_class_name() const
3473 CYG_REPORT_FUNCNAME("CdlParentable::get_class_name");
3474 CYG_PRECONDITION_THISC();
3475 CYG_REPORT_RETURN();
3476 return "parentable";
3479 // ----------------------------------------------------------------------------
3482 CdlParentableBody::check_this(cyg_assert_class_zeal zeal) const
3484 if (CdlParentableBody_Magic != cdlparentablebody_cookie) {
3487 CYGDBG_MEMLEAK_CHECKTHIS();
3488 return CdlNodeBody::check_this(zeal);
3491 // ----------------------------------------------------------------------------
3492 // Parsing support. There is just one property parser to be added.
3495 CdlParentableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
3497 CYG_REPORT_FUNCNAME("CdlParentable::add_property_parsers");
3499 static CdlInterpreterCommandEntry commands[] =
3501 CdlInterpreterCommandEntry("parent", &CdlParentableBody::parse_parent),
3502 CdlInterpreterCommandEntry("", 0)
3505 for (int i = 0; commands[i].command != 0; i++) {
3506 std::vector<CdlInterpreterCommandEntry>::const_iterator j;
3507 for (j = parsers.begin(); j != parsers.end(); j++) {
3508 if (commands[i].name == j->name) {
3509 if (commands[i].command != j->command) {
3510 CYG_FAIL("Property names are being re-used");
3515 if (j == parsers.end()) {
3516 parsers.push_back(commands[i]);
3519 CdlNodeBody::add_property_parsers(parsers);
3521 CYG_REPORT_RETURN();
3525 CdlParentableBody::check_properties(CdlInterpreter interp)
3527 CYG_REPORT_FUNCNAME("CdlParentable::check_properties");
3528 CYG_REPORT_FUNCARG2XV(this, interp);
3529 CYG_PRECONDITION_THISC();
3530 CYG_PRECONDITION_CLASSC(interp);
3532 if (has_property(CdlPropertyId_Parent)) {
3533 if (count_properties(CdlPropertyId_Parent) > 1) {
3534 CdlParse::report_error(interp, "", "There should be at most one `parent' property.");
3536 CdlProperty_Reference refprop = dynamic_cast<CdlProperty_Reference>(get_property(CdlPropertyId_Parent));
3537 CYG_ASSERT_CLASSC(this);
3538 if (get_name() == refprop->get_destination_name()) {
3539 CdlParse::report_error(interp, "", std::string("Node ") + get_name() + " cannot be its own parent.");
3543 CdlNodeBody::check_properties(interp);
3545 CYG_REPORT_RETURN();
3548 // ----------------------------------------------------------------------------
3549 // Syntax:: parent <reference to container>
3552 CdlParentableBody::update_handler(CdlTransaction transaction, CdlNode source, CdlProperty prop, CdlNode dest, CdlUpdate change)
3554 CYG_REPORT_FUNCNAME("CdlParentable::update_handler");
3555 CYG_PRECONDITION_CLASSC(source);
3556 CYG_PRECONDITION_ZERO_OR_CLASSC(dest);
3558 // Value and activity updates are of no interest.
3559 if ((CdlUpdate_ValueChange == change) || (CdlUpdate_ActiveChange == change)) {
3560 CYG_REPORT_RETURN();
3564 // Ditto for the second stage Init.
3565 if (CdlUpdate_Init == change) {
3566 CYG_REPORT_RETURN();
3570 // If this object is being unloaded then we need to clean up the hierarchy.
3571 // Ordinary nodes must be re-parented below the owning loadable. The
3572 // loadable itself must be re-parented below the toplevel. A subsequent
3573 // calls to remove_loadable_from_toplevel() will ensure that the loadable
3574 // is now completely isolated from the remaining configuration, but can
3575 // still be put back.
3576 if (CdlUpdate_Unloading == change) {
3577 CdlToplevel toplevel = source->get_toplevel();
3578 CYG_ASSERT_CLASSC(toplevel);
3579 CdlLoadable owner = source->get_owner();
3580 CYG_ASSERT_CLASSC(owner);
3581 CdlLoadable loadable = dynamic_cast<CdlLoadable>(source);
3582 CYG_ASSERT_ZERO_OR_CLASSC(loadable);
3584 if (0 != loadable) {
3585 toplevel->change_parent(owner, source->get_parent(), toplevel, source);
3587 toplevel->change_parent(owner, source->get_parent(), owner, source);
3590 CYG_REPORT_RETURN();
3595 // 1) change == Loaded, dest == (0 | valid)
3596 // 2) change == Created, dest == valid
3597 // 3) change == Destroyed, dest == valid (still)
3598 CYG_ASSERTC((CdlUpdate_Loaded == change) || (CdlUpdate_Created == change) || (CdlUpdate_Destroyed == change));
3599 CYG_ASSERTC((CdlUpdate_Created != change) || (0 != dest));
3600 CYG_ASSERTC((CdlUpdate_Destroyed != change) || (0 != dest));
3602 if (CdlUpdate_Destroyed == change) {
3606 // Now either dest is valid or it is not. If it is then we need to
3607 // reparent below the destination. Otherwise if the specified
3608 // parent is "" then we need to reparent below the root. Otherwise
3609 // the node ends up in the orphans container. There are a few
3610 // nasty special cases to consider like reparenting below
3611 // something that is not a container.
3613 CdlToplevel toplevel = source->get_toplevel();
3615 CdlProperty_Reference refprop = dynamic_cast<CdlProperty_Reference>(prop);
3616 if ("" == refprop->get_destination_name()) {
3618 // Now to find the correct insertion point. Nodes which should be
3619 // reparented below the root should come first, ahead of any nodes
3620 // which are not specifically reparented.
3621 const std::vector<CdlNode>& contents = toplevel->get_contents();
3623 for (index = 0; index < contents.size(); index++) {
3624 if (!contents[index]->has_property(CdlPropertyId_Parent)) {
3628 toplevel->change_parent(source->get_owner(), source->get_parent(), toplevel, source, index);
3631 // Orphan the node. It still has a parent, either as a
3632 // consequence of the loading process or because of a previous
3633 // binding operation.
3634 toplevel->change_parent(source->get_owner(), source->get_parent(), 0, source);
3637 // The Unresolved conflict is handled by
3638 // CdlProperty_Reference::update(). The "else" code below may
3639 // have created some additional data conflicts.
3640 transaction->clear_structural_conflicts(source, prop, &CdlConflict_DataBody::test);
3642 // Changing the parent may affect the "active" status.
3643 bool old_state = transaction->is_active(source);
3644 bool new_state = source->test_active(transaction);
3645 if (old_state != new_state) {
3646 transaction->set_active(source, new_state);
3650 // The node should no longer be an orphan - probably.
3652 // Check that the destination is actually a container. If it is,
3653 // reparenting is possible.
3654 CdlContainer dest_container = dynamic_cast<CdlContainer>(dest);
3655 if (0 == dest_container) {
3657 // The reference might be resolved, but reparenting is still not possible.
3658 // Leave the object orphaned as at present, and create a suitable conflict
3660 std::string msg = source->get_class_name() + " " + source->get_name() + " cannot be reparented below " +
3661 dest->get_class_name() + " " + dest->get_name() + "\n The latter is not a container.";
3662 CdlConflict_DataBody::make(transaction, source, prop, msg);
3666 CdlContainer tmp = dynamic_cast<CdlContainer>(source);
3667 if ((0 != tmp) && tmp->contains(dest_container, true)) {
3669 // Somebody trying to be clever and reparent an object
3670 // below one of its existing children? Note that with
3671 // sufficiently careful use of parent statements this
3672 // might actually be legal, but for now treat it as
3674 std::string msg = source->get_class_name() + " " + source->get_name() + " cannot be reparented below " +
3675 dest->get_class_name() + " " + dest->get_name() + "\n This would introduce a cycle.";
3676 CdlConflict_DataBody::make(transaction, source, prop, msg);
3680 // It is possible to reparent the object to its correct location
3681 CdlToplevel toplevel = source->get_toplevel();
3682 CYG_ASSERTC(toplevel == dest->get_toplevel());
3683 toplevel->change_parent(source->get_owner(), source->get_parent(), dest_container, source);
3685 bool old_state = transaction->is_active(source);
3686 bool new_state = source->test_active(transaction);
3687 if (old_state != new_state) {
3688 transaction->set_active(source, new_state);
3694 CYG_REPORT_RETURN();
3698 CdlParentableBody::parse_parent(CdlInterpreter interp, int argc, const char* argv[])
3700 CYG_REPORT_FUNCNAMETYPE("parse_parent", "result %d");
3702 int result = CdlParse::parse_reference_property(interp, argc, argv, CdlPropertyId_Parent, 0, 0, true, &update_handler);
3704 CYG_REPORT_RETVAL(result);