3 //============================================================================
7 // Provide access to Tcl interpreters
9 //============================================================================
10 //####COPYRIGHTBEGIN####
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 2002 Bart Veer
14 // Copyright (C) 1999, 2000, 2001 Red Hat, Inc.
16 // This file is part of the eCos host tools.
18 // This program is free software; you can redistribute it and/or modify it
19 // under the terms of the GNU General Public License as published by the Free
20 // Software Foundation; either version 2 of the License, or (at your option)
23 // This program is distributed in the hope that it will be useful, but WITHOUT
24 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
25 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
28 // You should have received a copy of the GNU General Public License along with
29 // this program; if not, write to the Free Software Foundation, Inc.,
30 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 // ----------------------------------------------------------------------------
34 //####COPYRIGHTEND####
35 //============================================================================
36 //#####DESCRIPTIONBEGIN####
43 //####DESCRIPTIONEND####
44 //============================================================================
49 // ----------------------------------------------------------------------------
50 #include "cdlconfig.h"
52 // Get the infrastructure types, assertions, tracing and similar
54 #include <cyg/infra/cyg_ass.h>
55 #include <cyg/infra/cyg_trac.h>
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. It also brings
61 #include <cdlcore.hxx>
67 // ----------------------------------------------------------------------------
68 // This key is used for accessing AssocData in the Tcl interpreters,
69 // specifically the CdlInterpreter object.
70 const char* CdlInterpreterBody::cdlinterpreter_assoc_data_key = "__cdlinterpreter";
72 CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlInterpreterBody);
75 //{{{ CdlInterpreter:: creation
77 // ----------------------------------------------------------------------------
78 // Default constructor. This will only get invoked via the make() static
81 CdlInterpreterBody::CdlInterpreterBody(Tcl_Interp* tcl_interp_arg)
83 CYG_REPORT_FUNCNAME("CdlInterpreter:: default constructor");
84 CYG_REPORT_FUNCARG2XV(this, tcl_interp_arg);
85 CYG_PRECONDITIONC(0 != tcl_interp_arg);
87 tcl_interp = tcl_interp_arg;
101 CYGDBG_MEMLEAK_CONSTRUCTOR();
102 cdlinterpreterbody_cookie = CdlInterpreterBody_Magic;
104 Tcl_SetAssocData(tcl_interp, cdlinterpreter_assoc_data_key, 0, static_cast<ClientData>(this));
107 CYG_POSTCONDITION_THISC();
111 // ----------------------------------------------------------------------------
112 // Create a new CDL interpreter. The underlying Tcl interpreter can be
113 // supplied by the caller, or else a suitable interpreter will be created
114 // with default settings. This default interpreter will only support Tcl,
115 // not Tk. There is no call to any AppInit() function, no support for
116 // autoloading packages, the "unknown" command is not implemented, and
117 // no command files will be read in.
119 // It is convenient to provide immediate access to two Tcl variables,
120 // cdl_version and cdl_interactive.
123 CdlInterpreterBody::make(Tcl_Interp* tcl_interp_arg)
125 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::make", "interpreter %p");
126 CYG_REPORT_FUNCARG1XV(tcl_interp_arg);
128 Tcl_Interp* tcl_interp = tcl_interp_arg;
129 if (0 == tcl_interp) {
130 tcl_interp = Tcl_CreateInterp();
131 if (0 == tcl_interp) {
132 throw std::bad_alloc();
135 // Make sure that this Tcl interpreter is not already used
136 // for another CdlInterpreter object.
137 ClientData tmp = Tcl_GetAssocData(tcl_interp, cdlinterpreter_assoc_data_key, 0);
139 CYG_FAIL("Attempt to use a Tcl interpreter for multiple CDL interpreters");
140 throw std::bad_alloc();
144 CdlInterpreter result = 0;
146 result = new CdlInterpreterBody(tcl_interp);
148 std::string version = Cdl::get_library_version();
149 if (0 == Tcl_SetVar(tcl_interp, "cdl_version", CDL_TCL_CONST_CAST(char*,version.c_str()), TCL_GLOBAL_ONLY)) {
150 throw std::bad_alloc();
152 if (0 == Tcl_SetVar(tcl_interp, "cdl_interactive", CDL_TCL_CONST_CAST(char*, (Cdl::is_interactive() ? "1" : "0")),
154 throw std::bad_alloc();
157 catch(std::bad_alloc) {
158 if (0 == tcl_interp_arg) {
159 Tcl_DeleteInterp(tcl_interp);
163 if (0 == tcl_interp_arg) {
164 result->owns_interp = true;
166 CYG_POSTCONDITION_CLASSC(result);
167 CYG_REPORT_RETVAL(result);
171 // ----------------------------------------------------------------------------
172 // Given a toplevel and a loadable, create a new slave interpreter
173 // for that loadable. There should be master interpreter associated
174 // with the toplevel already.
176 // FIXME: do slave interpreters automatically see cdl_version and
180 CdlInterpreterBody::create_slave(CdlLoadable loadable_arg, bool safe)
182 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::create_slave", "slave %p");
183 CYG_REPORT_FUNCARG3XV(this, loadable_arg, safe);
184 CYG_PRECONDITION_THISC();
185 CYG_PRECONDITION(0 == parent, "slave interpreters cannot be created inside slaves");
186 CYG_PRECONDITION(0 != toplevel, "CDL's slave interpreters need an associated toplevel");
187 CYG_PRECONDITION_CLASSC(loadable_arg);
189 // Slave interpreters need a name. Use a counter to create them uniquely.
190 static cdl_int next_slave = 1;
191 std::string slave_name;
192 Cdl::integer_to_string(next_slave++, slave_name);
193 slave_name = "slave" + slave_name;
195 // FIXME: creating a slave that is not safe appears to fail.
197 Tcl_Interp* slave = Tcl_CreateSlave(interp, CDL_TCL_CONST_CAST(char*, slave_name.c_str()), safe);
199 Tcl_Interp* slave = Tcl_CreateInterp();
202 throw std::bad_alloc();
205 CdlInterpreter result = 0;
207 result = new CdlInterpreterBody(slave);
209 catch(std::bad_alloc) {
210 Tcl_DeleteInterp(slave);
213 result->owns_interp = true;
216 slaves.push_back(result);
218 catch(std::bad_alloc) {
224 result->parent = this;
225 result->set_toplevel(toplevel);
226 result->loadable = loadable_arg;
227 result->set_variable("cdl_version", get_variable("cdl_version"));
228 result->set_variable("cdl_interactive", get_variable("cdl_interactive"));
230 CYG_POSTCONDITION_CLASSC(result);
231 CYG_REPORT_RETVAL(result);
235 // ----------------------------------------------------------------------------
236 // Given an existing interpreter, turn it into a safe one. This is a one-way
239 CdlInterpreterBody::make_safe(void)
241 CYG_REPORT_FUNCNAME("CdlInterpreter::make_safe");
242 CYG_PRECONDITION_THISC();
244 if (0 != Tcl_MakeSafe(tcl_interp)) {
245 throw std::bad_alloc();
251 //{{{ CdlInterpreter:: destructor
253 // ----------------------------------------------------------------------------
254 // Default destructor. It is necessary to worry about any slave
255 // interpreters, but otherwise there are no complications.
257 CdlInterpreterBody::~CdlInterpreterBody()
259 CYG_REPORT_FUNCNAME("CdlInterpreter:: destructor");
260 CYG_REPORT_FUNCARG1XV(this);
261 CYG_PRECONDITION_THISC();
263 cdlinterpreterbody_cookie = CdlInterpreterBody_Invalid;
273 current_commands = 0;
276 // Make sure slave interpreters get deleted before the current one
277 for (std::vector<CdlInterpreter>::iterator i = slaves.begin(); i != slaves.end(); i++) {
282 Tcl_DeleteAssocData(tcl_interp, cdlinterpreter_assoc_data_key);
284 Tcl_DeleteInterp(tcl_interp);
288 CYGDBG_MEMLEAK_DESTRUCTOR();
294 //{{{ CdlInterpreter:: check_this()
296 // ----------------------------------------------------------------------------
300 CdlInterpreterBody::check_this(cyg_assert_class_zeal zeal) const
302 if (CdlInterpreterBody_Magic != cdlinterpreterbody_cookie)
305 CYGDBG_MEMLEAK_CHECKTHIS();
308 case cyg_system_test :
310 if (slaves.size() > 0) {
311 for (std::vector<CdlInterpreter>::const_iterator i = slaves.begin(); i != slaves.end(); i++) {
312 if (!(*i)->check_this(cyg_quick)) {
318 if ((0 != toplevel) && !toplevel->check_this(cyg_quick)) {
321 if ((0 != transaction) && !transaction->check_this(cyg_quick)) {
324 if ((0 != loadable) && !loadable->check_this(cyg_quick)) {
327 if ((0 != container) && !container->check_this(cyg_quick)) {
330 if ((0 != node) && !node->check_this(cyg_quick)) {
334 // For now only the toplevel interpreter should have slaves.
335 if ((0 != parent) && (slaves.size() > 0)) {
338 if( 0 == tcl_interp) {
349 //{{{ CdlInterpreter:: set_toplevel() etc.
351 // ----------------------------------------------------------------------------
352 // Keep track of the current toplevel, container, etc. This gives commands
353 // added to the Tcl interpreter a simple way of figuring out the current
354 // state of the world so that properties get added to the right node, etc.
356 // set_toplevel() should only be called once, for the master interpreter
357 // associated with a toplevel. All slave interpreters inherit this value.
359 // There is no set_loadable(), instead the loadable field is filled in
360 // by create_slave() and cannot be changed.
363 CdlInterpreterBody::get_toplevel() const
365 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_toplevel", "result %p");
366 CYG_REPORT_FUNCARG1XV(this);
367 CYG_PRECONDITION_THISC();
369 CdlToplevel result = toplevel;
370 CYG_REPORT_RETVAL(result);
375 CdlInterpreterBody::get_transaction() const
377 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_transaction", "result %p");
378 CYG_REPORT_FUNCARG1XV(this);
379 CYG_PRECONDITION_THISC();
381 CdlTransaction result = transaction;
382 CYG_REPORT_RETVAL(result);
387 CdlInterpreterBody::get_loadable() const
389 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter:get_loadable", "result %p");
390 CYG_REPORT_FUNCARG1XV(this);
391 CYG_PRECONDITION_THISC();
393 CdlLoadable result = loadable;
394 CYG_REPORT_RETVAL(result);
399 CdlInterpreterBody::get_container() const
401 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_container", "result %p");
402 CYG_REPORT_FUNCARG1XV(this);
403 CYG_PRECONDITION_THISC();
405 CdlContainer result = container;
406 CYG_REPORT_RETVAL(result);
411 CdlInterpreterBody::get_node() const
413 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_node", "result %p");
414 CYG_REPORT_FUNCARG1XV(this);
415 CYG_PRECONDITION_THISC();
417 CdlNode result = node;
418 CYG_REPORT_RETVAL(result);
423 CdlInterpreterBody::get_context() const
425 CYG_REPORT_FUNCNAME("CdlInterpreter::get_context");
426 CYG_REPORT_FUNCARG1XV(this);
427 CYG_PRECONDITION_THISC();
434 CdlInterpreterBody::get_error_fn_ptr() const
436 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_error_fn_ptr", "result %p");
437 CYG_REPORT_FUNCARG1XV(this);
438 CYG_PRECONDITION_THISC();
440 CdlDiagnosticFnPtr result = error_fn_ptr;
441 CYG_REPORT_RETVAL(result);
446 CdlInterpreterBody::get_warning_fn_ptr() const
448 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_warning_fn_ptr", "result %p");
449 CYG_REPORT_FUNCARG1XV(this);
450 CYG_PRECONDITION_THISC();
452 CdlDiagnosticFnPtr result = warning_fn_ptr;
453 CYG_REPORT_RETVAL(result);
458 CdlInterpreterBody::set_toplevel(CdlToplevel new_toplevel)
460 CYG_REPORT_FUNCNAME("CdlInterpreter::set_toplevel");
461 CYG_REPORT_FUNCARG2XV(this, new_toplevel);
462 CYG_PRECONDITION_THISC();
463 CYG_PRECONDITION(0 == toplevel, "changing toplevels is not allowed");
464 CYG_PRECONDITION_CLASSC(new_toplevel);
466 toplevel = new_toplevel;
471 CdlInterpreterBody::set_transaction(CdlTransaction new_transaction)
473 CYG_REPORT_FUNCNAME("CdlInterpreter::set_transaction");
474 CYG_REPORT_FUNCARG2XV(this, new_transaction);
475 CYG_PRECONDITION_THISC();
476 CYG_PRECONDITION_ZERO_OR_CLASSC(new_transaction);
478 transaction = new_transaction;
483 CdlInterpreterBody::push_container(CdlContainer new_container)
485 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::push_container", "result %p");
486 CYG_REPORT_FUNCARG2XV(this, new_container);
487 CYG_PRECONDITION_THISC();
488 CYG_PRECONDITION_CLASSC(new_container);
490 CdlContainer result = container;
491 container = new_container;
492 CYG_REPORT_RETVAL(result);
497 CdlInterpreterBody::pop_container(CdlContainer old_container)
499 CYG_REPORT_FUNCNAME("CdlInterpreter::pop_container");
500 CYG_REPORT_FUNCARG2XV(this, old_container);
501 CYG_PRECONDITION_THISC();
502 CYG_PRECONDITION_ZERO_OR_CLASSC(old_container);
503 CYG_PRECONDITIONC(0 != container);
505 container = old_container;
511 CdlInterpreterBody::push_node(CdlNode new_node)
513 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::push_node", "result %p");
514 CYG_REPORT_FUNCARG2XV(this, new_node);
515 CYG_PRECONDITION_THISC();
516 CYG_PRECONDITION_CLASSC(new_node);
518 CdlNode result = node;
520 CYG_REPORT_RETVAL(result);
525 CdlInterpreterBody::pop_node(CdlNode old_node)
527 CYG_REPORT_FUNCNAME("CdlInterpreter::pop_node");
528 CYG_REPORT_FUNCARG2XV(this, old_node);
529 CYG_PRECONDITION_THISC();
530 CYG_PRECONDITIONC(0 != node);
531 CYG_PRECONDITION_ZERO_OR_CLASSC(old_node);
539 CdlInterpreterBody::push_context(std::string new_context)
541 CYG_REPORT_FUNCNAME("CdlInterpreter::push_context");
542 CYG_REPORT_FUNCARG1XV(this);
543 CYG_PRECONDITION_THISC();
544 CYG_PRECONDITIONC("" != new_context);
546 std::string result = context;
547 context = new_context;
552 CdlInterpreterBody::pop_context(std::string old_context)
554 CYG_REPORT_FUNCNAME("CdlInterpreter::pop_context");
555 CYG_REPORT_FUNCARG1XV(this);
556 CYG_PRECONDITION_THISC();
557 CYG_PRECONDITIONC("" != context);
559 context = old_context;
565 CdlInterpreterBody::push_error_fn_ptr(CdlDiagnosticFnPtr new_fn_ptr)
567 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::push_error_fn_ptr", "result %p");
568 CYG_REPORT_FUNCARG2XV(this, new_fn_ptr);
569 CYG_PRECONDITION_THISC();
570 CYG_PRECONDITIONC(0 != new_fn_ptr);
572 CdlDiagnosticFnPtr result = error_fn_ptr;
573 error_fn_ptr = new_fn_ptr;
574 CYG_REPORT_RETVAL(result);
579 CdlInterpreterBody::pop_error_fn_ptr(CdlDiagnosticFnPtr old_fn_ptr)
581 CYG_REPORT_FUNCNAME("CdlInterpreter::pop_error_fn_ptr");
582 CYG_REPORT_FUNCARG2XV(this, old_fn_ptr);
583 CYG_PRECONDITION_THISC();
584 CYG_PRECONDITIONC(0 != error_fn_ptr);
586 error_fn_ptr = old_fn_ptr;
592 CdlInterpreterBody::push_warning_fn_ptr(CdlDiagnosticFnPtr new_fn_ptr)
594 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::push_warning_fn_ptr", "result %p");
595 CYG_REPORT_FUNCARG2XV(this, new_fn_ptr);
596 CYG_PRECONDITION_THISC();
597 CYG_PRECONDITIONC(0 != new_fn_ptr);
599 CdlDiagnosticFnPtr result = warning_fn_ptr;
600 warning_fn_ptr = new_fn_ptr;
601 CYG_REPORT_RETVAL(result);
606 CdlInterpreterBody::pop_warning_fn_ptr(CdlDiagnosticFnPtr old_fn_ptr)
608 CYG_REPORT_FUNCNAME("CdlInterpreter::pop_warning_fn_ptr");
609 CYG_REPORT_FUNCARG2XV(this, old_fn_ptr);
610 CYG_PRECONDITION_THISC();
611 CYG_PRECONDITIONC(0 != warning_fn_ptr);
613 warning_fn_ptr = old_fn_ptr;
619 //{{{ CdlInterpreter:: get information
621 // ----------------------------------------------------------------------------
622 // Get hold of the underlying Tcl interpreter. This makes it easier to
623 // use miscellaneous Tcl library facilities such as Tcl_SplitList()
625 CdlInterpreterBody::get_tcl_interpreter(void) const
627 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_tcl_interpreter", "interpreter %p");
628 CYG_REPORT_FUNCARG1XV(this);
629 CYG_PRECONDITION_THISC();
631 Tcl_Interp* result = tcl_interp;
632 CYG_REPORT_RETVAL(result);
637 //{{{ CdlInterpreter:: eval()
639 // ----------------------------------------------------------------------------
640 // Evaluate a Cdl script held in a string. The result of this evaluation,
641 // e.g. TCL_OK, is returned directly. The string result is made available
642 // in an in-out parameter.
644 // According to the spec the underlying Tcl_Eval() routine needs to be able
645 // to make temporary changes to the script, so the latter must be held in
646 // writable memory. This requires a copy operation.
649 CdlInterpreterBody::eval(std::string script, std::string& str_result)
651 CYG_REPORT_FUNCNAMETYPE("CdInterpreter::eval", "result %d");
652 CYG_REPORT_FUNCARG1XV(this);
653 CYG_PRECONDITION_THISC();
656 int size = script.size();
658 // Distinguish between results set by the Tcl interpreter and results
659 // set by CDL-related commands running in that interpreter.
664 script.copy(buf, size, 0);
666 result = Tcl_Eval(tcl_interp, buf);
668 char* buf = static_cast<char*>(malloc(script.size() + 1));
670 this->set_result(CdlParse::construct_diagnostic(this, "internal error", "", "Out of memory"));
673 script.copy(buf, size, 0);
675 result = Tcl_Eval(tcl_interp, buf);
680 // The distinction between TCL_OK and TCL_RETURN is probably not worth
682 if (TCL_RETURN == result) {
686 // If we have an error condition that was raised by the Tcl
687 // interpreter rather than by the library, it needs to be
688 // raised up to the library level. That way the error count
689 // etc. are kept accurate.
690 if ((TCL_OK != result) && !cdl_result) {
691 const char* tcl_result = Tcl_GetStringResult(tcl_interp);
692 if ((0 == tcl_result) || ('\0' == tcl_result[0])) {
693 tcl_result = "Internal error, no additional information available.";
695 CdlParse::report_error(this, "", tcl_result);
698 str_result = Tcl_GetStringResult(tcl_interp);
699 CYG_REPORT_RETVAL(result);
703 // Ditto for Tcl Code that comes from a CDL file. Currently this is held
704 // as a string. In future it may be appropriate to store a byte-compiled
707 CdlInterpreterBody::eval_cdl_code(const cdl_tcl_code script, std::string& str_result)
709 CYG_REPORT_FUNCNAMETYPE("CdInterpreter::eval_cdl_code", "result %d");
710 CYG_REPORT_FUNCARG1XV(this);
711 CYG_PRECONDITION_THISC();
714 int size = script.size();
715 // Distinguish between results set by the Tcl interpreter and results
716 // set by CDL-related commands running in that interpreter.
721 script.copy(buf, size, 0);
723 result = Tcl_Eval(tcl_interp, buf);
725 char* buf = static_cast<char*>(malloc(script.size() + 1));
727 this->set_result(CdlParse::construct_diagnostic(this, "internal error", "", "Out of memory"));
730 script.copy(buf, size, 0);
732 result = Tcl_Eval(tcl_interp, buf);
736 // The distinction between TCL_OK and TCL_RETURN is probably not worth
738 if (TCL_RETURN == result) {
742 // If we have an error condition that was raised by the Tcl
743 // interpreter rather than by the library, it needs to be
744 // raised up to the library level. That way the error count
745 // etc. are kept accurate.
746 if ((TCL_OK != result) && !cdl_result) {
747 const char* tcl_result = Tcl_GetStringResult(tcl_interp);
748 if ((0 == tcl_result) || ('\0' == tcl_result[0])) {
749 tcl_result = "Internal error, no additional information available.";
751 CdlParse::report_error(this, "", tcl_result);
754 str_result = Tcl_GetStringResult(tcl_interp);
755 CYG_REPORT_RETVAL(result);
759 // Ditto for evaluating an entire file.
761 CdlInterpreterBody::eval_file(std::string script, std::string& str_result)
763 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::eval_file", "result %d");
764 CYG_REPORT_FUNCARG1XV(this);
765 CYG_PRECONDITION_THISC();
766 CYG_PRECONDITIONC("" != script);
768 // Distinguish between results set by the Tcl interpreter and results
769 // set by CDL-related commands running in that interpreter.
772 int result = Tcl_EvalFile(tcl_interp, CDL_TCL_CONST_CAST(char*, script.c_str()));
773 // The distinction between TCL_OK and TCL_RETURN is probably not worth
775 if (TCL_RETURN == result) {
779 // If we have an error condition that was raised by the Tcl
780 // interpreter rather than by the library, it needs to be
781 // raised up to the library level. That way the error count
782 // etc. are kept accurate.
783 if ((TCL_OK != result) && !cdl_result) {
784 const char* tcl_result = Tcl_GetStringResult(tcl_interp);
785 if ((0 == tcl_result) || ('\0' == tcl_result[0])) {
786 tcl_result = "Internal error, no additional information available.";
788 CdlParse::report_error(this, "", tcl_result);
791 str_result = Tcl_GetStringResult(tcl_interp);
792 CYG_REPORT_RETVAL(result);
796 // Variants for when the result string is of no interest
798 CdlInterpreterBody::eval(std::string script)
800 std::string result_string;
801 return this->eval(script, result_string);
805 CdlInterpreterBody::eval_cdl_code(const cdl_tcl_code script)
807 std::string result_string;
808 return this->eval_cdl_code(script, result_string);
812 CdlInterpreterBody::eval_file(std::string filename)
814 std::string result_string;
815 return this->eval_file(filename, result_string);
819 //{{{ CdlInterpreter:: set_result()
821 // ----------------------------------------------------------------------------
822 // Provide a way of setting an interpreter's result from a command implemented
826 CdlInterpreterBody::set_result(std::string result)
828 CYG_REPORT_FUNCNAME("CdlInterpreter::set_result");
829 CYG_PRECONDITION_THISC();
831 Tcl_SetResult(tcl_interp, const_cast<char*>(result.c_str()), TCL_VOLATILE);
832 this->cdl_result = true;
838 CdlInterpreterBody::result_set_by_cdl()
840 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::result_set_by_cdl", "result %d");
841 CYG_PRECONDITION_THISC();
843 bool result = this->cdl_result;
844 CYG_REPORT_RETVAL(result);
848 // ----------------------------------------------------------------------------
849 // Also allow the result to be extracted again.
851 CdlInterpreterBody::get_result()
853 CYG_REPORT_FUNCNAME("CdlInterpreter::get_result");
854 CYG_PRECONDITION_THISC();
856 std::string result = Tcl_GetStringResult(tcl_interp);
863 //{{{ CdlInterpreter:: add and remove commands
865 // ----------------------------------------------------------------------------
866 // This is the Tcl command proc that gets used for all CdlInterpreter
867 // commands. The ClientData field will be a CdlInterpreterCommand,
868 // i.e. a function pointer. That function needs a pointer to the
869 // CdlInterpreter object, which can be accessed via AssocData.
871 CdlInterpreterBody::tcl_command_proc(ClientData data, Tcl_Interp* tcl_interp, int argc, const char* argv[])
873 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::tcl_command_proc", "result %d");
874 CYG_REPORT_FUNCARG3XV(data, tcl_interp, argc);
875 CYG_PRECONDITIONC(0 != data);
881 CdlInterpreterCommand command;
884 CdlInterpreterCommand command = x.command;
886 data = Tcl_GetAssocData(tcl_interp, cdlinterpreter_assoc_data_key, 0);
887 CdlInterpreter interp = static_cast<CdlInterpreter>(data);
888 CYG_ASSERT_CLASSC(interp);
891 result = (*command)(interp, argc, argv);
892 } catch(std::bad_alloc e) {
893 interp->set_result(CdlParse::construct_diagnostic(interp, "internal error", "", "Out of memory."));
895 } catch(CdlStringException e) {
896 interp->set_result(e.get_message());
899 CYG_FAIL("Unexpected C++ exception");
900 interp->set_result(CdlParse::construct_diagnostic(interp, "internal error", "", "Unexpected C++ exception."));
904 CYG_REPORT_RETVAL(result);
909 CdlInterpreterBody::add_command(std::string name, CdlInterpreterCommand command)
911 CYG_REPORT_FUNCNAME("CdlInterpreter::add_command");
912 CYG_REPORT_FUNCARG2XV(this, command);
914 CYG_PRECONDITION_THISC();
915 CYG_PRECONDITIONC("" != name);
916 CYG_CHECK_FUNC_PTRC(command);
919 CdlInterpreterCommand command;
924 // Tcl 8.4 involves some incompatible API changes
925 #if (TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))
926 if (0 == Tcl_CreateCommand(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()), &tcl_command_proc, x.data, 0)) {
927 throw std::bad_alloc();
930 if (0 == Tcl_CreateCommand(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()),
931 (int (*)(ClientData,Tcl_Interp*, int, char*[])) &tcl_command_proc,
933 throw std::bad_alloc();
940 // ----------------------------------------------------------------------------
941 // Remove a command from an interpreter. This is just a wrapper for the
942 // Tcl_DeleteCommand() routine.
945 CdlInterpreterBody::remove_command(std::string name)
947 CYG_REPORT_FUNCNAME("CdlInterpreter::remove_command");
948 CYG_REPORT_FUNCARG1XV(this);
949 CYG_PRECONDITION_THISC();
950 CYG_PRECONDITIONC("" != name);
952 if (0 != Tcl_DeleteCommand(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()))) {
953 CYG_FAIL("attempt to delete non-existant command");
958 // ----------------------------------------------------------------------------
959 // It is also possible to add and remove whole sets of commands in one go,
960 // keeping track of the current set.
962 std::vector<CdlInterpreterCommandEntry>*
963 CdlInterpreterBody::push_commands(std::vector<CdlInterpreterCommandEntry>& new_commands)
965 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::push_commands", "result %p");
966 CYG_REPORT_FUNCARG2XV(this, &new_commands);
967 CYG_PRECONDITION_THISC();
969 std::vector<CdlInterpreterCommandEntry>* result = current_commands;
970 std::vector<CdlInterpreterCommandEntry>::iterator i;
972 // First uninstall all the old commands, if any
973 if (0 != current_commands) {
974 for (i = current_commands->begin(); i != current_commands->end(); i++) {
975 remove_command(i->name);
979 // Now install the new commands
980 for (i = new_commands.begin(); i != new_commands.end(); i++) {
981 add_command(i->name, i->command);
984 // Remember the current set in case of a subsequent push operation
985 current_commands = &new_commands;
987 CYG_REPORT_RETVAL(result);
992 CdlInterpreterBody::pop_commands(std::vector<CdlInterpreterCommandEntry>* original_commands)
994 CYG_REPORT_FUNCNAME("CdlInterpreter::pop_commands");
995 CYG_REPORT_FUNCARG2XV(this, &original_commands);
996 CYG_PRECONDITION_THISC();
997 CYG_PRECONDITION(0 != current_commands, "no pop without a previous push please");
999 std::vector<CdlInterpreterCommandEntry>::iterator i;
1000 // Uninstall the most recent set of commands
1001 for (i = current_commands->begin(); i != current_commands->end(); i++) {
1002 remove_command(i->name);
1005 // Reinstall the previous set, if any
1006 if (0 != original_commands) {
1007 for (i = original_commands->begin(); i != original_commands->end(); i++) {
1008 add_command(i->name, i->command);
1011 current_commands = original_commands;
1012 CYG_REPORT_RETURN();
1015 std::vector<CdlInterpreterCommandEntry>*
1016 CdlInterpreterBody::get_pushed_commands() const
1018 CYG_REPORT_FUNCNAME("CdlInterpreter::get_pushed_commands");
1019 CYG_REPORT_FUNCARG1XV(this);
1020 CYG_PRECONDITION_THISC();
1022 CYG_REPORT_RETURN();
1023 return current_commands;
1027 //{{{ CdlInterpreter:: variables
1029 // ----------------------------------------------------------------------------
1030 // Provide some more stubs, this time for accessing Tcl global variables.
1032 CdlInterpreterBody::set_variable(std::string name, std::string value)
1034 CYG_REPORT_FUNCNAME("CdlInterpreter::set_variable");
1035 CYG_REPORT_FUNCARG2("this %p, name %s", this, name.c_str());
1036 CYG_PRECONDITION_THISC();
1037 CYG_PRECONDITIONC("" != name);
1038 if (0 == Tcl_SetVar(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()), CDL_TCL_CONST_CAST(char*, value.c_str()), TCL_GLOBAL_ONLY)) {
1039 throw std::bad_alloc();
1041 CYG_REPORT_RETURN();
1045 CdlInterpreterBody::unset_variable(std::string name)
1047 CYG_REPORT_FUNCNAME("CdlInterpreter::unset_variable");
1048 CYG_REPORT_FUNCARG2("this %p, name %s", this, name.c_str());
1049 CYG_PRECONDITION_THISC();
1050 CYG_PRECONDITIONC("" != name);
1052 Tcl_UnsetVar(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()), TCL_GLOBAL_ONLY);
1053 CYG_REPORT_RETURN();
1057 CdlInterpreterBody::get_variable(std::string name)
1059 CYG_REPORT_FUNCNAME("CdlInterpreter::get_variable");
1060 CYG_REPORT_FUNCARG2("this %p, name %s", this, name.c_str());
1061 CYG_PRECONDITION_THISC();
1062 CYG_PRECONDITIONC("" != name);
1064 std::string result = "";
1065 const char *tmp = Tcl_GetVar(tcl_interp, CDL_TCL_CONST_CAST(char*, name.c_str()), TCL_GLOBAL_ONLY);
1070 CYG_REPORT_RETURN();
1075 //{{{ CdlInterpreter:: assoc data
1077 // ----------------------------------------------------------------------------
1078 // Associated data. It is useful to be able to store some C++ data with
1079 // Tcl interpreters, so that the implementations of various commands
1080 // can retrieve details of the current state. Tcl provides the necessary
1081 // underlying support via routines Tcl_SetAssocData() etc., and the
1082 // routines here are just stubs for the underlying Tcl ones.
1085 CdlInterpreterBody::set_assoc_data(const char* key, ClientData data, Tcl_InterpDeleteProc* del_proc)
1087 CYG_REPORT_FUNCNAME("CdlInterpreter::set_assoc_data");
1088 CYG_REPORT_FUNCARG3("this %p, key %s, data %p", this, key, data);
1089 CYG_PRECONDITION_THISC();
1090 CYG_PRECONDITIONC((0 != key) && ('\0' != key[0]));
1092 Tcl_SetAssocData(tcl_interp, CDL_TCL_CONST_CAST(char*, key), del_proc, data);
1093 CYG_REPORT_RETURN();
1097 CdlInterpreterBody::get_assoc_data(const char* key)
1099 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::get_assoc_data", "result %p");
1100 CYG_REPORT_FUNCARG2("this %p, key %s", this, key);
1101 CYG_PRECONDITION_THISC();
1102 CYG_PRECONDITIONC((0 != key) && ('\0' != key[0]));
1104 ClientData result = Tcl_GetAssocData(tcl_interp, CDL_TCL_CONST_CAST(char*, key), 0);
1105 CYG_REPORT_RETVAL(result);
1110 CdlInterpreterBody::delete_assoc_data(const char* key)
1112 CYG_REPORT_FUNCNAME("CdlInterpreter::delete_assoc_data");
1113 CYG_REPORT_FUNCARG2("this %p, key %s", this, key);
1114 CYG_PRECONDITION_THISC();
1115 CYG_PRECONDITIONC((0 != key) && ('\0' != key[0]));
1117 Tcl_DeleteAssocData(tcl_interp, CDL_TCL_CONST_CAST(char*, key));
1118 CYG_REPORT_RETURN();
1122 //{{{ CdlInterpreter:: file I/O
1124 // ----------------------------------------------------------------------------
1125 // Tcl provides file I/O facilities that are already known to be portable
1126 // to the platforms of interest.
1129 CdlInterpreterBody::is_directory(std::string name)
1131 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::is_directory", "result %d");
1132 CYG_REPORT_FUNCARG1XV(this);
1133 CYG_PRECONDITION_THISC();
1134 CYG_PRECONDITIONC("" != name);
1136 bool result = false;
1137 std::string command = "file isdirectory \"" + name + "\"";
1138 std::string tcl_result;
1139 if ((TCL_OK == this->eval(command, tcl_result)) && ("1" == tcl_result)) {
1143 CYG_REPORT_RETVAL(result);
1148 CdlInterpreterBody::is_file(std::string name)
1150 CYG_REPORT_FUNCNAMETYPE("CdlInterpreter::is_file", "result %d");
1151 CYG_REPORT_FUNCARG1XV(this);
1152 CYG_PRECONDITION_THISC();
1153 CYG_PRECONDITIONC("" != name);
1155 bool result = false;
1156 std::string command = "file isfile \"" + name + "\"";
1157 std::string tcl_result;
1158 if ((TCL_OK == this->eval(command, tcl_result)) && ("1" == tcl_result)) {
1162 CYG_REPORT_RETVAL(result);
1166 // ----------------------------------------------------------------------------
1169 CdlInterpreterBody::locate_subdirs(std::string directory, std::vector<std::string>& result)
1171 CYG_REPORT_FUNCNAME("CdlInterpreter::locate_subdirs");
1172 CYG_REPORT_FUNCARG2XV(this, &result);
1173 CYG_PRECONDITION_THISC();
1175 static char locate_subdirs_script[] = "\
1176 set pattern [file join \"$::cdl_locate_subdirs_path\" * .] \n\
1178 foreach entry [glob -nocomplain -- $pattern] { \n\
1179 set entry [file tail [file dirname $entry]] \n\
1180 if {($entry != \"CVS\") && ($entry != \"cvs\")} { \n\
1181 lappend result $entry \n\
1187 std::string tcl_result = "";
1188 set_variable("::cdl_locate_subdirs_path", directory);
1189 if (TCL_OK != eval(locate_subdirs_script, tcl_result)) {
1190 CYG_FAIL("Internal error evaluating Tcl script");
1195 if (TCL_OK != Tcl_SplitList(tcl_interp, CDL_TCL_CONST_CAST(char*, tcl_result.c_str()), &count, CDL_TCL_CONST_CAST(char***, &array))) {
1196 throw std::bad_alloc();
1198 for (int i = 0; i < count; i++) {
1199 result.push_back(array[i]);
1201 Tcl_Free((char*) array);
1203 CYG_REPORT_RETURN();
1206 // ----------------------------------------------------------------------------
1207 // Locating all subdirs requires some simple recursion
1209 CdlInterpreterBody::locate_all_subdirs(std::string directory, std::vector<std::string>& result)
1211 CYG_REPORT_FUNCNAME("CdlInterpreter::locate_all_subdirs");
1212 CYG_REPORT_FUNCARG2XV(this, &result);
1213 CYG_PRECONDITION_THISC();
1214 CYG_PRECONDITIONC("" != directory);
1216 std::vector<std::string> subdirs;
1217 locate_subdirs(directory, subdirs);
1218 std::vector<std::string>::const_iterator i, j;
1220 for (i = subdirs.begin(); i != subdirs.end(); i++) {
1221 result.push_back(*i);
1222 std::vector<std::string> its_subdirs;
1223 locate_all_subdirs(directory + "/" + *i, its_subdirs);
1224 for (j = its_subdirs.begin(); j != its_subdirs.end(); j++) {
1225 result.push_back(*i + "/" + *j);
1229 CYG_REPORT_RETURN();
1232 // ----------------------------------------------------------------------------
1233 // Locating the files in a particular subdirectory. This requires another
1234 // simple Tcl script.
1236 CdlInterpreterBody::locate_files(std::string directory, std::vector<std::string>& result)
1238 CYG_REPORT_FUNCNAME("CdlInterpreter::locate_files");
1239 CYG_REPORT_FUNCARG2XV(this, &result);
1240 CYG_PRECONDITION_THISC();
1241 CYG_PRECONDITIONC("" != directory);
1243 static char locate_files_script[] = "\
1244 set pattern [file join \"$::cdl_locate_files_path\" *] \n\
1246 foreach entry [glob -nocomplain -- $pattern] { \n\
1247 if ([file isdirectory $entry]) { \n\
1250 lappend result [file tail $entry] \n\
1255 std::string tcl_result;
1256 set_variable("::cdl_locate_files_path", directory);
1257 if (TCL_OK != eval(locate_files_script, tcl_result)) {
1258 CYG_FAIL("Internal error evaluating Tcl script");
1262 if (TCL_OK != Tcl_SplitList(tcl_interp, CDL_TCL_CONST_CAST(char*, tcl_result.c_str()), &count, CDL_TCL_CONST_CAST(char***, &array))) {
1263 throw std::bad_alloc();
1265 for (int i = 0; i < count; i++) {
1266 result.push_back(array[i]);
1268 Tcl_Free((char*) array);
1270 CYG_REPORT_RETURN();
1273 // ----------------------------------------------------------------------------
1274 // Locating all files can be achieved by combining locate_all_subdirs()
1275 // and locate_files().
1277 CdlInterpreterBody::locate_all_files(std::string directory, std::vector<std::string>& result)
1279 CYG_REPORT_FUNCNAME("CdlInterpreter::locate_all_files");
1280 CYG_REPORT_FUNCARG2XV(this, &result);
1281 CYG_PRECONDITION_THISC();
1282 CYG_PRECONDITIONC("" != directory);
1284 std::vector<std::string> files;
1285 std::vector<std::string>::const_iterator i, j;
1286 locate_files(directory, files);
1287 for (i = files.begin(); i != files.end(); i++) {
1288 result.push_back(*i);
1291 std::vector<std::string> all_subdirs;
1292 locate_all_subdirs(directory, all_subdirs);
1293 for (i = all_subdirs.begin(); i != all_subdirs.end(); i++) {
1294 std::vector<std::string> subdir_files;
1295 locate_files(directory + "/" + *i, subdir_files);
1296 for (j = subdir_files.begin(); j != subdir_files.end(); j++) {
1297 result.push_back(*i + "/" + *j);
1301 CYG_REPORT_RETURN();
1304 // ----------------------------------------------------------------------------
1305 // Write some data to a file, throwing an I/O exception on failure. This
1306 // functionality is needed whenever savefile data is generated, it is
1307 // convenient to have a utility function to do the job. Also, performing
1308 // the Tcl_Write involves passing const data as a non-const argument:
1309 // if this ever causes problems in future it is a good idea to isolate
1310 // the problem here.
1313 CdlInterpreterBody::write_data(Tcl_Channel chan, std::string data)
1315 CYG_REPORT_FUNCNAME("CdlInterpreter::write_data");
1316 CYG_REPORT_FUNCARG2XV(this, chan);
1317 CYG_PRECONDITION_THISC();
1319 if (-1 == Tcl_Write(chan, CDL_TCL_CONST_CAST(char*, data.data()), data.size())) {
1320 std::string msg = "Unexpected error writing to file " + this->get_context() + " : " + Tcl_PosixError(tcl_interp);
1321 throw CdlInputOutputException(msg);
1324 CYG_REPORT_RETURN();
1328 //{{{ CdlInterpreter:: quote() etc.
1330 // ----------------------------------------------------------------------------
1331 // Given a string, quote it in such a way that the Tcl interpreter will
1332 // process it as a single word, but keep the result as human-readable
1333 // as possible. If there are no special characters then just return the
1334 // string itself. Otherwise quoting is necessary.
1336 // The choice is between braces and double quotes. Generally braces
1337 // are better and more consistent, but there is a problem if the
1338 // string argument itself contains braces. These could be
1339 // backslash-escaped, but the Tcl interpreter will not automatically
1340 // remove the backslashes so we would end up with a discrepancy
1341 // between the original data and what is seen by the parser. In this
1342 // case quote marks have to be used instead.
1344 // NOTE: this code may not behave sensibly when it comes to i18n
1348 CdlInterpreterBody::quote(std::string src)
1350 CYG_REPORT_FUNCNAME("CdlInterpreter::quote");
1352 std::string result = "";
1353 bool contains_specials = false;
1356 if (0 == src.size()) {
1357 // An empty string. The best way to represent this is an empty
1360 CYG_REPORT_RETURN();
1364 if ('#' == src[0]) {
1365 contains_specials = true;
1368 for (i = 0; (i < src.size()) && !contains_specials; i++) {
1369 if (isspace(src[i])) {
1370 contains_specials = true;
1383 contains_specials = true;
1391 if (!contains_specials) {
1394 // If the data is a multiline item, it is better to start it in column 0.
1395 // Unfortunately there is the question of what to do with the opening
1396 // quote. Putting it on the current line, followed by a backslash-escaped
1397 // newline, introduces a space into the string. If the string begins with
1398 // a space anyway then arguably this would be harmless, but it could
1399 // be confusing to the user. Putting the opening double quote into column 0
1400 // means that the first line of data is indented relative to the rest of
1401 // the data, but still appears to be the best alternative.
1402 if (src.find('\n') != std::string::npos) {
1406 for (i = 0; i < src.size(); i++) {
1425 CYG_REPORT_RETURN();
1429 // ----------------------------------------------------------------------------
1430 // Given some data which may be multiline, return a string which corresponds
1431 // to that data turned into a comment.
1434 CdlInterpreterBody::multiline_comment(const std::string& orig, int first_indent, int second_indent)
1436 CYG_REPORT_FUNCNAME("CdlInterpreter::multiline_comment");
1438 std::string indent = std::string(first_indent, ' ') + "# " + std::string(second_indent, ' ');
1439 std::string result = "";
1440 bool indent_needed = true;
1442 std::string::const_iterator str_i;
1443 for (str_i = orig.begin(); str_i != orig.end(); str_i++) {
1444 if (indent_needed) {
1446 indent_needed = false;
1449 if ('\n' == *str_i) {
1450 indent_needed = true;
1454 CYG_REPORT_RETURN();
1458 // ----------------------------------------------------------------------------
1459 // Given some data, append it to the current line and add additional commented
1460 // and indented lines as required.
1462 CdlInterpreterBody::extend_comment(const std::string& orig, int first_indent, int second_indent)
1464 CYG_REPORT_FUNCNAME("CdlInterpreter::extend_comment");
1466 std::string indent = std::string(first_indent, ' ') + "# " + std::string(second_indent, ' ');
1467 std::string result = "";
1468 bool indent_needed = false;
1470 std::string::const_iterator str_i;
1471 for (str_i = orig.begin(); str_i != orig.end(); str_i++) {
1472 if (indent_needed) {
1474 indent_needed = false;
1477 if ('\n' == *str_i) {
1478 indent_needed = true;
1482 CYG_REPORT_RETURN();