unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / tools / src / libcdl / base.cxx
1 //{{{  Banner                           
2
3 //============================================================================
4 //
5 //     base.cxx
6 //
7 //     Implementations of the various base classes
8 //
9 //============================================================================
10 //####COPYRIGHTBEGIN####
11 //                                                                          
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 2002 Bart Veer
14 // Copyright (C) 1999, 2000 Red Hat, Inc.
15 //
16 // This file is part of the eCos host tools.
17 //
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) 
21 // any later version.
22 // 
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 
26 // more details.
27 // 
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.
31 //
32 // ----------------------------------------------------------------------------
33 //                                                                          
34 //####COPYRIGHTEND####
35 //============================================================================
36 //#####DESCRIPTIONBEGIN####
37 //
38 // Author(s):   bartv
39 // Contact(s):  bartv
40 // Date:        1999/02/18
41 // Version:     0.02
42 // Description: libcdl defines a hierarchy of base classes, used for
43 //              constructing higher-level entities such as options
44 //              and packages.
45 //
46 //####DESCRIPTIONEND####
47 //============================================================================
48
49 //}}}
50 //{{{  #include's                       
51
52 // ----------------------------------------------------------------------------
53 #include "cdlconfig.h"
54
55 // Get the infrastructure types, assertions, tracing and similar
56 // facilities.
57 #include <cyg/infra/cyg_ass.h>
58 #include <cyg/infra/cyg_trac.h>
59
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>
64
65 //}}}
66
67 //{{{  Statics                          
68
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);
76
77 //}}}
78 //{{{  CdlNodeBody                      
79
80 //{{{  Construction                             
81
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.
87
88 CdlNodeBody::CdlNodeBody(std::string name_arg)
89 {
90     CYG_REPORT_FUNCNAME("CdlNode:: constructor");
91     CYG_REPORT_FUNCARG1XV(this);
92     CYG_PRECONDITIONC("" != name_arg);
93
94     name        = name_arg;
95     parent      = 0;
96     owner       = 0;
97     toplevel    = 0;
98     active      = false;
99     remove_node_container_position = -1;
100
101     // The STL containers will take care of themselves.
102     
103     cdlnodebody_cookie = CdlNodeBody_Magic;
104     CYGDBG_MEMLEAK_CONSTRUCTOR();
105
106     CYG_POSTCONDITION_THISC();
107     CYG_REPORT_RETURN();
108 }
109
110 CdlNodeBody::CdlNodeBody()
111 {
112     CYG_PRECONDITION_THISC();
113 }
114
115 //}}}
116 //{{{  Destructor                               
117
118 // ----------------------------------------------------------------------------
119 // By the time the destructor gets invoked the node should already
120 // have been unbound and removed from the hierarchy.
121
122 CdlNodeBody::~CdlNodeBody()
123 {
124     CYG_REPORT_FUNCNAME("CdlNode:: destructor");
125     CYG_REPORT_FUNCARG1XV(this);
126     CYG_PRECONDITION_THISC();
127
128     // Make sure that the node is unbound: all references to and from
129     // this node should have been destroyed already inside a
130     // transaction.
131     CYG_PRECONDITIONC(0 == referrers.size());
132
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);
137     
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++) {
141         delete *prop_i;
142         *prop_i = 0;
143     }
144     properties.clear();
145
146     cdlnodebody_cookie  = CdlNodeBody_Invalid;
147     name   = "";
148     active = false;
149     unsupported_savefile_strings.clear();
150     
151     CYGDBG_MEMLEAK_DESTRUCTOR();
152
153     CYG_REPORT_RETURN();
154 }
155
156 //}}}
157 //{{{  Trivial data access                      
158
159 // ----------------------------------------------------------------------------
160
161 std::string
162 CdlNodeBody::get_name() const
163 {
164     CYG_REPORT_FUNCNAME("CdlNode::get_name");
165     CYG_REPORT_FUNCARG1XV(this);
166     CYG_PRECONDITION_THISC();
167
168     CYG_REPORT_RETURN();
169     return name;
170 }
171
172 void
173 CdlNodeBody::set_name(std::string name_arg)
174 {
175     CYG_REPORT_FUNCNAME("CdlNode::set_name");
176     CYG_REPORT_FUNCARG1XV(this);
177     CYG_PRECONDITION_THISC();
178
179     name = name_arg;
180
181     CYG_REPORT_RETURN();
182 }
183
184 CdlContainer
185 CdlNodeBody::get_parent() const
186 {
187     CYG_REPORT_FUNCNAMETYPE("CdlNode::get_parent", "parent %p");
188     CYG_REPORT_FUNCARG1XV(this);
189     CYG_PRECONDITION_THISC();
190
191     CdlContainer result = parent;
192     CYG_REPORT_RETVAL(result);
193     return result;
194 }
195
196 CdlLoadable
197 CdlNodeBody::get_owner() const
198 {
199     CYG_REPORT_FUNCNAMETYPE("CdlNode::get_owner", "owner %p");
200     CYG_REPORT_FUNCARG1XV(this);
201     CYG_PRECONDITION_THISC();
202
203     CdlLoadable result = owner;
204     CYG_REPORT_RETVAL(result);
205     return result;
206 }
207
208 CdlToplevel
209 CdlNodeBody::get_toplevel() const
210 {
211     CYG_REPORT_FUNCNAMETYPE("CdlNode::get_toplevel", "toplevel %p");
212     CYG_REPORT_FUNCARG1XV(this);
213     CYG_PRECONDITION_THISC();
214
215     CdlToplevel result = toplevel;
216     CYG_REPORT_RETVAL(result);
217     return result;
218 }
219
220 std::string
221 CdlNodeBody::get_class_name() const
222 {
223     CYG_REPORT_FUNCNAME("CdlNode::get_class_name");
224     CYG_PRECONDITION_THISC();
225     CYG_REPORT_RETURN();
226     return "node";
227 }
228
229 //}}}
230 //{{{  The properties vector                    
231
232 // ----------------------------------------------------------------------------
233 // Trivial manipulation of the properties vector.
234
235 const std::vector<CdlProperty>&
236 CdlNodeBody::get_properties() const
237 {
238     CYG_REPORT_FUNCNAME("CdlNode::get_properties");
239     CYG_REPORT_FUNCARG1XV(this);
240     CYG_PRECONDITION_THISC();
241
242     CYG_REPORT_RETURN();
243     return properties;
244 }
245
246 CdlProperty
247 CdlNodeBody::get_property(std::string id) const
248 {
249     CYG_REPORT_FUNCNAMETYPE("CdlNode::get_property", "result %p");
250     CYG_REPORT_FUNCARG1XV(this);
251     CYG_PRECONDITION_THISC();
252
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) {
257             result = *prop_i;
258             break;
259         }
260     }
261
262     CYG_REPORT_RETVAL(result);
263     return result;
264 }
265
266 void
267 CdlNodeBody::get_properties(std::string id, std::vector<CdlProperty>& result) const
268 {
269     CYG_REPORT_FUNCNAME("CdlNode::get_properties");
270     CYG_REPORT_FUNCARG1XV(this);
271     CYG_PRECONDITION_THISC();
272
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);
277         }
278     }
279
280     CYG_REPORT_RETURN();
281 }
282
283 std::vector<CdlProperty>
284 CdlNodeBody::get_properties(std::string id) const
285 {
286     CYG_REPORT_FUNCNAME("CdlNode::get_properties");
287     CYG_REPORT_FUNCARG1XV(this);
288     CYG_PRECONDITION_THISC();
289
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);
295         }
296     }
297
298     CYG_REPORT_RETURN();
299     return result;
300 }
301
302 bool
303 CdlNodeBody::has_property(std::string id) const
304 {
305     CYG_REPORT_FUNCNAMETYPE("CdlNode::has_property", "result %d");
306     CYG_REPORT_FUNCARG1XV(this);
307     CYG_PRECONDITION_THISC();
308
309     bool result = false;
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) {
313             result = true;
314             break;
315         }
316     }
317
318     CYG_REPORT_RETVAL(result);
319     return result;
320 }
321
322 int
323 CdlNodeBody::count_properties(std::string id) const
324 {
325     CYG_REPORT_FUNCNAMETYPE("CdlNode::count_properties", "result %d");
326     CYG_REPORT_FUNCARG1XV(this);
327     CYG_PRECONDITION_THISC();
328
329     int result = 0;
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) {
333             result++;
334         }
335     }
336
337     CYG_REPORT_RETVAL(result);
338     return result;
339 }
340
341 //}}}
342 //{{{  Conflicts                                
343
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.
347
348 void
349 CdlNodeBody::get_conflicts(std::vector<CdlConflict>& result) const
350 {
351     CYG_REPORT_FUNCNAME("CdlNode::get_conflicts");
352     CYG_REPORT_FUNCARG1XV(this);
353     CYG_PRECONDITION_THISC();
354
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);
360         }
361     }
362
363     CYG_REPORT_RETURN();
364 }
365
366 void
367 CdlNodeBody::get_conflicts(bool (*fn)(CdlConflict), std::vector<CdlConflict>& result) const
368 {
369     CYG_REPORT_FUNCNAME("CdlNode::get_conflicts");
370     CYG_REPORT_FUNCARG2XV(this, fn);
371     CYG_PRECONDITION_THISC();
372     CYG_CHECK_FUNC_PTRC(fn);
373
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);
379         }
380     }
381     
382     CYG_REPORT_RETURN();
383 }
384
385 void
386 CdlNodeBody::get_structural_conflicts(std::vector<CdlConflict>& result) const
387 {
388     CYG_REPORT_FUNCNAME("CdlNode::get_structural_conflicts");
389     CYG_REPORT_FUNCARG1XV(this);
390     CYG_PRECONDITION_THISC();
391
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);
397         }
398     }
399
400     CYG_REPORT_RETURN();
401 }
402
403 void
404 CdlNodeBody::get_structural_conflicts(bool (*fn)(CdlConflict), std::vector<CdlConflict>& result) const
405 {
406     CYG_REPORT_FUNCNAME("CdlNode::get_conflicts");
407     CYG_REPORT_FUNCARG2XV(this, fn);
408     CYG_PRECONDITION_THISC();
409     CYG_CHECK_FUNC_PTRC(fn);
410
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);
416         }
417     }
418     
419     CYG_REPORT_RETURN();
420 }
421
422 //}}}
423 //{{{  Referrers                                
424
425 // ----------------------------------------------------------------------------
426 // And access to the referrers vector.
427 const std::vector<CdlReferrer>&
428 CdlNodeBody::get_referrers() const
429 {
430     CYG_REPORT_FUNCNAME("CdlNode::get_referrers");
431     CYG_REPORT_FUNCARG1XV(this);
432     CYG_PRECONDITION_THISC();
433
434     CYG_REPORT_RETURN();
435     return referrers;
436 }
437
438 //}}}
439 //{{{  Property parsers                         
440
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.
445 void
446 CdlNodeBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
447 {
448     CYG_REPORT_FUNCNAME("CdlNode::add_property_parsers");
449     CYG_REPORT_RETURN();
450 }
451
452 void
453 CdlNodeBody::check_properties(CdlInterpreter interp)
454 {
455     CYG_REPORT_FUNCNAME("CdlNode::check_properties");
456     CYG_REPORT_FUNCARG2XV(this, interp);
457     CYG_PRECONDITION_THISC();
458     CYG_PRECONDITION_CLASSC(interp);
459
460     CYG_REPORT_RETURN();
461 }
462
463 //}}}
464 //{{{  is_active() etc.                         
465
466 // ----------------------------------------------------------------------------
467 // Associated with every node is a boolean that holds the current
468 // "active" state. Changes to this happen only at transaction
469 // commit time.
470
471 bool
472 CdlNodeBody::is_active() const
473 {
474     CYG_REPORT_FUNCNAMETYPE("CdlNode::is_active", "result %d");
475     CYG_REPORT_FUNCARG1XV(this);
476     CYG_PRECONDITION_THISC();
477
478     bool result = active;
479     CYG_REPORT_RETVAL(result);
480     return result;
481 }
482
483 bool
484 CdlNodeBody::is_active(CdlTransaction transaction)
485 {
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);
490
491     bool result;
492     if (0 != transaction) {
493         result = transaction->is_active(this);
494     } else {
495         result = active;
496     }
497     CYG_REPORT_RETVAL(result);
498     return result;
499 }
500
501 // This virtual member function allows nodes to check whether or not
502 // they should be active. Derived classes may impose additional
503 // constraints.
504 bool
505 CdlNodeBody::test_active(CdlTransaction transaction)
506 {
507     CYG_REPORT_FUNCNAMETYPE("CdlNode::test_active", "result %d");
508     CYG_PRECONDITION_THISC();
509     CYG_PRECONDITION_CLASSC(transaction);
510
511     bool result = false;
512     if ((0 != parent) && (transaction->is_active(parent))) {
513         CdlValuable valuable = dynamic_cast<CdlValuable>(parent);
514         if (0 == valuable) {
515             result = true;
516         } else if (valuable->is_enabled(transaction)) {
517             result = true;
518         }
519     }
520     
521     CYG_REPORT_RETVAL(result);
522     return result;
523 }
524
525 //}}}
526 //{{{  Propagation support                      
527
528 // ----------------------------------------------------------------------------
529 // In the base class nothing needs doing for propagation.
530 void
531 CdlNodeBody::update(CdlTransaction transaction, CdlUpdate change)
532 {
533     CYG_REPORT_FUNCNAME("CdlNode::update");
534     CYG_REPORT_FUNCARG1XV(this);
535     CYG_PRECONDITION_THISC();
536     CYG_PRECONDITION_CLASSC(transaction);
537     
538     CYG_REPORT_RETURN();
539 }
540
541 //}}}
542 //{{{  Persistence support                      
543
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
554 // to the save file.
555
556 void
557 CdlNodeBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
558 {
559     CYG_REPORT_FUNCNAME("CdlNode::save");
560     CYG_REPORT_FUNCARG5XV(this, interp, chan, indentation, minimal);
561
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";
573         }
574         interp->write_data(chan, data);
575     }
576
577     CYG_UNUSED_PARAM(bool, minimal);
578     CYG_REPORT_RETURN();
579 }
580
581 bool
582 CdlNodeBody::has_additional_savefile_information() const
583 {
584     CYG_REPORT_FUNCNAMETYPE("CdlNode::has_additional_savefile_information", "result %d");
585     CYG_REPORT_FUNCARG1XV(this);
586     CYG_PRECONDITION_THISC();
587
588     bool result = (0 != unsupported_savefile_strings.size());
589     CYG_REPORT_RETVAL(result);
590     return result;
591 }
592
593 //}}}
594 //{{{  check_this()                             
595
596 // ----------------------------------------------------------------------------
597 // Because of multiple and virtual inheritance, check_this() may
598 // get called rather a lot. Unfortunately all of the checks are
599 // useful.
600
601 bool
602 CdlNodeBody::check_this(cyg_assert_class_zeal zeal) const
603 {
604     if (CdlNodeBody_Magic != cdlnodebody_cookie) {
605         return false;
606     }
607     CYGDBG_MEMLEAK_CHECKTHIS();
608     
609     if ("" == name) {
610         return false;
611     }
612
613     // It is hard to validate the toplevel, owner, and parent
614     // fields.
615     //
616     // 1) when a node is newly created all three fields will
617     //    be null.
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.
624     //
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
632     if (0 != toplevel) {
633         if (0 == parent) {
634             return false;
635         }
636     }
637  
638     switch(zeal) {
639       case cyg_system_test :
640       case cyg_extreme     :
641       {
642         if ((0 != toplevel) && (toplevel != this)) {
643             if (!toplevel->check_this(cyg_quick)) {
644                 return false;
645             }
646             if (toplevel->lookup_table.find(name) == toplevel->lookup_table.end()) {
647                 return false;
648             }
649         }
650         if (0 != parent) {
651             if (!parent->check_this(cyg_quick)) {
652                 return false;
653             }
654             if (std::find(parent->contents.begin(), parent->contents.end(), this) == parent->contents.end()) {
655                 return false;
656             }
657         }
658         if (0 != owner) {
659             if (!owner->check_this(cyg_quick)) {
660                 return false;
661             }
662             if (std::find(owner->owned.begin(), owner->owned.end(), this) == owner->owned.end()) {
663                 return false;
664             }
665         }
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)) {
669                 return false;
670             }
671         }
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)) {
675                 return false;
676             }
677         }
678       }
679       case cyg_thorough    :
680       case cyg_quick       :
681       case cyg_trivial     :
682       case cyg_none        :
683       default              :
684           break;
685     }
686
687     return true;
688 }
689
690 //}}}
691
692 //}}}
693 //{{{  CdlContainerBody                 
694
695 //{{{  Constructors                     
696
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
703 // toplevel.
704 //
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
709 // functionality.
710
711 CdlContainerBody::CdlContainerBody()
712 {
713     CYG_REPORT_FUNCNAME("CdlContainer:: default constructor");
714     CYG_REPORT_FUNCARG1XV(this);
715
716     cdlcontainerbody_cookie = CdlContainerBody_Magic;
717     CYGDBG_MEMLEAK_CONSTRUCTOR();
718     
719     CYG_POSTCONDITION_THISC();
720     CYG_REPORT_RETURN();
721 }
722
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)
728 {
729     CYG_REPORT_FUNCNAME("CdlContainerBody:: constructor (name)");
730     CYG_REPORT_FUNCARG1XV(this);
731
732     cdlcontainerbody_cookie = CdlContainerBody_Magic;
733     CYGDBG_MEMLEAK_CONSTRUCTOR();
734
735     CYG_POSTCONDITION_THISC();
736     CYG_REPORT_RETURN();
737 }
738
739 //}}}
740 //{{{  Destructor                       
741
742 // ----------------------------------------------------------------------------
743
744 CdlContainerBody::~CdlContainerBody()
745 {
746     CYG_REPORT_FUNCNAME("CdlContainer:: destructor");
747     CYG_REPORT_FUNCARG1XV(this);
748     CYG_PRECONDITION_THISC();
749
750     // Containers should always be empty by the time they
751     // get deleted. The toplevel and loadable destructors should
752     // guarantee this.
753     CYG_ASSERTC(0 == contents.size());
754     
755     cdlcontainerbody_cookie = CdlContainerBody_Invalid;
756     CYGDBG_MEMLEAK_DESTRUCTOR();
757     
758     CYG_REPORT_RETURN();
759 }
760
761 //}}}
762 //{{{  Accessing the contents           
763
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.
768
769 const std::vector<CdlNode>&
770 CdlContainerBody::get_contents() const
771 {
772     CYG_REPORT_FUNCNAME("CdlContainer::get_contents");
773     CYG_REPORT_FUNCARG1XV(this);
774     CYG_PRECONDITION_THISC();
775
776     CYG_REPORT_RETURN();
777     return contents;
778 }
779
780 bool
781 CdlContainerBody::contains(CdlConstNode node, bool recurse) const
782 {
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);
787
788     bool result = false;
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) {
792             result = true;
793             break;
794         }
795         if (recurse) {
796             CdlConstContainer child = dynamic_cast<CdlConstContainer>(*node_i);
797             if ((0 != child) && child->contains(node, true)) {
798                 result = true;
799                 break;
800             }
801         }
802     }
803
804     CYG_REPORT_RETVAL(result);
805     return result;
806 }
807
808 bool
809 CdlContainerBody::contains(const std::string name, bool recurse) const
810 {
811     CYG_REPORT_FUNCNAMETYPE("CdlContainer::contains (name)", "result %d");
812     CYG_REPORT_FUNCARG2XV(this, recurse);
813     CYG_PRECONDITION_THISC();
814     CYG_PRECONDITIONC("" != name);
815
816     bool result = false;
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) {
820             result = true;
821             break;
822         }
823         if (recurse) {
824             CdlConstContainer child = dynamic_cast<CdlConstContainer>(*node_i);
825             if ((0 != child) && child->contains(name, true)) {
826                 result = true;
827                 break;
828             }
829         }
830     }
831
832     CYG_REPORT_RETVAL(result);
833     return result;
834 }
835
836 CdlNode
837 CdlContainerBody::find_node(const std::string name, bool recurse) const
838 {
839     CYG_REPORT_FUNCNAMETYPE("CdlContainer::find_node", "result %p");
840     CYG_REPORT_FUNCARG2XV(this, recurse);
841     CYG_PRECONDITION_THISC();
842     CYG_PRECONDITIONC("" != name);
843
844     CdlNode result = 0;
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) {
848             result = *node_i;
849             break;
850         }
851         if (recurse) {
852             CdlConstContainer child = dynamic_cast<CdlConstContainer>(*node_i);
853             if (0 != child) {
854                 result = child->find_node(name, true);
855                 if (0 != result) {
856                     break;
857                 }
858             }
859         }
860     }
861
862     CYG_REPORT_RETVAL(result);
863     return result;
864 }
865
866 //}}}
867 //{{{  Misc                             
868
869 // ----------------------------------------------------------------------------
870
871 std::string
872 CdlContainerBody::get_class_name() const
873 {
874     CYG_REPORT_FUNCNAME("CdlContainer::get_class_name");
875     CYG_PRECONDITION_THISC();
876     CYG_REPORT_RETURN();
877     return "container";
878 }
879
880 //}}}
881 //{{{  Propagation                      
882
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.
886
887 void
888 CdlContainerBody::update(CdlTransaction transaction, CdlUpdate change)
889 {
890     CYG_REPORT_FUNCNAME("CdlContainer::update");
891     CYG_REPORT_FUNCARG1XV(this);
892     CYG_PRECONDITION_THISC();
893     CYG_PRECONDITION_CLASSC(transaction);
894
895     if ((CdlUpdate_ActiveChange != change) && (CdlUpdate_ValueChange != change)) {
896         CYG_REPORT_RETURN();
897         return;
898     }
899
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;
905             
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);
911             }
912         }
913     } else {
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);
920             }
921         }
922     }
923     
924     CYG_REPORT_RETURN();
925 }
926
927 //}}}
928 //{{{  Persistence                      
929
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.
935 //
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
938 // will.
939
940 void
941 CdlContainerBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
942 {
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);
948
949     if (0 != contents.size()) {
950         if (!minimal) {
951             interp->write_data(chan, "# >\n");
952         }
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);
956         }
957         if (!minimal) {
958             interp->write_data(chan, "# <\n");
959         }
960     }
961     
962     CYG_REPORT_RETURN();
963 }
964
965 //}}}
966 //{{{  check_this()                     
967
968 // ----------------------------------------------------------------------------
969 bool
970 CdlContainerBody::check_this(cyg_assert_class_zeal zeal) const
971 {
972     if (CdlContainerBody_Magic != cdlcontainerbody_cookie) {
973         return false;
974     }
975     CYGDBG_MEMLEAK_CHECKTHIS();
976
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))) {
981                 return false;
982             }
983         }
984     }
985     return CdlNodeBody::check_this(zeal);
986 }
987
988 //}}}
989
990 //}}}
991 //{{{  CdlLoadableBody                  
992
993 //{{{  Constructor                              
994
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.
1001
1002 CdlLoadableBody::CdlLoadableBody(CdlToplevel toplevel, std::string dir)
1003     : CdlContainerBody()
1004 {
1005     CYG_REPORT_FUNCNAME("CdlLoadable:: constructor");
1006     CYG_REPORT_FUNCARG1XV(this);
1007     CYG_PRECONDITION_CLASSC(toplevel);
1008
1009     // Initialize enough of the object to support check_this()
1010     directory   = dir;
1011     interp      = 0;
1012     remove_node_loadables_position = -1;
1013     cdlloadablebody_cookie = CdlLoadableBody_Magic;
1014     
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();
1023     
1024     CYG_POSTCONDITION_THISC();
1025     CYG_REPORT_RETURN();
1026 }
1027
1028 // Needed by derived classes, but should never actually be used.
1029 CdlLoadableBody::CdlLoadableBody()
1030 {
1031     CYG_FAIL("CdlLoadable default constructor should never get invoked");
1032 }
1033
1034 //}}}
1035 //{{{  Destructor                               
1036
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
1043 // unbound.
1044 //
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.
1049
1050 CdlLoadableBody::~CdlLoadableBody()
1051 {
1052     CYG_REPORT_FUNCNAME("CdlLoadable:: destructor");
1053     CYG_REPORT_FUNCARG1XV(this);
1054     CYG_PRECONDITION_THISC();
1055
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);
1060     
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.
1066     int i;
1067     for (i = owned.size() - 1; i >= 0; i--) {
1068         CdlNode node = owned[i];
1069         CYG_LOOP_INVARIANT_CLASSC(node);
1070
1071         if (node != this) {
1072             CdlToplevelBody::remove_node(this, node->parent, node);
1073             delete node;
1074         }
1075     }
1076     
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]);
1083     this->owner = 0;
1084
1085     // Strictly speaking the owned vector should be clear by now,
1086     // but remove_node() does not actually bother to clear it.
1087     owned.clear();
1088     
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());
1093     delete interp;
1094     interp      = 0;
1095     directory   = "";
1096
1097     CYGDBG_MEMLEAK_DESTRUCTOR();
1098     
1099     CYG_REPORT_RETURN();
1100 }
1101
1102 //}}}
1103 //{{{  Simple information access                
1104
1105 // ----------------------------------------------------------------------------
1106
1107 const std::vector<CdlNode>&
1108 CdlLoadableBody::get_owned() const
1109 {
1110     CYG_REPORT_FUNCNAME("CdlLoadable::get_owned");
1111     CYG_REPORT_FUNCARG1XV(this);
1112     CYG_PRECONDITION_THISC();
1113
1114     CYG_REPORT_RETURN();
1115     return owned;
1116 }
1117
1118 bool
1119 CdlLoadableBody::owns(CdlConstNode node) const
1120 {
1121     CYG_REPORT_FUNCNAMETYPE("CdlLoadable::owns", "result %d");
1122     CYG_REPORT_FUNCARG2XV(this, node);
1123     CYG_PRECONDITION_THISC();
1124     CYG_PRECONDITION_CLASSC(node);
1125
1126     bool result = false;
1127     std::vector<CdlNode>::const_iterator i = std::find(owned.begin(), owned.end(), node);
1128     if (i != owned.end()) {
1129         result = true;
1130     }
1131
1132     CYG_REPORT_RETVAL(result);
1133     return result;
1134 }
1135
1136 CdlInterpreter
1137 CdlLoadableBody::get_interpreter() const
1138 {
1139     CYG_REPORT_FUNCNAMETYPE("CdlLoadable::get_interpreter", "result %p");
1140     CYG_REPORT_FUNCARG1XV(this);
1141     CYG_PRECONDITION_THISC();
1142
1143     CdlInterpreter result = interp;
1144     CYG_REPORT_RETVAL(result);
1145     return result;
1146 }
1147
1148 std::string
1149 CdlLoadableBody::get_directory() const
1150 {
1151     CYG_REPORT_FUNCNAME("CdlLoadable::get_directory");
1152     CYG_REPORT_FUNCARG1XV(this);
1153     CYG_PRECONDITION_THISC();
1154
1155     CYG_REPORT_RETURN();
1156     return directory;
1157 }
1158
1159 //}}}
1160 //{{{  Bind/unbind support                      
1161
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
1171 // in many cases.
1172 //
1173 // FIXME: error recovery?
1174
1175 void
1176 CdlLoadableBody::bind(CdlTransaction transaction)
1177 {
1178     CYG_REPORT_FUNCNAME("CdlLoadable::bind");
1179     CYG_REPORT_FUNCARG2XV(this, transaction);
1180     CYG_INVARIANT_THISC(CdlLoadableBody);
1181     CYG_INVARIANT_CLASSC(CdlTransactionBody, transaction);
1182
1183     // The loadable must already be part of the hierarchy.
1184     CdlToplevel toplevel = this->get_toplevel();
1185     CYG_ASSERT_CLASSC(toplevel);
1186         
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);
1196         }
1197     }
1198
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;
1205     do {
1206         CYG_ASSERT_CLASSC(current_transaction);
1207         const std::list<CdlConflict>& new_structural_conflicts = current_transaction->get_new_structural_conflicts();
1208         
1209         for (conf_i = new_structural_conflicts.begin(); conf_i != new_structural_conflicts.end(); ) {
1210             
1211             CdlConflict conflict = *conf_i++;
1212             CYG_LOOP_INVARIANT_CLASSC(conflict);
1213             
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());
1217                 if (0 != dest) {
1218                     CdlNode     node = unresolved_conflict->get_node();
1219                     CdlProperty prop = unresolved_conflict->get_property();
1220                     prop->update(transaction, node, dest, CdlUpdate_Created);
1221                 }                    
1222             }
1223         }
1224         current_transaction = current_transaction->get_parent();
1225     } while (0 != current_transaction);
1226
1227     const std::list<CdlConflict>& structural_conflicts = toplevel->get_all_structural_conflicts();
1228     for (conf_i = structural_conflicts.begin(); conf_i != structural_conflicts.end(); ) {
1229         
1230         CdlConflict conflict = *conf_i++;
1231         CYG_LOOP_INVARIANT_CLASSC(conflict);
1232             
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());
1236             if (0 != dest) {
1237                 CdlNode     node = this_conflict->get_node();
1238                 CdlProperty prop = this_conflict->get_property();
1239                 prop->update(transaction, node, dest, CdlUpdate_Created);
1240             }
1241         }
1242     }
1243
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);
1252         }
1253     }
1254
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
1258     // update instead.
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);
1264         }
1265     }
1266
1267     CYG_REPORT_RETURN();
1268 }
1269
1270 // ----------------------------------------------------------------------------
1271
1272 void
1273 CdlLoadableBody::unbind(CdlTransaction transaction)
1274 {
1275     CYG_REPORT_FUNCNAME("CdlLoadable::unbind");
1276     CYG_REPORT_FUNCARG2XV(this, transaction);
1277     CYG_PRECONDITION_THISC();
1278     CYG_PRECONDITION_CLASSC(transaction);
1279     
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");
1292         }
1293     }
1294
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);
1303         }
1304     }
1305
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);
1317         }
1318     }
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);
1326         }
1327     }
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);
1335         }
1336     }
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);
1344         }
1345     }
1346
1347     // FIXME: how about cleanup_orphans()
1348     
1349     CYG_REPORT_RETURN();
1350 }
1351
1352 // ----------------------------------------------------------------------------
1353 // These members are invoked for load and unload operations.
1354 //
1355 // Committing a load does not require anything, the loadable has
1356 // already been fully bound and all propagation has happened.
1357
1358 void
1359 CdlLoadableBody::transaction_commit_load(CdlTransaction transaction, CdlLoadable loadable)
1360 {
1361     CYG_REPORT_FUNCNAME("CdlLoadable::transaction_commit_load");
1362     CYG_REPORT_FUNCARG2XV(transaction, loadable);
1363     CYG_PRECONDITION_CLASSC(transaction);
1364     CYG_PRECONDITION_CLASSC(loadable);
1365
1366     CYG_UNUSED_PARAM(CdlTransaction, transaction);
1367     CYG_UNUSED_PARAM(CdlLoadable, loadable);
1368     
1369     CYG_REPORT_RETURN();
1370 }
1371
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
1375 // recover.
1376 void
1377 CdlLoadableBody::transaction_cancel_load(CdlTransaction transaction, CdlLoadable loadable)
1378 {
1379     CYG_REPORT_FUNCNAME("CdlLoadable::transaction_cancel_load");
1380     CYG_REPORT_FUNCARG2XV(transaction, loadable);
1381     CYG_PRECONDITION_CLASSC(transaction);
1382     CYG_PRECONDITION_CLASSC(loadable);
1383
1384     CdlToplevel toplevel = transaction->get_toplevel();
1385     CYG_PRECONDITION_CLASSC(toplevel);
1386     CYG_ASSERTC(toplevel == loadable->get_toplevel());
1387
1388     loadable->unbind(transaction);
1389     toplevel->remove_loadable_from_toplevel(loadable);
1390     delete loadable;
1391     
1392     CYG_REPORT_RETURN();
1393 }
1394
1395 // Committing an unload means that the loadable can now be deleted.
1396 // It should already be unbound and removed from the toplevel.
1397 void
1398 CdlLoadableBody::transaction_commit_unload(CdlTransaction transaction, CdlLoadable loadable)
1399 {
1400     CYG_REPORT_FUNCNAME("CdlLoadable::transaction_commit_unload");
1401     CYG_REPORT_FUNCARG2XV(transaction, loadable);
1402     CYG_PRECONDITION_CLASSC(transaction);
1403     CYG_PRECONDITION_CLASSC(loadable);
1404
1405     CYG_UNUSED_PARAM(CdlTransaction, transaction);
1406     delete loadable;
1407
1408     CYG_REPORT_RETURN();
1409 }
1410
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.
1417 void
1418 CdlLoadableBody::transaction_cancel_unload(CdlTransaction transaction, CdlLoadable loadable)
1419 {
1420     CYG_REPORT_FUNCNAME("CdlLoadable::transaction_cancel_unload");
1421     CYG_REPORT_FUNCARG2XV(transaction, loadable);
1422     CYG_PRECONDITION_CLASSC(transaction);
1423     CYG_PRECONDITION_CLASSC(loadable);
1424
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);
1430     
1431     CYG_REPORT_RETURN();
1432 }
1433
1434 //}}}
1435 //{{{  File search facilities                   
1436
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
1441 //
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.
1445 //
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.
1450 //
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.
1454 //
1455 // RFC1807: an anchor is everything after the first # in the URL.
1456
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\
1462   }                                                                             \n\
1463   set tmp \"\"                                                                  \n\
1464   set non_anchor \"\"                                                           \n\
1465   if { [regexp -- {^([^#])(#.*$)} $::cdl_target tmp non_anchor cdl_anchor] } {  \n\
1466       set ::cdl_target $non_anchor                                              \n\
1467   }                                                                             \n\
1468 }                                                                               \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\
1473     }                                                                           \n\
1474 }                                                                               \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\
1478 }                                                                               \n\
1479 return \"\"                                                                     \n\
1480 ";
1481
1482 std::string
1483 CdlLoadableBody::find_absolute_file(std::string filename, std::string dirname, bool allow_urls) const
1484 {
1485     CYG_REPORT_FUNCNAME("CdlLoadable::find_absolute_file");
1486     CYG_REPORT_FUNCARG1XV(this);
1487     CYG_PRECONDITION_THISC();
1488     CYG_PRECONDITIONC("" != filename);
1489
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");
1496
1497     std::string result;
1498     int tmp = interp->eval(find_absolute_file_script, result);
1499     if (tmp != TCL_OK) {
1500         result = "";
1501     }
1502     
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]) {
1508             result[i] = '/';
1509         }
1510     }
1511
1512     CYG_REPORT_RETURN();
1513     return result;
1514 }
1515
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\
1521     }                                                                           \n\
1522 }                                                                               \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\
1526 }                                                                               \n\
1527 return \"\"                                                                     \n\
1528 ";
1529
1530 std::string
1531 CdlLoadableBody::find_relative_file(std::string filename, std::string dirname) const
1532 {
1533     CYG_REPORT_FUNCNAME("CdlLoadable::find_relative_file");
1534     CYG_REPORT_FUNCARG1XV(this);
1535     CYG_PRECONDITION_THISC();
1536     CYG_PRECONDITIONC("" != filename);
1537
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);
1543
1544     std::string result;
1545     int tmp = interp->eval(find_relative_file_script, result);
1546     if (tmp != TCL_OK) {
1547         result = "";
1548     }
1549     
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]) {
1555             result[i] = '/';
1556         }
1557     }
1558
1559     CYG_REPORT_RETURN();
1560     return result;
1561 }
1562
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\
1566     return 0                                                            \n\
1567 }                                                                       \n\
1568 return 1                                                                \n\
1569 ";
1570
1571 bool
1572 CdlLoadableBody::has_subdirectory(std::string name) const
1573 {
1574     CYG_REPORT_FUNCNAMETYPE("CdlLoadable::has_subdirectory", "result %d");
1575     CYG_REPORT_FUNCARG1XV(this);
1576     CYG_PRECONDITION_THISC();
1577     CYG_PRECONDITIONC("" != name);
1578
1579     bool        result = false;
1580     
1581     interp->set_variable("::cdl_topdir",  get_toplevel()->get_directory());
1582     interp->set_variable("::cdl_pkgdir",  directory);
1583     interp->set_variable("::cdl_target",  name);
1584
1585     std::string tcl_result;
1586     int tmp = interp->eval(has_subdirectory_script, tcl_result);
1587     if ((TCL_OK == tmp) && ("1" == tcl_result)) {
1588         result = true;
1589     }
1590
1591     CYG_REPORT_RETVAL(result);
1592     return result;
1593 }
1594
1595 //}}}
1596 //{{{  Misc                                     
1597
1598 // ----------------------------------------------------------------------------
1599
1600 std::string
1601 CdlLoadableBody::get_class_name() const
1602 {
1603     CYG_REPORT_FUNCNAME("CdlLoadable::get_class_name");
1604     CYG_PRECONDITION_THISC();
1605     CYG_REPORT_RETURN();
1606     return "loadable";
1607 }
1608
1609 //}}}
1610 //{{{  check_this()                             
1611
1612 // ----------------------------------------------------------------------------
1613 bool
1614 CdlLoadableBody::check_this(cyg_assert_class_zeal zeal) const
1615 {
1616     if (CdlLoadableBody_Magic != cdlloadablebody_cookie) {
1617         return false;
1618     }
1619     CYGDBG_MEMLEAK_CHECKTHIS();
1620
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)) {
1625                 return false;
1626             }
1627         }
1628     }
1629     return CdlContainerBody::check_this(zeal);
1630 }
1631
1632 //}}}
1633
1634 //}}}
1635 //{{{  CdlToplevelBody                  
1636
1637 //{{{  Constructor                              
1638
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.
1643 //
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.
1647 //
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.
1654
1655 CdlToplevelBody::CdlToplevelBody(CdlInterpreter interp_arg, std::string directory_arg)
1656     : CdlContainerBody()
1657 {
1658     CYG_REPORT_FUNCNAME("CdlToplevel:: constructor");
1659     CYG_REPORT_FUNCARG2XV(this, interp_arg);
1660     CYG_PRECONDITION_CLASSC(interp_arg);
1661
1662     // The STL containers will take care of themselves.
1663     interp      = interp_arg;
1664     directory   = directory_arg;
1665     transaction = 0;
1666
1667     // A toplevel is always active, override the default setting for a node
1668     active      = true;
1669     
1670     // Make the object valid before creating the orphans container.
1671     orphans     = 0;
1672     description = "";
1673     cdltoplevelbody_cookie = CdlToplevelBody_Magic;
1674     
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.
1680     //
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);
1685
1686     // Let the interpreter know about its owning toplevel, as well as
1687     // vice versa.
1688     interp->set_toplevel(this);
1689     
1690     // The orphans container needs to be active as well.
1691     orphans->active = true;
1692
1693     CYGDBG_MEMLEAK_CONSTRUCTOR();
1694     
1695     CYG_POSTCONDITION_THISC();
1696     CYG_REPORT_RETURN();
1697 }
1698
1699 //}}}
1700 //{{{  Destructor                               
1701
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.
1709
1710 CdlToplevelBody::~CdlToplevelBody()
1711 {
1712     CYG_REPORT_FUNCNAME("CdlToplevel:: destructor");
1713     CYG_REPORT_FUNCARG1XV(this);
1714     CYG_PRECONDITION_THISC();
1715
1716     CYG_PRECONDITIONC(0 == loadables.size());
1717     CYG_PRECONDITIONC(0 == conflicts.size());
1718     CYG_PRECONDITIONC(0 == structural_conflicts.size());
1719     CYG_PRECONDITIONC(0 == transaction);
1720
1721     CYG_PRECONDITIONC(0 != orphans);
1722     this->remove_node_from_toplevel(orphans);
1723     CdlToplevelBody::remove_node(0, this, orphans);
1724     delete orphans;
1725     orphans = 0;
1726
1727     CYG_PRECONDITIONC(0 == contents.size());
1728     
1729     cdltoplevelbody_cookie = CdlToplevelBody_Magic;
1730     description = "";
1731     limbo.clear();
1732     unsupported_savefile_toplevel_strings.clear();
1733     unsupported_savefile_commands.clear();
1734     unsupported_savefile_subcommands.clear();
1735
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.
1739     interp = 0;
1740
1741     CYGDBG_MEMLEAK_DESTRUCTOR();
1742
1743     CYG_REPORT_RETURN();
1744 }
1745
1746 //}}}
1747 //{{{  Adding and removing nodes                
1748
1749 // ----------------------------------------------------------------------------
1750 // Adding and removing a node, and changing a parent.
1751 //
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.
1755 //
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
1759 // itself.
1760
1761 void
1762 CdlToplevelBody::add_node(CdlLoadable owner, CdlContainer parent, CdlNode node)
1763 {
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);
1770
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);
1775     
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;
1781
1782     node->owner                 = owner;
1783     if (0 != owner) {
1784         owner->owned.push_back(node);
1785     }
1786
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);
1793     }
1794     
1795     if (0 == parent) {
1796         parent = orphans;
1797     }
1798     node->parent = parent;
1799     parent->contents.push_back(node);
1800
1801     CYG_REPORT_RETURN();
1802 }
1803
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.
1810
1811 void
1812 CdlToplevelBody::remove_node_from_toplevel(CdlNode node)
1813 {
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);
1820
1821     node->toplevel = 0;
1822     lookup_table.erase(node->name);
1823
1824     CdlLoadable loadable = dynamic_cast<CdlLoadable>(node);
1825     if (0 != loadable) {
1826         CYG_ASSERTC(loadable == node->owner);
1827         CYG_ASSERTC(this == node->parent);
1828
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.
1834         int i;
1835         for (i = 0; i < (int) this->contents.size(); i++) {
1836             if (this->contents[i] == node) {
1837                 break;
1838             }
1839         }
1840         CYG_ASSERTC(i < (int) this->contents.size());
1841         node->remove_node_container_position = i;
1842         this->contents.erase(this->contents.begin() + i);
1843         node->parent = 0;
1844
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) {
1849                 break;
1850             }
1851         }
1852         CYG_ASSERTC(i < (int) this->loadables.size());
1853         loadable->remove_node_loadables_position = i;
1854         this->loadables.erase(this->loadables.begin() + i);
1855     }
1856     
1857     CYG_REPORT_RETURN();
1858 }
1859
1860 void
1861 CdlToplevelBody::remove_loadable_from_toplevel(CdlLoadable loadable)
1862 {
1863     CYG_REPORT_FUNCNAME("CdlToplevel::remove_loadable_from_toplevel");
1864     CYG_REPORT_FUNCARG2XV(this, loadable);
1865     CYG_PRECONDITION_THISC();
1866     CYG_PRECONDITION_CLASSC(loadable);
1867
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]);
1875         }
1876     }
1877     
1878     CYG_REPORT_RETURN();
1879 }
1880
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.
1883 void
1884 CdlToplevelBody::add_node_to_toplevel(CdlNode node)
1885 {
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);
1892
1893     CYG_ASSERTC(lookup_table.find(node->name) == lookup_table.end());
1894     node->toplevel = this;
1895     lookup_table[node->name] = node;
1896
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());
1903
1904         this->contents.insert(this->contents.begin() + node->remove_node_container_position, node);
1905         node->remove_node_container_position = -1;
1906         node->parent = this;
1907
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;
1911     }
1912
1913     CYG_REPORT_RETURN();
1914 }
1915
1916 void
1917 CdlToplevelBody::add_loadable_to_toplevel(CdlLoadable loadable)
1918 {
1919     CYG_REPORT_FUNCNAME("CdlToplevel::add_loadable_to_toplevel");
1920     CYG_REPORT_FUNCARG2XV(this, loadable);
1921     CYG_PRECONDITION_THISC();
1922     CYG_PRECONDITION_CLASSC(loadable);
1923     
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]);
1927     }
1928
1929     CYG_REPORT_RETURN();
1930 }
1931
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().
1936 void
1937 CdlToplevelBody::remove_node(CdlLoadable owner, CdlContainer parent, CdlNode node)
1938 {
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);
1947
1948     if (0 != owner) {
1949         node->owner = 0;
1950         owner->owned.erase(std::find(owner->owned.begin(), owner->owned.end(), node));
1951     }
1952     if (0 != parent) {
1953         node->parent = 0;
1954         parent->contents.erase(std::find(parent->contents.begin(), parent->contents.end(), node));
1955     }
1956
1957     CYG_REPORT_RETURN();
1958 }
1959
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.
1962 void
1963 CdlToplevelBody::change_parent(CdlLoadable owner, CdlContainer old_parent, CdlContainer new_parent, CdlNode node, int pos)
1964 {
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);
1975
1976     if (0 == new_parent) {
1977         new_parent = orphans;
1978     }
1979     old_parent->contents.erase(std::find(old_parent->contents.begin(), old_parent->contents.end(), node));
1980     node->parent = 0;
1981
1982     if (-1 == pos) {
1983         new_parent->contents.push_back(node);
1984     } else {
1985         CYG_ASSERTC(pos <= (int) new_parent->contents.size());
1986         new_parent->contents.insert(new_parent->contents.begin() + pos, node);
1987     }
1988     node->parent = new_parent;
1989
1990     CYG_REPORT_RETURN();
1991 }
1992
1993 // Cleaning up orphans.
1994 //
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
2002 // safely.
2003 //
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.
2011
2012 void
2013 CdlToplevelBody::cleanup_orphans()
2014 {
2015     CYG_REPORT_FUNCNAME("CdlToplevel::cleanup_orphans");
2016     CYG_REPORT_FUNCARG1XV(this);
2017     CYG_PRECONDITION_THISC();
2018
2019     // First figure out whether or not there are any interfaces along
2020     // these lines.
2021     std::vector<CdlInterface>   interfaces;
2022     const std::vector<CdlNode>& contents = orphans->get_contents();
2023     std::vector<CdlNode>::const_iterator node_i;
2024
2025     for (node_i = contents.begin(); node_i != contents.end(); node_i++) {
2026         CdlInterface intface = dynamic_cast<CdlInterface>(*node_i);
2027         if (0 == intface) {
2028             continue;
2029         }
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) {
2034                 break;
2035             }
2036         }
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);
2041         }
2042     }
2043     
2044     if (0 != interfaces.size()) {
2045         CYG_FAIL("Not yet implemented");
2046     }
2047 }
2048
2049 //}}}
2050 //{{{  Basic information                        
2051
2052 // ----------------------------------------------------------------------------
2053
2054 const std::vector<CdlLoadable>&
2055 CdlToplevelBody::get_loadables() const
2056 {
2057     CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_loadables", "result %p");
2058     CYG_REPORT_FUNCARG1XV(this);
2059     CYG_PRECONDITION_THISC();
2060
2061     const std::vector<CdlLoadable>& result = loadables;
2062     CYG_REPORT_RETVAL(&result);
2063     return result;
2064 }
2065
2066
2067 CdlNode
2068 CdlToplevelBody::lookup(const std::string name) const
2069 {
2070     CYG_REPORT_FUNCNAMETYPE("CdlToplevel::lookup", "result %p");
2071     CYG_REPORT_FUNCARG1XV(this);
2072     CYG_PRECONDITION_THISC();
2073     CYG_PRECONDITIONC("" != name);
2074
2075     CdlNode result = 0;
2076     std::map<std::string,CdlNode>::const_iterator i = lookup_table.find(name);
2077     if (i != lookup_table.end()) {
2078         result = i->second;
2079     }
2080
2081     CYG_REPORT_RETVAL(result);
2082     return result;
2083 }
2084
2085 CdlInterpreter
2086 CdlToplevelBody::get_interpreter() const
2087 {
2088     CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_interpreter", "result %p");
2089     CYG_REPORT_FUNCARG1XV(this);
2090     CYG_PRECONDITION_THISC();
2091
2092     CdlInterpreter result = interp;
2093     CYG_REPORT_RETVAL(result);
2094     return result;
2095 }
2096
2097 std::string
2098 CdlToplevelBody::get_description() const
2099 {
2100     CYG_REPORT_FUNCNAME("CdlToplevel::get_description");
2101     CYG_REPORT_FUNCARG1XV(this);
2102     CYG_PRECONDITION_THISC();
2103
2104     CYG_REPORT_RETURN();
2105     return description;
2106 }
2107
2108 void
2109 CdlToplevelBody::set_description(std::string new_description)
2110 {
2111     CYG_REPORT_FUNCNAME("CdlToplevel::set_description");
2112     CYG_REPORT_FUNCARG1XV(this);
2113     CYG_PRECONDITION_THISC();
2114
2115     description = new_description;
2116
2117     CYG_REPORT_RETURN();
2118 }
2119
2120 std::string
2121 CdlToplevelBody::get_directory() const
2122 {
2123     CYG_REPORT_FUNCNAME("CdlToplevel::get_directory");
2124     CYG_REPORT_FUNCARG1XV(this);
2125     CYG_PRECONDITION_THISC();
2126
2127     CYG_REPORT_RETURN();
2128     return directory;
2129 }
2130
2131 std::string
2132 CdlToplevelBody::get_class_name() const
2133 {
2134     CYG_REPORT_FUNCNAME("CdlToplevel::get_class_name");
2135     CYG_PRECONDITION_THISC();
2136     CYG_REPORT_RETURN();
2137     return "toplevel";
2138 }
2139
2140 CdlTransaction
2141 CdlToplevelBody::get_active_transaction() const
2142 {
2143     CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_active_transaction", "result %p");
2144     CYG_REPORT_FUNCARG1XV(this);
2145     CYG_PRECONDITION_THISC();
2146
2147     CdlTransaction result = transaction;
2148     CYG_REPORT_RETVAL(result);
2149     return result;
2150 }
2151
2152 //}}}
2153 //{{{  Conflict support                         
2154
2155 // ----------------------------------------------------------------------------
2156 const std::list<CdlConflict>&
2157 CdlToplevelBody::get_all_conflicts() const
2158 {
2159     CYG_REPORT_FUNCNAME("CdlToplevel::get_all_conflicts");
2160     CYG_REPORT_FUNCARG1XV(this);
2161     CYG_PRECONDITION_THISC();
2162
2163     const std::list<CdlConflict>& result = conflicts;
2164
2165     CYG_REPORT_RETURN();
2166     return result;
2167 }
2168
2169 const std::list<CdlConflict>&
2170 CdlToplevelBody::get_all_structural_conflicts() const
2171 {
2172     CYG_REPORT_FUNCNAME("CdlToplevel::get_all_structural_conflicts");
2173     CYG_REPORT_FUNCARG1XV(this);
2174     CYG_PRECONDITION_THISC();
2175
2176     const std::list<CdlConflict>& result = structural_conflicts;
2177
2178     CYG_REPORT_RETURN();
2179     return result;
2180 }
2181
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, ...
2187 void
2188 CdlToplevelBody::resolve_conflicts(const std::vector<CdlConflict>& conflicts_arg)
2189 {
2190     CYG_REPORT_FUNCNAME("CdlToplevel::resolve_conflicts");
2191     CYG_REPORT_FUNCARG1XV(this);
2192     CYG_PRECONDITION_THISC();
2193
2194     CdlTransaction transact = CdlTransactionBody::make(this);
2195     
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());
2200
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);
2206         }
2207     }
2208     transact->body();
2209     delete transact;
2210     
2211     CYG_REPORT_RETURN();
2212 }
2213
2214 void
2215 CdlToplevelBody::resolve_all_conflicts()
2216 {
2217     CYG_REPORT_FUNCNAME("CdlToplevel::resolve_all_conflicts");
2218     CYG_REPORT_FUNCARG1XV(this);
2219     CYG_PRECONDITION_THISC();
2220
2221     CdlTransaction transact = CdlTransactionBody::make(this);
2222     std::list<CdlConflict>::const_iterator conf_i;
2223
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);
2232         }
2233     }
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);
2242         }
2243     }
2244
2245     transact->body();
2246     delete transact;
2247
2248     CYG_REPORT_RETURN();
2249 }
2250
2251 //}}}
2252 //{{{  Limbo support                            
2253
2254 // ----------------------------------------------------------------------------
2255 // Limbo support. This is basically trivial, an STL map does all the
2256 // right things.
2257 void
2258 CdlToplevelBody::set_limbo_value(CdlValuable valuable)
2259 {
2260     CYG_REPORT_FUNCNAME("CdlToplevel::set_limbo_value");
2261     CYG_REPORT_FUNCARG2XV(this, valuable);
2262     CYG_PRECONDITION_THISC();
2263     CYG_PRECONDITION_CLASSC(valuable);
2264
2265     limbo[valuable->get_name()] = valuable->get_whole_value();
2266
2267     CYG_REPORT_RETURN();
2268 }
2269
2270 bool
2271 CdlToplevelBody::has_limbo_value(std::string name) const
2272 {
2273     CYG_REPORT_FUNCNAMETYPE("CdlToplevel::has_limbo_value", "result %d");
2274     CYG_REPORT_FUNCARG1XV(this);
2275     CYG_PRECONDITION_THISC();
2276     CYG_PRECONDITIONC("" != name);
2277
2278     bool result = false;
2279     if (limbo.find(name) != limbo.end()) {
2280         result = true;
2281     }
2282
2283     CYG_REPORT_RETVAL(result);
2284     return result;
2285 }
2286
2287 CdlValue
2288 CdlToplevelBody::get_limbo_value(std::string name) const
2289 {
2290     CYG_REPORT_FUNCNAME("CdlToplevel::get_limbo_value");
2291     CYG_REPORT_FUNCARG1XV(this);
2292     CYG_PRECONDITION_THISC();
2293     CYG_PRECONDITIONC("" != name);
2294
2295     std::map<std::string,CdlValue>::const_iterator limbo_i = limbo.find(name);
2296     CYG_ASSERTC(limbo_i != limbo.end());
2297
2298     CYG_REPORT_RETURN();
2299     return limbo_i->second;
2300 }
2301
2302 CdlValue
2303 CdlToplevelBody::get_and_remove_limbo_value(std::string name)
2304 {
2305     CYG_REPORT_FUNCNAME("CdlToplevel::get_and_remove_limbo_value");
2306     CYG_REPORT_FUNCARG1XV(this);
2307     CYG_PRECONDITION_THISC();
2308     CYG_PRECONDITIONC("" != name);
2309
2310     std::map<std::string,CdlValue>::iterator limbo_i = limbo.find(name);
2311     CYG_ASSERTC(limbo_i != limbo.end());
2312
2313     CdlValue local_copy = limbo_i->second;
2314     limbo.erase(limbo_i);
2315
2316     CYG_REPORT_RETURN();
2317     return local_copy;
2318 }
2319
2320 void
2321 CdlToplevelBody::clear_limbo()
2322 {
2323     CYG_REPORT_FUNCNAME("CdlToplevel::clear_limbo");
2324     CYG_REPORT_FUNCARG1XV(this);
2325     CYG_PRECONDITION_THISC();
2326
2327     limbo.clear();
2328
2329     CYG_REPORT_RETURN();
2330 }
2331
2332 //}}}
2333 //{{{  Persistence support                      
2334
2335 //{{{  Description                      
2336
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).
2341 //
2342 // However there is a need in the library for some generic savefile support:
2343 //
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.
2350 //
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.
2357 //
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.
2362 //
2363 // These needs are interrelated, and supported by the CdlToplevelBody
2364 // class. The functions of interest are:
2365 //
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.
2370 //
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.
2377 //
2378 // static bool savefile_support_initialized()
2379 //    Has there been a call to initialize_savefile_support() yet?
2380 //
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.
2388 //
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.
2393 //
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.
2401 //
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().
2406 //
2407 // static void get_savefile_subcommands(std::string, std::vector<CdlInterpreterCommandEntry>&)
2408 //    Ditto for subcommands.
2409 //
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
2413 //    be processed.
2414 //
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.
2418 //
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.
2423 //
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.
2427 //
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.
2432 //
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.
2436 //
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.
2443 //
2444 // void save_conflicts(CdlInterpreter, Tcl_Channel, int)
2445 //    Output details of all the conflicts in the current configuration
2446 //
2447 // void save_separator(CdlInterpreter, Tcl_Channel, int)
2448 //    A utility to add a separator line to a savefile. This has to
2449 //    go somewhere....
2450 //
2451 // FIXME: add limbo support
2452
2453 //}}}
2454 //{{{  Statics and initialization       
2455
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;
2460
2461 void
2462 CdlToplevelBody::initialize_savefile_support()
2463 {
2464     CYG_REPORT_FUNCNAME("CdlToplevel::initialize_savefile_support");
2465     CYG_REPORT_FUNCARG1XV(this);
2466     CYG_PRECONDITION_THISC();
2467     
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;
2472
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);
2477
2478     CYG_REPORT_RETURN();
2479 }
2480
2481 bool
2482 CdlToplevelBody::savefile_support_initialized()
2483 {
2484     CYG_REPORT_FUNCNAMETYPE("CdlToplevel::check_savefile_support_initialized", "result %d");
2485
2486     bool result = savefile_commands_initialized;
2487     CYG_REPORT_RETVAL(result);
2488     return result;
2489 }
2490
2491 //}}}
2492 //{{{  Command details                  
2493
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.
2499 //
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.
2504
2505 void
2506 CdlToplevelBody::add_savefile_command(std::string name, CdlSaveCallback save_callback, CdlInterpreterCommand load_command)
2507 {
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);
2512
2513     if (!savefile_commands_initialized) {
2514         this->initialize_savefile_support();
2515     }
2516     
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.");
2522             }
2523             break;
2524         }
2525     }
2526     if (cmd_i == savefile_commands.end()) {
2527         CdlSavefileCommand cmd;
2528         cmd.name                = name;
2529         cmd.save_callback       = save_callback;
2530         cmd.load_command        = load_command;
2531         savefile_commands.push_back(cmd);
2532
2533         std::vector<CdlSavefileCommand> subcommands;
2534         savefile_subcommands[name] = subcommands;
2535     }
2536
2537     CYG_REPORT_RETURN();
2538 }
2539
2540 // Add a new subcommand for a given command. The command should have been
2541 // defined already.
2542 void
2543 CdlToplevelBody::add_savefile_subcommand(std::string cmd, std::string subcommand, CdlSaveCallback save_callback,
2544                                          CdlInterpreterCommand load_command)
2545 {
2546     CYG_REPORT_FUNCNAME("CdlToplevel::add_savefile_subcommand");
2547     CYG_REPORT_FUNCARG3XV(this, save_callback, load_command);
2548     CYG_PRECONDITION_THISC();
2549     
2550     if (!savefile_commands_initialized) {
2551         this->initialize_savefile_support();
2552     }
2553     
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) {
2557             break;
2558         }
2559     }
2560     CYG_ASSERTC(cmd_i != savefile_commands.end());
2561
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.");
2566             }
2567         }
2568     }
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);
2575     }
2576
2577     CYG_REPORT_RETURN();
2578 }
2579
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.
2586
2587 void
2588 CdlToplevelBody::save_command_details(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
2589 {
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);
2595
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.
2599     //
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
2603     // be overwritten.
2604
2605     if (!minimal) {
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\
2610 # the savefile.\n\
2611 \n");
2612     }
2613
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";
2619
2620     std::vector<CdlSavefileCommand>::const_iterator cmd_i, cmd_j;
2621     std::vector<std::string>::const_iterator cmd_k, cmd_l;
2622     
2623     for (cmd_i = savefile_commands.begin(); cmd_i != savefile_commands.end(); cmd_i++) {
2624         savefile_data += "cdl_savefile_command " + cmd_i->name + " ";
2625         
2626         if ((0 == savefile_subcommands[cmd_i->name].size()) &&
2627             (0 == this->unsupported_savefile_subcommands[cmd_i->name].size())) {
2628             
2629             savefile_data += "{};\n";
2630             
2631         } else {
2632             
2633             savefile_data += "{";
2634             for (cmd_j = savefile_subcommands[cmd_i->name].begin();
2635                  cmd_j != savefile_subcommands[cmd_i->name].end();
2636                  cmd_j++) {
2637                 
2638                 savefile_data += " " + cmd_j->name;
2639             }
2640             for (cmd_l = this->unsupported_savefile_subcommands[cmd_i->name].begin();
2641                  cmd_l != this->unsupported_savefile_subcommands[cmd_i->name].end();
2642                  cmd_l++) {
2643
2644                 savefile_data += " " + *cmd_l;
2645             }
2646             savefile_data += " };\n";
2647         }
2648     }
2649     for (cmd_k = this->unsupported_savefile_commands.begin();
2650          cmd_k != this->unsupported_savefile_commands.end();
2651          cmd_k++) {
2652         savefile_data += "cdl_savefile_command " + *cmd_k + " ";
2653         if (0 == this->unsupported_savefile_subcommands[*cmd_k].size()) {
2654             
2655             savefile_data += "{};\n";
2656             
2657         } else {
2658
2659             savefile_data += "{";
2660             for (cmd_l = this->unsupported_savefile_subcommands[*cmd_k].begin();
2661                  cmd_l != this->unsupported_savefile_subcommands[*cmd_k].end();
2662                  cmd_l++) {
2663
2664                 savefile_data += " " + *cmd_l;
2665             }
2666             savefile_data += " };\n";
2667         }
2668     }
2669     savefile_data += "\n";
2670
2671     interp->write_data(chan, savefile_data);
2672
2673     CYG_REPORT_RETURN();
2674 }
2675
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().
2682
2683 void
2684 CdlToplevelBody::get_savefile_commands(std::vector<CdlInterpreterCommandEntry>& cmds)
2685 {
2686     CYG_REPORT_FUNCNAME("CdlToplevel::get_savefile_commands");
2687     CYG_REPORT_FUNCARG1XV(this);
2688     CYG_PRECONDITION_THISC();
2689
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);
2698     }
2699
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());
2704
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);
2709     
2710     CYG_REPORT_RETURN();
2711 }
2712
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.
2716 void
2717 CdlToplevelBody::get_savefile_subcommands(std::string main_command, std::vector<CdlInterpreterCommandEntry>& cmds)
2718 {
2719     CYG_REPORT_FUNCNAME("CdlToplevel::get_savefile_subcommands");
2720     CYG_REPORT_FUNCARG1XV(this);
2721     CYG_PRECONDITION_THISC();
2722
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();
2727          cmd_i++) {
2728
2729         local_cmd.name = cmd_i->name.c_str();
2730         local_cmd.command = cmd_i->load_command;
2731         cmds.push_back(local_cmd);
2732     }
2733
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();
2737          cmd_j++) {
2738
2739         local_cmd.name = cmd_j->c_str();
2740         local_cmd.command = &savefile_handle_unsupported;
2741         cmds.push_back(local_cmd);
2742     }
2743
2744     CYG_REPORT_RETURN();
2745 }
2746
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.
2751 int
2752 CdlToplevelBody::savefile_handle_command(CdlInterpreter interp, int argc, const char* argv[])
2753 {
2754     CYG_REPORT_FUNCNAME("CdlToplevel::savefile_handle_command");
2755     CYG_REPORT_FUNCARG2XV(interp, argc);
2756     CYG_PRECONDITION_CLASSC(interp);
2757     
2758     CdlToplevel toplevel = interp->get_toplevel();
2759     CYG_ASSERT_CLASSC(toplevel);
2760     CYG_ASSERTC(toplevel->savefile_commands_initialized);
2761
2762     if (1 == argc) {
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] + " }");
2771     }
2772
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;
2777         
2778     if (1 != argc) {
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;
2783                 break;
2784             }
2785         }
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();
2790                  cmd_j++) {
2791                 if (*cmd_j == argv[1]) {
2792                     break;
2793                 }
2794             }
2795             if (cmd_j == toplevel->unsupported_savefile_commands.end()) {
2796                 toplevel->unsupported_savefile_commands.push_back(argv[1]);
2797             }
2798         }
2799     }
2800
2801     // Now take care of all the subcommands.
2802     if (2 != argc) {
2803
2804         int          list_count = 0;
2805         const char** list_entries = 0;
2806
2807         try {
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] + "'.");
2811             }
2812
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();
2818                          cmd_i++) {
2819
2820                         if (cmd_i->name == list_entries[i]) {
2821                             known_subcommand = true;
2822                         }
2823                     }
2824                 }
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();
2828                          cmd_j++) {
2829
2830                         if (*cmd_j == list_entries[i]) {
2831                             known_subcommand = true;
2832                             break;
2833                         }
2834                     }
2835                 }
2836                 if (!known_subcommand) {
2837                     toplevel->unsupported_savefile_subcommands[argv[1]].push_back(list_entries[i]);
2838                 }
2839                 
2840             }
2841
2842             if (0 != list_entries) {
2843                 Tcl_Free((char *)list_entries);
2844             }
2845             
2846         } catch(...) {
2847             if (0 != list_entries) {
2848                 Tcl_Free((char *)list_entries);
2849             }
2850             throw;
2851         }
2852     }
2853     
2854     return TCL_OK;
2855 }
2856
2857 //}}}
2858 //{{{  handle_unsupported()             
2859
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.
2864 //
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.
2870 //
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.
2874 int
2875 CdlToplevelBody::savefile_handle_unsupported(CdlInterpreter interp, int argc, const char* argv[])
2876 {
2877     CYG_REPORT_FUNCNAME("CdlNode::savefile_handle_unsupported");
2878     CYG_REPORT_FUNCARG2XV(interp, argc);
2879     CYG_ASSERT_CLASSC(interp);
2880
2881     CdlToplevel toplevel = interp->get_toplevel();
2882     CYG_ASSERT_CLASSC(toplevel);
2883     CdlNode node = interp->get_node();
2884     CYG_ASSERT_ZERO_OR_CLASSC(node);
2885
2886     std::string tmp = CdlInterpreterBody::quote(argv[0]);
2887     for (int i = 1; i < argc; i++) {
2888         tmp = tmp + " " + CdlInterpreterBody::quote(argv[i]);
2889     }
2890     // Unknown commands may occur at the toplevel or inside
2891     // e.g. a cdl_option body. Toplevels are also nodes.
2892     if (0 == node) {
2893         toplevel->unsupported_savefile_toplevel_strings.push_back(tmp);
2894     } else {
2895         node->unsupported_savefile_strings.push_back(tmp);
2896     }
2897
2898     return TCL_OK;
2899 }
2900
2901 //}}}
2902 //{{{  save_unsupported()               
2903
2904 // ----------------------------------------------------------------------------
2905 // This code deals with any toplevel data present in the original save
2906 // file that was not recognised.
2907 void
2908 CdlToplevelBody::save_unsupported_commands(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
2909 {
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);
2915
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();
2920          str_i++) {
2921         data += *str_i + " ;\n";
2922     }
2923     interp->write_data(chan, data);
2924
2925     CYG_UNUSED_PARAM(bool, minimal);
2926     CYG_REPORT_RETURN();
2927 }
2928
2929 //}}}
2930 //{{{  handle_unknown()                 
2931
2932 // ----------------------------------------------------------------------------
2933
2934 int
2935 CdlToplevelBody::savefile_handle_unknown(CdlInterpreter interp, int argc, const char* argv[])
2936 {
2937     CYG_REPORT_FUNCNAME("CdlToplevel::savefile_handle_unknown");
2938     CYG_REPORT_FUNCARG2XV(interp, argc);
2939     CYG_PRECONDITION_CLASSC(interp);
2940
2941     CdlParse::report_error(interp, "", std::string("Unknown command `") + argv[1] + "'.");
2942     
2943     CYG_UNUSED_PARAM(int, argc);
2944     return TCL_OK;
2945 }
2946
2947 //}}}
2948 //{{{  versioning                       
2949
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;
2955
2956 cdl_int
2957 CdlToplevelBody::get_library_savefile_version()
2958 {
2959     CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_library_savefile_version", "result %ld");
2960
2961     cdl_int result = savefile_version;
2962     CYG_REPORT_RETVAL((long) result);
2963     return result;
2964 }
2965
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.
2969 int
2970 CdlToplevelBody::savefile_handle_version(CdlInterpreter interp, int argc, const char* argv[])
2971 {
2972     CYG_REPORT_FUNCNAME("CdlToplevel::savefile_handle_version");
2973     CYG_REPORT_FUNCARG2XV(interp, argc);
2974     CYG_PRECONDITION_CLASSC(interp);
2975
2976     if (1 == argc) {
2977         CdlParse::report_warning(interp, "", "Expecting one argument to cdl_savefile_version");
2978     } else {
2979         if (2 != argc) {
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.");
2983         }
2984         cdl_int tmp;
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");
2988         } else {
2989             // Store the data in a Tcl variable. This is at least as convenient
2990             // as assoc data.
2991             interp->set_variable("cdl_savefile_version", argv[1]);
2992         }
2993     }
2994
2995     return TCL_OK;
2996 }
2997
2998 cdl_int
2999 CdlToplevelBody::get_savefile_version(CdlInterpreter interp)
3000 {
3001     CYG_REPORT_FUNCNAMETYPE("CdlToplevel::get_savefile_version", "result %ld");
3002     CYG_REPORT_FUNCARG1XV(interp);
3003     CYG_PRECONDITION_CLASSC(interp);
3004     
3005     cdl_int result = 0;
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 + "'");
3010         }
3011     }
3012
3013     CYG_REPORT_RETVAL((long) result);
3014     return result;
3015 }
3016
3017 //}}}
3018 //{{{  conflicts                        
3019
3020 // ----------------------------------------------------------------------------
3021 void
3022 CdlToplevelBody::save_conflicts(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
3023 {
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);
3029
3030     // For now only comments are generated here, so in a minimal save
3031     // there is no need for any of this data
3032     if (!minimal) {
3033         std::string data = "";
3034         if (0 == conflicts.size()) {
3035             data += "# There are no conflicts.\n";
3036         } else {
3037             std::string tmp;
3038             Cdl::integer_to_string((cdl_int) this->conflicts.size(), tmp);
3039             data += "# There are " + tmp + " conflicts.\n";
3040
3041             std::list<CdlConflict>::const_iterator conf_i;
3042             for (conf_i = this->conflicts.begin(); conf_i != this->conflicts.end(); conf_i++) {
3043                 data += "#\n";
3044
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";
3051             }
3052             data += '\n';
3053         }
3054         data += '\n';
3055
3056         interp->write_data(chan, data);
3057     }
3058     
3059     CYG_REPORT_RETURN();
3060 }
3061
3062 //}}}
3063 //{{{  save_separator()                 
3064
3065 // ----------------------------------------------------------------------------
3066 void
3067 CdlToplevelBody::save_separator(CdlInterpreter interp, Tcl_Channel chan, std::string msg, bool minimal)
3068 {
3069     CYG_REPORT_FUNCNAME("CdlToplevel::save_separator");
3070     CYG_REPORT_FUNCARG1XV(interp);
3071     CYG_PRECONDITION_CLASSC(interp);
3072
3073     if (!minimal) {
3074         std::string data = "# ---- " + msg + ' ';
3075         if (72 > data.size()) {
3076             data += std::string(72 - data.size(), '-');
3077         }
3078         data += '\n';
3079         interp->write_data(chan, data);
3080     }
3081     
3082     CYG_REPORT_RETURN();
3083 }
3084
3085 //}}}
3086
3087 //}}}
3088 //{{{  check_this()                             
3089
3090 // ----------------------------------------------------------------------------
3091 bool
3092 CdlToplevelBody::check_this(cyg_assert_class_zeal zeal) const
3093 {
3094     if (CdlToplevelBody_Magic != cdltoplevelbody_cookie) {
3095         return false;
3096     }
3097     CYGDBG_MEMLEAK_CHECKTHIS();
3098
3099     if ((zeal == cyg_extreme) || (zeal == cyg_thorough)) {
3100         if (!interp->check_this(cyg_quick)) {
3101             return false;
3102         }
3103         if ((0 == orphans) || !orphans->check_this(cyg_quick)) {
3104             return false;
3105         }
3106         if (orphans != *contents.begin()) {
3107             return false;
3108         }
3109         if ((0 != transaction) && !transaction->check_this(cyg_quick)) {
3110             return false;
3111         }
3112     }
3113
3114     return CdlContainerBody::check_this(zeal);
3115 }
3116
3117 //}}}
3118
3119 //}}}
3120 //{{{  CdlUserVisiblebody               
3121
3122 //{{{  Basics                           
3123
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.
3129
3130 CdlUserVisibleBody::CdlUserVisibleBody()
3131 {
3132     CYG_REPORT_FUNCNAME("CdlUserVisible:: default constructor");
3133     CYG_REPORT_FUNCARG1XV(this);
3134
3135     cdluservisiblebody_cookie  = CdlUserVisibleBody_Magic;
3136     CYGDBG_MEMLEAK_CONSTRUCTOR();
3137     
3138     CYG_POSTCONDITION_THISC();
3139     CYG_REPORT_RETURN();
3140 }
3141
3142 CdlUserVisibleBody::~CdlUserVisibleBody()
3143 {
3144     CYG_REPORT_FUNCNAME("CdlUserVisible:: destructor");
3145     CYG_REPORT_FUNCARG1XV(this);
3146     CYG_PRECONDITION_THISC();
3147
3148     cdluservisiblebody_cookie = CdlUserVisibleBody_Invalid;
3149     CYGDBG_MEMLEAK_DESTRUCTOR();
3150     
3151     CYG_REPORT_RETURN();
3152 }
3153
3154 std::string
3155 CdlUserVisibleBody::get_class_name() const
3156 {
3157     CYG_REPORT_FUNCNAME("CdlUserVisible::get_class_name");
3158     CYG_PRECONDITION_THISC();
3159     CYG_REPORT_RETURN();
3160     return "uservisible";
3161 }
3162
3163 bool
3164 CdlUserVisibleBody::check_this(cyg_assert_class_zeal zeal) const
3165 {
3166     if (CdlUserVisibleBody_Magic != cdluservisiblebody_cookie) {
3167         return false;
3168     }
3169     CYGDBG_MEMLEAK_CHECKTHIS();
3170     return CdlNodeBody::check_this(zeal);
3171 }
3172
3173 //}}}
3174 //{{{  Extracting information           
3175
3176 // ----------------------------------------------------------------------------
3177 // Extracting the information.
3178
3179 std::string
3180 CdlUserVisibleBody::get_display() const
3181 {
3182     CYG_REPORT_FUNCNAME("CdlUserVisible::get_display");
3183     CYG_REPORT_FUNCARG1XV(this);
3184     CYG_PRECONDITION_THISC();
3185
3186     std::string result = "";
3187     CdlProperty property = get_property(CdlPropertyId_Display);
3188     if (0 != property) {
3189         
3190         CdlProperty_String string_property = dynamic_cast<CdlProperty_String>(property);
3191         CYG_ASSERTC(0 != string_property);
3192
3193         result = string_property->get_string();
3194     }
3195
3196     CYG_REPORT_RETURN();
3197     return result;
3198 }
3199
3200 std::string
3201 CdlUserVisibleBody::get_description() const
3202 {
3203     CYG_REPORT_FUNCNAME("CdlUserVisible::get_description");
3204     CYG_REPORT_FUNCARG1XV(this);
3205     CYG_PRECONDITION_THISC();
3206
3207     std::string result = "";
3208     CdlProperty property = get_property(CdlPropertyId_Description);
3209     if (0 != property) {
3210         
3211         CdlProperty_String string_property = dynamic_cast<CdlProperty_String>(property);
3212         CYG_ASSERTC(0 != string_property);
3213
3214         result = string_property->get_string();
3215     }
3216
3217     CYG_REPORT_RETURN();
3218     return result;
3219 }
3220
3221 std::string
3222 CdlUserVisibleBody::get_doc() const
3223 {
3224     CYG_REPORT_FUNCNAME("CdlUserVisible::get_doc");
3225     CYG_REPORT_FUNCARG1XV(this);
3226     CYG_PRECONDITION_THISC();
3227
3228     std::string result = "";
3229     CdlProperty property = get_property(CdlPropertyId_Doc);
3230     if (0 != property) {
3231         
3232         CdlProperty_String string_property = dynamic_cast<CdlProperty_String>(property);
3233         CYG_ASSERTC(0 != string_property);
3234
3235         result = string_property->get_string();
3236     }
3237
3238     CYG_REPORT_RETURN();
3239     return result;
3240 }
3241
3242 std::string
3243 CdlUserVisibleBody::get_doc_url() const
3244 {
3245     CYG_REPORT_FUNCNAME("CdlUserVisible::get_doc_url");
3246     CYG_REPORT_FUNCARG1XV(this);
3247     CYG_PRECONDITION_THISC();
3248
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);
3255     }
3256
3257     CYG_REPORT_RETURN();
3258     return result;
3259 }
3260
3261 //}}}
3262 //{{{  Parsing                          
3263
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.
3269
3270 void
3271 CdlUserVisibleBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
3272 {
3273     CYG_REPORT_FUNCNAME("CdlUserVisible::add_property_parsers");
3274
3275     static CdlInterpreterCommandEntry commands[] =
3276     {
3277         CdlInterpreterCommandEntry("display",     &parse_display),
3278         CdlInterpreterCommandEntry("description", &parse_description),
3279         CdlInterpreterCommandEntry("doc",         &parse_doc),
3280         CdlInterpreterCommandEntry("",            0)
3281     };
3282
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");
3289                 }
3290                 break;
3291             }
3292         }
3293         if (j == parsers.end()) {
3294             parsers.push_back(commands[i]);
3295         }
3296     }
3297     CdlNodeBody::add_property_parsers(parsers);
3298     
3299     CYG_REPORT_RETURN();
3300 }
3301
3302 void
3303 CdlUserVisibleBody::check_properties(CdlInterpreter interp)
3304 {
3305     CYG_REPORT_FUNCNAME("CdlUserVisible::check_properties");
3306     CYG_REPORT_FUNCARG2XV(this, interp);
3307     CYG_PRECONDITION_THISC();
3308     CYG_PRECONDITION_CLASSC(interp);
3309
3310     if (count_properties(CdlPropertyId_Display) > 1) {
3311         CdlParse::report_error(interp, "", "There should be at most one display property.");
3312     }
3313     if (count_properties(CdlPropertyId_Description) > 1) {
3314         CdlParse::report_error(interp, "", "There should be at most one description property.");
3315     }
3316     if (count_properties(CdlPropertyId_Doc) > 1) {
3317         CdlParse::report_error(interp, "", "There should be at most one doc property.");
3318     }
3319     
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.
3322
3323     CdlNodeBody::check_properties(interp);
3324     
3325     CYG_REPORT_RETURN();
3326 }
3327
3328 // ----------------------------------------------------------------------------
3329 // Syntax: description <string>
3330
3331 int
3332 CdlUserVisibleBody::parse_description(CdlInterpreter interp, int argc, const char* argv[])
3333 {
3334     CYG_REPORT_FUNCNAMETYPE("parse_description", "result %d");
3335
3336     int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Description, 0, 0);
3337     
3338     CYG_REPORT_RETVAL(result);
3339     return result;
3340 }
3341
3342
3343 // ----------------------------------------------------------------------------
3344 // Syntax: display <short description>
3345
3346 int
3347 CdlUserVisibleBody::parse_display(CdlInterpreter interp, int argc, const char* argv[])
3348 {
3349     CYG_REPORT_FUNCNAMETYPE("parse_display", "result %d");
3350
3351     int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Display, 0, 0);
3352     
3353     CYG_REPORT_RETVAL(result);
3354     return result;
3355 }
3356
3357 // ----------------------------------------------------------------------------
3358 // Syntax: doc <url>
3359
3360 int
3361 CdlUserVisibleBody::parse_doc(CdlInterpreter interp, int argc, const char* argv[])
3362 {
3363     CYG_REPORT_FUNCNAMETYPE("parse_doc", "result %d");
3364     
3365     int result = CdlParse::parse_string_property(interp, argc, argv, CdlPropertyId_Doc, 0, 0);
3366     
3367     CYG_REPORT_RETVAL(result);
3368     return result;
3369 }
3370
3371 //}}}
3372 //{{{  Persistence                      
3373
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.
3378 //
3379 // The intention is that the UserVisible information appears immediately
3380 // above the option/component/whatever definition, e.g.:
3381 // # <display string.
3382 // # doc <URL>
3383 // # <description
3384 // # ...>
3385 // #
3386
3387 void
3388 CdlUserVisibleBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
3389 {
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);
3395
3396     if (!minimal) {
3397         std::string data = "";
3398         std::string display = get_display();
3399         if ("" != display) {
3400             data = std::string("# ") + display + "\n";
3401         }
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
3406         // control.
3407         std::string doc = get_doc();
3408         if ("" != doc) {
3409             data += "# doc: " + doc + "\n";
3410         }
3411         std::string description = get_description();
3412         if ("" != description) {
3413             unsigned int i = 0;
3414             while (i < description.size()) {
3415                 data += "# ";
3416                 while ((i < description.size()) && isspace(description[i])) {
3417                     i++;
3418                 }
3419                 while ((i < description.size()) && ('\n' != description[i])) {
3420                     data += description[i++];
3421                 }
3422                 data += '\n';
3423             }
3424         }
3425         data += "#\n";
3426         
3427         interp->write_data(chan, data);
3428     }
3429     
3430     CYG_REPORT_RETURN();
3431 }
3432
3433 //}}}
3434
3435 //}}}
3436 //{{{  CdlParentableBody                
3437
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.
3442
3443 CdlParentableBody::CdlParentableBody()
3444 {
3445     CYG_REPORT_FUNCNAME("CdlParentable:: default constructor");
3446     CYG_REPORT_FUNCARG1XV(this);
3447
3448     change_parent_save_position = -1;
3449     cdlparentablebody_cookie  = CdlParentableBody_Magic;
3450     CYGDBG_MEMLEAK_CONSTRUCTOR();
3451     
3452     CYG_POSTCONDITION_THISC();
3453     CYG_REPORT_RETURN();
3454 }
3455
3456 CdlParentableBody::~CdlParentableBody()
3457 {
3458     CYG_REPORT_FUNCNAME("CdlParentable:: destructor");
3459     CYG_REPORT_FUNCARG1XV(this);
3460     CYG_PRECONDITION_THISC();
3461
3462     cdlparentablebody_cookie = CdlParentableBody_Invalid;
3463     CYGDBG_MEMLEAK_DESTRUCTOR();
3464     
3465     CYG_REPORT_RETURN();
3466 }
3467
3468 // ----------------------------------------------------------------------------
3469
3470 std::string
3471 CdlParentableBody::get_class_name() const
3472 {
3473     CYG_REPORT_FUNCNAME("CdlParentable::get_class_name");
3474     CYG_PRECONDITION_THISC();
3475     CYG_REPORT_RETURN();
3476     return "parentable";
3477 }
3478
3479 // ----------------------------------------------------------------------------
3480
3481 bool
3482 CdlParentableBody::check_this(cyg_assert_class_zeal zeal) const
3483 {
3484     if (CdlParentableBody_Magic != cdlparentablebody_cookie) {
3485         return false;
3486     }
3487     CYGDBG_MEMLEAK_CHECKTHIS();
3488     return CdlNodeBody::check_this(zeal);
3489 }
3490
3491 // ----------------------------------------------------------------------------
3492 // Parsing support. There is just one property parser to be added.
3493
3494 void
3495 CdlParentableBody::add_property_parsers(std::vector<CdlInterpreterCommandEntry>& parsers)
3496 {
3497     CYG_REPORT_FUNCNAME("CdlParentable::add_property_parsers");
3498
3499     static CdlInterpreterCommandEntry commands[] =
3500     {
3501         CdlInterpreterCommandEntry("parent", &CdlParentableBody::parse_parent),
3502         CdlInterpreterCommandEntry("",       0)
3503     };
3504
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");
3511                 }
3512                 break;
3513             }
3514         }
3515         if (j == parsers.end()) {
3516             parsers.push_back(commands[i]);
3517         }
3518     }
3519     CdlNodeBody::add_property_parsers(parsers);
3520     
3521     CYG_REPORT_RETURN();
3522 }
3523
3524 void
3525 CdlParentableBody::check_properties(CdlInterpreter interp)
3526 {
3527     CYG_REPORT_FUNCNAME("CdlParentable::check_properties");
3528     CYG_REPORT_FUNCARG2XV(this, interp);
3529     CYG_PRECONDITION_THISC();
3530     CYG_PRECONDITION_CLASSC(interp);
3531
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.");
3535         }
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.");
3540         }
3541     }
3542
3543     CdlNodeBody::check_properties(interp);
3544     
3545     CYG_REPORT_RETURN();
3546 }
3547
3548 // ----------------------------------------------------------------------------
3549 // Syntax:: parent <reference to container>
3550
3551 void
3552 CdlParentableBody::update_handler(CdlTransaction transaction, CdlNode source, CdlProperty prop, CdlNode dest, CdlUpdate change)
3553 {
3554     CYG_REPORT_FUNCNAME("CdlParentable::update_handler");
3555     CYG_PRECONDITION_CLASSC(source);
3556     CYG_PRECONDITION_ZERO_OR_CLASSC(dest);
3557
3558     // Value and activity updates are of no interest.
3559     if ((CdlUpdate_ValueChange == change) || (CdlUpdate_ActiveChange == change)) {
3560         CYG_REPORT_RETURN();
3561         return;
3562     }
3563
3564     // Ditto for the second stage Init.
3565     if (CdlUpdate_Init == change) {
3566         CYG_REPORT_RETURN();
3567         return;
3568     }
3569     
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);
3583         
3584         if (0 != loadable) {
3585             toplevel->change_parent(owner, source->get_parent(), toplevel, source);
3586         } else {
3587             toplevel->change_parent(owner, source->get_parent(), owner, source);
3588         }
3589         
3590         CYG_REPORT_RETURN();
3591         return;
3592     }
3593
3594     // We should have:
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));