scriptapp.cc

Go to the documentation of this file.
00001 
00003 
00004 //
00005 // Copyright (c) 2005-2007 University of Southern California
00006 // Rob Peters <rjpeters at usc dot edu>
00007 //
00008 // created: Mon Jun 27 13:34:19 2005
00009 // commit: $Id: scriptapp.cc 10065 2007-04-12 05:54:56Z rjpeters $
00010 // $HeadURL: file:///lab/rjpeters/svnrepo/code/trunk/groovx/src/tcl/scriptapp.cc $
00011 //
00012 // --------------------------------------------------------------------
00013 //
00014 // This file is part of GroovX.
00015 //   [http://ilab.usc.edu/rjpeters/groovx/]
00016 //
00017 // GroovX is free software; you can redistribute it and/or modify it
00018 // under the terms of the GNU General Public License as published by
00019 // the Free Software Foundation; either version 2 of the License, or
00020 // (at your option) any later version.
00021 //
00022 // GroovX is distributed in the hope that it will be useful, but
00023 // WITHOUT ANY WARRANTY; without even the implied warranty of
00024 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00025 // General Public License for more details.
00026 //
00027 // You should have received a copy of the GNU General Public License
00028 // along with GroovX; if not, write to the Free Software Foundation,
00029 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00030 //
00032 
00033 #ifndef GROOVX_TCL_SCRIPTAPP_CC_UTC20050628162421_DEFINED
00034 #define GROOVX_TCL_SCRIPTAPP_CC_UTC20050628162421_DEFINED
00035 
00036 #include "tcl/scriptapp.h"
00037 
00038 #include "nub/objfactory.h"
00039 
00040 #include "rutz/sfmt.h"
00041 
00042 #include "tcl/list.h"
00043 #include "tcl/eventloop.h"
00044 #include "tcl/pkg.h"
00045 #include "tcl/interp.h"
00046 
00047 #include <cstring>
00048 #include <iostream>
00049 #include <signal.h>
00050 #include <sstream>
00051 #include <tk.h>
00052 
00053 #include "rutz/debug.h"
00054 GVX_DBG_REGISTER
00055 #include "rutz/trace.h"
00056 
00057 namespace
00058 {
00059 
00060   bool havearg(char** args, const char* arg)
00061   {
00062     for ( ; *args != 0; ++args)
00063       if (strcmp(*args, arg) == 0)
00064         return true;
00065 
00066     return false;
00067   }
00068 
00069   std::string centerline(unsigned int totallen,
00070                          const char* pfx, const char* sfx,
00071                          std::string txt)
00072   {
00073     if (strlen(pfx) + strlen(sfx) >= totallen)
00074       {
00075         // ok, the line's too long, so don't do any centering, just
00076         // keep it as short as possible:
00077         std::string out(pfx);
00078         out += txt;
00079         out += sfx;
00080         return out;
00081       }
00082 
00083     unsigned int midlen = totallen - strlen(pfx) - strlen(sfx);
00084 
00085     std::string out(pfx);
00086 
00087     if (txt.length() < midlen)
00088       {
00089         int c = midlen - txt.length();
00090         while (--c >= 0)
00091           { if (c % 2) txt += ' '; else out += ' '; }
00092       }
00093 
00094     out += txt;
00095     out += sfx;
00096 
00097     return out;
00098   }
00099 
00100   std::string wrapstring(unsigned int totallen,
00101                          const char* pfx, const char* sfx,
00102                          const std::string& s)
00103   {
00104     GVX_ASSERT(strlen(pfx) + strlen(sfx) < totallen);
00105     unsigned int len = totallen - strlen(pfx) - strlen(sfx);
00106 
00107     std::istringstream strm(s);
00108     std::string out;
00109     std::string line;
00110     std::string word;
00111     while (strm >> word)
00112       {
00113         if (word.length() + line.length() + 1 <= len)
00114           {
00115             if (line.length() > 0)
00116               line += ' ';
00117             line += word;
00118           }
00119         else
00120           {
00121             out += line;
00122             out += '\n';
00123             line = word;
00124           }
00125       }
00126 
00127     out += line;
00128 
00129     return out;
00130   }
00131 
00132   std::string centerlines(unsigned int totallen,
00133                           const char* pfx, const char* sfx,
00134                           std::string ss)
00135   {
00136     if (ss.length() == 0)
00137       {
00138         return centerline(totallen, pfx, sfx, ss);
00139       }
00140 
00141     std::istringstream strm(ss);
00142     std::string line;
00143     std::string out;
00144     bool first = true;
00145     while (getline(strm, line))
00146       {
00147         if (!first) out += '\n';
00148         first = false;
00149         out += centerline(totallen, pfx, sfx, line);
00150       }
00151 
00152     return out;
00153   }
00154 
00155   std::string wrapcenterlines(unsigned int totallen,
00156                               const char* pfx, const char* sfx,
00157                               std::string ss, std::string emptyline)
00158   {
00159     std::istringstream strm(ss);
00160     std::string line;
00161     std::string out;
00162     while (getline(strm, line))
00163       {
00164         if (line.length() == 0)
00165           {
00166             out += emptyline;
00167             out += '\n';
00168           }
00169         else
00170           {
00171             out += centerlines(totallen, pfx, sfx,
00172                                wrapstring(totallen, pfx, sfx, line));
00173             out += '\n';
00174           }
00175       }
00176 
00177     return out;
00178   }
00179 
00180   // This is a fallback function to be used by the object
00181   // factory... if the factory can't figure out how to create a given
00182   // type, it will call this fallback function first before giving up
00183   // for good. This callback function tries to load a Tcl package
00184   // named after the desired object type.
00185   void factory_pkg_loader(const rutz::fstring& type)
00186   {
00187     dbg_eval_nl(3, type);
00188 
00189     tcl::pkg::lookup(tcl::event_loop::interp(), type.c_str());
00190   }
00191 
00192   void sig_handler(int signum)
00193   {
00194     switch (signum)
00195       {
00196         case SIGSEGV: GVX_PANIC("Segmentation fault (SIGSEGV)");
00197         case SIGFPE:  GVX_PANIC("Floating point exception (SIGFPE)");
00198         case SIGBUS:  GVX_PANIC("Bus error (SIGBUS)");
00199       }
00200     GVX_ASSERT(0);
00201   }
00202 
00203 }
00204 
00205 void tcl::script_app::init_in_macro_only()
00206 {
00207   signal(SIGSEGV, &sig_handler);
00208   signal(SIGFPE, &sig_handler);
00209   signal(SIGBUS, &sig_handler);
00210 }
00211 
00212 void tcl::script_app::handle_exception_in_macro_only
00213                                          (const std::exception* e)
00214 {
00215   if (e != 0)
00216     std::cerr << "caught in main: ("
00217               << rutz::demangled_name(typeid(*e))
00218               << "): " << e->what() << '\n';
00219   else
00220     std::cerr << "caught in main: (an exception of unknown type)\n";
00221 }
00222 
00223 tcl::script_app::script_app(const char* appname,
00224                             int argc_, char** argv_) throw()
00225   :
00226   m_appname(appname),
00227   m_script_argc(0),
00228   m_script_argv(new char*[argc_+1]),
00229   m_minimal(false),
00230   m_nowindow(false),
00231   m_splashmsg(),
00232   m_pkgdir(),
00233   m_pkgs(0),
00234   m_exitcode(0)
00235 {
00236   // We are going to take a quick pass over the command-line args here
00237   // to see if there are any we care about; if there are, then we will
00238   // cull those from the arg list that gets exposed to the script.
00239 
00240   // Quick check argv to optionally turn on global tracing and/or set
00241   // the global debug level. This method is particularly useful for
00242   // helping to diagnose problems that are occurring during
00243   // application startup, before we have a chance to get to the
00244   // command-line prompt and do a "::gtrace 1" or a "::dbgLevel 9".
00245   for (int i = 0; i < argc_; ++i)
00246     {
00247       if (strcmp(argv_[i], "-dbglevel") == 0)
00248         {
00249           ++i;
00250           if (argv_[i] != 0)
00251             rutz::debug::set_global_level( atoi(argv_[i]) );
00252         }
00253       else if (strcmp(argv_[i], "-gtrace") == 0)
00254         {
00255           rutz::trace::set_global_trace(true);
00256         }
00257       else if (strcmp(argv_[i], "-showinit") == 0)
00258         {
00259           tcl::pkg::verbose_init(true);
00260         }
00261       else if (strcmp(argv_[i], "-minimal") == 0)
00262         {
00263           this->m_minimal = true;
00264         }
00265       else if (strcmp(argv_[i], "-nw") == 0)
00266         {
00267           this->m_nowindow = true;
00268         }
00269       else
00270         {
00271           // ok, we didn't recognize this arg, so we'll pass it along
00272           // to the script:
00273           m_script_argv[m_script_argc++] = argv_[i];
00274         }
00275     }
00276 
00277   // now null-terminate the argv that will be passed to the script:
00278   m_script_argv[m_script_argc] = 0;
00279 }
00280 
00281 tcl::script_app::~script_app() throw()
00282 {
00283   delete [] m_script_argv;
00284 }
00285 
00286 void tcl::script_app::run()
00287 {
00288   tcl::event_loop tclmain(this->m_script_argc,
00289                           this->m_script_argv, this->m_nowindow);
00290 
00291   if (tcl::event_loop::is_interactive())
00292     {
00293       const char* const pfx = "###  ";
00294       const char* const sfx = "  ###";
00295       const unsigned int linelen = 75;
00296       std::string hashes(linelen, '#');
00297 
00298       std::cerr << hashes
00299                 << '\n'
00300                 << wrapcenterlines(linelen, pfx, sfx,
00301                                    m_splashmsg.c_str(), hashes)
00302                 << hashes << '\n' << '\n';
00303     }
00304 
00305   tcl::interpreter& interp = tclmain.interp();
00306 
00307   nub::obj_factory::instance().set_fallback(&factory_pkg_loader);
00308   nub::set_default_ref_vis(nub::PUBLIC);
00309 
00310   const rutz::time ru1 = rutz::time::user_rusage();
00311   const rutz::time rs1 = rutz::time::sys_rusage();
00312   const rutz::time wc1 = rutz::time::wall_clock_now();
00313 
00314   package_info IMMEDIATE_PKGS[] =
00315     {
00316       { "Tcl",      Tcl_Init,  "", false },
00317       { "Tk",       Tk_Init,   "", true },
00318     };
00319 
00320   for (size_t i = 0; i < sizeof(IMMEDIATE_PKGS)/sizeof(package_info); ++i)
00321     {
00322       if (m_nowindow && IMMEDIATE_PKGS[i].requires_gui)
00323         continue;
00324 
00325       int result = IMMEDIATE_PKGS[i].init_proc(interp.intp());
00326       if (result != TCL_OK)
00327         {
00328           std::cerr << "fatal initialization error (package '"
00329                     << IMMEDIATE_PKGS[i].name << "'):\n";
00330           rutz::fstring msg = interp.get_result<const char*>();
00331           if ( !msg.is_empty() )
00332             std::cerr << '\t' << msg << '\n';
00333           interp.reset_result();
00334 
00335           this->m_exitcode = 2; return;
00336         }
00337     }
00338 
00339   if (tcl::event_loop::is_interactive())
00340     {
00341       const rutz::time ru = rutz::time::user_rusage() - ru1;
00342       const rutz::time rs = rutz::time::sys_rusage() - rs1;
00343       const rutz::time wc = rutz::time::wall_clock_now() - wc1;
00344 
00345       fprintf(stderr, "\tstartup time (%6s) "
00346               "%6.3fs (user) %6.3fs (sys) %6.3fs (wall)\n",
00347               "tcl+tk", ru.sec(), rs.sec(), wc.sec());
00348     }
00349 
00350   const rutz::time ru2 = rutz::time::user_rusage();
00351   const rutz::time rs2 = rutz::time::sys_rusage();
00352   const rutz::time wc2 = rutz::time::wall_clock_now();
00353 
00354   for (const package_info* pkg = m_pkgs; pkg->name != 0; ++pkg)
00355     {
00356       Tcl_StaticPackage(static_cast<Tcl_Interp*>(0),
00357                         // (Tcl_Interp*) 0 means this package
00358                         // hasn't yet been loaded into any
00359                         // interpreter
00360                         pkg->name,
00361                         pkg->init_proc,
00362                         0);
00363 
00364       const rutz::fstring ifneededcmd =
00365         rutz::sfmt("package ifneeded %s %s {load {} %s }",
00366                    pkg->name, pkg->version, pkg->name);
00367 
00368       interp.eval(ifneededcmd);
00369     }
00370 
00371   if (!m_minimal)
00372     {
00373       for (const package_info* pkg = m_pkgs; pkg->name != 0; ++pkg)
00374         {
00375           if (m_nowindow && pkg->requires_gui)
00376             continue;
00377 
00378           const char* ver =
00379             Tcl_PkgRequire(interp.intp(), pkg->name, pkg->version, 0);
00380 
00381           if (ver == 0)
00382             {
00383               std::cerr << "initialization error (package '"
00384                         << pkg->name << "'):\n";
00385               rutz::fstring msg = interp.get_result<const char*>();
00386               if ( !msg.is_empty() )
00387                 std::cerr << '\t' << msg << '\n';
00388               interp.reset_result();
00389             }
00390         }
00391     }
00392 
00393   if (tcl::event_loop::is_interactive())
00394     {
00395       const rutz::time ru = rutz::time::user_rusage() - ru2;
00396       const rutz::time rs = rutz::time::sys_rusage() - rs2;
00397       const rutz::time wc = rutz::time::wall_clock_now() - wc2;
00398 
00399       fprintf(stderr, "\tstartup time (%6s) "
00400               "%6.3fs (user) %6.3fs (sys) %6.3fs (wall)\n",
00401               m_appname.c_str(), ru.sec(), rs.sec(), wc.sec());
00402     }
00403 
00404   tcl::list path = interp.get_global_var<tcl::list>("auto_path");
00405 
00406   if (m_pkgdir.length() > 0)
00407     path.append(m_pkgdir);
00408 
00409   interp.set_global_var("auto_path", path.as_obj());
00410 
00411   // specifies a file to be 'source'd upon startup
00412   interp.set_global_var("tcl_rcFileName",
00413                       tcl::convert_from("./groovx_startup.tcl"));
00414 
00415   tclmain.run();
00416 }
00417 
00418 static const char __attribute__((used)) vcid_groovx_tcl_scriptapp_cc_utc20050628162421[] = "$Id: scriptapp.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00419 #endif // !GROOVX_TCL_SCRIPTAPP_CC_UTC20050628162421_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.