00001
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
00032
00033
00035
00036 #ifndef GROOVX_TCL_EVENTLOOP_CC_UTC20050628162420_DEFINED
00037 #define GROOVX_TCL_EVENTLOOP_CC_UTC20050628162420_DEFINED
00038
00039 #include "tcl/eventloop.h"
00040
00041 #include "tcl/interp.h"
00042
00043 #include "rutz/backtrace.h"
00044 #include "rutz/backtraceformat.h"
00045 #include "rutz/error.h"
00046 #include "rutz/fstring.h"
00047 #include "rutz/sfmt.h"
00048
00049 #include <iostream>
00050 #include <sstream>
00051 #include <string>
00052 #include <tk.h>
00053 #include <unistd.h>
00054
00055 #ifndef GVX_NO_READLINE
00056 #define GVX_WITH_READLINE
00057 #endif
00058
00059 #ifdef GVX_WITH_READLINE
00060 # include <cstdlib>
00061 # include <readline/readline.h>
00062 # include <readline/history.h>
00063 #endif
00064
00065 #include "rutz/trace.h"
00066 #include "rutz/debug.h"
00067 GVX_DBG_REGISTER
00068
00069 namespace tcl
00070 {
00071 class event_loop_impl;
00072 }
00073
00074 namespace
00075 {
00076 void c_exit_handler(void* ) throw()
00077 {
00078 #ifdef GVX_WITH_READLINE
00079 rl_callback_handler_remove();
00080 #endif
00081 }
00082 }
00083
00084
00085 class tcl::event_loop_impl
00086 {
00087 private:
00088 static event_loop_impl* s_event_loop_impl;
00089
00090
00091
00092 int m_argc;
00093 const char** m_argv;
00094 tcl::interpreter m_interp;
00095 const char* m_startup_filename;
00096 const char* m_argv0;
00097 Tcl_Channel m_stdin_chan;
00098 std::string m_command;
00099 bool m_got_partial;
00100 bool m_is_interactive;
00101 rutz::fstring m_command_line;
00102 bool m_no_window;
00103
00104
00105
00106 event_loop_impl(int argc, char** argv, bool nowindow);
00107
00108 int history_next();
00109
00110 void do_prompt(const char* text, unsigned int length);
00111
00112 enum prompt_type { FULL, PARTIAL };
00113
00114 void prompt(prompt_type t);
00115
00116 void grab_input();
00117
00118 void handle_line(const char* line, int count);
00119
00120 void eval_command();
00121
00122 static void c_stdin_proc(void* , int );
00123
00124 public:
00125 static void create(int argc, char** argv, bool nowindow)
00126 {
00127 GVX_ASSERT(s_event_loop_impl == 0);
00128
00129 s_event_loop_impl = new event_loop_impl(argc, argv, nowindow);
00130 Tcl_CreateExitHandler(c_exit_handler, static_cast<void*>(0));
00131 }
00132
00133 static event_loop_impl* get()
00134 {
00135 if (s_event_loop_impl == 0)
00136 {
00137 throw rutz::error("no tcl::event_loop object has yet been created",
00138 SRC_POS);
00139 }
00140
00141 return s_event_loop_impl;
00142 }
00143
00144 bool is_interactive() const { return m_is_interactive; }
00145
00146 tcl::interpreter& interp() { return m_interp; }
00147
00148 void run();
00149
00150 int argc() const { return m_argc; }
00151
00152 const char* const* argv() const { return m_argv; }
00153
00154 rutz::fstring command_line() const { return m_command_line; }
00155
00156 #ifdef GVX_WITH_READLINE
00157 static void readline_line_complete(char* line);
00158 #endif
00159 };
00160
00161 tcl::event_loop_impl* tcl::event_loop_impl::s_event_loop_impl = 0;
00162
00163
00164
00165
00166
00167
00168
00169 tcl::event_loop_impl::event_loop_impl(int argc, char** argv, bool nowindow) :
00170 m_argc(argc),
00171 m_argv(const_cast<const char**>(argv)),
00172 m_interp(Tcl_CreateInterp()),
00173 m_startup_filename(0),
00174 m_argv0(0),
00175 m_stdin_chan(0),
00176 m_command(),
00177 m_got_partial(false),
00178 m_is_interactive(isatty(0)),
00179 m_command_line(),
00180 m_no_window(nowindow)
00181 {
00182 GVX_TRACE("tcl::event_loop_impl::event_loop_impl");
00183
00184 Tcl_FindExecutable(argv[0]);
00185
00186 {
00187 std::ostringstream buf;
00188
00189 buf << argv[0];
00190
00191 for (int i = 1; i < argc; ++i)
00192 {
00193 buf << " " << argv[i];
00194 }
00195
00196 m_command_line = rutz::fstring(buf.str().c_str());
00197 }
00198
00199
00200
00201
00202
00203 if ((argc > 1) && (argv[1][0] != '-'))
00204 {
00205 m_argv0 = m_startup_filename = argv[1];
00206 --argc;
00207 ++argv;
00208 m_is_interactive = false;
00209 }
00210 else
00211 {
00212 m_argv0 = argv[0];
00213 }
00214
00215
00216
00217
00218 m_interp.set_global_var("argc", tcl::convert_from(argc-1));
00219
00220 char* args = Tcl_Merge(argc-1,
00221 const_cast<const char**>(argv+1));
00222 m_interp.set_global_var("argv", tcl::convert_from(args));
00223 Tcl_Free(args);
00224
00225 m_interp.set_global_var("argv0", tcl::convert_from(m_argv0));
00226
00227 m_interp.set_global_var("tcl_interactive",
00228 tcl::convert_from(m_is_interactive ? 1 : 0));
00229
00230 #ifdef GVX_WITH_READLINE
00231 using_history();
00232 #endif
00233 }
00234
00235
00236
00237
00238
00239
00240
00241 int tcl::event_loop_impl::history_next()
00242 {
00243 GVX_TRACE("tcl::event_loop_impl::history_next");
00244
00245 #ifdef GVX_WITH_READLINE
00246 return history_length+1;
00247 #else
00248 tcl::obj obj = m_interp.get_result<Tcl_Obj*>();
00249
00250 m_interp.eval("history nextid", tcl::IGNORE_ERROR);
00251
00252 int result = m_interp.get_result<int>();
00253
00254 m_interp.set_result(obj);
00255
00256 return result;
00257 #endif
00258 }
00259
00260
00261
00262
00263
00264
00265
00266 void tcl::event_loop_impl::do_prompt(const char* text,
00267 #ifdef GVX_WITH_READLINE
00268 unsigned int
00269 #else
00270 unsigned int length
00271 #endif
00272 )
00273 {
00274 GVX_TRACE("tcl::event_loop_impl::do_prompt");
00275
00276 rutz::fstring color_prompt = text;
00277
00278 if (isatty(1))
00279 {
00280 #if defined(GVX_WITH_READLINE) && defined(RL_PROMPT_START_IGNORE)
00281 color_prompt = rutz::sfmt("%c\033[1;32m%c%s%c\033[0m%c",
00282 RL_PROMPT_START_IGNORE,
00283 RL_PROMPT_END_IGNORE,
00284 text,
00285 RL_PROMPT_START_IGNORE,
00286 RL_PROMPT_END_IGNORE);
00287 #else
00288 color_prompt = rutz::sfmt("\033[1;32m%s\033[0m", text);
00289 #endif
00290 }
00291
00292 #ifdef GVX_WITH_READLINE
00293 rl_callback_handler_install(color_prompt.c_str(),
00294 readline_line_complete);
00295 #else
00296 if (length > 0)
00297 {
00298 std::cout.write(color_prompt.c_str(), color_prompt.length());
00299 std::cout.flush();
00300 }
00301 #endif
00302 }
00303
00304
00305
00306
00307
00308
00309
00310 void tcl::event_loop_impl::prompt(tcl::event_loop_impl::prompt_type t)
00311 {
00312 GVX_TRACE("tcl::event_loop_impl::prompt");
00313
00314 if (t == PARTIAL)
00315 {
00316 do_prompt("", 0);
00317 }
00318 else
00319 {
00320 #ifdef GVX_WITH_READLINE
00321 const rutz::fstring text = rutz::sfmt("%s %d>>> ", m_argv0, history_next());
00322 #else
00323 const rutz::fstring text = rutz::sfmt("%s %d> ", m_argv0, history_next());
00324 #endif
00325
00326 do_prompt(text.c_str(), text.length());
00327 }
00328 }
00329
00330
00331
00332
00333
00334
00335
00336
00337 #ifdef GVX_WITH_READLINE
00338
00339 void tcl::event_loop_impl::readline_line_complete(char* line)
00340 {
00341 GVX_TRACE("tcl::event_loop_impl::readline_line_complete");
00342
00343 dbg_eval_nl(3, line);
00344
00345 rl_callback_handler_remove();
00346
00347 get()->handle_line(line, line == 0 ? -1 : int(strlen(line)));
00348 }
00349
00350 #endif
00351
00352
00353
00354
00355
00356
00357
00358
00359 void tcl::event_loop_impl::grab_input()
00360 {
00361 GVX_TRACE("tcl::event_loop_impl::grab_input");
00362
00363 #ifndef GVX_WITH_READLINE
00364 Tcl_DString line;
00365
00366 Tcl_DStringInit(&line);
00367
00368 int count = Tcl_Gets(m_stdin_chan, &line);
00369
00370 handle_line(Tcl_DStringValue(&line), count);
00371
00372 Tcl_DStringFree(&line);
00373
00374 #else // GVX_WITH_READLINE
00375 rl_callback_read_char();
00376 #endif
00377 }
00378
00379
00380
00381
00382
00383
00384
00385
00386 void tcl::event_loop_impl::handle_line(const char* line, int count)
00387 {
00388 GVX_TRACE("tcl::event_loop_impl::handle_line");
00389
00390 if (count < 0)
00391 {
00392 if (!m_got_partial)
00393 {
00394 if (m_is_interactive)
00395 {
00396
00397
00398
00399
00400
00401
00402 Tcl_Channel out_chan = Tcl_GetStdChannel(TCL_STDOUT);
00403 if (out_chan)
00404 {
00405 const char nl = '\n';
00406 Tcl_WriteChars(out_chan, &nl, 1);
00407 }
00408
00409 Tcl_Exit(0);
00410 }
00411 else
00412 {
00413 Tcl_DeleteChannelHandler(m_stdin_chan,
00414 &c_stdin_proc,
00415 static_cast<void*>(0));
00416 }
00417 }
00418 return;
00419 }
00420
00421 GVX_ASSERT(line != 0);
00422
00423 m_command += line;
00424 m_command += "\n";
00425
00426 dbg_eval_nl(3, m_command.c_str());
00427
00428 if (m_command.length() > 0 &&
00429 Tcl_CommandComplete(m_command.c_str()))
00430 {
00431 m_got_partial = false;
00432 eval_command();
00433 }
00434 else
00435 {
00436 m_got_partial = true;
00437 }
00438
00439 if (m_is_interactive)
00440 {
00441 prompt(m_got_partial ? PARTIAL : FULL);
00442 }
00443
00444 m_interp.reset_result();
00445 }
00446
00447
00448
00449
00450
00451
00452
00453 void tcl::event_loop_impl::eval_command()
00454 {
00455 GVX_TRACE("tcl::event_loop_impl::eval_command");
00456
00457
00458
00459
00460
00461
00462
00463 Tcl_CreateChannelHandler(m_stdin_chan, 0, &c_stdin_proc,
00464 static_cast<void*>(0));
00465
00466 bool should_display_result = false;
00467
00468 #ifdef GVX_WITH_READLINE
00469 char* expansion = 0;
00470 const int status =
00471 history_expand(const_cast<char*>(m_command.c_str()), &expansion);
00472 #else
00473 const char* expansion = m_command.data();
00474 const int status = 0;
00475 #endif
00476
00477 dbg_eval_nl(3, m_command.c_str());
00478 dbg_eval_nl(3, expansion);
00479 dbg_eval_nl(3, status);
00480
00481
00482
00483
00484
00485
00486 if (status == -1 || status == 2)
00487 {
00488 m_interp.append_result(expansion);
00489 should_display_result = true;
00490 }
00491
00492 if (status == 1)
00493 {
00494 Tcl_Channel out_chan = Tcl_GetStdChannel(TCL_STDOUT);
00495 if (out_chan)
00496 {
00497 Tcl_WriteChars(out_chan, expansion, -1);
00498 Tcl_Flush(out_chan);
00499 }
00500 }
00501
00502 if (status == 0 || status == 1)
00503 {
00504
00505
00506
00507
00508
00509
00510
00511
00512 #ifdef GVX_WITH_READLINE
00513 char* trimmed = expansion;
00514 #else
00515 const char* trimmed = expansion;
00516 #endif
00517
00518 while (isspace(trimmed[0]) && trimmed[0] != '\0')
00519 {
00520 ++trimmed;
00521 }
00522
00523 size_t len = strlen(trimmed);
00524
00525 if (len > 0)
00526 {
00527 int code = Tcl_RecordAndEval(m_interp.intp(),
00528 trimmed, TCL_EVAL_GLOBAL);
00529
00530 #ifdef GVX_WITH_READLINE
00531 char c = trimmed[len-1];
00532
00533 if (c == '\n')
00534 trimmed[len-1] = '\0';
00535
00536 add_history(trimmed);
00537
00538 trimmed[len-1] = c;
00539 #endif
00540
00541 dbg_eval_nl(3, m_interp.get_result<const char*>());
00542
00543 should_display_result =
00544 ((m_interp.get_result<const char*>())[0] != '\0') &&
00545 ((code != TCL_OK) || m_is_interactive);
00546 }
00547 }
00548
00549 if (should_display_result)
00550 {
00551 Tcl_Channel out_chan = Tcl_GetStdChannel(TCL_STDOUT);
00552 if (out_chan)
00553 {
00554 Tcl_WriteObj(out_chan, m_interp.get_result<Tcl_Obj*>());
00555 Tcl_WriteChars(out_chan, "\n", 1);
00556 }
00557 }
00558
00559 m_stdin_chan = Tcl_GetStdChannel(TCL_STDIN);
00560
00561 if (m_stdin_chan)
00562 {
00563 Tcl_CreateChannelHandler(m_stdin_chan, TCL_READABLE,
00564 &c_stdin_proc,
00565 static_cast<void*>(0));
00566 }
00567
00568 m_command.clear();
00569
00570 #ifdef GVX_WITH_READLINE
00571 free(expansion);
00572 #endif
00573 }
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584 void tcl::event_loop_impl::c_stdin_proc(void* , int )
00585 {
00586 GVX_TRACE("tcl::event_loop_impl::c_stdin_proc");
00587
00588 tcl::event_loop_impl::get()->grab_input();
00589 }
00590
00591
00592
00593
00594
00595
00596
00597 void tcl::event_loop_impl::run()
00598 {
00599 GVX_TRACE("tcl::event_loop_impl::run");
00600
00601
00602
00603
00604
00605 if (m_startup_filename != NULL)
00606 {
00607 m_interp.reset_result();
00608 bool success = m_interp.eval_file(m_startup_filename);
00609 if (!success)
00610 {
00611
00612 m_interp.add_error_info("");
00613
00614 rutz::backtrace b;
00615 rutz::error::get_last_backtrace(b);
00616 const rutz::fstring bt = rutz::format(b);
00617
00618 std::cerr << m_interp.get_global_var<const char*>("errorInfo")
00619 << "\n" << bt << "\nError in startup script\n";
00620 m_interp.destroy();
00621 Tcl_Exit(1);
00622 }
00623 }
00624 else
00625 {
00626
00627 m_interp.source_rc_file();
00628
00629
00630 m_stdin_chan = Tcl_GetStdChannel(TCL_STDIN);
00631 if (m_stdin_chan)
00632 {
00633 Tcl_CreateChannelHandler(m_stdin_chan, TCL_READABLE,
00634 &event_loop_impl::c_stdin_proc,
00635 static_cast<void*>(0));
00636 }
00637 if (m_is_interactive)
00638 {
00639 this->prompt(FULL);
00640 }
00641 }
00642
00643 Tcl_Channel out_channel = Tcl_GetStdChannel(TCL_STDOUT);
00644 if (out_channel)
00645 {
00646 Tcl_Flush(out_channel);
00647 }
00648 m_interp.reset_result();
00649
00650
00651
00652
00653 while ((m_is_interactive && m_no_window)
00654 || Tk_GetNumMainWindows() > 0)
00655 {
00656 Tcl_DoOneEvent(0);
00657 }
00658 m_interp.destroy();
00659 Tcl_Exit(0);
00660 }
00661
00663
00664
00665
00667
00668 tcl::event_loop::event_loop(int argc, char** argv, bool nowindow)
00669 {
00670 tcl::event_loop_impl::create(argc, argv, nowindow);
00671 }
00672
00673 tcl::event_loop::~event_loop()
00674 {}
00675
00676 bool tcl::event_loop::is_interactive()
00677 {
00678 GVX_TRACE("tcl::event_loop::is_interactive");
00679 return tcl::event_loop_impl::get()->is_interactive();
00680 }
00681
00682 tcl::interpreter& tcl::event_loop::interp()
00683 {
00684 GVX_TRACE("tcl::event_loop::interp");
00685 return tcl::event_loop_impl::get()->interp();
00686 }
00687
00688 void tcl::event_loop::run()
00689 {
00690 GVX_TRACE("tcl::event_loop::run");
00691 tcl::event_loop_impl::get()->run();
00692 }
00693
00694 int tcl::event_loop::argc()
00695 {
00696 GVX_TRACE("tcl::event_loop::argc");
00697 return tcl::event_loop_impl::get()->argc();
00698 }
00699
00700 const char* const* tcl::event_loop::argv()
00701 {
00702 GVX_TRACE("tcl::event_loop::argv");
00703 return tcl::event_loop_impl::get()->argv();
00704 }
00705
00706 rutz::fstring tcl::event_loop::command_line()
00707 {
00708 GVX_TRACE("tcl::event_loop::command_line");
00709 return tcl::event_loop_impl::get()->command_line();
00710 }
00711
00712 static const char __attribute__((used)) vcid_groovx_tcl_eventloop_cc_utc20050628162420[] = "$Id: eventloop.cc 10069 2007-04-12 18:51:44Z rjpeters $ $HeadURL: file:
00713 #endif // !GROOVX_TCL_EVENTLOOP_CC_UTC20050628162420_DEFINED