00001 /** @file tcl/scriptapp.cc helper class used in main() to initialize 00002 and run a scripting application */ 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 11876 2009-10-22 15:53:06Z icore $ 00010 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/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 // 00031 /////////////////////////////////////////////////////////////////////// 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 11876 2009-10-22 15:53:06Z icore $ $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/tcl/scriptapp.cc $"; 00419 #endif // !GROOVX_TCL_SCRIPTAPP_CC_UTC20050628162421_DEFINED