00001 /** @file tcl/pkg.cc tcl package class, holds a set of commands, wraps 00002 calls to Tcl_PkgProvide(), etc. */ 00003 /////////////////////////////////////////////////////////////////////// 00004 // 00005 // Copyright (c) 1999-2004 California Institute of Technology 00006 // Copyright (c) 2004-2007 University of Southern California 00007 // Rob Peters <rjpeters at usc dot edu> 00008 // 00009 // created: Tue Jun 15 12:33:54 1999 00010 // commit: $Id: pkg.cc 11876 2009-10-22 15:53:06Z icore $ 00011 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/tcl/pkg.cc $ 00012 // 00013 // -------------------------------------------------------------------- 00014 // 00015 // This file is part of GroovX 00016 // [http://ilab.usc.edu/rjpeters/groovx/] 00017 // 00018 // GroovX is free software; you can redistribute it and/or modify it 00019 // under the terms of the GNU General Public License as published by 00020 // the Free Software Foundation; either version 2 of the License, or 00021 // (at your option) any later version. 00022 // 00023 // GroovX is distributed in the hope that it will be useful, but 00024 // WITHOUT ANY WARRANTY; without even the implied warranty of 00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00026 // General Public License for more details. 00027 // 00028 // You should have received a copy of the GNU General Public License 00029 // along with GroovX; if not, write to the Free Software Foundation, 00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 00031 // 00032 /////////////////////////////////////////////////////////////////////// 00033 00034 #ifndef GROOVX_TCL_PKG_CC_UTC20050628162420_DEFINED 00035 #define GROOVX_TCL_PKG_CC_UTC20050628162420_DEFINED 00036 00037 #include "tcl/pkg.h" 00038 00039 #include "tcl/command.h" 00040 #include "tcl/interp.h" 00041 #include "tcl/list.h" 00042 #include "tcl/namesp.h" 00043 00044 #include "rutz/error.h" 00045 #include "rutz/fstring.h" 00046 #include "rutz/sfmt.h" 00047 #include "rutz/shared_ptr.h" 00048 00049 #include <tcl.h> 00050 #ifdef HAVE_TCLINT_H 00051 #include <tclInt.h> // for Tcl_FindNamespace() etc. 00052 #endif 00053 #include <cctype> 00054 #include <iostream> 00055 #include <typeinfo> 00056 #include <string> 00057 #include <vector> 00058 00059 #include "rutz/trace.h" 00060 #include "rutz/debug.h" 00061 GVX_DBG_REGISTER 00062 00063 #if (TCL_MAJOR_VERSION > 8) || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 5) 00064 # define HAVE_TCL_NAMESPACE_API 00065 #else 00066 # undef HAVE_TCL_NAMESPACE_API 00067 #endif 00068 00069 using std::string; 00070 using std::vector; 00071 using rutz::shared_ptr; 00072 00073 namespace 00074 { 00075 bool VERBOSE_INIT = false; 00076 00077 int INIT_DEPTH = 0; 00078 00079 // Construct a capitalization-correct version of the given name that 00080 // is just how Tcl likes it: first character uppercase, all others 00081 // lowercase. 00082 string make_clean_pkg_name(const string& name) 00083 { 00084 string clean; 00085 00086 clean += char(toupper(name[0])); 00087 00088 for (size_t i = 1; i < name.length(); ++i) 00089 { 00090 if (name[i] != '-' && name[i] != '_') 00091 clean += char(tolower(name[i])); 00092 } 00093 00094 return clean; 00095 } 00096 00097 string make_clean_version_string(const string& s) 00098 { 00099 string::size_type dollar1 = s.find_first_of("$"); 00100 string::size_type dollar2 = s.find_last_of("$"); 00101 00102 if (dollar1 == dollar2) 00103 return s; 00104 00105 const string r = s.substr(dollar1,dollar2+1-dollar1); 00106 00107 string::size_type n1 = r.find_first_of("0123456789"); 00108 string::size_type n2 = r.find_last_of("0123456789"); 00109 00110 string result(s); 00111 00112 if (n1 != string::npos) 00113 { 00114 const string n = r.substr(n1,n2+1-n1); 00115 result.replace(dollar1, dollar2+1-dollar1, n); 00116 } 00117 else 00118 { 00119 result.replace(dollar1, dollar2+1-dollar1, "0"); 00120 } 00121 00122 return result; 00123 } 00124 00125 void export_into(tcl::interpreter& interp, 00126 const char* from, const char* to, 00127 const char* pattern) 00128 { 00129 GVX_TRACE("export_into"); 00130 const rutz::fstring cmd = 00131 rutz::sfmt("namespace eval %s { namespace import ::%s::%s }", 00132 to, from, pattern); 00133 00134 interp.eval(cmd); 00135 } 00136 00137 tcl::list get_command_list(tcl::interpreter& interp, const char* namesp) 00138 { 00139 tcl::obj saveresult = interp.get_result<tcl::obj>(); 00140 rutz::fstring cmd = rutz::sfmt("info commands ::%s::*", namesp); 00141 interp.eval(cmd); 00142 tcl::list cmdlist = interp.get_result<tcl::list>(); 00143 interp.set_result(saveresult); 00144 return cmdlist; 00145 } 00146 00147 const char* get_name_tail(const char* name) 00148 { 00149 const char* p = name; 00150 while (*p != '\0') ++p; // skip to end of string 00151 while (--p > name) { 00152 if ((*p == ':') && (*(p-1) == ':')) { 00153 ++p; 00154 break; 00155 } 00156 } 00157 GVX_ASSERT(p >= name); 00158 return p; 00159 } 00160 } 00161 00162 const int tcl::pkg::STATUS_OK = TCL_OK; 00163 const int tcl::pkg::STATUS_ERR = TCL_ERROR; 00164 00165 /////////////////////////////////////////////////////////////////////// 00166 // 00167 // Helper functions that provide typesafe access to Tcl_LinkVar 00168 // 00169 /////////////////////////////////////////////////////////////////////// 00170 00171 struct tcl::pkg::impl 00172 { 00173 private: 00174 impl(const impl&); 00175 impl& operator=(const impl&); 00176 00177 public: 00178 impl(Tcl_Interp* interp, const char* name, const char* version); 00179 00180 ~impl() throw(); 00181 00182 tcl::interpreter interp; 00183 string const namesp_name; 00184 string const pkg_name; 00185 string const version; 00186 int init_status; 00187 std::vector<shared_ptr<int> > owned_ints; 00188 std::vector<shared_ptr<double> > owned_doubles; 00189 exit_callback* on_exit; 00190 00191 static void c_exit_handler(void* clientdata) 00192 { 00193 GVX_TRACE("tcl::pkg-c_exit_handler"); 00194 tcl::pkg* pkg = static_cast<tcl::pkg*>(clientdata); 00195 dbg_eval_nl(3, pkg->namesp_name()); 00196 delete pkg; 00197 } 00198 }; 00199 00200 tcl::pkg::impl::impl(Tcl_Interp* intp, 00201 const char* name, const char* vers) : 00202 interp(intp), 00203 namesp_name(name ? name : ""), 00204 pkg_name(make_clean_pkg_name(namesp_name)), 00205 version(make_clean_version_string(vers)), 00206 init_status(TCL_OK), 00207 owned_ints(), 00208 owned_doubles(), 00209 on_exit(0) 00210 { 00211 GVX_TRACE("tcl::pkg::impl::impl"); 00212 } 00213 00214 tcl::pkg::impl::~impl() throw() 00215 { 00216 GVX_TRACE("tcl::pkg::impl::~impl"); 00217 if (on_exit != 0) 00218 on_exit(); 00219 } 00220 00221 tcl::pkg::pkg(Tcl_Interp* interp, 00222 const char* name, const char* version) : 00223 rep(0) 00224 { 00225 GVX_TRACE("tcl::pkg::pkg"); 00226 00227 rep = new impl(interp, name, version); 00228 00229 ++INIT_DEPTH; 00230 } 00231 00232 tcl::pkg::~pkg() throw() 00233 { 00234 GVX_TRACE("tcl::pkg::~pkg"); 00235 00236 // To avoid double-deletion: 00237 Tcl_DeleteExitHandler(&impl::c_exit_handler, static_cast<void*>(this)); 00238 00239 try 00240 { 00241 #ifndef HAVE_TCL_NAMESPACE_API 00242 tcl::list cmdnames = get_command_list(rep->interp, 00243 rep->namesp_name.c_str()); 00244 00245 for (unsigned int i = 0; i < cmdnames.length(); ++i) 00246 { 00247 Tcl_DeleteCommand(rep->interp.intp(), 00248 cmdnames.get<const char*>(i)); 00249 } 00250 #else 00251 Tcl_Namespace* namesp = 00252 Tcl_FindNamespace(rep->interp.intp(), rep->namesp_name.c_str(), 00253 0, TCL_GLOBAL_ONLY); 00254 if (namesp != 0) 00255 Tcl_DeleteNamespace(namesp); 00256 #endif 00257 } 00258 catch (...) 00259 { 00260 rep->interp.handle_live_exception("tcl::pkg::~pkg", SRC_POS); 00261 } 00262 00263 delete rep; 00264 } 00265 00266 void tcl::pkg::on_exit(exit_callback* callback) 00267 { 00268 GVX_TRACE("tcl::pkg::on_exit"); 00269 rep->on_exit = callback; 00270 } 00271 00272 int tcl::pkg::destroy_on_unload(Tcl_Interp* intp, const char* pkgname) 00273 { 00274 GVX_TRACE("tcl::pkg::destroy_on_unload"); 00275 tcl::interpreter interp(intp); 00276 tcl::pkg* pkg = tcl::pkg::lookup(interp, pkgname); 00277 if (pkg != 0) 00278 { 00279 delete pkg; 00280 return 1; // TCL_OK 00281 } 00282 // else... 00283 return 0; // TCL_ERROR 00284 } 00285 00286 tcl::pkg* tcl::pkg::lookup(tcl::interpreter& interp, const char* name, 00287 const char* version) throw() 00288 { 00289 GVX_TRACE("tcl::pkg::lookup"); 00290 00291 void* clientdata = 0; 00292 00293 const string clean_name = make_clean_pkg_name(name); 00294 00295 tcl::obj saveresult = interp.get_result<tcl::obj>(); 00296 00297 const char* ver = 00298 Tcl_PkgRequireEx(interp.intp(), clean_name.c_str(), 00299 version, 0, &clientdata); 00300 00301 interp.set_result(saveresult); 00302 00303 if (ver != 0) 00304 { 00305 tcl::pkg* result = static_cast<tcl::pkg*>(clientdata); 00306 00307 result = dynamic_cast<tcl::pkg*>(result); 00308 00309 return result; 00310 } 00311 00312 return 0; 00313 } 00314 00315 int tcl::pkg::init_status() const throw() 00316 { 00317 GVX_TRACE("tcl::pkg::init_status"); 00318 if (rep->interp.get_result<const char*>()[0] != '\0') 00319 { 00320 rep->init_status = TCL_ERROR; 00321 } 00322 return rep->init_status; 00323 } 00324 00325 tcl::interpreter& tcl::pkg::interp() throw() 00326 { 00327 GVX_TRACE("tcl::pkg::interp"); 00328 return rep->interp; 00329 } 00330 00331 void tcl::pkg::handle_live_exception(const rutz::file_pos& pos) throw() 00332 { 00333 GVX_TRACE("tcl::pkg::handle_live_exception"); 00334 rep->interp.handle_live_exception(rep->pkg_name.c_str(), pos); 00335 this->set_init_status_error(); 00336 } 00337 00338 void tcl::pkg::namesp_alias(const char* namesp, const char* pattern) 00339 { 00340 GVX_TRACE("tcl::pkg::namesp_alias"); 00341 00342 export_into(rep->interp, rep->namesp_name.c_str(), namesp, pattern); 00343 } 00344 00345 void tcl::pkg::inherit_namesp(const char* namesp, const char* pattern) 00346 { 00347 GVX_TRACE("tcl::pkg::inherit_namesp"); 00348 00349 // (1) export commands from 'namesp' into this tcl::pkg's namespace 00350 export_into(rep->interp, namesp, rep->namesp_name.c_str(), pattern); 00351 00352 // (2) get the export patterns from 'namesp' and include those as 00353 // export patterns for this tcl::pkg's namespace 00354 const tcl::namesp otherns = tcl::namesp::lookup(rep->interp, namesp); 00355 00356 const tcl::list exportlist = otherns.get_export_list(rep->interp); 00357 00358 const tcl::namesp thisns(rep->interp, rep->namesp_name.c_str()); 00359 00360 for (unsigned int i = 0; i < exportlist.size(); ++i) 00361 { 00362 thisns.export_cmd(rep->interp, exportlist.get<const char*>(i)); 00363 } 00364 } 00365 00366 void tcl::pkg::inherit_pkg(const char* name, const char* version) 00367 { 00368 GVX_TRACE("tcl::pkg::inherit_pkg"); 00369 00370 tcl::pkg* other = lookup(rep->interp, name, version); 00371 00372 if (other == 0) 00373 throw rutz::error(rutz::sfmt("no tcl::pkg named '%s'", name), 00374 SRC_POS); 00375 00376 inherit_namesp(other->namesp_name()); 00377 } 00378 00379 const char* tcl::pkg::namesp_name() throw() 00380 { 00381 return rep->namesp_name.c_str(); 00382 } 00383 00384 const char* tcl::pkg::pkg_name() const throw() 00385 { 00386 return rep->pkg_name.c_str(); 00387 } 00388 00389 const char* tcl::pkg::version() const throw() 00390 { 00391 return rep->version.c_str(); 00392 } 00393 00394 const char* tcl::pkg::make_pkg_cmd_name(const char* cmd_name_cstr, 00395 int flags) 00396 { 00397 GVX_TRACE("tcl::pkg::make_pkg_cmd_name"); 00398 string cmd_name(cmd_name_cstr); 00399 00400 // Look for a namespace qualifier "::" -- if there is already one, 00401 // then we assume the caller has something special in mind -- if 00402 // there is not one, then we do the default thing and prepend the 00403 // package name as a namespace qualifier. 00404 if (cmd_name.find("::") != string::npos) 00405 { 00406 return cmd_name_cstr; 00407 } 00408 else 00409 { 00410 if (!(flags & NO_EXPORT)) 00411 { 00412 const tcl::namesp ns(rep->interp, rep->namesp_name.c_str()); 00413 00414 ns.export_cmd(rep->interp, cmd_name_cstr); 00415 } 00416 00417 static string name; 00418 name = namesp_name(); 00419 name += "::"; 00420 name += cmd_name; 00421 return name.c_str(); 00422 } 00423 } 00424 00425 void tcl::pkg::eval(const char* script) 00426 { 00427 GVX_TRACE("tcl::pkg::eval"); 00428 rep->interp.eval(script); 00429 } 00430 00431 void tcl::pkg::link_var(const char* var_name, int& var) 00432 { 00433 GVX_TRACE("tcl::pkg::link_var int"); 00434 rep->interp.link_int(var_name, &var, false); 00435 } 00436 00437 void tcl::pkg::link_var(const char* var_name, double& var) 00438 { 00439 GVX_TRACE("tcl::pkg::link_var double"); 00440 rep->interp.link_double(var_name, &var, false); 00441 } 00442 00443 void tcl::pkg::link_var_copy(const char* var_name, int var) 00444 { 00445 GVX_TRACE("tcl::pkg::link_var_copy int"); 00446 shared_ptr<int> copy(new int(var)); 00447 rep->owned_ints.push_back(copy); 00448 rep->interp.link_int(var_name, copy.get(), true); 00449 } 00450 00451 void tcl::pkg::link_var_copy(const char* var_name, double var) 00452 { 00453 GVX_TRACE("tcl::pkg::link_var_copy double"); 00454 shared_ptr<double> copy(new double(var)); 00455 rep->owned_doubles.push_back(copy); 00456 rep->interp.link_double(var_name, copy.get(), true); 00457 } 00458 00459 void tcl::pkg::link_var_const(const char* var_name, int& var) 00460 { 00461 GVX_TRACE("tcl::pkg::link_var_const int"); 00462 rep->interp.link_int(var_name, &var, true); 00463 } 00464 00465 void tcl::pkg::link_var_const(const char* var_name, double& var) 00466 { 00467 GVX_TRACE("tcl::pkg::link_var_const double"); 00468 rep->interp.link_double(var_name, &var, true); 00469 } 00470 00471 void tcl::pkg::set_init_status_error() throw() 00472 { 00473 GVX_TRACE("tcl::pkg::set_init_status_error"); 00474 rep->init_status = TCL_ERROR; 00475 } 00476 00477 void tcl::pkg::verbose_init(bool verbose) throw() 00478 { 00479 GVX_TRACE("tcl::pkg::verbose_init"); 00480 00481 VERBOSE_INIT = verbose; 00482 } 00483 00484 int tcl::pkg::finish_init() throw() 00485 { 00486 GVX_TRACE("tcl::pkg::finish_init"); 00487 00488 --INIT_DEPTH; 00489 00490 if (rep->init_status == TCL_OK) 00491 { 00492 if (VERBOSE_INIT) 00493 { 00494 for (int i = 0; i < INIT_DEPTH; ++i) 00495 std::cerr << " "; 00496 std::cerr << pkg_name() << " initialized.\n"; 00497 } 00498 00499 if ( !rep->pkg_name.empty() && !rep->version.empty() ) 00500 { 00501 Tcl_PkgProvideEx(rep->interp.intp(), 00502 rep->pkg_name.c_str(), rep->version.c_str(), 00503 static_cast<void*>(this)); 00504 } 00505 00506 Tcl_CreateExitHandler(&impl::c_exit_handler, 00507 static_cast<void*>(this)); 00508 00509 return rep->init_status; 00510 } 00511 00512 // else (rep->init_status != TCL_OK) 00513 00514 delete this; 00515 return TCL_ERROR; 00516 } 00517 00518 const char* const tcl::pkg::action_usage = "objref(s)"; 00519 const char* const tcl::pkg::getter_usage = "objref(s)"; 00520 const char* const tcl::pkg::setter_usage = "objref(s) new_value(s)"; 00521 00522 static const char __attribute__((used)) vcid_groovx_tcl_pkg_cc_utc20050628162420[] = "$Id: pkg.cc 11876 2009-10-22 15:53:06Z icore $ $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/tcl/pkg.cc $"; 00523 #endif // !GROOVX_TCL_PKG_CC_UTC20050628162420_DEFINED