00001
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
00033
00034 #ifndef GROOVX_TCL_COMMANDGROUP_CC_UTC20050628162421_DEFINED
00035 #define GROOVX_TCL_COMMANDGROUP_CC_UTC20050628162421_DEFINED
00036
00037 #include "tcl/commandgroup.h"
00038
00039 #include "tcl/command.h"
00040 #include "tcl/obj.h"
00041 #include "tcl/interp.h"
00042
00043 #include "rutz/fileposition.h"
00044 #include "rutz/fstring.h"
00045 #include "rutz/sfmt.h"
00046 #include "rutz/shared_ptr.h"
00047
00048 #include <list>
00049 #include <sstream>
00050 #include <tcl.h>
00051 #ifdef HAVE_TCLINT_H
00052 #include <tclInt.h>
00053 #endif
00054
00055 #include "rutz/trace.h"
00056 #include "rutz/debug.h"
00057 GVX_DBG_REGISTER
00058
00059 using rutz::fstring;
00060 using rutz::shared_ptr;
00061
00062 namespace
00063 {
00064 #ifdef HAVE_TCLINT_H
00065 fstring get_full_command_name(tcl::interpreter& interp,
00066 Tcl_Command token)
00067 {
00068 tcl::obj result;
00069
00070
00071 Tcl_GetCommandFullName(interp.intp(), token, result.get());
00072 return tcl::convert_to<rutz::fstring>(result);
00073 }
00074 #endif
00075
00076 void append_usage(std::ostream& dest, const fstring& usage)
00077 {
00078 if (!usage.is_empty())
00079 dest << " " << usage;
00080 }
00081 }
00082
00083 class tcl::command_group::impl
00084 {
00085 public:
00086 impl(tcl::interpreter& intp, const fstring& cmd_name,
00087 const rutz::file_pos& src_pos)
00088 :
00089 interp(intp),
00090 cmd_token(Tcl_CreateObjCommand(interp.intp(),
00091 cmd_name.c_str(),
00092
00093
00094
00095 static_cast<Tcl_ObjCmdProc*>(0),
00096 static_cast<ClientData>(0),
00097 static_cast<Tcl_CmdDeleteProc*>(0))),
00098 #ifdef HAVE_TCLINT_H
00099 initial_cmd_name(get_full_command_name(intp, cmd_token)),
00100 #else
00101 initial_cmd_name(cmd_name),
00102 #endif
00103 cmd_list(),
00104 prof_name(rutz::sfmt("tcl/%s", cmd_name.c_str())),
00105 prof(prof_name.c_str(), src_pos.m_file_name, src_pos.m_line_no)
00106 {}
00107
00108 ~impl() throw() {}
00109
00110 typedef std::list<shared_ptr<tcl::command> > cmd_list_type;
00111
00112 tcl::interpreter interp;
00113 Tcl_Command const cmd_token;
00114 fstring const initial_cmd_name;
00115 cmd_list_type cmd_list;
00116 fstring const prof_name;
00117 rutz::prof prof;
00118
00119 fstring usage_warning(const fstring& argv0) const;
00120
00121 static int c_invoke_callback(void* clientdata,
00122 Tcl_Interp* interp,
00123 int s_objc,
00124 Tcl_Obj *const objv[]) throw();
00125
00126 static void c_delete_callback(void* clientdata) throw();
00127
00128 static void c_exit_callback(void* clientdata) throw();
00129
00130 static tcl::command_group* lookup_helper(tcl::interpreter& interp,
00131 const char* name) throw();
00132
00133 private:
00134 impl(const impl&);
00135 impl& operator=(const impl&);
00136 };
00137
00138 fstring tcl::command_group::impl::usage_warning(
00139 const fstring& argv0) const
00140 {
00141 GVX_TRACE("tcl::command_group::usage_warning");
00142
00143 std::ostringstream warning;
00144 warning << "wrong # args: should be ";
00145
00146 if (cmd_list.size() == 1)
00147 {
00148 warning << "\"" << argv0;
00149 append_usage(warning, cmd_list.front()->usage_string());
00150 warning << "\"";
00151 }
00152 else
00153 {
00154 warning << "one of:";
00155 for (impl::cmd_list_type::const_iterator
00156 itr = cmd_list.begin(),
00157 end = cmd_list.end();
00158 itr != end;
00159 ++itr)
00160 {
00161 warning << "\n\t\"" << argv0;
00162 append_usage(warning, (*itr)->usage_string());
00163 warning << "\"";
00164 }
00165 }
00166
00167 warning << "\n(";
00168
00169 if (argv0 != initial_cmd_name)
00170 warning << "resolves to " << initial_cmd_name << ", ";
00171
00172 warning << "defined at "
00173 << prof.src_file_name() << ":"
00174 << prof.src_line_no() << ")";
00175
00176 return fstring(warning.str().c_str());
00177 }
00178
00179 #ifdef REAL_BACKTRACE
00180 #include <execinfo.h>
00181 #endif
00182
00183 int tcl::command_group::impl::c_invoke_callback(
00184 void* clientdata,
00185 Tcl_Interp* interp,
00186 int s_objc,
00187 Tcl_Obj *const objv[]) throw()
00188 {
00189 command_group* c = static_cast<command_group*>(clientdata);
00190
00191 GVX_ASSERT(c != 0);
00192 GVX_ASSERT(interp == c->rep->interp.intp());
00193
00194 #ifdef REAL_BACKTRACE
00195 typedef void* voidp;
00196
00197 voidp addresses[64];
00198
00199 const int n = backtrace(&addresses[0], 64);
00200
00201 char** names = backtrace_symbols(&addresses[0], n);
00202
00203 for (int i = 0; i < n; ++i)
00204 {
00205 dbg_print_nl(0, names[i]);
00206 }
00207
00208 free(names);
00209 #endif
00210
00211 return c->invoke_raw(s_objc, objv);
00212 }
00213
00214 void tcl::command_group::impl::c_delete_callback(
00215 void* clientdata) throw()
00216 {
00217 GVX_TRACE("tcl::command_group::impl::c_delete_callback");
00218 command_group* c = static_cast<command_group*>(clientdata);
00219 GVX_ASSERT(c != 0);
00220 delete c;
00221 }
00222
00223 void tcl::command_group::impl::c_exit_callback(
00224 void* clientdata) throw()
00225 {
00226 GVX_TRACE("tcl::command_group::c_exit_callback");
00227 command_group* c = static_cast<command_group*>(clientdata);
00228 GVX_ASSERT(c != 0);
00229 Tcl_DeleteCommandFromToken(c->rep->interp.intp(), c->rep->cmd_token);
00230 }
00231
00232 tcl::command_group* tcl::command_group::impl::lookup_helper(
00233 tcl::interpreter& interp,
00234 const char* name) throw()
00235 {
00236 GVX_TRACE("tcl::command_group::impl::lookup_helper");
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250 Tcl_CmdInfo info;
00251 const int result = Tcl_GetCommandInfo(interp.intp(), name, &info);
00252
00253 if (result == 1 &&
00254 info.isNativeObjectProc == 1 &&
00255 info.objProc == &impl::c_invoke_callback &&
00256 info.deleteProc == &impl::c_delete_callback)
00257 {
00258 return static_cast<command_group*>(info.objClientData);
00259 }
00260 return 0;
00261 }
00262
00263 tcl::command_group::command_group(tcl::interpreter& interp,
00264 const fstring& cmd_name,
00265 const rutz::file_pos& src_pos)
00266 :
00267 rep(new impl(interp, cmd_name, src_pos))
00268 {
00269 GVX_TRACE("tcl::command_group::command_group");
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280 Tcl_CmdInfo info;
00281 const int result = Tcl_GetCommandInfoFromToken(rep->cmd_token, &info);
00282 GVX_ASSERT(result == 1);
00283 GVX_ASSERT(info.isNativeObjectProc == 1);
00284 info.objClientData = static_cast<void*>(this);
00285 info.objProc = &impl::c_invoke_callback;
00286 info.deleteData = static_cast<void*>(this);
00287 info.deleteProc = &impl::c_delete_callback;
00288 Tcl_SetCommandInfoFromToken(rep->cmd_token, &info);
00289
00290 Tcl_CreateExitHandler(&impl::c_exit_callback,
00291 static_cast<void*>(this));
00292 }
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310 tcl::command_group::~command_group() throw()
00311 {
00312 GVX_TRACE("tcl::command_group::~command_group");
00313
00314 Tcl_DeleteExitHandler(&impl::c_exit_callback,
00315 static_cast<void*>(this));
00316
00317 delete rep;
00318 }
00319
00320 tcl::command_group* tcl::command_group::lookup(tcl::interpreter& interp,
00321 const char* name) throw()
00322 {
00323 GVX_TRACE("tcl::command_group::lookup");
00324
00325 return impl::lookup_helper(interp, name);
00326 }
00327
00328 tcl::command_group* tcl::command_group::lookup_original(
00329 tcl::interpreter& interp,
00330 const char* name) throw()
00331 {
00332 GVX_TRACE("tcl::command_group::lookup_original");
00333
00334 const fstring script = rutz::sfmt("namespace origin %s", name);
00335 if (interp.eval(script, tcl::IGNORE_ERROR) == false)
00336 {
00337 return 0;
00338 }
00339
00340
00341 const fstring original = interp.get_result<fstring>();
00342 return impl::lookup_helper(interp, original.c_str());
00343 }
00344
00345 shared_ptr<tcl::command>
00346 tcl::command_group::make(tcl::interpreter& interp,
00347 shared_ptr<tcl::function> callback,
00348 const char* cmd_name,
00349 const char* usage,
00350 const arg_spec& spec,
00351 const rutz::file_pos& src_pos)
00352 {
00353 GVX_TRACE("tcl::command_group::make");
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364 command_group* group =
00365 tcl::command_group::lookup(interp, cmd_name);
00366
00367 if (group == 0)
00368 group = new command_group(interp, cmd_name, src_pos);
00369
00370 GVX_ASSERT(group != 0);
00371
00372 shared_ptr<tcl::command> cmd(new tcl::command(callback, usage, spec));
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382 group->add(cmd);
00383
00384 return cmd;
00385 }
00386
00387 void tcl::command_group::add(shared_ptr<tcl::command> p)
00388 {
00389 GVX_TRACE("tcl::command_group::add");
00390 rep->cmd_list.push_back(p);
00391 }
00392
00393 fstring tcl::command_group::resolved_name() const
00394 {
00395 #ifdef HAVE_TCLINT_H
00396 return get_full_command_name(rep->interp, rep->cmd_token);
00397 #else
00398 return rep->initial_cmd_name;
00399 #endif
00400 }
00401
00402 fstring tcl::command_group::usage() const
00403 {
00404 GVX_TRACE("tcl::command_group::usage");
00405
00406 std::ostringstream result;
00407
00408 impl::cmd_list_type::const_iterator
00409 itr = rep->cmd_list.begin(),
00410 end = rep->cmd_list.end();
00411
00412 while (true)
00413 {
00414 result << "\t" << resolved_name();
00415 append_usage(result, (*itr)->usage_string());
00416 result << "\n";
00417 if (++itr == end)
00418 break;
00419 }
00420
00421 result << "\t(defined at "
00422 << rep->prof.src_file_name() << ":"
00423 << rep->prof.src_line_no() << ")";
00424
00425 return fstring(result.str().c_str());
00426 }
00427
00428 int tcl::command_group::invoke_raw(int s_objc,
00429 Tcl_Obj *const objv[]) throw()
00430 {
00431 GVX_TRACE("tcl::command_group::invoke_raw");
00432
00433
00434
00435
00436 rutz::trace tracer(rep->prof, GVX_TRACE_EXPR);
00437
00438
00439
00440 GVX_ASSERT(s_objc >= 1);
00441
00442 if (GVX_DBG_LEVEL() > 1)
00443 {
00444 for (int argi = 0; argi < s_objc; ++argi)
00445 {
00446 const char* arg = Tcl_GetString(objv[argi]);
00447 dbg_print(1, argi);
00448 dbg_print(1, " argv = ");
00449 dbg_print_nl(1, arg);
00450 }
00451 }
00452
00453 unsigned int objc = static_cast<unsigned int>(s_objc);
00454
00455
00456 try
00457 {
00458 for (impl::cmd_list_type::const_iterator
00459 itr = rep->cmd_list.begin(),
00460 end = rep->cmd_list.end();
00461 itr != end;
00462 ++itr)
00463 {
00464 if ((*itr)->rejects_argc(objc))
00465 continue;
00466
00467
00468 (*itr)->call(rep->interp, objc, objv);
00469
00470 if (GVX_DBG_LEVEL() > 1)
00471 {
00472 const char* result = rep->interp.get_result<const char*>();
00473 dbg_eval_nl(1, result);
00474 }
00475 return TCL_OK;
00476 }
00477
00478 const fstring argv0(Tcl_GetString(objv[0]));
00479
00480
00481 rep->interp.reset_result();
00482 rep->interp.append_result(rep->usage_warning(argv0).c_str());
00483 return TCL_ERROR;
00484 }
00485 catch (...)
00486 {
00487 rep->interp.handle_live_exception(Tcl_GetString(objv[0]), SRC_POS);
00488 }
00489
00490 return TCL_ERROR;
00491 }
00492
00493 static const char __attribute__((used)) vcid_groovx_tcl_commandgroup_cc_utc20050628162421[] = "$Id: commandgroup.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00494 #endif // !GROOVX_TCL_COMMANDGROUP_CC_UTC20050628162421_DEFINED