]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - tools/src/libcdl/config.cxx
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / tools / src / libcdl / config.cxx
1 //{{{  Banner                                           
2
3 //============================================================================
4 //
5 //     config.cxx
6 //
7 //     Implementation of the CdlConfiguration class
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/03/06
41 // Version:     0.02
42 //
43 //####DESCRIPTIONEND####
44 //============================================================================
45
46 //}}}
47 //{{{  #include's                                       
48
49 // ----------------------------------------------------------------------------
50 #include "cdlconfig.h"
51
52 // Get the infrastructure types, assertions, tracing and similar
53 // facilities.
54 #include <cyg/infra/cyg_ass.h>
55 #include <cyg/infra/cyg_trac.h>
56
57 // <cdl.hxx> defines everything implemented in this module.
58 // It implicitly supplies <string>, <vector> and <map> because
59 // the class definitions rely on these headers.
60 #include <cdl.hxx>
61
62 //}}}
63
64 //{{{  CdlConfiguration constants and statics           
65
66 // ----------------------------------------------------------------------------
67 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlConfigurationBody);
68
69 //}}}
70 //{{{  CdlConfiguration:: creation                      
71
72 // ----------------------------------------------------------------------------
73 // The toplevel class will take care of just about everything.
74
75 CdlConfigurationBody::CdlConfigurationBody(std::string name, CdlPackagesDatabase db, CdlInterpreter interp)
76     : CdlNodeBody(name),
77       CdlToplevelBody(interp, db->get_component_repository())
78 {
79     CYG_REPORT_FUNCNAME("CdlConfiguration:: constructor");
80     CYG_REPORT_FUNCARG1XV(this);
81
82     current_hardware    = "";
83     current_template    = "";
84     database            = db;
85     save_file           = "";
86     description         = "";
87     
88     cdlconfigurationbody_cookie = CdlConfigurationBody_Magic;
89     CYGDBG_MEMLEAK_CONSTRUCTOR();
90     
91     CYG_POSTCONDITION_THISC();
92     CYG_REPORT_RETURN();
93 }
94
95 // ----------------------------------------------------------------------------
96 // The exported interface
97
98 CdlConfiguration
99 CdlConfigurationBody::make(std::string name, CdlPackagesDatabase db, CdlInterpreter interp)
100 {
101     CYG_REPORT_FUNCNAMETYPE("CdlConfiguration::make", "result %p");
102     CYG_REPORT_FUNCARG2XV(db, interp);
103     CYG_PRECONDITIONC("" != name);
104     CYG_PRECONDITION_CLASSC(db);
105     CYG_PRECONDITION_CLASSC(interp);
106
107     CdlConfiguration result = new CdlConfigurationBody(name, db, interp);
108     CYG_REPORT_RETVAL(result);
109     return result;
110 }
111
112 //}}}
113 //{{{  CdlConfiguration:: destructor                    
114
115 // ----------------------------------------------------------------------------
116 CdlConfigurationBody::~CdlConfigurationBody()
117 {
118     CYG_REPORT_FUNCNAME("CdlConfiguration:: default destructor");
119     CYG_REPORT_FUNCARG1("this %p", this);
120     CYG_PRECONDITION_THISC();
121
122     // Removing all the packages has to happen here, and in the
123     // context of a transaction. The main reason is the extensive
124     // use of dynamic casts, after this destructor returns
125     // any dynamic casts for this configuration will fail.
126     //
127     // Arguably some of the unloads should happen by clearing
128     // the hardware and template (assuming those are currently
129     // set). In practice that would not really gain anything.
130     //
131     // Unloading the individual packages is a bit more expensive
132     // than it should be, since lots of care is taken to keep
133     // remaining packages consistent and then those get unloaded
134     // as well. However it is the safe approach.
135     CdlLocalTransaction transaction(this);
136     const std::vector<CdlLoadable>& loadables = this->get_loadables();
137     for (int i = loadables.size() - 1; i >= 0; i--) {
138         CdlPackage pkg = dynamic_cast<CdlPackage>(loadables[i]);
139         if (0 != pkg) {
140             this->unload_package(transaction.get(), pkg);
141         }
142     }
143     transaction.propagate();
144     transaction.commit();
145     
146     cdlconfigurationbody_cookie = CdlConfigurationBody_Invalid;
147     current_hardware            = "";
148     current_template            = "";
149     database                    = 0;
150     save_file                   = "";
151     
152     CYGDBG_MEMLEAK_DESTRUCTOR();
153     
154     CYG_REPORT_RETURN();
155 }
156
157 //}}}
158 //{{{  CdlConfiguration::check_this()                   
159
160 // ----------------------------------------------------------------------------
161 // There is very little information associated with a configuration.
162
163 bool
164 CdlConfigurationBody::check_this(cyg_assert_class_zeal zeal) const
165 {
166     if (CdlConfigurationBody_Magic != cdlconfigurationbody_cookie) {
167         return false;
168     }
169     CYGDBG_MEMLEAK_CHECKTHIS();
170
171     switch(zeal) {
172       case cyg_system_test      :
173       case cyg_extreme          :
174           if ((0 == database) || !database->check_this(cyg_quick)) {
175               return false;
176           }
177       case cyg_thorough         :
178           if (("" != current_hardware) && !database->is_known_target(current_hardware)) {
179               return false;
180           }
181           if (("" != current_template) && !database->is_known_template(current_template)) {
182               return false;
183           }
184       case cyg_quick            :
185           if (0 == database) {
186               return false;
187           }
188       case cyg_trivial          :
189       case cyg_none             :
190       default                   :
191           break;
192     }
193
194     return CdlNodeBody::check_this(zeal) && CdlContainerBody::check_this(zeal) && CdlToplevelBody::check_this(zeal);
195 }
196
197 //}}}
198 //{{{  CdlConfiguration:: basic info                    
199
200 // ----------------------------------------------------------------------------
201 // Provide ready access to configuration-specific data.
202
203 CdlPackagesDatabase
204 CdlConfigurationBody::get_database() const
205 {
206     CYG_REPORT_FUNCNAMETYPE("CdlConfiguration::get_database", "result %p");
207     CYG_REPORT_FUNCARG1XV(this);
208     CYG_PRECONDITION_THISC();
209
210     CYG_REPORT_RETVAL(database);
211     return database;
212 }
213
214 std::string
215 CdlConfigurationBody::get_hardware() const
216 {
217     CYG_REPORT_FUNCNAME("CdlConfiguration::get_hardware");
218     CYG_REPORT_FUNCARG1XV(this);
219     CYG_PRECONDITION_THISC();
220
221     CYG_REPORT_RETURN();
222     return current_hardware;
223 }
224
225 void
226 CdlConfigurationBody::set_hardware_name(std::string new_name)
227 {
228     CYG_REPORT_FUNCNAME("CdlConfiguration::set_hardware_name");
229     CYG_REPORT_FUNCARG1XV(this);
230     CYG_PRECONDITION_THISC();
231
232     current_hardware = new_name;
233     
234     CYG_REPORT_RETURN();
235 }
236
237 std::string
238 CdlConfigurationBody::get_template() const
239 {
240     CYG_REPORT_FUNCNAME("CdlConfiguration::get_template");
241     CYG_REPORT_FUNCARG1XV(this);
242     CYG_PRECONDITION_THISC();
243
244     CYG_REPORT_RETURN();
245     return current_template;
246 }
247
248 void
249 CdlConfigurationBody::set_template_name(std::string new_name)
250 {
251     CYG_REPORT_FUNCNAME("CdlConfiguration::set_template_name");
252     CYG_REPORT_FUNCARG1XV(this);
253     CYG_PRECONDITION_THISC();
254
255     current_template = new_name;
256     
257     CYG_REPORT_RETURN();
258 }
259
260 std::string
261 CdlConfigurationBody::get_save_file() const
262 {
263     CYG_REPORT_FUNCNAME("CdlConfiguration::get_save_file");
264     CYG_REPORT_FUNCARG1XV(this);
265     CYG_PRECONDITION_THISC();
266
267     CYG_REPORT_RETURN();
268     return save_file;
269 }
270
271 // ----------------------------------------------------------------------------
272
273 std::string
274 CdlConfigurationBody::get_class_name() const
275 {
276     CYG_REPORT_FUNCNAME("CdlConfiguration::get_class_name");
277     CYG_PRECONDITION_THISC();
278     CYG_REPORT_RETURN();
279     return "CdlConfiguration";
280 }
281
282 //}}}
283 //{{{  Load and unload operations - wrappers            
284
285 // ----------------------------------------------------------------------------
286 // These members are basically wrappers for the functions that do the
287 // real work. They do things like running the real functions inside
288 // a newly created transaction.
289
290 void
291 CdlConfigurationBody::load_package(std::string name, std::string version,
292                                    CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
293 {
294     CYG_REPORT_FUNCNAME("CdlConfiguration::load_package");
295     CYG_REPORT_FUNCARG1XV(this);
296     CYG_PRECONDITION_THISC();
297
298     CdlLocalTransaction transaction(this);
299     this->load_package(transaction.get(), name, version, error_fn, warn_fn, limbo);
300     transaction.body();
301
302     CYG_REPORT_RETURN();
303 }
304
305 void
306 CdlConfigurationBody::unload_package(std::string name, bool limbo)
307 {
308     CYG_REPORT_FUNCNAME("CdlConfiguration::unload_package");
309     CYG_REPORT_FUNCARG1XV(this);
310     CYG_PRECONDITION_THISC();
311
312     CdlLocalTransaction transaction(this);
313     this->unload_package(transaction.get(), name, limbo);
314     transaction.body();
315
316     CYG_REPORT_RETURN();
317 }
318
319 void
320 CdlConfigurationBody::unload_package(CdlPackage package, bool limbo)
321 {
322     CYG_REPORT_FUNCNAME("CdlConfiguration::unload_package");
323     CYG_REPORT_FUNCARG1XV(this);
324
325     CdlLocalTransaction transaction(this);
326     this->unload_package(transaction.get(), package, limbo);
327     transaction.body();
328
329     CYG_REPORT_RETURN();
330 }
331
332 void
333 CdlConfigurationBody::unload_package(CdlTransaction transaction, std::string name, bool limbo)
334 {
335     CYG_REPORT_FUNCNAME("CdlConfiguration::unload_package");
336     CYG_REPORT_FUNCARG3XV(this, transaction, limbo);
337     CYG_INVARIANT_THISC(CdlConfigurationBody);
338     CYG_PRECONDITION_CLASSC(transaction);
339
340     CdlNode node = lookup(name);
341     CYG_ASSERTC(0 != node);
342     CdlPackage package  = dynamic_cast<CdlPackage>(node);
343     CYG_ASSERTC(0 != package);
344
345     this->unload_package(transaction, package, limbo);
346
347     CYG_REPORT_RETURN();
348 }
349
350 void
351 CdlConfigurationBody::change_package_version(std::string name, std::string version,
352                                              CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
353 {
354     CYG_REPORT_FUNCNAME("CdlConfiguration::change_package_version");
355     CYG_REPORT_FUNCARG1XV(this);
356
357     CdlLocalTransaction transaction(this);
358     this->change_package_version(transaction.get(), name, version, error_fn, warn_fn, limbo);
359     transaction.body();
360
361     CYG_REPORT_RETURN();
362 }
363
364 void
365 CdlConfigurationBody::change_package_version(CdlPackage package, std::string version,
366                                              CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
367 {
368     CYG_REPORT_FUNCNAME("CdlConfiguration::change_package_version");
369     CYG_REPORT_FUNCARG1XV(this);
370
371     CdlLocalTransaction transaction(this);
372     this->change_package_version(transaction.get(), package, version, error_fn, warn_fn, limbo);
373     transaction.body();
374
375     CYG_REPORT_RETURN();
376 }
377
378 void
379 CdlConfigurationBody::change_package_version(CdlTransaction transaction, std::string name, std::string new_version,
380                                              CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
381 {
382     CYG_REPORT_FUNCNAME("CdlConfiguration::change_package_version");
383     CYG_REPORT_FUNCARG2XV(this, transaction);
384     CYG_PRECONDITION_THISC();
385     CYG_PRECONDITION_CLASSC(transaction);
386     CYG_PRECONDITIONC("" != name);
387
388     CdlPackage package = 0;
389     CdlNode node = this->lookup(name);
390     if (0 != node) {
391         package = dynamic_cast<CdlPackage>(node);
392     }
393     // For now it is illegal to change the version of package that has
394     // not been loaded yet
395     if (0 == package) {
396         throw CdlInputOutputException(std::string("Cannot change version of \"") + name + "\" , this package is not loaded");
397     }
398     CYG_ASSERT_CLASSC(package);
399     
400     this->change_package_version(transaction, package, new_version, error_fn, warn_fn, limbo);
401
402     CYG_REPORT_RETURN();
403 }
404
405 void
406 CdlConfigurationBody::add(std::string filename,
407                           CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn)
408 {
409     CYG_REPORT_FUNCNAME("CdlConfiguration::add");
410     CYG_REPORT_FUNCARG1XV(this);
411
412     CdlLocalTransaction transaction(this);
413     this->add(transaction.get(), filename, error_fn, warn_fn);
414     transaction.body();
415
416     CYG_REPORT_RETURN();
417 }
418
419 void
420 CdlConfigurationBody::set_template(std::string name, std::string version,
421                                    CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
422 {
423     CYG_REPORT_FUNCNAME("CdlConfiguration::set_template");
424     CYG_REPORT_FUNCARG1XV(this);
425
426     CdlLocalTransaction transaction(this);
427     this->set_template(transaction.get(), name, version, error_fn, warn_fn, limbo);
428     transaction.body();
429
430     CYG_REPORT_RETURN();
431 }
432
433 void
434 CdlConfigurationBody::set_template_file(std::string filename,
435                                         CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
436 {
437     CYG_REPORT_FUNCNAME("CdlConfiguration::set_template_file");
438     CYG_REPORT_FUNCARG1XV(this);
439
440     CdlLocalTransaction transaction(this);
441     this->set_template_file(transaction.get(), filename, error_fn, warn_fn, limbo);
442     transaction.body();
443
444     CYG_REPORT_RETURN();
445 }
446
447 void
448 CdlConfigurationBody::set_template(CdlTransaction transaction, std::string template_name, std::string version,
449                                    CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
450 {
451     CYG_REPORT_FUNCNAME("CdlConfiguration::set_template");
452     CYG_REPORT_FUNCARG2XV(this, transaction);
453
454     // Some consistency checks before doing anything damaging
455     if (!this->database->is_known_template(template_name)) {
456         throw CdlInputOutputException("Unknown template " + template_name);
457     }
458     std::string template_filename = this->database->get_template_filename(template_name, version);
459     if ("" == template_filename) {
460         if ("" == version) {
461             throw CdlInputOutputException("There is no template file corresponding to " + template_name);
462         } else {
463             throw CdlInputOutputException("There is no temmplate file corresponding to version "
464                                           + version + " of " + template_name);
465         }
466     }
467
468     // Now use set_template_file() to do the hard work.
469     this->set_template_file(transaction, template_filename, error_fn, warn_fn, limbo);
470     current_template = template_name;
471
472     CYG_REPORT_RETURN();
473 }
474
475 void
476 CdlConfigurationBody::unload_template(bool limbo)
477 {
478     CYG_REPORT_FUNCNAME("CdlConfiguration::unload_template");
479     CYG_REPORT_FUNCARG1XV(this);
480
481     CdlLocalTransaction transaction(this);
482     this->unload_template(transaction.get(), limbo);
483     transaction.body();
484
485     CYG_REPORT_RETURN();
486 }
487
488 void
489 CdlConfigurationBody::set_hardware(std::string name,
490                                    CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
491 {
492     CYG_REPORT_FUNCNAME("CdlConfiguration::set_hardware");
493     CYG_REPORT_FUNCARG1XV(this);
494
495     CdlLocalTransaction transaction(this);
496     this->set_hardware(transaction.get(), name, error_fn, warn_fn, limbo);
497     transaction.body();
498
499     CYG_REPORT_RETURN();
500 }
501
502 void
503 CdlConfigurationBody::unload_hardware(bool limbo)
504 {
505     CYG_REPORT_FUNCNAME("CdlConfiguration::unload_hardware");
506     CYG_REPORT_FUNCARG1XV(this);
507
508     CdlLocalTransaction transaction(this);
509     this->unload_hardware(transaction.get(), limbo);
510     transaction.body();
511
512     CYG_REPORT_RETURN();
513 }
514
515 //}}}
516 //{{{  Load and unload - transaction support            
517
518 // ----------------------------------------------------------------------------
519 // A number of commit/cancel auxiliary classes are needed to allow the
520 // load/unload code to integrate properly with the transaction code.
521
522 class CdlConfiguration_CommitCancelLoad :
523     public CdlTransactionCommitCancelOp
524 {
525     friend class CdlTest;
526
527   public:
528
529     CdlConfiguration_CommitCancelLoad(CdlPackage package_arg)
530         : CdlTransactionCommitCancelOp()
531     {
532         CYG_ASSERT_CLASSC(package_arg);
533         package = package_arg;
534     }
535     ~CdlConfiguration_CommitCancelLoad()
536     {
537         package = 0;
538     }
539     void commit(CdlTransaction transaction)
540     {
541         CYG_ASSERT_CLASSC(package);
542         CdlLoadableBody::transaction_commit_load(transaction, package);
543         package = 0;
544     }
545     void cancel(CdlTransaction transaction)
546     {
547         CYG_ASSERT_CLASSC(package);
548         CdlLoadableBody::transaction_cancel_load(transaction, package);
549         package = 0;
550     }
551     
552   protected:
553     
554   private:
555     CdlConfiguration_CommitCancelLoad()
556     {
557     }
558     CdlPackage  package;
559 };
560
561 class CdlConfiguration_CommitCancelUnload :
562     public CdlTransactionCommitCancelOp
563 {
564     friend class CdlTest;
565
566   public:
567     CdlConfiguration_CommitCancelUnload(CdlPackage package_arg)
568         : CdlTransactionCommitCancelOp()
569     {
570         CYG_ASSERT_CLASSC(package_arg);
571         package = package_arg;
572     }
573     ~CdlConfiguration_CommitCancelUnload()
574     {
575         package = 0;
576     }
577     void commit(CdlTransaction transaction)
578     {
579         CYG_PRECONDITION_CLASSC(package);
580         CdlLoadableBody::transaction_commit_unload(transaction, package);
581         package = 0;
582     }
583     void cancel(CdlTransaction transaction)
584     {
585         CYG_PRECONDITION_CLASSC(package);
586         CdlLoadableBody::transaction_cancel_unload(transaction, package);
587         package = 0;
588     }
589
590   protected:
591
592   private:
593     CdlConfiguration_CommitCancelUnload()
594     {
595     }
596     CdlPackage package;
597 };
598
599 // These utility classes can be used to control the hardware and
600 // template names. If the transaction is cancelled the previous
601 // name gets re-installed
602 class CdlConfiguration_CommitCancelHardwareName :
603     public CdlTransactionCommitCancelOp
604 {
605     friend class CdlTest;
606     
607   public:
608     CdlConfiguration_CommitCancelHardwareName(std::string old_name_arg)
609         : CdlTransactionCommitCancelOp()
610     {
611         old_name = old_name_arg;
612     }
613     ~CdlConfiguration_CommitCancelHardwareName()
614     {
615         old_name = "";
616     }
617     void commit(CdlTransaction transaction)
618     {
619         // The new name is already installed, nothing more needs to happen.
620         CYG_UNUSED_PARAM(CdlTransaction, transaction);
621     }
622     void cancel(CdlTransaction transaction)
623     {
624         // Restore the old name
625         CdlToplevel toplevel = transaction->get_toplevel();
626         CYG_ASSERTC(0 != toplevel);
627         CdlConfiguration configuration = dynamic_cast<CdlConfiguration>(toplevel);
628         CYG_ASSERT_CLASSC(configuration);
629
630         configuration->set_hardware_name(old_name);
631         CYG_UNUSED_PARAM(CdlTransaction, transaction);
632     }
633     
634   protected:
635
636   private:
637     CdlConfiguration_CommitCancelHardwareName()
638     {
639     }
640     std::string old_name;
641 };
642
643 class CdlConfiguration_CommitCancelTemplateName :
644     public CdlTransactionCommitCancelOp
645 {
646     friend class CdlTest;
647     
648   public:
649     CdlConfiguration_CommitCancelTemplateName(std::string old_name_arg)
650         : CdlTransactionCommitCancelOp()
651     {
652         old_name = old_name_arg;
653     }
654     ~CdlConfiguration_CommitCancelTemplateName()
655     {
656         old_name = "";
657     }
658     void commit(CdlTransaction transaction)
659     {
660         // The new name is already installed, nothing more needs to happen.
661         CYG_UNUSED_PARAM(CdlTransaction, transaction);
662     }
663     void cancel(CdlTransaction transaction)
664     {
665         // Restore the old name
666         CdlToplevel toplevel = transaction->get_toplevel();
667         CYG_ASSERTC(0 != toplevel);
668         CdlConfiguration configuration = dynamic_cast<CdlConfiguration>(toplevel);
669         CYG_ASSERT_CLASSC(configuration);
670
671         configuration->set_template_name(old_name);
672         CYG_UNUSED_PARAM(CdlTransaction, transaction);
673     }
674     
675   protected:
676
677   private:
678     CdlConfiguration_CommitCancelTemplateName()
679     {
680     }
681     std::string old_name;
682 };
683
684 //}}}
685 //{{{  CdlConfiguration::load_package()                 
686
687 // ----------------------------------------------------------------------------
688 // Loading a package into the current level. This involves the following
689 // stages.
690 //
691 //  1) check that the specified package name and version is valid, by
692 //     comparing it with the database. When the database was created there
693 //     will have been checks to make sure that the initial CDL script was
694 //     present, but more checks can be done here.
695 //
696 //  2) before allocating any resources, check that there is no name conflict
697 //     for the package itself.
698 //
699 //  3) create the package object, and add it to the toplevel of the current
700 //     configuration. It may get reparented later on. Part of the creation
701 //     process is to allocate a new slave interpreter, which can be updated
702 //     with various bits of information.
703 //
704 //  4) evaluate the toplevel script. Subsidiary component scripts will
705 //     get evaluated as a side effect. The various nodes will be added
706 //     to the hierarchy as they are created, but no property binding
707 //     happens yet.
708 //
709 //     Any failure up to this point should result in the entire package
710 //     being removed from the hierarchy and then destroyed, thus leaving
711 //     the configuration in its original state.
712 //
713 //  5) now property binding needs to take place. This can have lots
714 //     of side effects, e.g. default values may get calculated, the
715 //     hierarchy may change because of parent properties, etc.
716 //     The work is done inside CdlLoadable::bind() which will undo
717 //     everything on failure - although bad_alloc is the only
718 //     failure that should occur.
719 //
720 //  6) load operations can get cancelled, so a suitable commit/cancel
721 //     operation needs to allocated and added to the transaction.
722 //
723 //  7) if limbo is enabled, previous values should be extracted from
724 //     limbo if at all possible. In addition the package's value can
725 //     be set to its version.
726
727 void
728 CdlConfigurationBody::load_package(CdlTransaction transaction, std::string name, std::string version,
729                                    CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
730 {
731     CYG_REPORT_FUNCNAME("CdlConfiguration::load_package");
732     CYG_REPORT_FUNCARG1XV(this);
733     CYG_PRECONDITION_THISC();
734     CYG_PRECONDITION_CLASSC(transaction);
735     CYG_PRECONDITIONC("" != name);
736
737     // Locate the database entry. Also check the version (filling it in if necessary).
738     // Get hold of the package directory and the initial script.
739     if (!database->is_known_package(name)) {
740         throw CdlInputOutputException("Unknown package " + name);
741     }
742     const std::vector<std::string>& versions = database->get_package_versions(name);
743     if ("" == version) {
744         version = *(versions.begin());
745     } else {
746         if (std::find(versions.begin(), versions.end(), version) == versions.end()) {
747             throw CdlInputOutputException("Package " + name + " does not have an installed version `" + version + "'.");
748         }
749     }
750     std::string directory       = database->get_package_directory(name);
751     std::string script          = database->get_package_script(name);
752     CYG_ASSERTC(("" != directory) && ("" != script));
753
754     // Check that the directory actually exists. For this the configuration's own
755     // interpreter can be used.
756     CdlInterpreter interp = get_interpreter();
757     CYG_ASSERT_CLASSC(interp);
758
759     std::string tcl_cmd = "regsub -all -- {\\\\} [file join " + directory + " " + version + "] / result; return $result";
760     std::string tcl_result;
761     if (TCL_OK != interp->eval(tcl_cmd, tcl_result)) {
762         throw CdlInputOutputException("Cannot load package `" + name + "', internal error constructing pathname.");
763     }
764     directory = tcl_result;
765     
766     tcl_cmd   = "file isdirectory [file join \"" + database->get_component_repository() + "\" " + directory + "]";
767     if ((TCL_OK != interp->eval(tcl_cmd, tcl_result)) || ("1" != tcl_result)) {
768         throw CdlInputOutputException("Cannot load package `" + name + "', there is no directory `" + directory + "'.");
769     }
770     
771     // Make sure that there is no name conflict. No resources have been allocated
772     // yet, so this is a good time.
773     CdlNode node = lookup(name);
774     if (0 != node) {
775         if (0 != dynamic_cast<CdlPackage>(node)) {
776             throw CdlInputOutputException("Package `" + name + "' is already loaded.");
777         } else {
778
779             std::string msg = "Name clash for package `" + name + "',there is a `" +
780                 node->get_class_name() + " " + name + "' already loaded";
781             CdlLoadable owner_pkg = node->get_owner();
782             if (0 != owner_pkg) {
783                 msg += " in package " + owner_pkg->get_name();
784             }
785             throw CdlInputOutputException(msg);
786         }
787     }
788
789     // Now create the package object itself.
790     CdlPackage package  = 0;
791     bool       bound    = false;
792     CdlConfiguration_CommitCancelLoad* load_op  = 0;
793     
794     try {
795         package = new CdlPackageBody(name, this, directory);
796
797         // The package should be added to the hierarchy immediately.
798         // All nodes will get added to the hierarchy as they are
799         // created, an operation that has to be undone during
800         // failure. 
801         this->add_node(package, this, package);
802         
803         // Load the package data. The various nodes will all end up
804         // in a hierarchy below the package, but without any checks
805         // for name conflicts etc and ignoring any re-parenting.
806         CdlInterpreter interp = package->get_interpreter();
807         CYG_ASSERT_CLASSC(interp);
808
809         interp->add_command("unknown",  &CdlParse::unknown_command);
810         CdlInterpreterBody::DiagSupport diag_support(interp, error_fn, warn_fn);
811         
812         // Next figure out the script name, and make sure that it exists.
813         std::string actual_script = package->find_absolute_file(script, "cdl");
814         if ("" == actual_script) {
815             throw CdlInputOutputException("Package " + name + ", unable to find initial script " + script);
816         }
817         tcl_cmd = "file isfile \"" + actual_script + "\"";
818         if ((TCL_OK != interp->eval(tcl_cmd, tcl_result)) || ("1" != tcl_result)) {
819             throw CdlInputOutputException("Package " + name + ", " + actual_script + " is not a CDL script");
820         }
821
822         // The script is valid. Set up the interpreter appropriately.
823         CdlParse::clear_error_count(interp);
824         static CdlInterpreterCommandEntry commands[] =
825         {
826             CdlInterpreterCommandEntry("cdl_package",    &CdlPackageBody::parse_package     ),
827             CdlInterpreterCommandEntry("cdl_component",  &CdlComponentBody::parse_component ),
828             CdlInterpreterCommandEntry("cdl_option",     &CdlOptionBody::parse_option       ),
829             CdlInterpreterCommandEntry("cdl_interface",  &CdlInterfaceBody::parse_interface ),
830             CdlInterpreterCommandEntry("cdl_dialog",     &CdlDialogBody::parse_dialog       ),
831             CdlInterpreterCommandEntry("cdl_wizard",     &CdlWizardBody::parse_wizard       ),
832             CdlInterpreterCommandEntry("",               0                                  )
833         };
834         CdlInterpreterBody::CommandSupport   interp_cmds(interp, commands);
835         CdlInterpreterBody::ContainerSupport interp_container(interp, package);
836         CdlInterpreterBody::ContextSupport   interp_context(interp, actual_script);
837         
838         // The interpreter is now ready.
839         (void) interp->eval_file(actual_script);
840
841         // Clean out the commands etc. This interpreter may get used again
842         // in future, and it should not be possible to define new options
843         // etc. in that invocation.
844         interp->remove_command("unknown");
845         
846         // All the data has been read in without generating an
847         // exception. However there may have been errors reported via
848         // the parse_error_fn, and any errors at all should result
849         // in an exception.
850         int error_count = CdlParse::get_error_count(interp);
851         if (error_count > 0) {
852             std::string tmp;
853             Cdl::integer_to_string(error_count, tmp);
854             throw CdlParseException("Package " + name + ", " + tmp + " error" +
855                                     ((error_count > 1) ? "s" : "") +
856                                     " occurred while reading in the CDL data.");
857         }
858
859         // All the data has been read in, implying that there are no
860         // fatal problems with the data. Now try to bind all
861         // references to and from this loadable.
862         package->bind(transaction);
863         bound = true;
864
865         // Finally, create a suitable transaction commit/cancel object
866         // and add it to the transaction.
867         load_op = new CdlConfiguration_CommitCancelLoad(package);
868         transaction->add_commit_cancel_op(load_op);
869         
870     } catch (...) {
871
872         // Something went wrong during the create or load. It is necessary
873         // to delete the package. Undo all the operations above, in
874         // reverse order. The add_commit_cancel_op() was the last step,
875         // so need not be undone here.
876         if (0 != load_op) {
877             delete load_op;
878         }
879         
880         if (0 != package) {
881             // Note: no attempt is made to recover from errors here
882             if (bound) {
883                 package->unbind(transaction);
884             }
885             this->remove_loadable_from_toplevel(package);
886             delete package;
887         } 
888         throw;
889     }
890
891     // FIXME: implement limbo support
892     
893     // We also have a sensible value for the package as a whole.
894     // Use this value for both default and user - after all the
895     // user has selected the package.
896     package->enable_and_set_value(transaction, version, CdlValueSource_Default);
897     package->enable_and_set_value(transaction, version, CdlValueSource_User);
898
899     CYG_REPORT_RETURN();
900 }
901
902 //}}}
903 //{{{  CdlConfiguration::unload_package()               
904
905 // ----------------------------------------------------------------------------
906 // Unloading a package is very simple. If requested, save all current
907 // values to limbo: there is no point in saving default values, these
908 // will get recalculated from the default_value property anyway;
909 // inferred values should be saved, there is no guarantee that the exact
910 // same value will be calculated again, and if the inferred value is no
911 // longer correct then the inference engine can freely update it.
912 //
913 // Next, unbind the package and remove it from the hierarchy. These
914 // operations are reversible if the transaction gets cancelled.
915 // A suitable transaction commit/cancel object is created and
916 // added to the transaction.
917 void
918 CdlConfigurationBody::unload_package(CdlTransaction transaction, CdlPackage package, bool limbo)
919 {
920     CYG_REPORT_FUNCNAME("CdlConfiguration::unload_package");
921     CYG_REPORT_FUNCARG4XV(this, transaction, package, limbo);
922     CYG_INVARIANT_THISC(CdlConfigurationBody);
923     CYG_INVARIANT_CLASSC(CdlTransactionBody, transaction);
924     CYG_PRECONDITION_CLASSC(package);
925
926     if (limbo) {
927         const std::vector<CdlNode>& pkg_contents = package->get_owned();
928         std::vector<CdlNode>::const_iterator node_i;
929
930         for (node_i = pkg_contents.begin(); node_i != pkg_contents.end(); node_i++) {
931             CdlValuable valuable = dynamic_cast<CdlValuable>(*node_i);
932             if (0 != valuable) {
933                 if (valuable->has_source(CdlValueSource_Inferred) ||
934                     valuable->has_source(CdlValueSource_Wizard)   ||
935                     valuable->has_source(CdlValueSource_User)) {
936                     
937                     set_limbo_value(valuable);
938                 }
939             }
940         }
941     }
942
943     bool unbound = false;
944     bool removed = false;
945     CdlConfiguration_CommitCancelUnload* unload_op = 0;
946     try {
947         
948         package->unbind(transaction);
949         unbound = true;
950         this->remove_loadable_from_toplevel(package);
951         removed = true;
952         unload_op = new CdlConfiguration_CommitCancelUnload(package);
953         transaction->add_commit_cancel_op(unload_op);
954         
955     } catch(...) {
956         if (0 != unload_op) {
957             delete unload_op;
958         }
959         if (removed) {
960             this->add_loadable_to_toplevel(package);
961         }
962         if (unbound) {
963             package->bind(transaction);
964         }
965         throw;
966     }
967     
968     CYG_REPORT_RETURN();
969 }
970
971 //}}}
972 //{{{  CdlConfiguration::change_package_version()       
973
974 // ----------------------------------------------------------------------------
975 // Changing a package version is just a case of unloading the old version
976 // and then loading in the new version. Because this all happens in the
977 // context of a transaction it is possible to undo the unload on
978 // failure, and the whole transaction can be cancelled at a higher level.
979
980 void
981 CdlConfigurationBody::change_package_version(CdlTransaction transaction, CdlPackage package, std::string new_version,
982                                              CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
983 {
984     CYG_REPORT_FUNCNAME("CdlConfiguration::change_package_version");
985     CYG_REPORT_FUNCARG3XV(this, transaction, package);
986     CYG_PRECONDITION_THISC();
987     CYG_INVARIANT_CLASSC(CdlTransactionBody, transaction);
988     CYG_PRECONDITION_CLASSC(package);
989     // "" is valid for the version, it indicates the default
990
991     // Since the package is already loaded it must be in the database,
992     // but it is possible that the desired version does not exist.
993     std::string name = package->get_name();
994     const std::vector<std::string>& pkg_versions = database->get_package_versions(name);
995     if ("" == new_version) {
996         new_version = *(pkg_versions.begin());
997     } else if (std::find(pkg_versions.begin(), pkg_versions.end(), new_version) == pkg_versions.end()) {
998         throw CdlInputOutputException("Version " + new_version + " of package " + name + " is not installed.");
999     }
1000
1001     bool unloaded = false;
1002     try {
1003         this->unload_package(transaction, package, limbo);
1004         unloaded = true;
1005         this->load_package(transaction, name, new_version, error_fn, warn_fn, limbo);
1006     } catch(...) {
1007         if (unloaded) {
1008             // There should be a commit/cancel op for the unload package step.
1009             // This can be undone.
1010             CdlTransactionCommitCancelOp* unload_op = transaction->get_last_commit_cancel_op();
1011             CYG_ASSERTC(0 != unload_op);
1012             CYG_ASSERTC(0 != dynamic_cast<CdlConfiguration_CommitCancelUnload*>(unload_op));
1013             transaction->cancel_last_commit_cancel_op();
1014             CYG_UNUSED_PARAM(CdlTransactionCommitCancelOp*, unload_op);
1015         }
1016         throw;
1017     }
1018
1019     CYG_REPORT_RETURN();
1020 }
1021
1022 //}}}
1023 //{{{  CdlConfiguration::set_hardware() etc.            
1024
1025 // ----------------------------------------------------------------------------
1026 // Setting the hardware involves unloading the old hardware, if any, and
1027 // then loading in the new one. Obviously this should only happen if
1028 // the new hardware name is valid. It would be possible to optimise for
1029 // the case where the old and new hardware are the same, subject
1030 // to dynamic database reload support.
1031
1032 void
1033 CdlConfigurationBody::set_hardware(CdlTransaction transaction, std::string target_name,
1034                                    CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
1035 {
1036     CYG_REPORT_FUNCNAME("CdlConfiguration::set_hardware");
1037     CYG_REPORT_FUNCARG2XV(this, transaction);
1038     CYG_PRECONDITION_THISC();
1039     CYG_PRECONDITION_CLASSC(transaction);
1040
1041     // Minimal consistency check before attempting anything complicated.
1042     if (!database->is_known_target(target_name)) {
1043         throw CdlInputOutputException("Unknown target " + target_name);
1044     }
1045
1046     CdlInterpreter interp = this->get_interpreter();
1047     CdlInterpreterBody::DiagSupport    diag_support(interp, error_fn, warn_fn);
1048     CdlInterpreterBody::ContextSupport context_support(interp, "Hardware selection");
1049     
1050     CdlConfiguration_CommitCancelHardwareName* rename_op = new CdlConfiguration_CommitCancelHardwareName(current_hardware);
1051     try {
1052         transaction->add_commit_cancel_op(rename_op);
1053         const std::vector<CdlLoadable>& loadables = this->get_loadables();
1054         int i;
1055         for (i = (int) loadables.size() - 1; i >= 0; i--) {
1056             CdlPackage package = dynamic_cast<CdlPackage>(loadables[i]);
1057             if ((0 != package) && package->belongs_to_hardware()) {
1058                 this->unload_package(transaction, package, limbo);
1059             }
1060         }
1061         current_hardware = "";
1062
1063         if ("" != target_name) {
1064             
1065             const std::vector<std::string>& packages = database->get_target_packages(target_name);
1066             std::vector<std::string>::const_iterator    name_i;
1067             for (name_i = packages.begin(); name_i != packages.end(); name_i++) {
1068                 // Target specifications may refer to packages that are not
1069                 // installed. This is useful in e.g. an anoncvs environment.
1070                 if (database->is_known_package(*name_i)) {
1071                     // It is possible for a hardware package to have been
1072                     // loaded separately, in which case there is no point in
1073                     // loading it again.
1074                     if (0 == this->lookup(*name_i)) {
1075                         this->load_package(transaction, *name_i, "",
1076                                            error_fn, warn_fn, limbo);
1077                         CdlPackage package = dynamic_cast<CdlPackage>(this->lookup(*name_i));
1078                         CYG_LOOP_INVARIANT_CLASSC(package);
1079                         package->loaded_for_hardware = true;
1080                     }
1081                 } else {
1082                     CdlParse::report_warning(interp, "",
1083                                              std::string("The target specification lists a package `") + *name_i +
1084                                              "' which is not present in the component repository.");
1085                 }
1086             }
1087         }
1088         current_hardware = target_name;
1089         
1090     } catch(...) {
1091         // Cancel all operations up to and including the rename_op
1092         CdlTransactionCommitCancelOp* cancel_op = 0;
1093         do {
1094             cancel_op = transaction->get_last_commit_cancel_op();
1095             CYG_LOOP_INVARIANTC(0 != cancel_op);
1096             transaction->cancel_last_commit_cancel_op();
1097         } while(cancel_op != rename_op);
1098         throw;
1099     }
1100
1101     // There may have been enables/disables and value data for that target
1102     // FIXME: any problems get ignored quietly. There should at least
1103     // be some warnings.
1104     if ("" != target_name) {
1105         const std::vector<std::string>& enables  = database->get_target_enables(target_name);
1106         const std::vector<std::string>& disables = database->get_target_disables(target_name);
1107         const std::vector<std::pair<std::string, std::string> >& set_values = database->get_target_set_values(target_name);
1108         
1109         if ((0 != enables.size()) || (0 != disables.size()) || (0 != set_values.size())) {
1110             std::vector<std::string>::const_iterator opt_i;
1111             CdlNode     node;
1112             CdlValuable valuable;
1113             CdlValueFlavor flavor;
1114             
1115             for (opt_i = enables.begin(); opt_i != enables.end(); opt_i++) {
1116                 valuable = 0;
1117                 node     = this->lookup(*opt_i);
1118                 if (0 != node) {
1119                     valuable = dynamic_cast<CdlValuable>(node);
1120                     if (0 != valuable) {
1121                     }
1122                 }
1123                 if (0 != valuable) {
1124                     flavor = valuable->get_flavor();
1125                     if ((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) {
1126                         valuable->enable(transaction, CdlValueSource_User);
1127                     } else {
1128                         CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1129                                                  std::string("The option `") + *opt_i +
1130                                                  "' is supposed to be enabled for this target.\n" +
1131                                                  "However the option does not have a bool or booldata flavors.");
1132                     }
1133                 } else {
1134                     CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1135                                              std::string("The option `") + *opt_i +
1136                                              "' is supposed to be enabled for this target.\n" +
1137                                              "However this option is not in the current configuration.");
1138                 }
1139             }
1140             for (opt_i = disables.begin(); opt_i != disables.end(); opt_i++) {
1141                 valuable = 0;
1142                 node = this->lookup(*opt_i);
1143                 if (0 != node) {
1144                     valuable = dynamic_cast<CdlValuable>(node);
1145                 }
1146                 if (0 != valuable) {
1147                     flavor = valuable->get_flavor();
1148                     if ((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) {
1149                         valuable->disable(transaction, CdlValueSource_User);
1150                     } else {
1151                         CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1152                                                  std::string("The option `") + *opt_i +
1153                                                  "' is supposed to be disabled for this target.\n" +
1154                                                  "However the option does not have a bool or booldata flavors.");
1155                     }
1156                 } else {
1157                     CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1158                                              std::string("The option `") + *opt_i +
1159                                              "' is supposed to be disabled for this target.\n" +
1160                                              "However this option is not in the current configuration.");
1161                 }
1162             }
1163             std::vector<std::pair<std::string,std::string> >::const_iterator value_i;
1164             for (value_i = set_values.begin(); value_i != set_values.end(); value_i++) {
1165                 valuable = 0;
1166                 node = this->lookup(value_i->first);
1167                 if (0 != node) {
1168                     valuable = dynamic_cast<CdlValuable>(node);
1169                 }
1170                 if (0 != valuable) {
1171                     flavor = valuable->get_flavor();
1172                     if ((CdlValueFlavor_BoolData == flavor) || (CdlValueFlavor_Data == flavor)) {
1173                         valuable->set_value(transaction, value_i->second, CdlValueSource_User);
1174                     } else {
1175                         CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1176                                                  std::string("The option `") + *opt_i +
1177                                                  "' is supposed to be given the value `" + value_i->second +
1178                                                  "' for this target.\n" +
1179                                                  "However the option does not have a data or booldata flavor.");
1180                     }
1181                 } else {
1182                     CdlParse::report_warning(interp, std::string("target `") + target_name + "'",
1183                                              std::string("The option `") + *opt_i +
1184                                              "' is supposed to be given the value `" + value_i->second +
1185                                              "' for this target.\n" +
1186                                              "However this option is not in the current configuration.");
1187                 }
1188             }
1189         }
1190     }
1191
1192     CYG_REPORT_RETURN();
1193 }
1194
1195 void
1196 CdlConfigurationBody::unload_hardware(CdlTransaction transaction, bool limbo)
1197 {
1198     CYG_REPORT_FUNCNAME("CdlConfiguration::unload_hardware");
1199     CYG_REPORT_FUNCARG3XV(this, transaction, limbo);
1200     CYG_PRECONDITION_THISC();
1201
1202     CdlConfiguration_CommitCancelHardwareName* rename_op = new CdlConfiguration_CommitCancelHardwareName(current_hardware);
1203     try {
1204         transaction->add_commit_cancel_op(rename_op);
1205     } catch(...) {
1206         delete rename_op;
1207         throw;
1208     }
1209     current_hardware = "";
1210
1211     try {
1212         const std::vector<CdlLoadable>& loadables = this->get_loadables();
1213         for (int i = (int) loadables.size() - 1; i >= 0; i--) {
1214             CdlPackage package = dynamic_cast<CdlPackage>(loadables[i]);
1215             if ((0 != package) && package->belongs_to_hardware()) {
1216                 this->unload_package(transaction, package, limbo);
1217             }
1218         }
1219     } catch(...) {
1220         CdlTransactionCommitCancelOp* cancel_op = 0;
1221         do {
1222             cancel_op = transaction->get_last_commit_cancel_op();
1223             CYG_LOOP_INVARIANTC(0 != cancel_op);
1224             transaction->cancel_last_commit_cancel_op();
1225         } while(cancel_op != rename_op);
1226         throw;
1227     }
1228
1229     CYG_REPORT_RETURN();
1230 }
1231
1232 //}}}
1233 //{{{  CdlConfiguration::set_template() etc             
1234
1235 // ----------------------------------------------------------------------------
1236 void
1237 CdlConfigurationBody::set_template_file(CdlTransaction transaction, std::string filename,
1238                                         CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn, bool limbo)
1239 {
1240     CYG_REPORT_FUNCNAME("CdlConfiguration::set_template_file");
1241     CYG_REPORT_FUNCARG3XV(this, transaction, limbo);
1242     CYG_PRECONDITION_THISC();
1243
1244     int i;
1245     CdlConfiguration_CommitCancelTemplateName* rename_op = new CdlConfiguration_CommitCancelTemplateName(current_template);
1246
1247     // The hard work is done by add(), which loads in a partial savefile.
1248     // This can have undesirable side effects: changing the name,
1249     // description, or hardware settings. It must be possible to undo
1250     // these.
1251     std::string saved_name        = this->get_name();
1252     std::string saved_description = this->get_description();
1253     std::string saved_hardware    = this->get_hardware();
1254
1255     // New packages will end up at the end of the loadables vector.
1256     // Each new package needs to be registered as a template one.
1257     // NOTE: this may break if we start doing more interesting things
1258     // with savefiles.
1259     const std::vector<CdlLoadable>& loadables = this->get_loadables();
1260     unsigned int load_i = loadables.size();
1261     
1262     try {
1263         transaction->add_commit_cancel_op(rename_op);
1264         const std::vector<CdlLoadable>& loadables = this->get_loadables();
1265         for (i = (int) loadables.size() - 1; i >= 0; i--) {
1266             CdlPackage package = dynamic_cast<CdlPackage>(loadables[i]);
1267             if ((0 != package) && package->belongs_to_template()) {
1268                 this->unload_package(transaction, package, limbo);
1269             }
1270         }
1271         current_template = "";
1272
1273         this->add(transaction, filename, error_fn, warn_fn);
1274         this->current_template = filename;
1275         this->set_name(saved_name);
1276         this->description = saved_description;
1277         this->current_hardware = saved_hardware;
1278
1279         for ( ; load_i < loadables.size(); load_i++) {
1280             CdlPackage pkg = dynamic_cast<CdlPackage>(loadables[load_i]);
1281             CYG_ASSERT_CLASSC(pkg);
1282             pkg->loaded_for_template = true;
1283         }
1284         
1285     } catch(...) {
1286         
1287         this->set_name(saved_name);
1288         this->description = saved_description;
1289         this->current_hardware = saved_hardware;
1290         
1291         // Cancel all operations up to and including the rename_op
1292         CdlTransactionCommitCancelOp* cancel_op = 0;
1293         do {
1294             cancel_op = transaction->get_last_commit_cancel_op();
1295             CYG_LOOP_INVARIANTC(0 != cancel_op);
1296             transaction->cancel_last_commit_cancel_op();
1297         } while(cancel_op != rename_op);
1298         throw;
1299     }
1300     
1301
1302     CYG_REPORT_RETURN();
1303 }
1304
1305 void
1306 CdlConfigurationBody::unload_template(CdlTransaction transaction, bool limbo)
1307 {
1308     CYG_REPORT_FUNCNAME("CdlConfiguration::unload_template");
1309     CYG_REPORT_FUNCARG2XV(this, transaction);
1310     CYG_PRECONDITION_THISC();
1311     CYG_PRECONDITION_CLASSC(transaction);
1312
1313     CdlConfiguration_CommitCancelTemplateName* rename_op = new CdlConfiguration_CommitCancelTemplateName(current_template);
1314     try {
1315         transaction->add_commit_cancel_op(rename_op);
1316     } catch(...) {
1317         delete rename_op;
1318         throw;
1319     }
1320     current_template = "";
1321
1322     try {
1323         const std::vector<CdlLoadable>& loadables = this->get_loadables();
1324         for (int i = (int) loadables.size() - 1; i >= 0; i--) {
1325             CdlPackage package = dynamic_cast<CdlPackage>(loadables[i]);
1326             if ((0 != package) && package->belongs_to_template()) {
1327                 this->unload_package(transaction, package, limbo);
1328             }
1329         }
1330     } catch(...) {
1331         CdlTransactionCommitCancelOp* cancel_op = 0;
1332         do {
1333             cancel_op = transaction->get_last_commit_cancel_op();
1334             CYG_LOOP_INVARIANTC(0 != cancel_op);
1335             transaction->cancel_last_commit_cancel_op();
1336         } while(cancel_op != rename_op);
1337         throw;
1338     }
1339     
1340     CYG_REPORT_RETURN();
1341 }
1342
1343 //}}}
1344 //{{{  Persistence support                              
1345
1346 //{{{  initialize_savefile_support()                    
1347
1348 // ----------------------------------------------------------------------------
1349 // Initialization. The purpose of this code is to determine all the
1350 // commands that can end up in a particular savefile. This includes
1351 // the cdl_configuration command, commands relevant to packages,
1352 // options, and components, the generic library commands, and
1353 // application-specific commands.
1354 //
1355 // This is a virtual function, it may get invoked indirectly from
1356 // e.g. CdlToplevel::add_savefile_command().
1357
1358 void
1359 CdlConfigurationBody::initialize_savefile_support()
1360 {
1361     CYG_REPORT_FUNCNAME("CdlConfiguration::initialize_savefile_support");
1362     CYG_REPORT_FUNCARG1XV(this);
1363     CYG_PRECONDITION_THISC();
1364     
1365     // Start with the generic stuff such as cdl_savefile_version and
1366     // cdl_command.
1367     this->CdlToplevelBody::initialize_savefile_support();
1368
1369     // Now add in the cdl_configuration command and its subcommands.
1370     this->add_savefile_command("cdl_configuration", 0, &savefile_configuration_command);
1371     this->add_savefile_subcommand("cdl_configuration", "description", 0, &savefile_description_command);
1372     this->add_savefile_subcommand("cdl_configuration", "hardware",    0, &savefile_hardware_command);
1373     this->add_savefile_subcommand("cdl_configuration", "template",    0, &savefile_template_command);
1374     this->add_savefile_subcommand("cdl_configuration", "package",     0, &savefile_package_command);
1375
1376     CdlPackageBody::initialize_savefile_support(this);
1377     CdlComponentBody::initialize_savefile_support(this);
1378     CdlOptionBody::initialize_savefile_support(this);
1379     CdlInterfaceBody::initialize_savefile_support(this);
1380 }
1381
1382 //}}}
1383 //{{{  CdlConfiguration::save() - internal              
1384
1385 // ----------------------------------------------------------------------------
1386 // The exported interface is CdlConfiguration::save(). This takes a single
1387 // argument, a filename. It opens the file, and then invokes various
1388 // functions that output the relevants bits of the file.
1389 //
1390 // This member function is responsible for outputting a cdl_configuration
1391 // command.
1392
1393 void
1394 CdlConfigurationBody::save(CdlInterpreter interp, Tcl_Channel chan, int indentation, bool minimal)
1395 {
1396     CYG_REPORT_FUNCNAME("CdlConfiguration::save");
1397     CYG_REPORT_FUNCARG5XV(this, interp, chan, indentation, minimal);
1398     CYG_PRECONDITION_THISC();
1399     CYG_PRECONDITION_CLASSC(interp);
1400     CYG_PRECONDITIONC(0 == indentation);
1401
1402     std::string text = "";
1403     if (!minimal) {
1404       text = 
1405 "# This section defines the toplevel configuration object. The only\n\
1406 # values that can be changed are the name of the configuration and\n\
1407 # the description field. It is not possible to modify the target,\n\
1408 # the template or the set of packages simply by editing the lines\n\
1409 # below because these changes have wide-ranging effects. Instead\n\
1410 # the appropriate tools should be used to make such modifications.\n\
1411 \n";
1412     }
1413
1414     text += "cdl_configuration " + CdlInterpreterBody::quote(this->get_name()) + " {\n";
1415
1416     std::string config_data = this->get_description();
1417     if (!minimal || ("" != text)) {
1418         text += "    description " + CdlInterpreterBody::quote(config_data) + " ;\n";
1419     }
1420
1421     // Repeat the warning.
1422     if (!minimal) {
1423         text += "\n    # These fields should not be modified.\n";
1424     }
1425     config_data = this->get_hardware();
1426     if ("" != config_data) {
1427         text += "    hardware    " + CdlInterpreterBody::quote(config_data) + " ;\n";
1428     }
1429     config_data = this->get_template();
1430     if ("" != config_data) {
1431         text += "    template    " + CdlInterpreterBody::quote(config_data) + " ;\n";
1432     }
1433     std::vector<CdlLoadable>::const_iterator load_i;
1434     const std::vector<CdlLoadable>& packages = get_loadables();
1435     for (load_i = packages.begin(); load_i != packages.end(); load_i++) {
1436         CdlPackage pkg = dynamic_cast<CdlPackage>(*load_i);
1437         CYG_ASSERT_CLASSC(pkg);
1438         text += "    package ";
1439         if (pkg->belongs_to_template()) {
1440             text += "-template ";
1441         }
1442         if (pkg->belongs_to_hardware()) {
1443             text += "-hardware ";
1444         }
1445         text += CdlInterpreterBody::quote(pkg->get_name()) + " " + CdlInterpreterBody::quote(pkg->get_value()) + " ;\n";
1446     }
1447
1448     interp->write_data(chan, text);
1449     
1450     // If the package was loaded from a file then there may be additional
1451     // data associated with the configuration that is not currently
1452     // recognised. This call preserves that data.
1453     this->CdlNodeBody::save(interp, chan, indentation + 4, minimal);
1454
1455     interp->write_data(chan, "};\n\n");
1456
1457     CYG_REPORT_RETURN();
1458 }
1459
1460 //}}}
1461 //{{{  CdlConfiguration::save() - exported interface    
1462
1463 // ----------------------------------------------------------------------------
1464 // This is the exported interface for saving a configuration. The specified
1465 // file is opened via the appropriate Tcl library routines, and then the
1466 // relevant member functions are invoked to output the actual configuration
1467 // date.
1468
1469 void
1470 CdlConfigurationBody::save(std::string filename, bool minimal)
1471 {
1472     CYG_REPORT_FUNCNAME("CdlConfiguration::save");
1473     CYG_REPORT_FUNCARG2XV(this, minimal);
1474     CYG_PRECONDITION_THISC();
1475     CYG_PRECONDITIONC("" != filename);
1476
1477     // Make sure that the savefile support is properly initialized.
1478     // This happens during the first save or load operation, or when
1479     // the application starts to register its own savefile extensions.
1480     if (!CdlToplevelBody::savefile_support_initialized()) {
1481         this->initialize_savefile_support();
1482     }
1483
1484     // A Tcl interpreter is needed for the call to OpenFileChannel(),
1485     // and will also be passed to the individual save functions.
1486     CdlInterpreter interp = this->get_interpreter();
1487     CYG_ASSERT_CLASSC(interp);
1488
1489     // Do not worry about forward vs. backward slashes, since the filename
1490     // is not manipulated in any way. Instead just pass it to Tcl.
1491     Tcl_Channel chan = Tcl_OpenFileChannel(interp->get_tcl_interpreter(), const_cast<char*>(filename.c_str()), "w", 0666);
1492     if (0 == chan) {
1493         throw CdlInputOutputException("Unable to open file " + filename + "\n" + interp->get_result());
1494     }
1495
1496     // The channel may end up being registered in various different
1497     // interpreters, so Tcl_Close() is not the right way to close down
1498     // the channel. Instead Tcl_RegisterChannel() should be used here
1499     // to provide reference counting semantics.
1500     Tcl_RegisterChannel(0, chan);
1501
1502     // A try/catch body is needed here to make sure that the file gets
1503     // properly cleaned up.
1504     std::string tmp;
1505     try {
1506
1507         if (!minimal) {
1508             interp->write_data(chan, "# eCos saved configuration\n\n");
1509         }
1510         
1511         CdlToplevelBody::save_separator(interp, chan, "commands", minimal);
1512         this->CdlToplevelBody::save_command_details(interp, chan, 0, minimal);
1513         CdlToplevelBody::save_separator(interp, chan, "toplevel", minimal);
1514         this->save(interp, chan, 0, minimal);
1515         CdlToplevelBody::save_separator(interp, chan, "conflicts", minimal);
1516         this->CdlToplevelBody::save_conflicts(interp, chan, 0, minimal);
1517         CdlToplevelBody::save_separator(interp, chan, "contents", minimal);
1518         this->CdlContainerBody::save(interp, chan, 0, minimal);
1519         this->save_unsupported_commands(interp, chan, 0, minimal);
1520         
1521     } catch(...) {
1522         Tcl_UnregisterChannel(0, chan);
1523         // NOTE: deleting the file is necessary, it is a bad idea to
1524         // end up with incomplete save files. It would be even better
1525         // to write to a temporary file and only overwrite the old
1526         // savefile on success.
1527         //
1528         // Tcl does not provide direct access to the file delete
1529         // facility, so it is necessary to evaluate a script. This
1530         // introduces quoting and security problems, since the
1531         // filename might contain spaces, square brackets, braces...
1532         // To avoid these problems a variable is used.
1533         interp->set_variable("__cdlconfig_filename", filename);
1534         interp->eval("file delete $__cdlconfig_filename", tmp);
1535         interp->unset_variable("__cdlconfig_filename");
1536         
1537         throw;
1538     }
1539
1540     // This call will perform the appropriate close.
1541     Tcl_UnregisterChannel(0, chan);
1542 }
1543
1544 //}}}
1545 //{{{  CdlConfiguration::load() and add()               
1546
1547 // ----------------------------------------------------------------------------
1548 // Most of the work is done in add(). load() simply creates a new configuration
1549 // and then invokes add().
1550 CdlConfiguration
1551 CdlConfigurationBody::load(std::string filename, CdlPackagesDatabase db, CdlInterpreter interp,
1552                            CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn)
1553 {
1554     CYG_REPORT_FUNCNAMETYPE("CdlConfiguration::load", "result %p");
1555     CYG_REPORT_FUNCARG4XV(db, interp, error_fn, warn_fn);
1556     CYG_PRECONDITION_CLASSC(db);
1557     CYG_PRECONDITION_CLASSC(interp);
1558     
1559     CdlConfiguration result = CdlConfigurationBody::make("eCos", db, interp);
1560     if (0 == result) {
1561         CYG_REPORT_RETVAL(result);
1562         return result;
1563     }
1564     
1565     try {
1566         result->add(filename, error_fn, warn_fn);
1567         result->save_file = filename;
1568     } catch(...) {
1569         delete result;
1570         throw;
1571     }
1572
1573     CYG_REPORT_RETVAL(result);
1574     return result;
1575 }
1576
1577 // ----------------------------------------------------------------------------
1578 void
1579 CdlConfigurationBody::add(CdlTransaction transaction, std::string filename,
1580                           CdlDiagnosticFnPtr error_fn, CdlDiagnosticFnPtr warn_fn)
1581 {
1582     CYG_REPORT_FUNCNAME("CdlConfiguration::add");
1583     CYG_REPORT_FUNCARG3XV(this, error_fn, warn_fn);
1584     CYG_PRECONDITION_THISC();
1585
1586     // Initialize the savefile support, so that it is known what
1587     // commands can occur in a savefile.
1588     if (!CdlToplevelBody::savefile_support_initialized()) {
1589         this->initialize_savefile_support();
1590     }
1591     
1592     // The interpreter should not have any left-over junk.
1593     CdlInterpreter interp = this->get_interpreter();
1594     CYG_PRECONDITION_CLASSC(interp);
1595     CYG_ASSERTC(0 == interp->get_loadable());
1596     CYG_ASSERTC(0 == interp->get_container());
1597     CYG_ASSERTC(0 == interp->get_node());
1598     CYG_ASSERTC(0 == interp->get_transaction());
1599
1600     // Keep track of enough information to undo all the changes.
1601     CdlParse::clear_error_count(interp);
1602     CdlInterpreterBody::DiagSupport    diag_support(interp, error_fn, warn_fn);
1603     CdlInterpreterBody::ContextSupport context_support(interp, filename);
1604
1605     try {
1606         interp->set_transaction(transaction);
1607
1608         std::vector<CdlInterpreterCommandEntry> commands;
1609         this->get_savefile_commands(commands);
1610         CdlInterpreterBody::CommandSupport interp_cmds(interp, commands);
1611
1612         interp->eval_file(filename);
1613         
1614         // All the data has been read in without generating an
1615         // exception. However there may have been errors reported via
1616         // the error_fn handling, and any errors at all should result
1617         // in an exception.
1618         int error_count = CdlParse::get_error_count(interp);
1619         if (error_count > 0) {
1620             std::string tmp;
1621             Cdl::integer_to_string(error_count, tmp);
1622             throw CdlInputOutputException("Invalid savefile \"" + filename + "\".\n" +
1623                                           tmp + " error" + ((error_count > 1) ? "s" : "") +
1624                                           " occurred while reading in the savefile data.");
1625         }
1626         
1627     } catch(...) {
1628         interp->set_transaction(0);
1629         throw;
1630     }
1631
1632     interp->set_transaction(0);
1633
1634     CYG_REPORT_RETURN();
1635 }
1636
1637 //}}}
1638 //{{{  savefile commands                                
1639
1640 // ----------------------------------------------------------------------------
1641 // A cdl_configuration command does not actually do very much. It acts as
1642 // a container for subcommands, and it can be used to change the name.
1643 //
1644 // The command could also check that the current configuration is empty.
1645 // This is not done, to allow multiple savefiles to be loaded into
1646 // a single configuration in future.
1647 int
1648 CdlConfigurationBody::savefile_configuration_command(CdlInterpreter interp, int argc, const char* argv[])
1649 {
1650     CYG_REPORT_FUNCNAMETYPE("CdlConfiguration::savefile_configuration_command", "result %d");
1651     CYG_PRECONDITION_CLASSC(interp);
1652
1653     int result = TCL_OK;
1654     CdlToplevel toplevel = interp->get_toplevel();
1655     CYG_ASSERT_CLASSC(toplevel);
1656     CdlConfiguration config = dynamic_cast<CdlConfiguration>(toplevel);
1657     CYG_ASSERT_CLASSC(config);
1658
1659     std::vector<CdlInterpreterCommandEntry> subcommands;
1660     std::vector<CdlInterpreterCommandEntry>* toplevel_commands = 0;
1661     
1662     try {
1663         std::vector<std::pair<std::string,std::string> > options;
1664         int data_index = CdlParse::parse_options(interp, "cdl_configuration command", 0, argc, argv, 1, options);
1665
1666         // A broken cdl_configuration command is pretty fatal, chances are
1667         // that the entire load is going to fail.
1668         if (data_index != (argc - 2)) {
1669             CdlParse::report_error(interp, "", "Invalid cdl_configuration command in savefile, expecting two arguments.");
1670         } else {
1671             config->set_name(argv[1]);
1672             config->get_savefile_subcommands("cdl_configuration", subcommands);
1673             toplevel_commands = interp->push_commands(subcommands);
1674
1675             std::string tcl_result;
1676             result = interp->eval(argv[2], tcl_result);
1677             
1678             interp->pop_commands(toplevel_commands);
1679             toplevel_commands = 0;
1680         }
1681         
1682     } catch(...) {
1683         if (0 != toplevel_commands) {
1684             interp->pop_commands(toplevel_commands);
1685         }
1686         throw;
1687     }
1688
1689     CYG_REPORT_RETVAL(result);
1690     return result;
1691 }
1692
1693 // ----------------------------------------------------------------------------
1694 int
1695 CdlConfigurationBody::savefile_description_command(CdlInterpreter interp, int argc, const char* argv[])
1696 {
1697     CYG_REPORT_FUNCNAME("CdlConfiguration::savefile_description_command");
1698     CYG_PRECONDITION_CLASSC(interp);
1699
1700     CdlToplevel toplevel = interp->get_toplevel();
1701     CYG_ASSERT_CLASSC(toplevel);
1702     CdlConfiguration config = dynamic_cast<CdlConfiguration>(toplevel);
1703     CYG_ASSERT_CLASSC(config);
1704
1705     std::vector<std::pair<std::string,std::string> > options;
1706     int data_index = CdlParse::parse_options(interp, "cdl_configuration/description command", 0, argc, argv, 1, options);
1707         
1708     if (data_index != (argc - 1)) {
1709         CdlParse::report_warning(interp, "",
1710                                  "Ignoring invalid configuration description command, expecting a single argument.");
1711     } else {
1712         config->description = argv[1];
1713     }    
1714     return TCL_OK;
1715 }
1716
1717 // ----------------------------------------------------------------------------
1718 int
1719 CdlConfigurationBody::savefile_hardware_command(CdlInterpreter interp, int argc, const char* argv[])
1720 {
1721     CYG_REPORT_FUNCNAME("CdlConfiguration::savefile_hardware_command");
1722     CYG_PRECONDITION_CLASSC(interp);
1723
1724     CdlToplevel toplevel = interp->get_toplevel();
1725     CYG_ASSERT_CLASSC(toplevel);
1726     CdlConfiguration config = dynamic_cast<CdlConfiguration>(toplevel);
1727     CYG_ASSERT_CLASSC(config);
1728
1729     std::vector<std::pair<std::string,std::string> > options;
1730     int data_index = CdlParse::parse_options(interp, "cdl_configuration/hardware command", 0, argc, argv, 1, options);
1731         
1732     if (data_index != (argc - 1)) {
1733         CdlParse::report_warning(interp, "", "Ignoring invalid configuration hardware command, expecting a single argument.");
1734     } else {
1735         config->current_hardware = argv[1];
1736     }
1737
1738     return TCL_OK;
1739 }
1740
1741 // ----------------------------------------------------------------------------
1742 int
1743 CdlConfigurationBody::savefile_template_command(CdlInterpreter interp, int argc, const char* argv[])
1744 {
1745     CYG_REPORT_FUNCNAME("CdlConfiguration::savefile_template_command");
1746     CYG_PRECONDITION_CLASSC(interp);
1747
1748     CdlToplevel toplevel = interp->get_toplevel();
1749     CYG_ASSERT_CLASSC(toplevel);
1750     CdlConfiguration config = dynamic_cast<CdlConfiguration>(toplevel);
1751     CYG_ASSERT_CLASSC(config);
1752
1753     std::vector<std::pair<std::string,std::string> > options;
1754     int data_index = CdlParse::parse_options(interp, "cdl_configuration/template command", 0, argc, argv, 1, options);
1755         
1756     if (data_index != (argc - 1)) {
1757         CdlParse::report_warning(interp, "", "Ignoring invalid configuration template command, expecting a single argument.");
1758     } else {
1759         config->current_template = argv[1];
1760     }
1761     
1762     return TCL_OK;
1763 }
1764
1765 // ----------------------------------------------------------------------------
1766 int
1767 CdlConfigurationBody::savefile_package_command(CdlInterpreter interp, int argc, const char* argv[])
1768 {
1769     CYG_REPORT_FUNCNAME("CdlConfiguration::savefile_package_command");
1770     CYG_PRECONDITION_CLASSC(interp);
1771     
1772     CdlToplevel toplevel = interp->get_toplevel();
1773     CYG_ASSERT_CLASSC(toplevel);
1774     CdlConfiguration config = dynamic_cast<CdlConfiguration>(toplevel);
1775     CYG_ASSERT_CLASSC(config);
1776     CdlPackagesDatabase db = config->get_database();
1777     CYG_ASSERT_CLASSC(db);
1778
1779     std::string pkgname;
1780     std::string pkgversion;
1781     CdlPackage  pkg = 0;
1782     
1783     std::vector<std::pair<std::string,std::string> > options;
1784     static char* optlist[] = {
1785         "template:f",
1786         "hardware:f",
1787         0
1788     };
1789     int data_index = CdlParse::parse_options(interp, "cdl_configuration/package command", optlist, argc, argv, 1, options);
1790
1791     if (data_index == (argc - 1)) {
1792         CdlParse::report_warning(interp, "", std::string("Missing version information for package `")
1793                                  + argv[argc - 1] + "'.");
1794         pkgname = argv[argc - 1];
1795         pkgversion = "";
1796     } else if (data_index == (argc - 2)) {
1797         pkgname = argv[argc - 2];
1798         pkgversion = argv[argc - 1];
1799     } else {
1800         // If we cannot load all the packages then much of the
1801         // savefile is likely to be problematical.
1802         CdlParse::report_error(interp, "", "Invalid cdl_configuration/package command, expecting name and version");
1803         CYG_REPORT_RETURN();
1804         return TCL_OK;
1805     }
1806
1807     if (0 != config->lookup(pkgname)) {
1808         // If the package was already loaded, check the version string. If the versions
1809         // are identical then we do not need to worry any further. Otherwise a mismatch
1810         // warning is appropriate.
1811         CdlNode node = config->lookup(pkgname);
1812         CYG_ASSERT_CLASSC(node);
1813         pkg = dynamic_cast<CdlPackage>(node);
1814         if (0 == pkg) {
1815             // The name is in use, but it is not a package
1816             CdlParse::report_error(interp, "",
1817                                    std::string("Unable to load package `") + pkgname + "', the name is already in use.");
1818         } else if (pkgversion != pkg->get_value()) {
1819             CdlParse::report_warning(interp, "",
1820                                      std::string("Cannot load version `") + pkgversion + "' of package `" +
1821                                      pkgname + "', version `" + pkg->get_value() + "' is already loaded.");
1822         }
1823     } else if (!db->is_known_package(pkgname)) {
1824         CdlParse::report_error(interp, "",
1825                                std::string("Attempt to load an unknown package `") + pkgname + "'.");
1826     } else {
1827         if ("" != pkgversion) {
1828             const std::vector<std::string>& versions = db->get_package_versions(pkgname);
1829             if (versions.end() == std::find(versions.begin(), versions.end(), pkgversion)) {
1830                 CdlParse::report_warning(interp, "",
1831                                          std::string("The savefile specifies version `") + pkgversion +
1832                                          "' for package `" + pkgname + "'\nThis version is not available.\n" +
1833                                          "Using the most recent version instead.");
1834                 pkgversion = "";
1835             }
1836         }
1837         CdlDiagnosticFnPtr error_fn    = interp->get_error_fn_ptr();
1838         CdlDiagnosticFnPtr warn_fn     = interp->get_warning_fn_ptr();
1839         CdlTransaction     transaction = interp->get_transaction();
1840         config->load_package(transaction, pkgname, pkgversion, error_fn, warn_fn, false);
1841         CdlNode pkg_node = config->lookup(pkgname);
1842         CYG_ASSERTC(0 != pkg_node);
1843         pkg = dynamic_cast<CdlPackage>(pkg_node);
1844         CYG_ASSERT_CLASSC(pkg);
1845     }
1846
1847     if ((0 != pkg) && (0 != options.size())) {
1848         std::vector<std::pair<std::string,std::string> >::const_iterator opt_i;
1849         for (opt_i = options.begin(); opt_i != options.end(); opt_i++) {
1850             if (opt_i->first == "template") {
1851                 pkg->loaded_for_template = true;
1852             } else if (opt_i->first == "hardware") {
1853                 pkg->loaded_for_hardware = true;
1854             } 
1855         }
1856     }
1857         
1858     return TCL_OK;
1859 }
1860
1861 //}}}
1862
1863 //}}}
1864