00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "TclCommand.h"
00039 #include "HelpCommand.h"
00040 #include "DebugCommand.h"
00041 #include "LogCommand.h"
00042
00043 #include "debug/DebugUtils.h"
00044 #include "io/NetUtils.h"
00045 #include "thread/SpinLock.h"
00046 #include "util/StringBuffer.h"
00047 #include "util/InitSequencer.h"
00048
00049 namespace oasys {
00050
00051
00052
00053
00054
00055
00056
00057 TclCommandInterp* TclCommandInterp::instance_;
00058 TclCommandList* TclCommandInterp::auto_reg_ = NULL;
00059
00060 #include "command-init-tcl.c"
00061
00062 TclCommandInterp::TclCommandInterp()
00063 : Logger("TclCommandInterp", "/command")
00064 {}
00065
00066 int
00067 TclCommandInterp::do_init(char* argv0, bool no_default_cmds)
00068 {
00069 interp_ = Tcl_CreateInterp();
00070 lock_ = new SpinLock();
00071 Tcl_Preserve(interp_);
00072
00073
00074
00075
00076 Tcl_FindExecutable(argv0);
00077
00078
00079
00080
00081 if (Tcl_Init(interp_) != TCL_OK) {
00082 StringBuffer err("initialization problem calling Tcl_Init: %s\n"
00083 "(this is not a fatal error, continuing initialization...)\n\n",
00084 interp_->result);
00085 log_multiline(LOG_WARN, err.c_str());
00086 }
00087
00088
00089 if (auto_reg_) {
00090 ASSERT(auto_reg_);
00091 while (!auto_reg_->empty()) {
00092 TclCommand* m = auto_reg_->front();
00093 auto_reg_->pop_front();
00094 reg(m);
00095 }
00096
00097 delete auto_reg_;
00098 auto_reg_ = NULL;
00099 }
00100
00101
00102 if (! no_default_cmds) {
00103 HelpCommand* help = new HelpCommand();
00104 reg(help);
00105
00106 LogCommand* log = new LogCommand();
00107 reg(log);
00108
00109 DebugCommand* debug = new DebugCommand();
00110 reg(debug);
00111 }
00112
00113
00114
00115 char* cmd = strdup(INIT_COMMAND);
00116 if (Tcl_Eval(interp_, cmd) != TCL_OK) {
00117 log_err("error in init commands: \"%s\"", interp_->result);
00118 return TCL_ERROR;
00119 }
00120 free(cmd);
00121
00122 return TCL_OK;
00123 }
00124
00125 TclCommandInterp::~TclCommandInterp()
00126 {
00127 log_notice("shutting down interpreter");
00128 TclCommandList::iterator iter;
00129 for (iter = commands_.begin();
00130 iter != commands_.end();
00131 ++iter)
00132 {
00133 log_debug("deleting %s command", (*iter)->name_.c_str());
00134 delete *iter;
00135 }
00136
00137 log_debug("all commands deleted");
00138
00139 commands_.clear();
00140
00141 Tcl_DeleteInterp(interp_);
00142 Tcl_Release(interp_);
00143
00144 delete lock_;
00145 }
00146
00147 void
00148 TclCommandInterp::shutdown()
00149 {
00150 delete instance_;
00151 instance_ = NULL;
00152 }
00153
00154 int
00155 TclCommandInterp::init(char* argv0, bool no_default_cmds)
00156 {
00157 ASSERT(instance_ == NULL);
00158 instance_ = new TclCommandInterp();
00159
00160 return instance_->do_init(argv0, no_default_cmds);
00161 }
00162
00163 int
00164 TclCommandInterp::exec_file(const char* file)
00165 {
00166 int err;
00167 ScopeLock l(lock_, "TclCommandInterp::exec_file");
00168
00169 log_debug("executing command file %s", file);
00170
00171 err = Tcl_EvalFile(interp_, (char*)file);
00172
00173 if (err != TCL_OK) {
00174 logf(LOG_ERR, "error: line %d: '%s':\n%s",
00175 interp_->errorLine, Tcl_GetStringResult(interp_),
00176 Tcl_GetVar(interp_, "errorInfo", TCL_GLOBAL_ONLY));
00177 }
00178
00179 return err;
00180 }
00181
00182 int
00183 TclCommandInterp::exec_command(const char* command)
00184 {
00185 int err;
00186 ScopeLock l(lock_, "TclCommandInterp::exec_command");
00187
00188
00189 if (command[0] == '\0')
00190 return TCL_OK;
00191
00192
00193
00194 char* buf = strdup(command);
00195
00196 log_debug("executing command '%s'", buf);
00197
00198 err = Tcl_Eval(interp_, buf);
00199
00200 free(buf);
00201
00202 if (err != TCL_OK) {
00203 logf(LOG_ERR, "error: line %d: '%s':\n%s",
00204 interp_->errorLine, Tcl_GetStringResult(interp_),
00205 Tcl_GetVar(interp_, "errorInfo", TCL_GLOBAL_ONLY));
00206 }
00207
00208 return err;
00209 }
00210
00211 void
00212 TclCommandInterp::command_server(const char* prompt,
00213 in_addr_t addr, u_int16_t port)
00214 {
00215 log_debug("starting command server on %s:%d", intoa(addr), port);
00216 StringBuffer cmd("command_server \"%s\" %s %d", prompt, intoa(addr), port);
00217
00218 if (Tcl_Eval(interp_, const_cast<char*>(cmd.c_str())) != TCL_OK) {
00219 log_err("tcl error starting command_server: \"%s\"",
00220 interp_->result);
00221 }
00222 }
00223
00224 void
00225 TclCommandInterp::command_loop(const char* prompt)
00226 {
00227 StringBuffer cmd("command_loop \"%s\"", prompt);
00228
00229 if (Tcl_Eval(interp_, const_cast<char*>(cmd.c_str())) != TCL_OK) {
00230 log_err("tcl error in command_loop: \"%s\"", interp_->result);
00231 }
00232 }
00233
00234 void
00235 TclCommandInterp::event_loop()
00236 {
00237 if (Tcl_Eval(interp_, "event_loop") != TCL_OK) {
00238 log_err("tcl error in event_loop: \"%s\"", interp_->result);
00239 }
00240 }
00241
00242 void
00243 TclCommandInterp::exit_event_loop()
00244 {
00245 if (Tcl_Eval(interp_, "exit_event_loop") != TCL_OK) {
00246 log_err("tcl error in event_loop: \"%s\"", interp_->result);
00247 }
00248 }
00249
00250 void
00251 TclCommandInterp::reg(TclCommand *command)
00252 {
00253 ScopeLock l(lock_, "TclCommandInterp::reg");
00254
00255 command->logf(LOG_DEBUG, "%s command registering", command->name());
00256
00257 Tcl_CmdInfo info;
00258 if (Tcl_GetCommandInfo(interp_, (char*)command->name(), &info) != 0) {
00259 log_warn("re-registering command %s over existing command",
00260 command->name());
00261 }
00262
00263 Tcl_CreateObjCommand(interp_,
00264 const_cast<char*>(command->name()),
00265 TclCommandInterp::tcl_cmd,
00266 (ClientData)command,
00267 NULL);
00268
00269 commands_.push_front(command);
00270 }
00271
00272 bool
00273 TclCommandInterp::lookup(const char* command, TclCommand** commandp)
00274 {
00275 Tcl_CmdInfo info;
00276
00277 if (Tcl_GetCommandInfo(interp_, (char*)command, &info) == 0) {
00278 log_debug("lookup tcl command %s: does not exist", command);
00279 return false;
00280 }
00281
00282 if (info.objProc == TclCommandInterp::tcl_cmd) {
00283 log_debug("lookup tcl command %s: exists and is TclCommand %p",
00284 command, info.clientData);
00285
00286 if (commandp)
00287 *commandp = (TclCommand*)info.objClientData;
00288
00289 } else {
00290 log_debug("lookup tcl command %s: exists but is not a TclCommand",
00291 command);
00292 }
00293
00294 return true;
00295 }
00296
00297 void
00298 TclCommandInterp::auto_reg(TclCommand *command)
00299 {
00300
00301
00302 ASSERT(instance_ == NULL);
00303
00304
00305
00306 if (!auto_reg_)
00307 auto_reg_ = new TclCommandList();
00308
00309 auto_reg_->push_back(command);
00310 }
00311
00312 void
00313 TclCommandInterp::reg_atexit(void(*fn)(void*), void* data)
00314 {
00315 ScopeLock l(lock_, "TclCommandInterp::reg_atexit");
00316 Tcl_CreateExitHandler(fn, data);
00317 }
00318
00319 int
00320 TclCommandInterp::tcl_cmd(ClientData client_data, Tcl_Interp* interp,
00321 int objc, Tcl_Obj* const* objv)
00322 {
00323 TclCommand* command = (TclCommand*)client_data;
00324
00325
00326 if (command->do_builtins_)
00327 {
00328 if (objc == 2)
00329 {
00330 const char* cmd = Tcl_GetStringFromObj(objv[1], NULL);
00331 if (strcmp(cmd, "info") == 0) {
00332 return command->cmd_info(interp);
00333 }
00334 }
00335 else if (objc > 2)
00336 {
00337 const char* cmd = Tcl_GetStringFromObj(objv[1], NULL);
00338 if (strcmp(cmd, "set") == 0) {
00339 return command->cmd_set(objc, (Tcl_Obj**)objv, interp);
00340 }
00341 }
00342 }
00343
00344 return command->exec(objc, (Tcl_Obj**)objv, interp);
00345 }
00346
00347 void
00348 TclCommandInterp::set_result(const char* result)
00349 {
00350 Tcl_SetResult(interp_, (char*)result, TCL_VOLATILE);
00351 }
00352
00353 void
00354 TclCommandInterp::set_objresult(Tcl_Obj* obj)
00355 {
00356 Tcl_SetObjResult(interp_, obj);
00357 }
00358
00359 void
00360 TclCommandInterp::append_result(const char* result)
00361 {
00362 Tcl_AppendResult(interp_, (char*)result, NULL);
00363 }
00364
00365 void
00366 TclCommandInterp::vresultf(const char* fmt, va_list ap, bool append)
00367 {
00368 StringBuffer buf;
00369 buf.vappendf(fmt, ap);
00370
00371 if (append) {
00372 Tcl_AppendResult(interp_, buf.c_str(), NULL);
00373 } else {
00374 Tcl_SetResult(interp_, const_cast<char*>(buf.c_str()), TCL_VOLATILE);
00375 }
00376 }
00377
00378 void
00379 TclCommandInterp::resultf(const char* fmt, ...)
00380 {
00381 va_list ap;
00382 va_start(ap, fmt);
00383 vresultf(fmt, ap, false);
00384 va_end(ap);
00385 }
00386
00387 void
00388 TclCommandInterp::append_resultf(const char* fmt, ...)
00389 {
00390 va_list ap;
00391 va_start(ap, fmt);
00392 vresultf(fmt, ap, true);
00393 va_end(ap);
00394 }
00395
00396 void
00397 TclCommandInterp::wrong_num_args(int argc, const char** argv, int parsed,
00398 int min, int max)
00399 {
00400 set_result("wrong number of arguments to '");
00401 append_result(argv[0]);
00402
00403 for (int i = 1; i < parsed; ++i) {
00404 append_result(" ");
00405 append_result(argv[i]);
00406 }
00407 append_result("'");
00408
00409 if (max == min) {
00410 append_resultf(" expected %d, got %d", min, argc);
00411 } else if (max == INT_MAX) {
00412 append_resultf(" expected at least %d, got %d", min, argc);
00413 } else {
00414 append_resultf(" expected %d - %d, got %d", min, max, argc);
00415 }
00416 }
00417
00418 void
00419 TclCommandInterp::wrong_num_args(int objc, Tcl_Obj** objv, int parsed,
00420 int min, int max)
00421 {
00422 char* argv[objc];
00423 for (int i = 0; i < objc; ++i) {
00424 argv[i] = Tcl_GetStringFromObj(objv[i], NULL);
00425 }
00426 wrong_num_args(objc, (const char**)argv, parsed, min, max);
00427 }
00428
00429 const char*
00430 TclCommandInterp::get_result()
00431 {
00432 return Tcl_GetStringResult(interp_);
00433 }
00434
00435
00436
00437
00438
00439
00440 TclCommand::TclCommand(const char* name, const char* theNamespace)
00441 : Logger("TclCommand", "/command/%s", name),
00442 do_builtins_(true)
00443 {
00444
00445 if (theNamespace != 0) {
00446 name_ += theNamespace;
00447 name_ += "::";
00448 }
00449
00450 name_ += name;
00451 }
00452
00453 TclCommand::~TclCommand()
00454 {
00455 BindingTable::iterator iter;
00456 for (iter = bindings_.begin(); iter != bindings_.end(); ++iter) {
00457 delete iter->second;
00458 }
00459 bindings_.clear();
00460 }
00461
00462 int
00463 TclCommand::exec(int objc, Tcl_Obj** objv, Tcl_Interp* interp)
00464 {
00465
00466
00467 char* argv[objc];
00468
00469 for (int i = 0; i < objc; ++i) {
00470 argv[i] = Tcl_GetStringFromObj(objv[i], NULL);
00471 }
00472
00473 return exec(objc, (const char**) argv, interp);
00474 }
00475
00476 int
00477 TclCommand::exec(int argc, const char** argv, Tcl_Interp* interp)
00478 {
00479 (void)argc;
00480 (void)interp;
00481
00482 resultf("command %s unknown argument", argv[0]);
00483 return TCL_ERROR;
00484 }
00485
00486 void
00487 TclCommand::resultf(const char* fmt, ...)
00488 {
00489 va_list ap;
00490 va_start(ap, fmt);
00491 TclCommandInterp::instance()->vresultf(fmt, ap, false);
00492 va_end(ap);
00493 }
00494
00495 void
00496 TclCommand::append_resultf(const char* fmt, ...)
00497 {
00498 va_list ap;
00499 va_start(ap, fmt);
00500 TclCommandInterp::instance()->vresultf(fmt, ap, true);
00501 va_end(ap);
00502 }
00503
00504
00505 int
00506 TclCommand::cmd_info(Tcl_Interp* interp)
00507 {
00508 (void)interp;
00509
00510 StringBuffer buf;
00511
00512 for (BindingTable::iterator itr = bindings_.begin();
00513 itr != bindings_.end(); ++itr)
00514 {
00515 buf.appendf("%s ", (*itr).first.c_str());
00516 }
00517
00518 set_result(buf.c_str());
00519 return TCL_OK;
00520 }
00521
00522 int
00523 TclCommand::cmd_set(int objc, Tcl_Obj** objv, Tcl_Interp* interp)
00524 {
00525 ASSERT(objc >= 2);
00526
00527
00528 if (objc < 3 || objc > 4) {
00529 resultf("wrong number of args: expected 3-4, got %d", objc);
00530 return TCL_ERROR;
00531 }
00532
00533 const char* var = Tcl_GetStringFromObj(objv[2], NULL);
00534 Tcl_Obj* val = objv[3];
00535
00536 BindingTable::iterator itr;
00537 itr = bindings_.find(var);
00538
00539 if (itr == bindings_.end()) {
00540 resultf("set: binding for %s does not exist", var);
00541 return TCL_ERROR;
00542 }
00543
00544
00545 Binding* b = (*itr).second;
00546
00547 if (objc == 4)
00548 {
00549 switch(b->type_)
00550 {
00551 case BINDING_INT:
00552 if (Tcl_GetIntFromObj(interp, val, b->val_.intval_) != TCL_OK) {
00553 resultf("%s set: %s not an integer value",
00554 Tcl_GetStringFromObj(objv[0], 0),
00555 Tcl_GetStringFromObj(val, 0));
00556 return TCL_ERROR;
00557 }
00558 break;
00559
00560 case BINDING_INT16:
00561 int intval;
00562 if (Tcl_GetIntFromObj(interp, val, &intval) != TCL_OK) {
00563 resultf("%s set: %s not an integer value",
00564 Tcl_GetStringFromObj(objv[0], 0),
00565 Tcl_GetStringFromObj(val, 0));
00566 return TCL_ERROR;
00567 }
00568
00569 if (intval > 0xffff) {
00570 resultf("%s set: %s too big for short integer",
00571 Tcl_GetStringFromObj(objv[0], 0),
00572 Tcl_GetStringFromObj(val, 0));
00573 return TCL_ERROR;
00574 }
00575
00576 *(b->val_.int16val_) = intval;
00577 break;
00578
00579 case BINDING_DOUBLE:
00580 if (Tcl_GetDoubleFromObj(interp, val, b->val_.doubleval_) != TCL_OK) {
00581 resultf("%s set: %s not an double value",
00582 Tcl_GetStringFromObj(objv[0], 0),
00583 Tcl_GetStringFromObj(val, 0));
00584 return TCL_ERROR;
00585 }
00586 break;
00587
00588 case BINDING_BOOL:
00589 int boolval;
00590 if (Tcl_GetBooleanFromObj(interp, val, &boolval) != TCL_OK) {
00591 resultf("%s set: %s not an integer value",
00592 Tcl_GetStringFromObj(objv[0], 0),
00593 Tcl_GetStringFromObj(val, 0));
00594 return TCL_ERROR;
00595 }
00596 *(b->val_.boolval_) = boolval;
00597 break;
00598
00599 case BINDING_ADDR:
00600 {
00601 char* addr = Tcl_GetStringFromObj(val, 0);
00602 if (gethostbyname(addr, b->val_.addrval_) != 0) {
00603 resultf("%s set: invalid value '%s' for addr",
00604 Tcl_GetStringFromObj(objv[0], 0), addr);
00605 return TCL_ERROR;
00606 }
00607 break;
00608
00609 }
00610 case BINDING_STRING:
00611 b->val_.stringval_->assign(Tcl_GetStringFromObj(val, 0));
00612 break;
00613
00614 default:
00615 logf(LOG_CRIT, "unimplemented binding type %d", b->type_);
00616 ASSERT(0);
00617 }
00618 }
00619
00620 switch(b->type_)
00621 {
00622 case BINDING_INT:
00623 resultf("%d", *(b->val_.intval_));
00624 break;
00625
00626 case BINDING_INT16:
00627 resultf("%d", *(b->val_.int16val_));
00628 break;
00629
00630 case BINDING_DOUBLE:
00631 resultf("%f", *(b->val_.doubleval_));
00632 break;
00633
00634 case BINDING_BOOL:
00635 if (*(b->val_.boolval_))
00636 set_result("true");
00637 else
00638 set_result("false");
00639 break;
00640
00641 case BINDING_ADDR:
00642 resultf("%s", intoa(*(b->val_.addrval_)));
00643 break;
00644
00645 case BINDING_STRING:
00646 set_result(b->val_.stringval_->c_str());
00647 break;
00648
00649 default:
00650 logf(LOG_CRIT, "unimplemented binding type %d", b->type_);
00651 ASSERT(0);
00652 }
00653
00654 return 0;
00655 }
00656
00657
00658 #define BIND_FUNCTIONS(_fn, _type, _typecode) \
00659 void \
00660 _fn(const char* name, _type* val, const char* help) \
00661 { \
00662 if (bindings_.find(name) != bindings_.end()) \
00663 { if (Log::initialized()) { \
00664 log_warn("warning, binding for %s already exists", name); \
00665 } \
00666 } \
00667 \
00668 if (Log::initialized()) { \
00669 log_debug("creating %s binding for %s -> %p", \
00670 #_type, name, val); \
00671 } \
00672 \
00673 bindings_[name] = new Binding(_typecode, val); \
00674 StringBuffer subcmd("set %s", name); \
00675 add_to_help(subcmd.c_str(), help); \
00676 } \
00677 \
00678 void \
00679 _fn(const char* name, _type* val, _type initval, const char* help) \
00680 { \
00681 *val = initval; \
00682 if (bindings_.find(name) != bindings_.end()) \
00683 { \
00684 if (Log::initialized()) { \
00685 log_warn("warning, binding for %s already exists", name); \
00686 } \
00687 } \
00688 \
00689 \
00690 if (Log::initialized()) { \
00691 log_debug("creating %s binding for %s -> %p", \
00692 #_type, name, val); \
00693 } \
00694 \
00695 bindings_[name] = new Binding(_typecode, val); \
00696 StringBuffer subcmd("set %s", name); \
00697 add_to_help(subcmd.c_str(), help); \
00698 }
00699
00700 BIND_FUNCTIONS(TclCommand::bind_i, int, BINDING_INT);
00701 BIND_FUNCTIONS(TclCommand::bind_i, int16_t, BINDING_INT16);
00702 BIND_FUNCTIONS(TclCommand::bind_d, double, BINDING_DOUBLE);
00703 BIND_FUNCTIONS(TclCommand::bind_b, bool, BINDING_BOOL);
00704 BIND_FUNCTIONS(TclCommand::bind_addr, in_addr_t, BINDING_ADDR);
00705
00706 void
00707 TclCommand::bind_s(const char* name, std::string* val,
00708 const char* initval, const char* help)
00709 {
00710 if (initval)
00711 val->assign(initval);
00712
00713 if (bindings_.find(name) != bindings_.end()) {
00714 if (Log::initialized()) {
00715 log_warn("warning, binding for %s already exists", name);
00716 }
00717 }
00718
00719 if (Log::initialized()) {
00720 log_debug("creating string binding for %s -> %p", name, val);
00721 }
00722
00723 bindings_[name] = new Binding(BINDING_STRING, val);
00724 StringBuffer subcmd("set %s", name);
00725 add_to_help(subcmd.c_str(), help);
00726 }
00727
00728 void
00729 TclCommand::unbind(const char* name)
00730 {
00731 BindingTable::iterator iter = bindings_.find(name);
00732
00733 if (iter == bindings_.end()) {
00734 if (Log::initialized()) {
00735 log_warn("warning, binding for %s doesn't exist", name);
00736 }
00737 return;
00738 }
00739
00740 if (Log::initialized()) {
00741 log_debug("removing binding for %s", name);
00742 }
00743
00744 Binding* old = iter->second;
00745 bindings_.erase(iter);
00746
00747 delete old;
00748 }
00749
00750 }