pkg.cc

Go to the documentation of this file.
00001 
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 10065 2007-04-12 05:54:56Z rjpeters $
00011 // $HeadURL: file:///lab/rjpeters/svnrepo/code/trunk/groovx/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 //
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 
00166 //
00167 // Helper functions that provide typesafe access to Tcl_LinkVar
00168 //
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 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00523 #endif // !GROOVX_TCL_PKG_CC_UTC20050628162420_DEFINED

The software described here is Copyright (c) 1998-2005, Rob Peters.
This page was generated Wed Dec 3 06:49:41 2008 by Doxygen version 1.5.5.