#include #include #include #include #include #include "TclUtils/TclCommandObject.hh" #define tcl_require_argc(N) do {\ if (argc != N)\ {\ Tcl_AppendResult(interp, getTclHandle(), " ", argv[0],\ " : wrong # of arguments", 0);\ return TCL_ERROR;\ }\ } while (0); unsigned long TclCommandObject::_objectCounter = 0; TclCommandObject::TclCommandObject() : _objNum(_objectCounter++), _handleName(0), _classname(0) { } TclCommandObject::~TclCommandObject() { if (_handleName) { // _classname acts as a guard variable. We need it because // the object may be deleted both from Tcl and from C++. // Compare with _delete method. if (_classname) { _classname = 0; Tcl_DeleteCommand(_interp, _handleName); } delete [] _handleName; _handleName = 0; } } int TclCommandObject::setupTclHandle(Tcl_Interp * const interp, const char * const handle, const char * const classname, const bool overwrite) { assert(interp); // This function is not necessarily called from Tcl, therefore // clean interpreter result is not guaranteed. Reset it. Tcl_ResetResult(interp); if (handle == 0 || classname == 0) { Tcl_AppendResult(interp, "TclCommandObject::setupTclHandle: ", "one of the input pointers is 0", 0); return TCL_ERROR; } if (_handleName) { Tcl_AppendResult(interp, "can't assign new name \"", handle, "\" to the existing command named \"", _handleName, "\"", 0); return TCL_ERROR; } if (!overwrite || (*handle && !*classname)) { // Check that a command with this name does not exist yet Tcl_CmdInfo cmdInfo; std::string tmp(handle); if (Tcl_GetCommandInfo(interp, const_cast(tmp.c_str()),&cmdInfo)) { Tcl_AppendResult(interp, "command named \"", handle, "\" already exists", 0); return TCL_ERROR; } } // Make up a command name if necessary std::ostrstream os; if (!*handle) os << classname << "::TclCommandObject" << _objNum << std::ends; else os << handle << std::ends; // Create the command std::string command(os.str()); if (Tcl_CreateCommand(interp, const_cast(command.c_str()), reinterpret_cast(_handleCommand), reinterpret_cast(this), reinterpret_cast(_delete)) == NULL) return TCL_ERROR; // We have succeeded. Remember the state. _interp = interp; _classname = classname; int size = strlen(os.str())+1; _handleName = new char[size]; memcpy(_handleName, os.str(), size); Tcl_AppendResult(interp, os.str(), 0); return TCL_OK; } const char * TclCommandObject::getTclHandle(void) const { return _handleName; } const char * TclCommandObject::getTclClassName(void) const { return _classname; } void TclCommandObject::_delete(ClientData clientData) { TclCommandObject *obj = reinterpret_cast(clientData); if (obj->_classname) { obj->_classname = 0; delete obj; } } int TclCommandObject::_handleCommand(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { TclCommandObject *obj = reinterpret_cast(clientData); if (argc == 1) return obj->parseCommand(interp, 0, 0); else return obj->parseCommand(interp, argc-1, argv+1); } extern "C" { static int charcompare(const void *a, const void *b) { return strcmp(*(char **)a, *(char **)b); } } int TclCommandObject::_sortTopicListUnique(char *unsorted, std::string *result) { Tcl_Interp *listproc = Tcl_CreateInterp(); int listlen = 0; char **listelem = 0; if (Tcl_SplitList(listproc, unsorted, &listlen, &listelem) != TCL_OK) { #if (TCL_MAJOR_VERSION > 7) *result = Tcl_GetStringResult(listproc); #else *result = listproc->result; #endif Tcl_Free(reinterpret_cast(listelem)); Tcl_DeleteInterp(listproc); return TCL_ERROR; } qsort(listelem, listlen, sizeof(char *), charcompare); result->erase(); if (listlen > 0) { *result += listelem[0]; for (int i=1; i(listelem)); Tcl_DeleteInterp(listproc); return TCL_OK; } int TclCommandObject::parseCommand(Tcl_Interp * const interp, int argc, char *argv[]) { if (argc == 0) { Tcl_AppendResult(interp, getTclHandle(), 0); } else if (strcmp(argv[0], "help") == 0) { struct _helpInfo { char *command; char *args; char *info; } helpData[] = { {"class", "", "Returns the name of the object's class."}, {"help", "", "Returns the list of available help topics."}, {"help", "topic", "Returns a help message on a given topic."}, {"methods", "", "Returns the list of Tcl methods defined for this object."} }; int size = sizeof(helpData)/sizeof(helpData[0]); if (argc == 1) { for (int i=0; i 7) char *topics = Tcl_GetStringResult(interp); #else char *topics = interp->result; #endif std::string tmp; int status = _sortTopicListUnique(topics, &tmp); Tcl_ResetResult(interp); if (status == TCL_OK) { Tcl_AppendResult(interp, "Please type \"", getTclHandle(), " help topic\" to get information", " on a particular\ntopic. Available", " topics are: ", tmp.c_str(), ".", 0); } else { Tcl_AppendResult(interp, "Please type \"", getTclHandle(), " help topic\" to get information", " on a particular\ntopic. Available", " topics are: ", ".", 0); } return status; } tcl_require_argc(2); bool found = false; std::ostrstream answer; for (int i=0; i