Ruby  2.0.0p481(2014-05-08revision45883)
ext/tk/tcltklib.c
Go to the documentation of this file.
00001 /*
00002  *      tcltklib.c
00003  *              Aug. 27, 1997   Y. Shigehiro
00004  *              Oct. 24, 1997   Y. Matsumoto
00005  */
00006 
00007 #define TCLTKLIB_RELEASE_DATE "2010-08-25"
00008 /* #define CREATE_RUBYTK_KIT */
00009 
00010 #include "ruby.h"
00011 
00012 #ifdef HAVE_RUBY_ENCODING_H
00013 #include "ruby/encoding.h"
00014 #endif
00015 #ifndef RUBY_VERSION
00016 #define RUBY_VERSION "(unknown version)"
00017 #endif
00018 #ifndef RUBY_RELEASE_DATE
00019 #define RUBY_RELEASE_DATE "unknown release-date"
00020 #endif
00021 
00022 #ifdef RUBY_VM
00023 static int rb_thread_critical; /* dummy */
00024 int rb_thread_check_trap_pending();
00025 #else
00026 /* use rb_thread_critical on Ruby 1.8.x */
00027 #include "rubysig.h"
00028 #endif
00029 
00030 #if !defined(RSTRING_PTR)
00031 #define RSTRING_PTR(s) (RSTRING(s)->ptr)
00032 #define RSTRING_LEN(s) (RSTRING(s)->len)
00033 #endif
00034 #if !defined(RSTRING_LENINT)
00035 #define RSTRING_LENINT(s) ((int)RSTRING_LEN(s))
00036 #endif
00037 #if !defined(RARRAY_PTR)
00038 #define RARRAY_PTR(s) (RARRAY(s)->ptr)
00039 #define RARRAY_LEN(s) (RARRAY(s)->len)
00040 #endif
00041 
00042 #ifdef OBJ_UNTRUST
00043 #define RbTk_OBJ_UNTRUST(x)  do {OBJ_TAINT(x); OBJ_UNTRUST(x);} while (0)
00044 #else
00045 #define RbTk_OBJ_UNTRUST(x)  OBJ_TAINT(x)
00046 #endif
00047 #define RbTk_ALLOC_N(type, n) (type *)ckalloc((int)(sizeof(type) * (n)))
00048 
00049 #if defined(HAVE_RB_PROC_NEW) && !defined(RUBY_VM)
00050 /* Ruby 1.8 :: rb_proc_new() was hidden from intern.h at 2008/04/22 */
00051 extern VALUE rb_proc_new _((VALUE (*)(ANYARGS/* VALUE yieldarg[, VALUE procarg] */), VALUE));
00052 #endif
00053 
00054 #undef EXTERN   /* avoid conflict with tcl.h of tcl8.2 or before */
00055 #include <stdio.h>
00056 #ifdef HAVE_STDARG_PROTOTYPES
00057 #include <stdarg.h>
00058 #define va_init_list(a,b) va_start(a,b)
00059 #else
00060 #include <varargs.h>
00061 #define va_init_list(a,b) va_start(a)
00062 #endif
00063 #include <string.h>
00064 
00065 #if !defined HAVE_VSNPRINTF && !defined vsnprintf
00066 #  ifdef WIN32
00067      /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
00068 #    define vsnprintf _vsnprintf
00069 #  else
00070 #    ifdef HAVE_RUBY_RUBY_H
00071 #      include "ruby/missing.h"
00072 #    else
00073 #      include "missing.h"
00074 #    endif
00075 #  endif
00076 #endif
00077 
00078 #include <tcl.h>
00079 #include <tk.h>
00080 
00081 #ifndef HAVE_RUBY_NATIVE_THREAD_P
00082 #define ruby_native_thread_p() is_ruby_native_thread()
00083 #undef RUBY_USE_NATIVE_THREAD
00084 #else
00085 #define RUBY_USE_NATIVE_THREAD 1
00086 #endif
00087 
00088 #ifndef HAVE_RB_ERRINFO
00089 #define rb_errinfo() (ruby_errinfo+0) /* cannot be l-value */
00090 #else
00091 VALUE rb_errinfo(void);
00092 #endif
00093 #ifndef HAVE_RB_SAFE_LEVEL
00094 #define rb_safe_level() (ruby_safe_level+0)
00095 #endif
00096 #ifndef HAVE_RB_SOURCEFILE
00097 #define rb_sourcefile() (ruby_sourcefile+0)
00098 #endif
00099 
00100 #include "stubs.h"
00101 
00102 #ifndef TCL_ALPHA_RELEASE
00103 #define TCL_ALPHA_RELEASE       0  /* "alpha" */
00104 #define TCL_BETA_RELEASE        1  /* "beta"  */
00105 #define TCL_FINAL_RELEASE       2  /* "final" */
00106 #endif
00107 
00108 static struct {
00109   int major;
00110   int minor;
00111   int type;  /* ALPHA==0, BETA==1, FINAL==2 */
00112   int patchlevel;
00113 } tcltk_version = {0, 0, 0, 0};
00114 
00115 static void
00116 set_tcltk_version()
00117 {
00118     if (tcltk_version.major) return;
00119 
00120     Tcl_GetVersion(&(tcltk_version.major),
00121                    &(tcltk_version.minor),
00122                    &(tcltk_version.patchlevel),
00123                    &(tcltk_version.type));
00124 }
00125 
00126 #if TCL_MAJOR_VERSION >= 8
00127 # ifndef CONST84
00128 #  if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 4 /* Tcl8.0.x -- 8.4b1 */
00129 #   define CONST84
00130 #  else /* unknown (maybe TCL_VERSION >= 8.5) */
00131 #   ifdef CONST
00132 #    define CONST84 CONST
00133 #   else
00134 #    define CONST84
00135 #   endif
00136 #  endif
00137 # endif
00138 #else  /* TCL_MAJOR_VERSION < 8 */
00139 # ifdef CONST
00140 #  define CONST84 CONST
00141 # else
00142 #  define CONST
00143 #  define CONST84
00144 # endif
00145 #endif
00146 
00147 #ifndef CONST86
00148 # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 5 /* Tcl8.0.x -- 8.5.x */
00149 #  define CONST86
00150 # else
00151 #  define CONST86 CONST84
00152 # endif
00153 #endif
00154 
00155 /* copied from eval.c */
00156 #define TAG_RETURN      0x1
00157 #define TAG_BREAK       0x2
00158 #define TAG_NEXT        0x3
00159 #define TAG_RETRY       0x4
00160 #define TAG_REDO        0x5
00161 #define TAG_RAISE       0x6
00162 #define TAG_THROW       0x7
00163 #define TAG_FATAL       0x8
00164 
00165 /* for ruby_debug */
00166 #define DUMP1(ARG1) if (ruby_debug) { fprintf(stderr, "tcltklib: %s\n", ARG1); fflush(stderr); }
00167 #define DUMP2(ARG1, ARG2) if (ruby_debug) { fprintf(stderr, "tcltklib: ");\
00168 fprintf(stderr, ARG1, ARG2); fprintf(stderr, "\n"); fflush(stderr); }
00169 #define DUMP3(ARG1, ARG2, ARG3) if (ruby_debug) { fprintf(stderr, "tcltklib: ");\
00170 fprintf(stderr, ARG1, ARG2, ARG3); fprintf(stderr, "\n"); fflush(stderr); }
00171 /*
00172 #define DUMP1(ARG1)
00173 #define DUMP2(ARG1, ARG2)
00174 #define DUMP3(ARG1, ARG2, ARG3)
00175 */
00176 
00177 /* release date */
00178 static const char tcltklib_release_date[] = TCLTKLIB_RELEASE_DATE;
00179 
00180 /* finalize_proc_name */
00181 static const char finalize_hook_name[] = "INTERP_FINALIZE_HOOK";
00182 
00183 static void ip_finalize _((Tcl_Interp*));
00184 
00185 static int at_exit = 0;
00186 
00187 #ifdef HAVE_RUBY_ENCODING_H
00188 static VALUE cRubyEncoding;
00189 
00190 /* encoding */
00191 static int ENCODING_INDEX_UTF8;
00192 static int ENCODING_INDEX_BINARY;
00193 #endif
00194 static VALUE ENCODING_NAME_UTF8;
00195 static VALUE ENCODING_NAME_BINARY;
00196 
00197 static VALUE create_dummy_encoding_for_tk_core _((VALUE, VALUE, VALUE));
00198 static VALUE create_dummy_encoding_for_tk _((VALUE, VALUE));
00199 static int update_encoding_table _((VALUE, VALUE, VALUE));
00200 static VALUE encoding_table_get_name_core _((VALUE, VALUE, VALUE));
00201 static VALUE encoding_table_get_obj_core _((VALUE, VALUE, VALUE));
00202 static VALUE encoding_table_get_name _((VALUE, VALUE));
00203 static VALUE encoding_table_get_obj _((VALUE, VALUE));
00204 static VALUE create_encoding_table _((VALUE));
00205 static VALUE ip_get_encoding_table _((VALUE));
00206 
00207 
00208 /* for callback break & continue */
00209 static VALUE eTkCallbackReturn;
00210 static VALUE eTkCallbackBreak;
00211 static VALUE eTkCallbackContinue;
00212 
00213 static VALUE eLocalJumpError;
00214 
00215 static VALUE eTkLocalJumpError;
00216 static VALUE eTkCallbackRetry;
00217 static VALUE eTkCallbackRedo;
00218 static VALUE eTkCallbackThrow;
00219 
00220 static VALUE tcltkip_class;
00221 
00222 static ID ID_at_enc;
00223 static ID ID_at_interp;
00224 
00225 static ID ID_encoding_name;
00226 static ID ID_encoding_table;
00227 
00228 static ID ID_stop_p;
00229 static ID ID_alive_p;
00230 static ID ID_kill;
00231 static ID ID_join;
00232 static ID ID_value;
00233 
00234 static ID ID_call;
00235 static ID ID_backtrace;
00236 static ID ID_message;
00237 
00238 static ID ID_at_reason;
00239 static ID ID_return;
00240 static ID ID_break;
00241 static ID ID_next;
00242 
00243 static ID ID_to_s;
00244 static ID ID_inspect;
00245 
00246 static VALUE ip_invoke_real _((int, VALUE*, VALUE));
00247 static VALUE ip_invoke _((int, VALUE*, VALUE));
00248 static VALUE ip_invoke_with_position _((int, VALUE*, VALUE, Tcl_QueuePosition));
00249 static VALUE tk_funcall _((VALUE(), int, VALUE*, VALUE));
00250 static VALUE callq_safelevel_handler _((VALUE, VALUE));
00251 
00252 /* Tcl's object type */
00253 #if TCL_MAJOR_VERSION >= 8
00254 static const char Tcl_ObjTypeName_ByteArray[] = "bytearray";
00255 static CONST86 Tcl_ObjType *Tcl_ObjType_ByteArray;
00256 
00257 static const char Tcl_ObjTypeName_String[]    = "string";
00258 static CONST86 Tcl_ObjType *Tcl_ObjType_String;
00259 
00260 #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1)
00261 #define IS_TCL_BYTEARRAY(obj)    ((obj)->typePtr == Tcl_ObjType_ByteArray)
00262 #define IS_TCL_STRING(obj)       ((obj)->typePtr == Tcl_ObjType_String)
00263 #define IS_TCL_VALID_STRING(obj) ((obj)->bytes != (char*)NULL)
00264 #endif
00265 #endif
00266 
00267 #ifndef HAVE_RB_HASH_LOOKUP
00268 #define rb_hash_lookup rb_hash_aref
00269 #endif
00270 
00271 /* safe Tcl_Eval and Tcl_GlobalEval */
00272 static int
00273 #ifdef HAVE_PROTOTYPES
00274 tcl_eval(Tcl_Interp *interp, const char *cmd)
00275 #else
00276 tcl_eval(interp, cmd)
00277     Tcl_Interp *interp;
00278     const char *cmd; /* don't have to be writable */
00279 #endif
00280 {
00281     char *buf = strdup(cmd);
00282     int ret;
00283 
00284     Tcl_AllowExceptions(interp);
00285     ret = Tcl_Eval(interp, buf);
00286     free(buf);
00287     return ret;
00288 }
00289 
00290 #undef Tcl_Eval
00291 #define Tcl_Eval tcl_eval
00292 
00293 static int
00294 #ifdef HAVE_PROTOTYPES
00295 tcl_global_eval(Tcl_Interp *interp, const char *cmd)
00296 #else
00297 tcl_global_eval(interp, cmd)
00298     Tcl_Interp *interp;
00299     const char *cmd; /* don't have to be writable */
00300 #endif
00301 {
00302     char *buf = strdup(cmd);
00303     int ret;
00304 
00305     Tcl_AllowExceptions(interp);
00306     ret = Tcl_GlobalEval(interp, buf);
00307     free(buf);
00308     return ret;
00309 }
00310 
00311 #undef Tcl_GlobalEval
00312 #define Tcl_GlobalEval tcl_global_eval
00313 
00314 /* Tcl_{Incr|Decr}RefCount for tcl7.x or earlier */
00315 #if TCL_MAJOR_VERSION < 8
00316 #define Tcl_IncrRefCount(obj) (1)
00317 #define Tcl_DecrRefCount(obj) (1)
00318 #endif
00319 
00320 /* Tcl_GetStringResult for tcl7.x or earlier */
00321 #if TCL_MAJOR_VERSION < 8
00322 #define Tcl_GetStringResult(interp) ((interp)->result)
00323 #endif
00324 
00325 /* Tcl_[GS]etVar2Ex for tcl8.0 */
00326 #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0
00327 static Tcl_Obj *
00328 Tcl_GetVar2Ex(interp, name1, name2, flags)
00329     Tcl_Interp *interp;
00330     CONST char *name1;
00331     CONST char *name2;
00332     int flags;
00333 {
00334     Tcl_Obj *nameObj1, *nameObj2 = NULL, *retObj;
00335 
00336     nameObj1 = Tcl_NewStringObj((char*)name1, -1);
00337     Tcl_IncrRefCount(nameObj1);
00338 
00339     if (name2) {
00340         nameObj2 = Tcl_NewStringObj((char*)name2, -1);
00341         Tcl_IncrRefCount(nameObj2);
00342     }
00343 
00344     retObj = Tcl_ObjGetVar2(interp, nameObj1, nameObj2, flags);
00345 
00346     if (name2) {
00347         Tcl_DecrRefCount(nameObj2);
00348     }
00349 
00350     Tcl_DecrRefCount(nameObj1);
00351 
00352     return retObj;
00353 }
00354 
00355 static Tcl_Obj *
00356 Tcl_SetVar2Ex(interp, name1, name2, newValObj, flags)
00357     Tcl_Interp *interp;
00358     CONST char *name1;
00359     CONST char *name2;
00360     Tcl_Obj *newValObj;
00361     int flags;
00362 {
00363     Tcl_Obj *nameObj1, *nameObj2 = NULL, *retObj;
00364 
00365     nameObj1 = Tcl_NewStringObj((char*)name1, -1);
00366     Tcl_IncrRefCount(nameObj1);
00367 
00368     if (name2) {
00369         nameObj2 = Tcl_NewStringObj((char*)name2, -1);
00370         Tcl_IncrRefCount(nameObj2);
00371     }
00372 
00373     retObj = Tcl_ObjSetVar2(interp, nameObj1, nameObj2, newValObj, flags);
00374 
00375     if (name2) {
00376         Tcl_DecrRefCount(nameObj2);
00377     }
00378 
00379     Tcl_DecrRefCount(nameObj1);
00380 
00381     return retObj;
00382 }
00383 #endif
00384 
00385 /* from tkAppInit.c */
00386 
00387 #if TCL_MAJOR_VERSION < 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION < 4)
00388 #  if !defined __MINGW32__ && !defined __BORLANDC__
00389 /*
00390  * The following variable is a special hack that is needed in order for
00391  * Sun shared libraries to be used for Tcl.
00392  */
00393 
00394 extern int matherr();
00395 int *tclDummyMathPtr = (int *) matherr;
00396 #  endif
00397 #endif
00398 
00399 /*---- module TclTkLib ----*/
00400 
00401 struct invoke_queue {
00402     Tcl_Event ev;
00403     int argc;
00404 #if TCL_MAJOR_VERSION >= 8
00405     Tcl_Obj **argv;
00406 #else /* TCL_MAJOR_VERSION < 8 */
00407     char **argv;
00408 #endif
00409     VALUE interp;
00410     int *done;
00411     int safe_level;
00412     VALUE result;
00413     VALUE thread;
00414 };
00415 
00416 struct eval_queue {
00417     Tcl_Event ev;
00418     char *str;
00419     int len;
00420     VALUE interp;
00421     int *done;
00422     int safe_level;
00423     VALUE result;
00424     VALUE thread;
00425 };
00426 
00427 struct call_queue {
00428     Tcl_Event ev;
00429     VALUE (*func)();
00430     int argc;
00431     VALUE *argv;
00432     VALUE interp;
00433     int *done;
00434     int safe_level;
00435     VALUE result;
00436     VALUE thread;
00437 };
00438 
00439 void
00440 invoke_queue_mark(struct invoke_queue *q)
00441 {
00442     rb_gc_mark(q->interp);
00443     rb_gc_mark(q->result);
00444     rb_gc_mark(q->thread);
00445 }
00446 
00447 void
00448 eval_queue_mark(struct eval_queue *q)
00449 {
00450     rb_gc_mark(q->interp);
00451     rb_gc_mark(q->result);
00452     rb_gc_mark(q->thread);
00453 }
00454 
00455 void
00456 call_queue_mark(struct call_queue *q)
00457 {
00458     int i;
00459 
00460     for(i = 0; i < q->argc; i++) {
00461         rb_gc_mark(q->argv[i]);
00462     }
00463 
00464     rb_gc_mark(q->interp);
00465     rb_gc_mark(q->result);
00466     rb_gc_mark(q->thread);
00467 }
00468 
00469 
00470 static VALUE eventloop_thread;
00471 static Tcl_Interp *eventloop_interp;
00472 #ifdef RUBY_USE_NATIVE_THREAD
00473 Tcl_ThreadId tk_eventloop_thread_id;  /* native thread ID of Tcl interpreter */
00474 #endif
00475 static VALUE eventloop_stack;
00476 static int   window_event_mode = ~0;
00477 
00478 static VALUE watchdog_thread;
00479 
00480 Tcl_Interp  *current_interp;
00481 
00482 /* thread control strategy */
00483 /* multi-tk works with the following settings only ???
00484     : CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE 1
00485     : USE_TOGGLE_WINDOW_MODE_FOR_IDLE 0
00486     : DO_THREAD_SCHEDULE_AT_CALLBACK_DONE 0
00487 */
00488 #ifdef RUBY_USE_NATIVE_THREAD
00489 #define CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE 1
00490 #define USE_TOGGLE_WINDOW_MODE_FOR_IDLE 0
00491 #define DO_THREAD_SCHEDULE_AT_CALLBACK_DONE 1
00492 #else /* ! RUBY_USE_NATIVE_THREAD */
00493 #define CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE 1
00494 #define USE_TOGGLE_WINDOW_MODE_FOR_IDLE 0
00495 #define DO_THREAD_SCHEDULE_AT_CALLBACK_DONE 0
00496 #endif
00497 
00498 #if CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE
00499 static int have_rb_thread_waiting_for_value = 0;
00500 #endif
00501 
00502 /*
00503  *  'event_loop_max' is a maximum events which the eventloop processes in one
00504  *  term of thread scheduling. 'no_event_tick' is the count-up value when
00505  *  there are no event for processing.
00506  *  'timer_tick' is a limit of one term of thread scheduling.
00507  *  If 'timer_tick' == 0, then not use the timer for thread scheduling.
00508  */
00509 #ifdef RUBY_USE_NATIVE_THREAD
00510 #define DEFAULT_EVENT_LOOP_MAX        800/*counts*/
00511 #define DEFAULT_NO_EVENT_TICK          10/*counts*/
00512 #define DEFAULT_NO_EVENT_WAIT           5/*milliseconds ( 1 -- 999 ) */
00513 #define WATCHDOG_INTERVAL              10/*milliseconds ( 1 -- 999 ) */
00514 #define DEFAULT_TIMER_TICK              0/*milliseconds ( 0 -- 999 ) */
00515 #define NO_THREAD_INTERRUPT_TIME      100/*milliseconds ( 1 -- 999 ) */
00516 #else /* ! RUBY_USE_NATIVE_THREAD */
00517 #define DEFAULT_EVENT_LOOP_MAX        800/*counts*/
00518 #define DEFAULT_NO_EVENT_TICK          10/*counts*/
00519 #define DEFAULT_NO_EVENT_WAIT          20/*milliseconds ( 1 -- 999 ) */
00520 #define WATCHDOG_INTERVAL              10/*milliseconds ( 1 -- 999 ) */
00521 #define DEFAULT_TIMER_TICK              0/*milliseconds ( 0 -- 999 ) */
00522 #define NO_THREAD_INTERRUPT_TIME      100/*milliseconds ( 1 -- 999 ) */
00523 #endif
00524 
00525 #define EVENT_HANDLER_TIMEOUT         100/*milliseconds*/
00526 
00527 static int event_loop_max = DEFAULT_EVENT_LOOP_MAX;
00528 static int no_event_tick  = DEFAULT_NO_EVENT_TICK;
00529 static int no_event_wait  = DEFAULT_NO_EVENT_WAIT;
00530 static int timer_tick     = DEFAULT_TIMER_TICK;
00531 static int req_timer_tick = DEFAULT_TIMER_TICK;
00532 static int run_timer_flag = 0;
00533 
00534 static int event_loop_wait_event   = 0;
00535 static int event_loop_abort_on_exc = 1;
00536 static int loop_counter = 0;
00537 
00538 static int check_rootwidget_flag = 0;
00539 
00540 
00541 /* call ruby interpreter */
00542 #if TCL_MAJOR_VERSION >= 8
00543 static int ip_ruby_eval _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST*));
00544 static int ip_ruby_cmd _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST*));
00545 #else /* TCL_MAJOR_VERSION < 8 */
00546 static int ip_ruby_eval _((ClientData, Tcl_Interp *, int, char **));
00547 static int ip_ruby_cmd _((ClientData, Tcl_Interp *, int, char **));
00548 #endif
00549 
00550 struct cmd_body_arg {
00551     VALUE receiver;
00552     ID    method;
00553     VALUE args;
00554 };
00555 
00556 /*----------------------------*/
00557 /* use Tcl internal functions */
00558 /*----------------------------*/
00559 #ifndef TCL_NAMESPACE_DEBUG
00560 #define TCL_NAMESPACE_DEBUG 0
00561 #endif
00562 
00563 #if TCL_NAMESPACE_DEBUG
00564 
00565 #if TCL_MAJOR_VERSION >= 8
00566 EXTERN struct TclIntStubs *tclIntStubsPtr;
00567 #endif
00568 
00569 /*-- Tcl_GetCurrentNamespace --*/
00570 #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION < 5
00571 /* Tcl7.x doesn't have namespace support.                            */
00572 /* Tcl8.5+ has definition of Tcl_GetCurrentNamespace() in tclDecls.h */
00573 #  ifndef Tcl_GetCurrentNamespace
00574 EXTERN Tcl_Namespace *  Tcl_GetCurrentNamespace _((Tcl_Interp *));
00575 #  endif
00576 #  if defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS)
00577 #    ifndef Tcl_GetCurrentNamespace
00578 #      ifndef FunctionNum_of_GetCurrentNamespace
00579 #define FunctionNum_of_GetCurrentNamespace 124
00580 #      endif
00581 struct DummyTclIntStubs_for_GetCurrentNamespace {
00582     int magic;
00583     struct TclIntStubHooks *hooks;
00584     void (*func[FunctionNum_of_GetCurrentNamespace])();
00585     Tcl_Namespace * (*tcl_GetCurrentNamespace) _((Tcl_Interp *));
00586 };
00587 
00588 #define Tcl_GetCurrentNamespace \
00589    (((struct DummyTclIntStubs_for_GetCurrentNamespace *)tclIntStubsPtr)->tcl_GetCurrentNamespace)
00590 #    endif
00591 #  endif
00592 #endif
00593 
00594 /* namespace check */
00595 /* ip_null_namespace(Tcl_Interp *interp) */
00596 #if TCL_MAJOR_VERSION < 8
00597 #define ip_null_namespace(interp) (0)
00598 #else /* support namespace */
00599 #define ip_null_namespace(interp) \
00600     (Tcl_GetCurrentNamespace(interp) == (Tcl_Namespace *)NULL)
00601 #endif
00602 
00603 /* rbtk_invalid_namespace(tcltkip *ptr) */
00604 #if TCL_MAJOR_VERSION < 8
00605 #define rbtk_invalid_namespace(ptr) (0)
00606 #else /* support namespace */
00607 #define rbtk_invalid_namespace(ptr) \
00608     ((ptr)->default_ns == (Tcl_Namespace*)NULL || Tcl_GetCurrentNamespace((ptr)->ip) != (ptr)->default_ns)
00609 #endif
00610 
00611 /*-- Tcl_PopCallFrame & Tcl_PushCallFrame --*/
00612 #if TCL_MAJOR_VERSION >= 8
00613 #  ifndef CallFrame
00614 typedef struct CallFrame {
00615     Tcl_Namespace *nsPtr;
00616     int dummy1;
00617     int dummy2;
00618     char *dummy3;
00619     struct CallFrame *callerPtr;
00620     struct CallFrame *callerVarPtr;
00621     int level;
00622     char *dummy7;
00623     char *dummy8;
00624     int dummy9;
00625     char* dummy10;
00626 } CallFrame;
00627 #  endif
00628 
00629 #  if !defined(TclGetFrame) && !defined(TclGetFrame_TCL_DECLARED)
00630 EXTERN int  TclGetFrame _((Tcl_Interp *, CONST char *, CallFrame **));
00631 #  endif
00632 #  if defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS)
00633 #    ifndef TclGetFrame
00634 #      ifndef FunctionNum_of_GetFrame
00635 #define FunctionNum_of_GetFrame 32
00636 #      endif
00637 struct DummyTclIntStubs_for_GetFrame {
00638     int magic;
00639     struct TclIntStubHooks *hooks;
00640     void (*func[FunctionNum_of_GetFrame])();
00641     int (*tclGetFrame) _((Tcl_Interp *, CONST char *, CallFrame **));
00642 };
00643 #define TclGetFrame \
00644    (((struct DummyTclIntStubs_for_GetFrame *)tclIntStubsPtr)->tclGetFrame)
00645 #    endif
00646 #  endif
00647 
00648 #  if !defined(Tcl_PopCallFrame) && !defined(Tcl_PopCallFrame_TCL_DECLARED)
00649 EXTERN void Tcl_PopCallFrame _((Tcl_Interp *));
00650 EXTERN int  Tcl_PushCallFrame _((Tcl_Interp *, Tcl_CallFrame *, Tcl_Namespace *, int));
00651 #  endif
00652 #  if defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS)
00653 #    ifndef Tcl_PopCallFrame
00654 #      ifndef FunctionNum_of_PopCallFrame
00655 #define FunctionNum_of_PopCallFrame 128
00656 #      endif
00657 struct DummyTclIntStubs_for_PopCallFrame {
00658     int magic;
00659     struct TclIntStubHooks *hooks;
00660     void (*func[FunctionNum_of_PopCallFrame])();
00661     void (*tcl_PopCallFrame) _((Tcl_Interp *));
00662     int  (*tcl_PushCallFrame) _((Tcl_Interp *, Tcl_CallFrame *, Tcl_Namespace *, int));
00663 };
00664 
00665 #define Tcl_PopCallFrame \
00666    (((struct DummyTclIntStubs_for_PopCallFrame *)tclIntStubsPtr)->tcl_PopCallFrame)
00667 #define Tcl_PushCallFrame \
00668    (((struct DummyTclIntStubs_for_PopCallFrame *)tclIntStubsPtr)->tcl_PushCallFrame)
00669 #    endif
00670 #  endif
00671 
00672 #else /* Tcl7.x */
00673 #  ifndef CallFrame
00674 typedef struct CallFrame {
00675     Tcl_HashTable varTable;
00676     int level;
00677     int argc;
00678     char **argv;
00679     struct CallFrame *callerPtr;
00680     struct CallFrame *callerVarPtr;
00681 } CallFrame;
00682 #  endif
00683 #  ifndef Tcl_CallFrame
00684 #define Tcl_CallFrame CallFrame
00685 #  endif
00686 
00687 #  if !defined(TclGetFrame) && !defined(TclGetFrame_TCL_DECLARED)
00688 EXTERN int  TclGetFrame _((Tcl_Interp *, CONST char *, CallFrame **));
00689 #  endif
00690 
00691 #  if !defined(Tcl_PopCallFrame) && !defined(Tcl_PopCallFrame_TCL_DECLARED)
00692 typedef struct DummyInterp {
00693     char *dummy1;
00694     char *dummy2;
00695     int  dummy3;
00696     Tcl_HashTable dummy4;
00697     Tcl_HashTable dummy5;
00698     Tcl_HashTable dummy6;
00699     int numLevels;
00700     int maxNestingDepth;
00701     CallFrame *framePtr;
00702     CallFrame *varFramePtr;
00703 } DummyInterp;
00704 
00705 static void
00706 Tcl_PopCallFrame(interp)
00707     Tcl_Interp *interp;
00708 {
00709     DummyInterp *iPtr = (DummyInterp*)interp;
00710     CallFrame *frame = iPtr->varFramePtr;
00711 
00712     /* **** DUMMY **** */
00713     iPtr->framePtr = frame.callerPtr;
00714     iPtr->varFramePtr = frame.callerVarPtr;
00715 
00716     return TCL_OK;
00717 }
00718 
00719 /* dummy */
00720 #define Tcl_Namespace char
00721 
00722 static int
00723 Tcl_PushCallFrame(interp, framePtr, nsPtr, isProcCallFrame)
00724     Tcl_Interp *interp;
00725     Tcl_CallFrame *framePtr;
00726     Tcl_Namespace *nsPtr;
00727     int isProcCallFrame;
00728 {
00729     DummyInterp *iPtr = (DummyInterp*)interp;
00730     CallFrame *frame = (CallFrame *)framePtr;
00731 
00732     /* **** DUMMY **** */
00733     Tcl_InitHashTable(&frame.varTable, TCL_STRING_KEYS);
00734     if (iPtr->varFramePtr != NULL) {
00735         frame.level = iPtr->varFramePtr->level + 1;
00736     } else {
00737         frame.level = 1;
00738     }
00739     frame.callerPtr = iPtr->framePtr;
00740     frame.callerVarPtr = iPtr->varFramePtr;
00741     iPtr->framePtr = &frame;
00742     iPtr->varFramePtr = &frame;
00743 
00744     return TCL_OK;
00745 }
00746 #  endif
00747 
00748 #endif
00749 
00750 #endif /* TCL_NAMESPACE_DEBUG */
00751 
00752 
00753 /*---- class TclTkIp ----*/
00754 struct tcltkip {
00755     Tcl_Interp *ip;              /* the interpreter */
00756 #if TCL_NAMESPACE_DEBUG
00757     Tcl_Namespace *default_ns;   /* default namespace */
00758 #endif
00759 #ifdef RUBY_USE_NATIVE_THREAD
00760     Tcl_ThreadId tk_thread_id;   /* native thread ID of Tcl interpreter */
00761 #endif
00762     int has_orig_exit;           /* has original 'exit' command ? */
00763     Tcl_CmdInfo orig_exit_info;  /* command info of original 'exit' command */
00764     int ref_count;               /* reference count of rbtk_preserve_ip call */
00765     int allow_ruby_exit;         /* allow exiting ruby by 'exit' function */
00766     int return_value;            /* return value */
00767 };
00768 
00769 static struct tcltkip *
00770 get_ip(self)
00771     VALUE self;
00772 {
00773     struct tcltkip *ptr;
00774 
00775     Data_Get_Struct(self, struct tcltkip, ptr);
00776     if (ptr == 0) {
00777         /* rb_raise(rb_eTypeError, "uninitialized TclTkIp"); */
00778         return((struct tcltkip *)NULL);
00779     }
00780     if (ptr->ip == (Tcl_Interp*)NULL) {
00781         /* rb_raise(rb_eRuntimeError, "deleted IP"); */
00782         return((struct tcltkip *)NULL);
00783     }
00784     return ptr;
00785 }
00786 
00787 static int
00788 deleted_ip(ptr)
00789     struct tcltkip *ptr;
00790 {
00791     if (!ptr || !ptr->ip || Tcl_InterpDeleted(ptr->ip)
00792 #if TCL_NAMESPACE_DEBUG
00793           || rbtk_invalid_namespace(ptr)
00794 #endif
00795     ) {
00796         DUMP1("ip is deleted");
00797         return 1;
00798     }
00799     return 0;
00800 }
00801 
00802 /* increment/decrement reference count of tcltkip */
00803 static int
00804 rbtk_preserve_ip(ptr)
00805     struct tcltkip *ptr;
00806 {
00807     ptr->ref_count++;
00808     if (ptr->ip == (Tcl_Interp*)NULL) {
00809         /* deleted IP */
00810         ptr->ref_count = 0;
00811     } else {
00812         Tcl_Preserve((ClientData)ptr->ip);
00813     }
00814     return(ptr->ref_count);
00815 }
00816 
00817 static int
00818 rbtk_release_ip(ptr)
00819     struct tcltkip *ptr;
00820 {
00821     ptr->ref_count--;
00822     if (ptr->ref_count < 0) {
00823         ptr->ref_count = 0;
00824     } else if (ptr->ip == (Tcl_Interp*)NULL) {
00825         /* deleted IP */
00826         ptr->ref_count = 0;
00827     } else {
00828         Tcl_Release((ClientData)ptr->ip);
00829     }
00830     return(ptr->ref_count);
00831 }
00832 
00833 
00834 static VALUE
00835 #ifdef HAVE_STDARG_PROTOTYPES
00836 create_ip_exc(VALUE interp, VALUE exc, const char *fmt, ...)
00837 #else
00838 create_ip_exc(interp, exc, fmt, va_alist)
00839     VALUE interp:
00840     VALUE exc;
00841     const char *fmt;
00842     va_dcl
00843 #endif
00844 {
00845     va_list args;
00846     VALUE msg;
00847     VALUE einfo;
00848     struct tcltkip *ptr = get_ip(interp);
00849 
00850     va_init_list(args,fmt);
00851     msg = rb_vsprintf(fmt, args);
00852     va_end(args);
00853     einfo = rb_exc_new3(exc, msg);
00854     rb_ivar_set(einfo, ID_at_interp, interp);
00855     if (ptr) {
00856         Tcl_ResetResult(ptr->ip);
00857     }
00858 
00859     return einfo;
00860 }
00861 
00862 
00863 /*####################################################################*/
00864 #if defined CREATE_RUBYTK_KIT || defined CREATE_RUBYKIT
00865 
00866 /*--------------------------------------------------------*/
00867 
00868 #if 10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION < 84
00869 #error Ruby/Tk-Kit requires Tcl/Tk8.4 or later.
00870 #endif
00871 
00872 /*--------------------------------------------------------*/
00873 
00874 /* Many part of code to support Ruby/Tk-Kit is quoted from Tclkit.       */
00875 /* But, never ask Tclkit community about Ruby/Tk-Kit.                    */
00876 /* Please ask Ruby (Ruby/Tk) community (e.g. "ruby-dev" mailing list).   */
00877 /*
00878 ----<< license terms of TclKit (from kitgen's "README" file) >>---------------
00879 The Tclkit-specific sources are license free, they just have a copyright. Hold
00880 the author(s) harmless and any lawful use is permitted.
00881 
00882 This does *not* apply to any of the sources of the other major Open Source
00883 Software used in Tclkit, which each have very liberal BSD/MIT-like licenses:
00884 
00885   * Tcl/Tk, TclVFS, Thread, Vlerq, Zlib
00886 ------------------------------------------------------------------------------
00887  */
00888 /* Tcl/Tk stubs may work, but probably it is meaningless. */
00889 #if defined USE_TCL_STUBS || defined USE_TK_STUBS
00890 #  error Not support Tcl/Tk stubs with Ruby/Tk-Kit or Rubykit.
00891 #endif
00892 
00893 #ifndef KIT_INCLUDES_ZLIB
00894 #if 10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION < 86
00895 #define KIT_INCLUDES_ZLIB 1
00896 #else
00897 #define KIT_INCLUDES_ZLIB 0
00898 #endif
00899 #endif
00900 
00901 #ifdef _WIN32
00902 #define WIN32_LEAN_AND_MEAN
00903 #include <windows.h>
00904 #undef WIN32_LEAN_AND_MEAN
00905 #endif
00906 
00907 #if 10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION < 86
00908 EXTERN Tcl_Obj* TclGetStartupScriptPath();
00909 EXTERN void TclSetStartupScriptPath _((Tcl_Obj*));
00910 #define Tcl_GetStartupScript(encPtr) TclGetStartupScriptPath()
00911 #define Tcl_SetStartupScript(path,enc) TclSetStartupScriptPath(path)
00912 #endif
00913 #if !defined(TclSetPreInitScript) && !defined(TclSetPreInitScript_TCL_DECLARED)
00914 EXTERN char* TclSetPreInitScript _((char *));
00915 #endif
00916 
00917 #ifndef KIT_INCLUDES_TK
00918 #  define KIT_INCLUDES_TK  1
00919 #endif
00920 /* #define KIT_INCLUDES_ITCL 1 */
00921 /* #define KIT_INCLUDES_THREAD  1 */
00922 
00923 Tcl_AppInitProc Vfs_Init, Rechan_Init;
00924 #if 10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION < 85
00925 Tcl_AppInitProc Pwb_Init;
00926 #endif
00927 
00928 #ifdef KIT_LITE
00929 Tcl_AppInitProc Vlerq_Init, Vlerq_SafeInit;
00930 #else
00931 Tcl_AppInitProc Mk4tcl_Init;
00932 #endif
00933 
00934 #if defined TCL_THREADS && defined KIT_INCLUDES_THREAD
00935 Tcl_AppInitProc Thread_Init;
00936 #endif
00937 
00938 #if KIT_INCLUDES_ZLIB
00939 Tcl_AppInitProc Zlib_Init;
00940 #endif
00941 
00942 #ifdef KIT_INCLUDES_ITCL
00943 Tcl_AppInitProc Itcl_Init;
00944 #endif
00945 
00946 #ifdef _WIN32
00947 Tcl_AppInitProc Dde_Init, Dde_SafeInit, Registry_Init;
00948 #endif
00949 
00950 /*--------------------------------------------------------*/
00951 
00952 #define RUBYTK_KITPATH_CONST_NAME "RUBYTK_KITPATH"
00953 
00954 static char *rubytk_kitpath = NULL;
00955 
00956 static char rubytkkit_preInitCmd[] =
00957 "proc tclKitPreInit {} {\n"
00958     "rename tclKitPreInit {}\n"
00959     "load {} rubytk_kitpath\n"
00960 #if KIT_INCLUDES_ZLIB
00961     "catch {load {} zlib}\n"
00962 #endif
00963 #ifdef KIT_LITE
00964     "load {} vlerq\n"
00965     "namespace eval ::vlerq {}\n"
00966     "if {[catch { vlerq open $::tcl::kitpath } ::vlerq::starkit_root]} {\n"
00967       "set n -1\n"
00968     "} else {\n"
00969       "set files [vlerq get $::vlerq::starkit_root 0 dirs 0 files]\n"
00970       "set n [lsearch [vlerq get $files * name] boot.tcl]\n"
00971     "}\n"
00972     "if {$n >= 0} {\n"
00973         "array set a [vlerq get $files $n]\n"
00974 #else
00975     "load {} Mk4tcl\n"
00976 #if defined KIT_VFS_WRITABLE && !defined CREATE_RUBYKIT
00977     /* running command cannot open itself for writing */
00978     "mk::file open exe $::tcl::kitpath\n"
00979 #else
00980     "mk::file open exe $::tcl::kitpath -readonly\n"
00981 #endif
00982     "set n [mk::select exe.dirs!0.files name boot.tcl]\n"
00983     "if {[llength $n] == 1} {\n"
00984         "array set a [mk::get exe.dirs!0.files!$n]\n"
00985 #endif
00986         "if {![info exists a(contents)]} { error {no boot.tcl file} }\n"
00987         "if {$a(size) != [string length $a(contents)]} {\n"
00988                 "set a(contents) [zlib decompress $a(contents)]\n"
00989         "}\n"
00990         "if {$a(contents) eq \"\"} { error {empty boot.tcl} }\n"
00991         "uplevel #0 $a(contents)\n"
00992 #if 0
00993     "} elseif {[lindex $::argv 0] eq \"-init-\"} {\n"
00994         "uplevel #0 { source [lindex $::argv 1] }\n"
00995         "exit\n"
00996 #endif
00997     "} else {\n"
00998         /* When cannot find VFS data, try to use a real directory */
00999         "set vfsdir \"[file rootname $::tcl::kitpath].vfs\"\n"
01000         "if {[file isdirectory $vfsdir]} {\n"
01001            "set ::tcl_library [file join $vfsdir lib tcl$::tcl_version]\n"
01002            "set ::tcl_libPath [list $::tcl_library [file join $vfsdir lib]]\n"
01003            "catch {uplevel #0 [list source [file join $vfsdir config.tcl]]}\n"
01004            "uplevel #0 [list source [file join $::tcl_library init.tcl]]\n"
01005            "set ::auto_path $::tcl_libPath\n"
01006         "} else {\n"
01007            "error \"\n  $::tcl::kitpath has no VFS data to start up\"\n"
01008         "}\n"
01009     "}\n"
01010 "}\n"
01011 "tclKitPreInit"
01012 ;
01013 
01014 #if 0
01015 /* Not use this script.
01016    It's a memo to support an initScript for Tcl interpreters in the future. */
01017 static const char initScript[] =
01018 "if {[file isfile [file join $::tcl::kitpath main.tcl]]} {\n"
01019     "if {[info commands console] != {}} { console hide }\n"
01020     "set tcl_interactive 0\n"
01021     "incr argc\n"
01022     "set argv [linsert $argv 0 $argv0]\n"
01023     "set argv0 [file join $::tcl::kitpath main.tcl]\n"
01024 "} else continue\n"
01025 ;
01026 #endif
01027 
01028 /*--------------------------------------------------------*/
01029 
01030 static char*
01031 set_rubytk_kitpath(const char *kitpath)
01032 {
01033   if (kitpath) {
01034     int len = (int)strlen(kitpath);
01035     if (rubytk_kitpath) {
01036       ckfree(rubytk_kitpath);
01037     }
01038 
01039     rubytk_kitpath = (char *)ckalloc(len + 1);
01040     memcpy(rubytk_kitpath, kitpath, len);
01041     rubytk_kitpath[len] = '\0';
01042   }
01043   return rubytk_kitpath;
01044 }
01045 
01046 /*--------------------------------------------------------*/
01047 
01048 #ifdef WIN32
01049 #define DEV_NULL "NUL"
01050 #else
01051 #define DEV_NULL "/dev/null"
01052 #endif
01053 
01054 static void
01055 check_tclkit_std_channels()
01056 {
01057     Tcl_Channel chan;
01058 
01059     /*
01060      * We need to verify if we have the standard channels and create them if
01061      * not.  Otherwise internals channels may get used as standard channels
01062      * (like for encodings) and panic.
01063      */
01064     chan = Tcl_GetStdChannel(TCL_STDIN);
01065     if (chan == NULL) {
01066         chan = Tcl_OpenFileChannel(NULL, DEV_NULL, "r", 0);
01067         if (chan != NULL) {
01068             Tcl_SetChannelOption(NULL, chan, "-encoding", "utf-8");
01069         }
01070         Tcl_SetStdChannel(chan, TCL_STDIN);
01071     }
01072     chan = Tcl_GetStdChannel(TCL_STDOUT);
01073     if (chan == NULL) {
01074         chan = Tcl_OpenFileChannel(NULL, DEV_NULL, "w", 0);
01075         if (chan != NULL) {
01076             Tcl_SetChannelOption(NULL, chan, "-encoding", "utf-8");
01077         }
01078         Tcl_SetStdChannel(chan, TCL_STDOUT);
01079     }
01080     chan = Tcl_GetStdChannel(TCL_STDERR);
01081     if (chan == NULL) {
01082         chan = Tcl_OpenFileChannel(NULL, DEV_NULL, "w", 0);
01083         if (chan != NULL) {
01084             Tcl_SetChannelOption(NULL, chan, "-encoding", "utf-8");
01085         }
01086         Tcl_SetStdChannel(chan, TCL_STDERR);
01087     }
01088 }
01089 
01090 /*--------------------------------------------------------*/
01091 
01092 static int
01093 rubytk_kitpathObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
01094 {
01095     const char* str;
01096     if (objc == 2) {
01097         set_rubytk_kitpath(Tcl_GetString(objv[1]));
01098     } else if (objc > 2) {
01099         Tcl_WrongNumArgs(interp, 1, objv, "?path?");
01100     }
01101     str = rubytk_kitpath ? rubytk_kitpath : Tcl_GetNameOfExecutable();
01102     Tcl_SetObjResult(interp, Tcl_NewStringObj(str, -1));
01103     return TCL_OK;
01104 }
01105 
01106 /*
01107  * Public entry point for ::tcl::kitpath.
01108  * Creates both link variable name and Tcl command ::tcl::kitpath.
01109  */
01110 static int
01111 rubytk_kitpath_init(Tcl_Interp *interp)
01112 {
01113     Tcl_CreateObjCommand(interp, "::tcl::kitpath", rubytk_kitpathObjCmd, 0, 0);
01114     if (Tcl_LinkVar(interp, "::tcl::kitpath", (char *) &rubytk_kitpath,
01115                 TCL_LINK_STRING | TCL_LINK_READ_ONLY) != TCL_OK) {
01116         Tcl_ResetResult(interp);
01117     }
01118 
01119     Tcl_CreateObjCommand(interp, "::tcl::rubytk_kitpath", rubytk_kitpathObjCmd, 0, 0);
01120     if (Tcl_LinkVar(interp, "::tcl::rubytk_kitpath", (char *) &rubytk_kitpath,
01121                 TCL_LINK_STRING | TCL_LINK_READ_ONLY) != TCL_OK) {
01122         Tcl_ResetResult(interp);
01123     }
01124 
01125     if (rubytk_kitpath == NULL) {
01126         /*
01127          * XXX: We may want to avoid doing this to allow tcl::kitpath calls
01128          * XXX: to obtain changes in nameofexe, if they occur.
01129          */
01130         set_rubytk_kitpath(Tcl_GetNameOfExecutable());
01131     }
01132 
01133     return Tcl_PkgProvide(interp, "rubytk_kitpath", "1.0");
01134 }
01135 
01136 /*--------------------------------------------------------*/
01137 
01138 static void
01139 init_static_tcltk_packages()
01140 {
01141     /*
01142      * Ensure that std channels exist (creating them if necessary)
01143      */
01144     check_tclkit_std_channels();
01145 
01146 #ifdef KIT_INCLUDES_ITCL
01147     Tcl_StaticPackage(0, "Itcl", Itcl_Init, NULL);
01148 #endif
01149 #ifdef KIT_LITE
01150     Tcl_StaticPackage(0, "Vlerq", Vlerq_Init, Vlerq_SafeInit);
01151 #else
01152     Tcl_StaticPackage(0, "Mk4tcl", Mk4tcl_Init, NULL);
01153 #endif
01154 #if 10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION < 85
01155     Tcl_StaticPackage(0, "pwb", Pwb_Init, NULL);
01156 #endif
01157     Tcl_StaticPackage(0, "rubytk_kitpath", rubytk_kitpath_init, NULL);
01158     Tcl_StaticPackage(0, "rechan", Rechan_Init, NULL);
01159     Tcl_StaticPackage(0, "vfs", Vfs_Init, NULL);
01160 #if KIT_INCLUDES_ZLIB
01161     Tcl_StaticPackage(0, "zlib", Zlib_Init, NULL);
01162 #endif
01163 #if defined TCL_THREADS && defined KIT_INCLUDES_THREAD
01164     Tcl_StaticPackage(0, "Thread", Thread_Init, Thread_SafeInit);
01165 #endif
01166 #ifdef _WIN32
01167 #if 10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION > 84
01168     Tcl_StaticPackage(0, "dde", Dde_Init, Dde_SafeInit);
01169 #else
01170     Tcl_StaticPackage(0, "dde", Dde_Init, NULL);
01171 #endif
01172     Tcl_StaticPackage(0, "registry", Registry_Init, NULL);
01173 #endif
01174 #ifdef KIT_INCLUDES_TK
01175     Tcl_StaticPackage(0, "Tk", Tk_Init, Tk_SafeInit);
01176 #endif
01177 }
01178 
01179 /*--------------------------------------------------------*/
01180 
01181 static int
01182 call_tclkit_init_script(Tcl_Interp  *interp)
01183 {
01184 #if 0
01185   /* Currently, do nothing in this function.
01186      It's a memo (quoted from kitInit.c of Tclkit)
01187      to support an initScript for Tcl interpreters in the future. */
01188   if (Tcl_EvalEx(interp, initScript, -1, TCL_EVAL_GLOBAL) == TCL_OK) {
01189     const char *encoding = NULL;
01190     Tcl_Obj* path = Tcl_GetStartupScript(&encoding);
01191     Tcl_SetStartupScript(Tcl_GetObjResult(interp), encoding);
01192     if (path == NULL) {
01193       Tcl_Eval(interp, "incr argc -1; set argv [lrange $argv 1 end]");
01194     }
01195   }
01196 #endif
01197 
01198   return 1;
01199 }
01200 
01201 /*--------------------------------------------------------*/
01202 
01203 #ifdef __WIN32__
01204 /* #include <tkWinInt.h> *//* conflict definition of struct timezone */
01205 /* #include <tkIntPlatDecls.h> */
01206 /* #include <windows.h> */
01207 EXTERN void TkWinSetHINSTANCE(HINSTANCE hInstance);
01208 void rbtk_win32_SetHINSTANCE(const char *module_name)
01209 {
01210   /* TCHAR szBuf[256]; */
01211   HINSTANCE hInst;
01212 
01213   /* hInst = GetModuleHandle(NULL); */
01214   /* hInst = GetModuleHandle("tcltklib.so"); */
01215   hInst = GetModuleHandle(module_name);
01216   TkWinSetHINSTANCE(hInst);
01217 
01218   /* GetModuleFileName(hInst, szBuf, sizeof(szBuf) / sizeof(TCHAR)); */
01219   /* MessageBox(NULL, szBuf, TEXT("OK"), MB_OK); */
01220 }
01221 #endif
01222 
01223 /*--------------------------------------------------------*/
01224 
01225 static void
01226 setup_rubytkkit()
01227 {
01228   init_static_tcltk_packages();
01229 
01230   {
01231     ID const_id;
01232     const_id = rb_intern(RUBYTK_KITPATH_CONST_NAME);
01233 
01234     if (rb_const_defined(rb_cObject, const_id)) {
01235       volatile VALUE pathobj;
01236       pathobj = rb_const_get(rb_cObject, const_id);
01237 
01238       if (rb_obj_is_kind_of(pathobj, rb_cString)) {
01239 #ifdef HAVE_RUBY_ENCODING_H
01240         pathobj = rb_str_export_to_enc(pathobj, rb_utf8_encoding());
01241 #endif
01242         set_rubytk_kitpath(RSTRING_PTR(pathobj));
01243       }
01244     }
01245   }
01246 
01247 #ifdef CREATE_RUBYTK_KIT
01248   if (rubytk_kitpath == NULL) {
01249 #ifdef __WIN32__
01250     /* rbtk_win32_SetHINSTANCE("tcltklib.so"); */
01251     {
01252       volatile VALUE basename;
01253       basename = rb_funcall(rb_cFile, rb_intern("basename"), 1,
01254                             rb_str_new2(rb_sourcefile()));
01255       rbtk_win32_SetHINSTANCE(RSTRING_PTR(basename));
01256     }
01257 #endif
01258     set_rubytk_kitpath(rb_sourcefile());
01259   }
01260 #endif
01261 
01262   if (rubytk_kitpath == NULL) {
01263     set_rubytk_kitpath(Tcl_GetNameOfExecutable());
01264   }
01265 
01266   TclSetPreInitScript(rubytkkit_preInitCmd);
01267 }
01268 
01269 /*--------------------------------------------------------*/
01270 
01271 #endif /* defined CREATE_RUBYTK_KIT || defined CREATE_RUBYKIT */
01272 /*####################################################################*/
01273 
01274 
01275 /**********************************************************************/
01276 
01277 /* stub status */
01278 static void
01279 tcl_stubs_check()
01280 {
01281     if (!tcl_stubs_init_p()) {
01282         int st = ruby_tcl_stubs_init();
01283         switch(st) {
01284         case TCLTK_STUBS_OK:
01285             break;
01286         case NO_TCL_DLL:
01287             rb_raise(rb_eLoadError, "tcltklib: fail to open tcl_dll");
01288         case NO_FindExecutable:
01289             rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_FindExecutable");
01290         case NO_CreateInterp:
01291             rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_CreateInterp()");
01292         case NO_DeleteInterp:
01293             rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_DeleteInterp()");
01294         case FAIL_CreateInterp:
01295             rb_raise(rb_eRuntimeError, "tcltklib: fail to create a new IP to call Tcl_InitStubs()");
01296         case FAIL_Tcl_InitStubs:
01297             rb_raise(rb_eRuntimeError, "tcltklib: fail to Tcl_InitStubs()");
01298         default:
01299             rb_raise(rb_eRuntimeError, "tcltklib: unknown error(%d) on ruby_tcl_stubs_init()", st);
01300         }
01301     }
01302 }
01303 
01304 
01305 static VALUE
01306 tcltkip_init_tk(interp)
01307     VALUE interp;
01308 {
01309     struct tcltkip *ptr = get_ip(interp);
01310 
01311 #if TCL_MAJOR_VERSION >= 8
01312     int  st;
01313 
01314     if (Tcl_IsSafe(ptr->ip)) {
01315         DUMP1("Tk_SafeInit");
01316         st = ruby_tk_stubs_safeinit(ptr->ip);
01317         switch(st) {
01318         case TCLTK_STUBS_OK:
01319             break;
01320         case NO_Tk_Init:
01321             return rb_exc_new2(rb_eLoadError,
01322                                "tcltklib: can't find Tk_SafeInit()");
01323         case FAIL_Tk_Init:
01324             return create_ip_exc(interp, rb_eRuntimeError,
01325                                  "tcltklib: fail to Tk_SafeInit(). %s",
01326                                  Tcl_GetStringResult(ptr->ip));
01327         case FAIL_Tk_InitStubs:
01328             return create_ip_exc(interp, rb_eRuntimeError,
01329                                  "tcltklib: fail to Tk_InitStubs(). %s",
01330                                  Tcl_GetStringResult(ptr->ip));
01331         default:
01332             return create_ip_exc(interp, rb_eRuntimeError,
01333                                  "tcltklib: unknown error(%d) on ruby_tk_stubs_safeinit", st);
01334         }
01335     } else {
01336         DUMP1("Tk_Init");
01337         st = ruby_tk_stubs_init(ptr->ip);
01338         switch(st) {
01339         case TCLTK_STUBS_OK:
01340             break;
01341         case NO_Tk_Init:
01342             return rb_exc_new2(rb_eLoadError,
01343                                "tcltklib: can't find Tk_Init()");
01344         case FAIL_Tk_Init:
01345             return create_ip_exc(interp, rb_eRuntimeError,
01346                                  "tcltklib: fail to Tk_Init(). %s",
01347                                  Tcl_GetStringResult(ptr->ip));
01348         case FAIL_Tk_InitStubs:
01349             return create_ip_exc(interp, rb_eRuntimeError,
01350                                  "tcltklib: fail to Tk_InitStubs(). %s",
01351                                  Tcl_GetStringResult(ptr->ip));
01352         default:
01353             return create_ip_exc(interp, rb_eRuntimeError,
01354                                  "tcltklib: unknown error(%d) on ruby_tk_stubs_init", st);
01355         }
01356     }
01357 
01358 #else /* TCL_MAJOR_VERSION < 8 */
01359     DUMP1("Tk_Init");
01360     if (ruby_tk_stubs_init(ptr->ip) != TCLTK_STUBS_OK) {
01361         return rb_exc_new2(rb_eRuntimeError, ptr->ip->result);
01362     }
01363 #endif
01364 
01365 #ifdef RUBY_USE_NATIVE_THREAD
01366     ptr->tk_thread_id = Tcl_GetCurrentThread();
01367 #endif
01368 
01369     return Qnil;
01370 }
01371 
01372 
01373 /* treat excetiopn on Tcl side */
01374 static VALUE rbtk_pending_exception;
01375 static int rbtk_eventloop_depth = 0;
01376 static int rbtk_internal_eventloop_handler = 0;
01377 
01378 
01379 static int
01380 pending_exception_check0()
01381 {
01382     volatile VALUE exc = rbtk_pending_exception;
01383 
01384     if (!NIL_P(exc) && rb_obj_is_kind_of(exc, rb_eException)) {
01385         DUMP1("find a pending exception");
01386         if (rbtk_eventloop_depth > 0
01387             || rbtk_internal_eventloop_handler > 0
01388             ) {
01389             return 1; /* pending */
01390         } else {
01391             rbtk_pending_exception = Qnil;
01392 
01393             if (rb_obj_is_kind_of(exc, eTkCallbackRetry)) {
01394                 DUMP1("pending_exception_check0: call rb_jump_tag(retry)");
01395                 rb_jump_tag(TAG_RETRY);
01396             } else if (rb_obj_is_kind_of(exc, eTkCallbackRedo)) {
01397                 DUMP1("pending_exception_check0: call rb_jump_tag(redo)");
01398                 rb_jump_tag(TAG_REDO);
01399             } else if (rb_obj_is_kind_of(exc, eTkCallbackThrow)) {
01400                 DUMP1("pending_exception_check0: call rb_jump_tag(throw)");
01401                 rb_jump_tag(TAG_THROW);
01402             }
01403 
01404             rb_exc_raise(exc);
01405         }
01406     } else {
01407         return 0;
01408     }
01409 
01410     UNREACHABLE;
01411 }
01412 
01413 static int
01414 pending_exception_check1(thr_crit_bup, ptr)
01415     int thr_crit_bup;
01416     struct tcltkip *ptr;
01417 {
01418     volatile VALUE exc = rbtk_pending_exception;
01419 
01420     if (!NIL_P(exc) && rb_obj_is_kind_of(exc, rb_eException)) {
01421         DUMP1("find a pending exception");
01422 
01423         if (rbtk_eventloop_depth > 0
01424             || rbtk_internal_eventloop_handler > 0
01425             ) {
01426             return 1; /* pending */
01427         } else {
01428             rbtk_pending_exception = Qnil;
01429 
01430             if (ptr != (struct tcltkip *)NULL) {
01431                 /* Tcl_Release(ptr->ip); */
01432                 rbtk_release_ip(ptr);
01433             }
01434 
01435             rb_thread_critical = thr_crit_bup;
01436 
01437             if (rb_obj_is_kind_of(exc, eTkCallbackRetry)) {
01438                 DUMP1("pending_exception_check1: call rb_jump_tag(retry)");
01439                 rb_jump_tag(TAG_RETRY);
01440             } else if (rb_obj_is_kind_of(exc, eTkCallbackRedo)) {
01441                 DUMP1("pending_exception_check1: call rb_jump_tag(redo)");
01442                 rb_jump_tag(TAG_REDO);
01443             } else if (rb_obj_is_kind_of(exc, eTkCallbackThrow)) {
01444                 DUMP1("pending_exception_check1: call rb_jump_tag(throw)");
01445                 rb_jump_tag(TAG_THROW);
01446             }
01447             rb_exc_raise(exc);
01448         }
01449     } else {
01450         return 0;
01451     }
01452 
01453     UNREACHABLE;
01454 }
01455 
01456 
01457 /* call original 'exit' command */
01458 static void
01459 call_original_exit(ptr, state)
01460     struct tcltkip *ptr;
01461     int state;
01462 {
01463     int  thr_crit_bup;
01464     Tcl_CmdInfo *info;
01465 #if TCL_MAJOR_VERSION >= 8
01466     Tcl_Obj *cmd_obj;
01467     Tcl_Obj *state_obj;
01468 #endif
01469     DUMP1("original_exit is called");
01470 
01471     if (!(ptr->has_orig_exit)) return;
01472 
01473     thr_crit_bup = rb_thread_critical;
01474     rb_thread_critical = Qtrue;
01475 
01476     Tcl_ResetResult(ptr->ip);
01477 
01478     info = &(ptr->orig_exit_info);
01479 
01480     /* memory allocation for arguments of this command */
01481 #if TCL_MAJOR_VERSION >= 8
01482     state_obj = Tcl_NewIntObj(state);
01483     Tcl_IncrRefCount(state_obj);
01484 
01485     if (info->isNativeObjectProc) {
01486         Tcl_Obj **argv;
01487 #define USE_RUBY_ALLOC 0
01488 #if USE_RUBY_ALLOC
01489         argv = (Tcl_Obj **)ALLOC_N(Tcl_Obj *, 3);
01490 #else /* not USE_RUBY_ALLOC */
01491         argv = RbTk_ALLOC_N(Tcl_Obj *, 3);
01492 #if 0 /* use Tcl_Preserve/Release */
01493         Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
01494 #endif
01495 #endif
01496         cmd_obj = Tcl_NewStringObj("exit", 4);
01497         Tcl_IncrRefCount(cmd_obj);
01498 
01499         argv[0] = cmd_obj;
01500         argv[1] = state_obj;
01501         argv[2] = (Tcl_Obj *)NULL;
01502 
01503         ptr->return_value
01504             = (*(info->objProc))(info->objClientData, ptr->ip, 2, argv);
01505 
01506         Tcl_DecrRefCount(cmd_obj);
01507 
01508 #if USE_RUBY_ALLOC
01509         xfree(argv);
01510 #else /* not USE_RUBY_ALLOC */
01511 #if 0 /* use Tcl_EventuallyFree */
01512         Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
01513 #else
01514 #if 0 /* use Tcl_Preserve/Release */
01515         Tcl_Release((ClientData)argv); /* XXXXXXXX */
01516 #else
01517         /* free(argv); */
01518         ckfree((char*)argv);
01519 #endif
01520 #endif
01521 #endif
01522 #undef USE_RUBY_ALLOC
01523 
01524     } else {
01525         /* string interface */
01526         CONST84 char **argv;
01527 #define USE_RUBY_ALLOC 0
01528 #if USE_RUBY_ALLOC
01529         argv = ALLOC_N(char *, 3); /* XXXXXXXXXX */
01530 #else /* not USE_RUBY_ALLOC */
01531         argv = RbTk_ALLOC_N(CONST84 char *, 3);
01532 #if 0 /* use Tcl_Preserve/Release */
01533         Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
01534 #endif
01535 #endif
01536         argv[0] = (char *)"exit";
01537         /* argv[1] = Tcl_GetString(state_obj); */
01538         argv[1] = Tcl_GetStringFromObj(state_obj, (int*)NULL);
01539         argv[2] = (char *)NULL;
01540 
01541         ptr->return_value = (*(info->proc))(info->clientData, ptr->ip, 2, argv);
01542 
01543 #if USE_RUBY_ALLOC
01544         xfree(argv);
01545 #else /* not USE_RUBY_ALLOC */
01546 #if 0 /* use Tcl_EventuallyFree */
01547         Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
01548 #else
01549 #if 0 /* use Tcl_Preserve/Release */
01550         Tcl_Release((ClientData)argv); /* XXXXXXXX */
01551 #else
01552         /* free(argv); */
01553         ckfree((char*)argv);
01554 #endif
01555 #endif
01556 #endif
01557 #undef USE_RUBY_ALLOC
01558     }
01559 
01560     Tcl_DecrRefCount(state_obj);
01561 
01562 #else /* TCL_MAJOR_VERSION < 8 */
01563     {
01564         /* string interface */
01565         char **argv;
01566 #define USE_RUBY_ALLOC 0
01567 #if USE_RUBY_ALLOC
01568         argv = (char **)ALLOC_N(char *, 3);
01569 #else /* not USE_RUBY_ALLOC */
01570         argv = RbTk_ALLOC_N(char *, 3);
01571 #if 0 /* use Tcl_Preserve/Release */
01572         Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
01573 #endif
01574 #endif
01575         argv[0] = "exit";
01576         argv[1] = RSTRING_PTR(rb_fix2str(INT2NUM(state), 10));
01577         argv[2] = (char *)NULL;
01578 
01579         ptr->return_value = (*(info->proc))(info->clientData, ptr->ip,
01580                                             2, argv);
01581 
01582 #if USE_RUBY_ALLOC
01583         xfree(argv);
01584 #else /* not USE_RUBY_ALLOC */
01585 #if 0 /* use Tcl_EventuallyFree */
01586         Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
01587 #else
01588 #if 0 /* use Tcl_Preserve/Release */
01589         Tcl_Release((ClientData)argv); /* XXXXXXXX */
01590 #else
01591         /* free(argv); */
01592         ckfree(argv);
01593 #endif
01594 #endif
01595 #endif
01596 #undef USE_RUBY_ALLOC
01597     }
01598 #endif
01599     DUMP1("complete original_exit");
01600 
01601     rb_thread_critical = thr_crit_bup;
01602 }
01603 
01604 /* Tk_ThreadTimer */
01605 static Tcl_TimerToken timer_token = (Tcl_TimerToken)NULL;
01606 
01607 /* timer callback */
01608 static void _timer_for_tcl _((ClientData));
01609 static void
01610 _timer_for_tcl(clientData)
01611     ClientData clientData;
01612 {
01613     int thr_crit_bup;
01614 
01615     /* struct invoke_queue *q, *tmp; */
01616     /* VALUE thread; */
01617 
01618     DUMP1("call _timer_for_tcl");
01619 
01620     thr_crit_bup = rb_thread_critical;
01621     rb_thread_critical = Qtrue;
01622 
01623     Tcl_DeleteTimerHandler(timer_token);
01624 
01625     run_timer_flag = 1;
01626 
01627     if (timer_tick > 0) {
01628         timer_token = Tcl_CreateTimerHandler(timer_tick, _timer_for_tcl,
01629                                              (ClientData)0);
01630     } else {
01631         timer_token = (Tcl_TimerToken)NULL;
01632     }
01633 
01634     rb_thread_critical = thr_crit_bup;
01635 
01636     /* rb_thread_schedule(); */
01637     /* tick_counter += event_loop_max; */
01638 }
01639 
01640 #ifdef RUBY_USE_NATIVE_THREAD
01641 #if USE_TOGGLE_WINDOW_MODE_FOR_IDLE
01642 static int
01643 toggle_eventloop_window_mode_for_idle()
01644 {
01645   if (window_event_mode & TCL_IDLE_EVENTS) {
01646     /* idle -> event */
01647     window_event_mode |= TCL_WINDOW_EVENTS;
01648     window_event_mode &= ~TCL_IDLE_EVENTS;
01649     return 1;
01650   } else {
01651     /* event -> idle */
01652     window_event_mode |= TCL_IDLE_EVENTS;
01653     window_event_mode &= ~TCL_WINDOW_EVENTS;
01654     return 0;
01655   }
01656 }
01657 #endif
01658 #endif
01659 
01660 static VALUE
01661 set_eventloop_window_mode(self, mode)
01662     VALUE self;
01663     VALUE mode;
01664 {
01665     rb_secure(4);
01666 
01667     if (RTEST(mode)) {
01668       window_event_mode = ~0;
01669     } else {
01670       window_event_mode = ~TCL_WINDOW_EVENTS;
01671     }
01672 
01673     return mode;
01674 }
01675 
01676 static VALUE
01677 get_eventloop_window_mode(self)
01678     VALUE self;
01679 {
01680     if ( ~window_event_mode ) {
01681       return Qfalse;
01682     } else {
01683       return Qtrue;
01684     }
01685 }
01686 
01687 static VALUE
01688 set_eventloop_tick(self, tick)
01689     VALUE self;
01690     VALUE tick;
01691 {
01692     int ttick = NUM2INT(tick);
01693     int thr_crit_bup;
01694 
01695     rb_secure(4);
01696 
01697     if (ttick < 0) {
01698         rb_raise(rb_eArgError,
01699                  "timer-tick parameter must be 0 or positive number");
01700     }
01701 
01702     thr_crit_bup = rb_thread_critical;
01703     rb_thread_critical = Qtrue;
01704 
01705     /* delete old timer callback */
01706     Tcl_DeleteTimerHandler(timer_token);
01707 
01708     timer_tick = req_timer_tick = ttick;
01709     if (timer_tick > 0) {
01710         /* start timer callback */
01711         timer_token = Tcl_CreateTimerHandler(timer_tick, _timer_for_tcl,
01712                                              (ClientData)0);
01713     } else {
01714         timer_token = (Tcl_TimerToken)NULL;
01715     }
01716 
01717     rb_thread_critical = thr_crit_bup;
01718 
01719     return tick;
01720 }
01721 
01722 static VALUE
01723 get_eventloop_tick(self)
01724     VALUE self;
01725 {
01726     return INT2NUM(timer_tick);
01727 }
01728 
01729 static VALUE
01730 ip_set_eventloop_tick(self, tick)
01731     VALUE self;
01732     VALUE tick;
01733 {
01734     struct tcltkip *ptr = get_ip(self);
01735 
01736     /* ip is deleted? */
01737     if (deleted_ip(ptr)) {
01738         return get_eventloop_tick(self);
01739     }
01740 
01741     if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
01742         /* slave IP */
01743         return get_eventloop_tick(self);
01744     }
01745     return set_eventloop_tick(self, tick);
01746 }
01747 
01748 static VALUE
01749 ip_get_eventloop_tick(self)
01750     VALUE self;
01751 {
01752     return get_eventloop_tick(self);
01753 }
01754 
01755 static VALUE
01756 set_no_event_wait(self, wait)
01757     VALUE self;
01758     VALUE wait;
01759 {
01760     int t_wait = NUM2INT(wait);
01761 
01762     rb_secure(4);
01763 
01764     if (t_wait <= 0) {
01765         rb_raise(rb_eArgError,
01766                  "no_event_wait parameter must be positive number");
01767     }
01768 
01769     no_event_wait = t_wait;
01770 
01771     return wait;
01772 }
01773 
01774 static VALUE
01775 get_no_event_wait(self)
01776     VALUE self;
01777 {
01778     return INT2NUM(no_event_wait);
01779 }
01780 
01781 static VALUE
01782 ip_set_no_event_wait(self, wait)
01783     VALUE self;
01784     VALUE wait;
01785 {
01786     struct tcltkip *ptr = get_ip(self);
01787 
01788     /* ip is deleted? */
01789     if (deleted_ip(ptr)) {
01790         return get_no_event_wait(self);
01791     }
01792 
01793     if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
01794         /* slave IP */
01795         return get_no_event_wait(self);
01796     }
01797     return set_no_event_wait(self, wait);
01798 }
01799 
01800 static VALUE
01801 ip_get_no_event_wait(self)
01802     VALUE self;
01803 {
01804     return get_no_event_wait(self);
01805 }
01806 
01807 static VALUE
01808 set_eventloop_weight(self, loop_max, no_event)
01809     VALUE self;
01810     VALUE loop_max;
01811     VALUE no_event;
01812 {
01813     int lpmax = NUM2INT(loop_max);
01814     int no_ev = NUM2INT(no_event);
01815 
01816     rb_secure(4);
01817 
01818     if (lpmax <= 0 || no_ev <= 0) {
01819         rb_raise(rb_eArgError, "weight parameters must be positive numbers");
01820     }
01821 
01822     event_loop_max = lpmax;
01823     no_event_tick  = no_ev;
01824 
01825     return rb_ary_new3(2, loop_max, no_event);
01826 }
01827 
01828 static VALUE
01829 get_eventloop_weight(self)
01830     VALUE self;
01831 {
01832     return rb_ary_new3(2, INT2NUM(event_loop_max), INT2NUM(no_event_tick));
01833 }
01834 
01835 static VALUE
01836 ip_set_eventloop_weight(self, loop_max, no_event)
01837     VALUE self;
01838     VALUE loop_max;
01839     VALUE no_event;
01840 {
01841     struct tcltkip *ptr = get_ip(self);
01842 
01843     /* ip is deleted? */
01844     if (deleted_ip(ptr)) {
01845         return get_eventloop_weight(self);
01846     }
01847 
01848     if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
01849         /* slave IP */
01850         return get_eventloop_weight(self);
01851     }
01852     return set_eventloop_weight(self, loop_max, no_event);
01853 }
01854 
01855 static VALUE
01856 ip_get_eventloop_weight(self)
01857     VALUE self;
01858 {
01859     return get_eventloop_weight(self);
01860 }
01861 
01862 static VALUE
01863 set_max_block_time(self, time)
01864     VALUE self;
01865     VALUE time;
01866 {
01867     struct Tcl_Time tcl_time;
01868     VALUE divmod;
01869 
01870     switch(TYPE(time)) {
01871     case T_FIXNUM:
01872     case T_BIGNUM:
01873         /* time is micro-second value */
01874         divmod = rb_funcall(time, rb_intern("divmod"), 1, LONG2NUM(1000000));
01875         tcl_time.sec  = NUM2LONG(RARRAY_PTR(divmod)[0]);
01876         tcl_time.usec = NUM2LONG(RARRAY_PTR(divmod)[1]);
01877         break;
01878 
01879     case T_FLOAT:
01880         /* time is second value */
01881         divmod = rb_funcall(time, rb_intern("divmod"), 1, INT2FIX(1));
01882         tcl_time.sec  = NUM2LONG(RARRAY_PTR(divmod)[0]);
01883         tcl_time.usec = (long)(NUM2DBL(RARRAY_PTR(divmod)[1]) * 1000000);
01884 
01885     default:
01886         {
01887             VALUE tmp = rb_funcall(time, ID_inspect, 0, 0);
01888             rb_raise(rb_eArgError, "invalid value for time: '%s'",
01889                      StringValuePtr(tmp));
01890         }
01891     }
01892 
01893     Tcl_SetMaxBlockTime(&tcl_time);
01894 
01895     return Qnil;
01896 }
01897 
01898 static VALUE
01899 lib_evloop_thread_p(self)
01900     VALUE self;
01901 {
01902     if (NIL_P(eventloop_thread)) {
01903         return Qnil;    /* no eventloop */
01904     } else if (rb_thread_current() == eventloop_thread) {
01905         return Qtrue;   /* is eventloop */
01906     } else {
01907         return Qfalse;  /* not eventloop */
01908     }
01909 }
01910 
01911 static VALUE
01912 lib_evloop_abort_on_exc(self)
01913     VALUE self;
01914 {
01915     if (event_loop_abort_on_exc > 0) {
01916         return Qtrue;
01917     } else if (event_loop_abort_on_exc == 0) {
01918         return Qfalse;
01919     } else {
01920         return Qnil;
01921     }
01922 }
01923 
01924 static VALUE
01925 ip_evloop_abort_on_exc(self)
01926     VALUE self;
01927 {
01928     return lib_evloop_abort_on_exc(self);
01929 }
01930 
01931 static VALUE
01932 lib_evloop_abort_on_exc_set(self, val)
01933     VALUE self, val;
01934 {
01935     rb_secure(4);
01936     if (RTEST(val)) {
01937         event_loop_abort_on_exc =  1;
01938     } else if (NIL_P(val)) {
01939         event_loop_abort_on_exc = -1;
01940     } else {
01941         event_loop_abort_on_exc =  0;
01942     }
01943     return lib_evloop_abort_on_exc(self);
01944 }
01945 
01946 static VALUE
01947 ip_evloop_abort_on_exc_set(self, val)
01948     VALUE self, val;
01949 {
01950     struct tcltkip *ptr = get_ip(self);
01951 
01952     rb_secure(4);
01953 
01954     /* ip is deleted? */
01955     if (deleted_ip(ptr)) {
01956         return lib_evloop_abort_on_exc(self);
01957     }
01958 
01959     if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
01960         /* slave IP */
01961         return lib_evloop_abort_on_exc(self);
01962     }
01963     return lib_evloop_abort_on_exc_set(self, val);
01964 }
01965 
01966 static VALUE
01967 lib_num_of_mainwindows_core(self, argc, argv)
01968     VALUE self;
01969     int   argc;   /* dummy */
01970     VALUE *argv;  /* dummy */
01971 {
01972     if (tk_stubs_init_p()) {
01973         return INT2FIX(Tk_GetNumMainWindows());
01974     } else {
01975         return INT2FIX(0);
01976     }
01977 }
01978 
01979 static VALUE
01980 lib_num_of_mainwindows(self)
01981     VALUE self;
01982 {
01983 #ifdef RUBY_USE_NATIVE_THREAD  /* Ruby 1.9+ !!! */
01984     return tk_funcall(lib_num_of_mainwindows_core, 0, (VALUE*)NULL, self);
01985 #else
01986     return lib_num_of_mainwindows_core(self, 0, (VALUE*)NULL);
01987 #endif
01988 }
01989 
01990 void
01991 rbtk_EventSetupProc(ClientData clientData, int flag)
01992 {
01993     Tcl_Time tcl_time;
01994     tcl_time.sec  = 0;
01995     tcl_time.usec = 1000L * (long)no_event_tick;
01996     Tcl_SetMaxBlockTime(&tcl_time);
01997 }
01998 
01999 void
02000 rbtk_EventCheckProc(ClientData clientData, int flag)
02001 {
02002     rb_thread_schedule();
02003 }
02004 
02005 
02006 #ifdef RUBY_USE_NATIVE_THREAD  /* Ruby 1.9+ !!! */
02007 static VALUE
02008 #ifdef HAVE_PROTOTYPES
02009 call_DoOneEvent_core(VALUE flag_val)
02010 #else
02011 call_DoOneEvent_core(flag_val)
02012     VALUE flag_val;
02013 #endif
02014 {
02015     int flag;
02016 
02017     flag = FIX2INT(flag_val);
02018     if (Tcl_DoOneEvent(flag)) {
02019         return Qtrue;
02020     } else {
02021         return Qfalse;
02022     }
02023 }
02024 
02025 static VALUE
02026 #ifdef HAVE_PROTOTYPES
02027 call_DoOneEvent(VALUE flag_val)
02028 #else
02029 call_DoOneEvent(flag_val)
02030     VALUE flag_val;
02031 #endif
02032 {
02033   return tk_funcall(call_DoOneEvent_core, 0, (VALUE*)NULL, flag_val);
02034 }
02035 
02036 #else  /* Ruby 1.8- */
02037 static VALUE
02038 #ifdef HAVE_PROTOTYPES
02039 call_DoOneEvent(VALUE flag_val)
02040 #else
02041 call_DoOneEvent(flag_val)
02042     VALUE flag_val;
02043 #endif
02044 {
02045     int flag;
02046 
02047     flag = FIX2INT(flag_val);
02048     if (Tcl_DoOneEvent(flag)) {
02049         return Qtrue;
02050     } else {
02051         return Qfalse;
02052     }
02053 }
02054 #endif
02055 
02056 
02057 #if 0
02058 static VALUE
02059 #ifdef HAVE_PROTOTYPES
02060 eventloop_sleep(VALUE dummy)
02061 #else
02062 eventloop_sleep(dummy)
02063     VALUE dummy;
02064 #endif
02065 {
02066     struct timeval t;
02067 
02068     if (no_event_wait <= 0) {
02069       return Qnil;
02070     }
02071 
02072     t.tv_sec = 0;
02073     t.tv_usec = (int)(no_event_wait*1000.0);
02074 
02075 #ifdef HAVE_NATIVETHREAD
02076 #ifndef RUBY_USE_NATIVE_THREAD
02077     if (!ruby_native_thread_p()) {
02078         rb_bug("cross-thread violation on eventloop_sleep()");
02079     }
02080 #endif
02081 #endif
02082 
02083     DUMP2("eventloop_sleep: rb_thread_wait_for() at thread : %lx", rb_thread_current());
02084     rb_thread_wait_for(t);
02085     DUMP2("eventloop_sleep: finish at thread : %lx", rb_thread_current());
02086 
02087 #ifdef HAVE_NATIVETHREAD
02088 #ifndef RUBY_USE_NATIVE_THREAD
02089     if (!ruby_native_thread_p()) {
02090         rb_bug("cross-thread violation on eventloop_sleep()");
02091     }
02092 #endif
02093 #endif
02094 
02095     return Qnil;
02096 }
02097 #endif
02098 
02099 #define USE_EVLOOP_THREAD_ALONE_CHECK_FLAG 0
02100 
02101 #if USE_EVLOOP_THREAD_ALONE_CHECK_FLAG
02102 static int
02103 get_thread_alone_check_flag()
02104 {
02105 #ifdef RUBY_USE_NATIVE_THREAD
02106   return 0;
02107 #else
02108   set_tcltk_version();
02109 
02110   if (tcltk_version.major < 8) {
02111     /* Tcl/Tk 7.x */
02112     return 1;
02113   } else if (tcltk_version.major == 8) {
02114     if (tcltk_version.minor < 5) {
02115       /* Tcl/Tk 8.0 - 8.4 */
02116       return 1;
02117     } else if (tcltk_version.minor == 5) {
02118       if (tcltk_version.type < TCL_FINAL_RELEASE) {
02119         /* Tcl/Tk 8.5a? - 8.5b? */
02120         return 1;
02121       } else {
02122         /* Tcl/Tk 8.5.x */
02123         return 0;
02124       }
02125     } else {
02126       /* Tcl/Tk 8.6 - 8.9 ?? */
02127       return 0;
02128     }
02129   } else {
02130     /* Tcl/Tk 9+ ?? */
02131     return 0;
02132   }
02133 #endif
02134 }
02135 #endif
02136 
02137 #define TRAP_CHECK() do { \
02138     if (trap_check(check_var) == 0) return 0; \
02139 } while (0)
02140 
02141 static int
02142 trap_check(int *check_var)
02143 {
02144     DUMP1("trap check");
02145 
02146 #ifdef RUBY_VM
02147     if (rb_thread_check_trap_pending()) {
02148         if (check_var != (int*)NULL) {
02149             /* wait command */
02150             return 0;
02151         }
02152         else {
02153             rb_thread_check_ints();
02154         }
02155     }
02156 #else
02157     if (rb_trap_pending) {
02158       run_timer_flag = 0;
02159       if (rb_prohibit_interrupt || check_var != (int*)NULL) {
02160         /* pending or on wait command */
02161         return 0;
02162       } else {
02163         rb_trap_exec();
02164       }
02165     }
02166 #endif
02167 
02168     return 1;
02169 }
02170 
02171 static int
02172 check_eventloop_interp()
02173 {
02174   DUMP1("check eventloop_interp");
02175   if (eventloop_interp != (Tcl_Interp*)NULL
02176       && Tcl_InterpDeleted(eventloop_interp)) {
02177     DUMP2("eventloop_interp(%p) was deleted", eventloop_interp);
02178     return 1;
02179   }
02180 
02181   return 0;
02182 }
02183 
02184 static int
02185 lib_eventloop_core(check_root, update_flag, check_var, interp)
02186     int check_root;
02187     int update_flag;
02188     int *check_var;
02189     Tcl_Interp *interp;
02190 {
02191     volatile VALUE current = eventloop_thread;
02192     int found_event = 1;
02193     int event_flag;
02194     struct timeval t;
02195     int thr_crit_bup;
02196     int status;
02197     int depth = rbtk_eventloop_depth;
02198 #if USE_EVLOOP_THREAD_ALONE_CHECK_FLAG
02199     int thread_alone_check_flag = 1;
02200 #endif
02201 
02202     if (update_flag) DUMP1("update loop start!!");
02203 
02204     t.tv_sec = 0;
02205     t.tv_usec = 1000 * no_event_wait;
02206 
02207     Tcl_DeleteTimerHandler(timer_token);
02208     run_timer_flag = 0;
02209     if (timer_tick > 0) {
02210         thr_crit_bup = rb_thread_critical;
02211         rb_thread_critical = Qtrue;
02212         timer_token = Tcl_CreateTimerHandler(timer_tick, _timer_for_tcl,
02213                                              (ClientData)0);
02214         rb_thread_critical = thr_crit_bup;
02215     } else {
02216         timer_token = (Tcl_TimerToken)NULL;
02217     }
02218 
02219 #if USE_EVLOOP_THREAD_ALONE_CHECK_FLAG
02220     /* version check */
02221     thread_alone_check_flag = get_thread_alone_check_flag();
02222 #endif
02223 
02224     for(;;) {
02225         if (check_eventloop_interp()) return 0;
02226 
02227 #if USE_EVLOOP_THREAD_ALONE_CHECK_FLAG
02228         if (thread_alone_check_flag && rb_thread_alone()) {
02229 #else
02230         if (rb_thread_alone()) {
02231 #endif
02232             DUMP1("no other thread");
02233             event_loop_wait_event = 0;
02234 
02235             if (update_flag) {
02236                 event_flag = update_flag;
02237                 /* event_flag = update_flag | TCL_DONT_WAIT; */ /* for safety */
02238             } else {
02239                 event_flag = TCL_ALL_EVENTS;
02240                 /* event_flag = TCL_ALL_EVENTS | TCL_DONT_WAIT; */
02241             }
02242 
02243             if (timer_tick == 0 && update_flag == 0) {
02244                 timer_tick = NO_THREAD_INTERRUPT_TIME;
02245                 timer_token = Tcl_CreateTimerHandler(timer_tick,
02246                                                      _timer_for_tcl,
02247                                                      (ClientData)0);
02248             }
02249 
02250             if (check_var != (int *)NULL) {
02251                 if (*check_var || !found_event) {
02252                     return found_event;
02253                 }
02254                 if (interp != (Tcl_Interp*)NULL
02255                     && Tcl_InterpDeleted(interp)) {
02256                     /* IP for check_var is deleted */
02257                     return 0;
02258                 }
02259             }
02260 
02261             /* found_event = Tcl_DoOneEvent(event_flag); */
02262             found_event = RTEST(rb_protect(call_DoOneEvent,
02263                                            INT2FIX(event_flag), &status));
02264             if (status) {
02265                 switch (status) {
02266                 case TAG_RAISE:
02267                     if (NIL_P(rb_errinfo())) {
02268                         rbtk_pending_exception
02269                             = rb_exc_new2(rb_eException, "unknown exception");
02270                     } else {
02271                         rbtk_pending_exception = rb_errinfo();
02272 
02273                         if (!NIL_P(rbtk_pending_exception)) {
02274                             if (rbtk_eventloop_depth == 0) {
02275                                 VALUE exc = rbtk_pending_exception;
02276                                 rbtk_pending_exception = Qnil;
02277                                 rb_exc_raise(exc);
02278                             } else {
02279                                 return 0;
02280                             }
02281                         }
02282                     }
02283                     break;
02284 
02285                 case TAG_FATAL:
02286                     if (NIL_P(rb_errinfo())) {
02287                         rb_exc_raise(rb_exc_new2(rb_eFatal, "FATAL"));
02288                     } else {
02289                         rb_exc_raise(rb_errinfo());
02290                     }
02291                 }
02292             }
02293 
02294             if (depth != rbtk_eventloop_depth) {
02295                 DUMP2("DoOneEvent(1) abnormal exit!! %d",
02296                       rbtk_eventloop_depth);
02297             }
02298 
02299             if (check_var != (int*)NULL && !NIL_P(rbtk_pending_exception)) {
02300                 DUMP1("exception on wait");
02301                 return 0;
02302             }
02303 
02304             if (pending_exception_check0()) {
02305                 /* pending -> upper level */
02306                 return 0;
02307             }
02308 
02309             if (update_flag != 0) {
02310               if (found_event) {
02311                 DUMP1("next update loop");
02312                 continue;
02313               } else {
02314                 DUMP1("update complete");
02315                 return 0;
02316               }
02317             }
02318 
02319             TRAP_CHECK();
02320             if (check_eventloop_interp()) return 0;
02321 
02322             DUMP1("check Root Widget");
02323             if (check_root && tk_stubs_init_p() && Tk_GetNumMainWindows() == 0) {
02324                 run_timer_flag = 0;
02325                 TRAP_CHECK();
02326                 return 1;
02327             }
02328 
02329             if (loop_counter++ > 30000) {
02330                 /* fprintf(stderr, "loop_counter > 30000\n"); */
02331                 loop_counter = 0;
02332             }
02333 
02334         } else {
02335             int tick_counter;
02336 
02337             DUMP1("there are other threads");
02338             event_loop_wait_event = 1;
02339 
02340             found_event = 1;
02341 
02342             if (update_flag) {
02343                 event_flag = update_flag; /* for safety */
02344                 /* event_flag = update_flag | TCL_DONT_WAIT; */ /* for safety */
02345             } else {
02346                 event_flag = TCL_ALL_EVENTS;
02347                 /* event_flag = TCL_ALL_EVENTS | TCL_DONT_WAIT; */
02348             }
02349 
02350             timer_tick = req_timer_tick;
02351             tick_counter = 0;
02352             while(tick_counter < event_loop_max) {
02353                 if (check_var != (int *)NULL) {
02354                     if (*check_var || !found_event) {
02355                         return found_event;
02356                     }
02357                     if (interp != (Tcl_Interp*)NULL
02358                         && Tcl_InterpDeleted(interp)) {
02359                         /* IP for check_var is deleted */
02360                         return 0;
02361                     }
02362                 }
02363 
02364                 if (NIL_P(eventloop_thread) || current == eventloop_thread) {
02365                     int st;
02366                     int status;
02367 
02368 #ifdef RUBY_USE_NATIVE_THREAD
02369                     if (update_flag) {
02370                       st = RTEST(rb_protect(call_DoOneEvent,
02371                                             INT2FIX(event_flag), &status));
02372                     } else {
02373                       st = RTEST(rb_protect(call_DoOneEvent,
02374                                             INT2FIX(event_flag & window_event_mode),
02375                                             &status));
02376 #if USE_TOGGLE_WINDOW_MODE_FOR_IDLE
02377                       if (!st) {
02378                         if (toggle_eventloop_window_mode_for_idle()) {
02379                           /* idle-mode -> event-mode*/
02380                           tick_counter = event_loop_max;
02381                         } else {
02382                           /* event-mode -> idle-mode */
02383                           tick_counter = 0;
02384                         }
02385                       }
02386 #endif
02387                     }
02388 #else
02389                     /* st = Tcl_DoOneEvent(event_flag); */
02390                     st = RTEST(rb_protect(call_DoOneEvent,
02391                                           INT2FIX(event_flag), &status));
02392 #endif
02393 
02394 #if CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE
02395                     if (have_rb_thread_waiting_for_value) {
02396                       have_rb_thread_waiting_for_value = 0;
02397                       rb_thread_schedule();
02398                     }
02399 #endif
02400 
02401                     if (status) {
02402                         switch (status) {
02403                         case TAG_RAISE:
02404                             if (NIL_P(rb_errinfo())) {
02405                                 rbtk_pending_exception
02406                                     = rb_exc_new2(rb_eException,
02407                                                   "unknown exception");
02408                             } else {
02409                                 rbtk_pending_exception = rb_errinfo();
02410 
02411                                 if (!NIL_P(rbtk_pending_exception)) {
02412                                     if (rbtk_eventloop_depth == 0) {
02413                                         VALUE exc = rbtk_pending_exception;
02414                                         rbtk_pending_exception = Qnil;
02415                                         rb_exc_raise(exc);
02416                                     } else {
02417                                         return 0;
02418                                     }
02419                                 }
02420                             }
02421                             break;
02422 
02423                         case TAG_FATAL:
02424                             if (NIL_P(rb_errinfo())) {
02425                                 rb_exc_raise(rb_exc_new2(rb_eFatal, "FATAL"));
02426                             } else {
02427                                 rb_exc_raise(rb_errinfo());
02428                             }
02429                         }
02430                     }
02431 
02432                     if (depth != rbtk_eventloop_depth) {
02433                         DUMP2("DoOneEvent(2) abnormal exit!! %d",
02434                               rbtk_eventloop_depth);
02435                         return 0;
02436                     }
02437 
02438                     TRAP_CHECK();
02439 
02440                     if (check_var != (int*)NULL
02441                         && !NIL_P(rbtk_pending_exception)) {
02442                         DUMP1("exception on wait");
02443                         return 0;
02444                     }
02445 
02446                     if (pending_exception_check0()) {
02447                         /* pending -> upper level */
02448                         return 0;
02449                     }
02450 
02451                     if (st) {
02452                         tick_counter++;
02453                     } else {
02454                         if (update_flag != 0) {
02455                             DUMP1("update complete");
02456                             return 0;
02457                         }
02458 
02459                         tick_counter += no_event_tick;
02460 
02461 #if 0
02462                         /* rb_thread_wait_for(t); */
02463                         rb_protect(eventloop_sleep, Qnil, &status);
02464 
02465                         if (status) {
02466                             switch (status) {
02467                             case TAG_RAISE:
02468                                 if (NIL_P(rb_errinfo())) {
02469                                     rbtk_pending_exception
02470                                         = rb_exc_new2(rb_eException,
02471                                                       "unknown exception");
02472                                 } else {
02473                                     rbtk_pending_exception = rb_errinfo();
02474 
02475                                     if (!NIL_P(rbtk_pending_exception)) {
02476                                         if (rbtk_eventloop_depth == 0) {
02477                                             VALUE exc = rbtk_pending_exception;
02478                                             rbtk_pending_exception = Qnil;
02479                                             rb_exc_raise(exc);
02480                                         } else {
02481                                             return 0;
02482                                         }
02483                                     }
02484                                 }
02485                                 break;
02486 
02487                             case TAG_FATAL:
02488                                 if (NIL_P(rb_errinfo())) {
02489                                     rb_exc_raise(rb_exc_new2(rb_eFatal,
02490                                                              "FATAL"));
02491                                 } else {
02492                                     rb_exc_raise(rb_errinfo());
02493                                 }
02494                             }
02495                         }
02496 #endif
02497                     }
02498 
02499                 } else {
02500                     DUMP2("sleep eventloop %lx", current);
02501                     DUMP2("eventloop thread is %lx", eventloop_thread);
02502                     /* rb_thread_stop(); */
02503                     rb_thread_sleep_forever();
02504                 }
02505 
02506                 if (!NIL_P(watchdog_thread) && eventloop_thread != current) {
02507                     return 1;
02508                 }
02509 
02510                 TRAP_CHECK();
02511                 if (check_eventloop_interp()) return 0;
02512 
02513                 DUMP1("check Root Widget");
02514                 if (check_root && tk_stubs_init_p() && Tk_GetNumMainWindows() == 0) {
02515                     run_timer_flag = 0;
02516                     TRAP_CHECK();
02517                     return 1;
02518                 }
02519 
02520                 if (loop_counter++ > 30000) {
02521                     /* fprintf(stderr, "loop_counter > 30000\n"); */
02522                     loop_counter = 0;
02523                 }
02524 
02525                 if (run_timer_flag) {
02526                     /*
02527                     DUMP1("timer interrupt");
02528                     run_timer_flag = 0;
02529                     */
02530                     break; /* switch to other thread */
02531                 }
02532             }
02533 
02534             DUMP1("thread scheduling");
02535             rb_thread_schedule();
02536         }
02537 
02538         DUMP1("check interrupts");
02539 #if defined(RUBY_USE_NATIVE_THREAD) || defined(RUBY_VM)
02540         if (update_flag == 0) rb_thread_check_ints();
02541 #else
02542         if (update_flag == 0) CHECK_INTS;
02543 #endif
02544 
02545     }
02546     return 1;
02547 }
02548 
02549 
02550 struct evloop_params {
02551     int check_root;
02552     int update_flag;
02553     int *check_var;
02554     Tcl_Interp *interp;
02555     int thr_crit_bup;
02556 };
02557 
02558 VALUE
02559 lib_eventloop_main_core(args)
02560     VALUE args;
02561 {
02562     struct evloop_params *params = (struct evloop_params *)args;
02563 
02564     check_rootwidget_flag = params->check_root;
02565 
02566     Tcl_CreateEventSource(rbtk_EventSetupProc, rbtk_EventCheckProc, (ClientData)args);
02567 
02568     if (lib_eventloop_core(params->check_root,
02569                            params->update_flag,
02570                            params->check_var,
02571                            params->interp)) {
02572         return Qtrue;
02573     } else {
02574         return Qfalse;
02575     }
02576 }
02577 
02578 VALUE
02579 lib_eventloop_main(args)
02580     VALUE args;
02581 {
02582     return lib_eventloop_main_core(args);
02583 
02584 #if 0
02585     volatile VALUE ret;
02586     int status = 0;
02587 
02588     ret = rb_protect(lib_eventloop_main_core, args, &status);
02589 
02590     switch (status) {
02591     case TAG_RAISE:
02592         if (NIL_P(rb_errinfo())) {
02593             rbtk_pending_exception
02594                 = rb_exc_new2(rb_eException, "unknown exception");
02595         } else {
02596             rbtk_pending_exception = rb_errinfo();
02597         }
02598         return Qnil;
02599 
02600     case TAG_FATAL:
02601         if (NIL_P(rb_errinfo())) {
02602             rbtk_pending_exception = rb_exc_new2(rb_eFatal, "FATAL");
02603         } else {
02604             rbtk_pending_exception = rb_errinfo();
02605         }
02606         return Qnil;
02607     }
02608 
02609     return ret;
02610 #endif
02611 }
02612 
02613 VALUE
02614 lib_eventloop_ensure(args)
02615     VALUE args;
02616 {
02617     struct evloop_params *ptr = (struct evloop_params *)args;
02618     volatile VALUE current_evloop = rb_thread_current();
02619 
02620     Tcl_DeleteEventSource(rbtk_EventSetupProc, rbtk_EventCheckProc, (ClientData)args);
02621 
02622     DUMP2("eventloop_ensure: current-thread : %lx", current_evloop);
02623     DUMP2("eventloop_ensure: eventloop-thread : %lx", eventloop_thread);
02624     if (eventloop_thread != current_evloop) {
02625         DUMP2("finish eventloop %lx (NOT current eventloop)", current_evloop);
02626 
02627         rb_thread_critical = ptr->thr_crit_bup;
02628 
02629         xfree(ptr);
02630         /* ckfree((char*)ptr); */
02631 
02632         return Qnil;
02633     }
02634 
02635     while((eventloop_thread = rb_ary_pop(eventloop_stack))) {
02636         DUMP2("eventloop-ensure: new eventloop-thread -> %lx",
02637               eventloop_thread);
02638 
02639         if (eventloop_thread == current_evloop) {
02640             rbtk_eventloop_depth--;
02641             DUMP2("eventloop %lx : back from recursive call", current_evloop);
02642             break;
02643         }
02644 
02645         if (NIL_P(eventloop_thread)) {
02646           Tcl_DeleteTimerHandler(timer_token);
02647           timer_token = (Tcl_TimerToken)NULL;
02648 
02649           break;
02650         }
02651 
02652 #ifdef RUBY_VM
02653         if (RTEST(rb_funcall(eventloop_thread, ID_alive_p, 0, 0))) {
02654 #else
02655         if (RTEST(rb_thread_alive_p(eventloop_thread))) {
02656 #endif
02657             DUMP2("eventloop-enshure: wake up parent %lx", eventloop_thread);
02658             rb_thread_wakeup(eventloop_thread);
02659 
02660             break;
02661         }
02662     }
02663 
02664 #ifdef RUBY_USE_NATIVE_THREAD
02665     if (NIL_P(eventloop_thread)) {
02666         tk_eventloop_thread_id = (Tcl_ThreadId) 0;
02667     }
02668 #endif
02669 
02670     rb_thread_critical = ptr->thr_crit_bup;
02671 
02672     xfree(ptr);
02673     /* ckfree((char*)ptr);*/
02674 
02675     DUMP2("finish current eventloop %lx", current_evloop);
02676     return Qnil;
02677 }
02678 
02679 static VALUE
02680 lib_eventloop_launcher(check_root, update_flag, check_var, interp)
02681     int check_root;
02682     int update_flag;
02683     int *check_var;
02684     Tcl_Interp *interp;
02685 {
02686     volatile VALUE parent_evloop = eventloop_thread;
02687     struct evloop_params *args = ALLOC(struct evloop_params);
02688     /* struct evloop_params *args = RbTk_ALLOC_N(struct evloop_params, 1); */
02689 
02690     tcl_stubs_check();
02691 
02692     eventloop_thread = rb_thread_current();
02693 #ifdef RUBY_USE_NATIVE_THREAD
02694     tk_eventloop_thread_id = Tcl_GetCurrentThread();
02695 #endif
02696 
02697     if (parent_evloop == eventloop_thread) {
02698         DUMP2("eventloop: recursive call on %lx", parent_evloop);
02699         rbtk_eventloop_depth++;
02700     }
02701 
02702     if (!NIL_P(parent_evloop) && parent_evloop != eventloop_thread) {
02703         DUMP2("wait for stop of parent_evloop %lx", parent_evloop);
02704         while(!RTEST(rb_funcall(parent_evloop, ID_stop_p, 0))) {
02705             DUMP2("parent_evloop %lx doesn't stop", parent_evloop);
02706             rb_thread_run(parent_evloop);
02707         }
02708         DUMP1("succeed to stop parent");
02709     }
02710 
02711     rb_ary_push(eventloop_stack, parent_evloop);
02712 
02713     DUMP3("tcltklib: eventloop-thread : %lx -> %lx\n",
02714                 parent_evloop, eventloop_thread);
02715 
02716     args->check_root   = check_root;
02717     args->update_flag  = update_flag;
02718     args->check_var    = check_var;
02719     args->interp       = interp;
02720     args->thr_crit_bup = rb_thread_critical;
02721 
02722     rb_thread_critical = Qfalse;
02723 
02724 #if 0
02725     return rb_ensure(lib_eventloop_main, (VALUE)args,
02726                      lib_eventloop_ensure, (VALUE)args);
02727 #endif
02728     return rb_ensure(lib_eventloop_main_core, (VALUE)args,
02729                      lib_eventloop_ensure, (VALUE)args);
02730 }
02731 
02732 /* execute Tk_MainLoop */
02733 static VALUE
02734 lib_mainloop(argc, argv, self)
02735     int   argc;
02736     VALUE *argv;
02737     VALUE self;
02738 {
02739     VALUE check_rootwidget;
02740 
02741     if (rb_scan_args(argc, argv, "01", &check_rootwidget) == 0) {
02742         check_rootwidget = Qtrue;
02743     } else if (RTEST(check_rootwidget)) {
02744         check_rootwidget = Qtrue;
02745     } else {
02746         check_rootwidget = Qfalse;
02747     }
02748 
02749     return lib_eventloop_launcher(RTEST(check_rootwidget), 0,
02750                                   (int*)NULL, (Tcl_Interp*)NULL);
02751 }
02752 
02753 static VALUE
02754 ip_mainloop(argc, argv, self)
02755     int   argc;
02756     VALUE *argv;
02757     VALUE self;
02758 {
02759     volatile VALUE ret;
02760     struct tcltkip *ptr = get_ip(self);
02761 
02762     /* ip is deleted? */
02763     if (deleted_ip(ptr)) {
02764         return Qnil;
02765     }
02766 
02767     if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
02768         /* slave IP */
02769         return Qnil;
02770     }
02771 
02772     eventloop_interp = ptr->ip;
02773     ret = lib_mainloop(argc, argv, self);
02774     eventloop_interp = (Tcl_Interp*)NULL;
02775     return ret;
02776 }
02777 
02778 
02779 static VALUE
02780 watchdog_evloop_launcher(check_rootwidget)
02781     VALUE check_rootwidget;
02782 {
02783     return lib_eventloop_launcher(RTEST(check_rootwidget), 0,
02784                                   (int*)NULL, (Tcl_Interp*)NULL);
02785 }
02786 
02787 #define EVLOOP_WAKEUP_CHANCE 3
02788 
02789 static VALUE
02790 lib_watchdog_core(check_rootwidget)
02791     VALUE check_rootwidget;
02792 {
02793     VALUE evloop;
02794     int   prev_val = -1;
02795     int   chance = 0;
02796     int   check = RTEST(check_rootwidget);
02797     struct timeval t0, t1;
02798 
02799     t0.tv_sec  = 0;
02800     t0.tv_usec = (long)((NO_THREAD_INTERRUPT_TIME)*1000.0);
02801     t1.tv_sec  = 0;
02802     t1.tv_usec = (long)((WATCHDOG_INTERVAL)*1000.0);
02803 
02804     /* check other watchdog thread */
02805     if (!NIL_P(watchdog_thread)) {
02806         if (RTEST(rb_funcall(watchdog_thread, ID_stop_p, 0))) {
02807             rb_funcall(watchdog_thread, ID_kill, 0);
02808         } else {
02809             return Qnil;
02810         }
02811     }
02812     watchdog_thread = rb_thread_current();
02813 
02814     /* watchdog start */
02815     do {
02816         if (NIL_P(eventloop_thread)
02817             || (loop_counter == prev_val && chance >= EVLOOP_WAKEUP_CHANCE)) {
02818             /* start new eventloop thread */
02819             DUMP2("eventloop thread %lx is sleeping or dead",
02820                   eventloop_thread);
02821             evloop = rb_thread_create(watchdog_evloop_launcher,
02822                                       (void*)&check_rootwidget);
02823             DUMP2("create new eventloop thread %lx", evloop);
02824             loop_counter = -1;
02825             chance = 0;
02826             rb_thread_run(evloop);
02827         } else {
02828             prev_val = loop_counter;
02829             if (RTEST(rb_funcall(eventloop_thread, ID_stop_p, 0))) {
02830                 ++chance;
02831             } else {
02832                 chance = 0;
02833             }
02834             if (event_loop_wait_event) {
02835                 rb_thread_wait_for(t0);
02836             } else {
02837                 rb_thread_wait_for(t1);
02838             }
02839             /* rb_thread_schedule(); */
02840         }
02841     } while(!check || !tk_stubs_init_p() || Tk_GetNumMainWindows() != 0);
02842 
02843     return Qnil;
02844 }
02845 
02846 VALUE
02847 lib_watchdog_ensure(arg)
02848     VALUE arg;
02849 {
02850     eventloop_thread = Qnil; /* stop eventloops */
02851 #ifdef RUBY_USE_NATIVE_THREAD
02852     tk_eventloop_thread_id = (Tcl_ThreadId) 0;
02853 #endif
02854     return Qnil;
02855 }
02856 
02857 static VALUE
02858 lib_mainloop_watchdog(argc, argv, self)
02859     int   argc;
02860     VALUE *argv;
02861     VALUE self;
02862 {
02863     VALUE check_rootwidget;
02864 
02865 #ifdef RUBY_VM
02866     rb_raise(rb_eNotImpError,
02867              "eventloop_watchdog is not implemented on Ruby VM.");
02868 #endif
02869 
02870     if (rb_scan_args(argc, argv, "01", &check_rootwidget) == 0) {
02871         check_rootwidget = Qtrue;
02872     } else if (RTEST(check_rootwidget)) {
02873         check_rootwidget = Qtrue;
02874     } else {
02875         check_rootwidget = Qfalse;
02876     }
02877 
02878     return rb_ensure(lib_watchdog_core, check_rootwidget,
02879                      lib_watchdog_ensure, Qnil);
02880 }
02881 
02882 static VALUE
02883 ip_mainloop_watchdog(argc, argv, self)
02884     int   argc;
02885     VALUE *argv;
02886     VALUE self;
02887 {
02888     struct tcltkip *ptr = get_ip(self);
02889 
02890     /* ip is deleted? */
02891     if (deleted_ip(ptr)) {
02892         return Qnil;
02893     }
02894 
02895     if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
02896         /* slave IP */
02897         return Qnil;
02898     }
02899     return lib_mainloop_watchdog(argc, argv, self);
02900 }
02901 
02902 
02903 /* thread-safe(?) interaction between Ruby and Tk */
02904 struct thread_call_proc_arg {
02905     VALUE proc;
02906     int *done;
02907 };
02908 
02909 void
02910 _thread_call_proc_arg_mark(struct thread_call_proc_arg *q)
02911 {
02912     rb_gc_mark(q->proc);
02913 }
02914 
02915 static VALUE
02916 _thread_call_proc_core(arg)
02917     VALUE arg;
02918 {
02919     struct thread_call_proc_arg *q = (struct thread_call_proc_arg*)arg;
02920     return rb_funcall(q->proc, ID_call, 0);
02921 }
02922 
02923 static VALUE
02924 _thread_call_proc_ensure(arg)
02925     VALUE arg;
02926 {
02927     struct thread_call_proc_arg *q = (struct thread_call_proc_arg*)arg;
02928     *(q->done) = 1;
02929     return Qnil;
02930 }
02931 
02932 static VALUE
02933 _thread_call_proc(arg)
02934     VALUE arg;
02935 {
02936     struct thread_call_proc_arg *q = (struct thread_call_proc_arg*)arg;
02937 
02938     return rb_ensure(_thread_call_proc_core, (VALUE)q,
02939                      _thread_call_proc_ensure, (VALUE)q);
02940 }
02941 
02942 static VALUE
02943 #ifdef HAVE_PROTOTYPES
02944 _thread_call_proc_value(VALUE th)
02945 #else
02946 _thread_call_proc_value(th)
02947     VALUE th;
02948 #endif
02949 {
02950     return rb_funcall(th, ID_value, 0);
02951 }
02952 
02953 static VALUE
02954 lib_thread_callback(argc, argv, self)
02955     int argc;
02956     VALUE *argv;
02957     VALUE self;
02958 {
02959     struct thread_call_proc_arg *q;
02960     VALUE proc, th, ret;
02961     int status, foundEvent;
02962 
02963     if (rb_scan_args(argc, argv, "01", &proc) == 0) {
02964         proc = rb_block_proc();
02965     }
02966 
02967     q = (struct thread_call_proc_arg *)ALLOC(struct thread_call_proc_arg);
02968     /* q = RbTk_ALLOC_N(struct thread_call_proc_arg, 1); */
02969     q->proc = proc;
02970     q->done = (int*)ALLOC(int);
02971     /* q->done = RbTk_ALLOC_N(int, 1); */
02972     *(q->done) = 0;
02973 
02974     /* create call-proc thread */
02975     th = rb_thread_create(_thread_call_proc, (void*)q);
02976 
02977     rb_thread_schedule();
02978 
02979     /* start sub-eventloop */
02980     foundEvent = RTEST(lib_eventloop_launcher(/* not check root-widget */0, 0,
02981                                               q->done, (Tcl_Interp*)NULL));
02982 
02983 #ifdef RUBY_VM
02984     if (RTEST(rb_funcall(th, ID_alive_p, 0))) {
02985 #else
02986     if (RTEST(rb_thread_alive_p(th))) {
02987 #endif
02988         rb_funcall(th, ID_kill, 0);
02989         ret = Qnil;
02990     } else {
02991         ret = rb_protect(_thread_call_proc_value, th, &status);
02992     }
02993 
02994     xfree(q->done);
02995     xfree(q);
02996     /* ckfree((char*)q->done); */
02997     /* ckfree((char*)q); */
02998 
02999     if (NIL_P(rbtk_pending_exception)) {
03000         /* return rb_errinfo(); */
03001         if (status) {
03002             rb_exc_raise(rb_errinfo());
03003         }
03004     } else {
03005         VALUE exc = rbtk_pending_exception;
03006         rbtk_pending_exception = Qnil;
03007         /* return exc; */
03008         rb_exc_raise(exc);
03009     }
03010 
03011     return ret;
03012 }
03013 
03014 
03015 /* do_one_event */
03016 static VALUE
03017 lib_do_one_event_core(argc, argv, self, is_ip)
03018     int   argc;
03019     VALUE *argv;
03020     VALUE self;
03021     int   is_ip;
03022 {
03023     volatile VALUE vflags;
03024     int flags;
03025     int found_event;
03026 
03027     if (!NIL_P(eventloop_thread)) {
03028         rb_raise(rb_eRuntimeError, "eventloop is already running");
03029     }
03030 
03031     tcl_stubs_check();
03032 
03033     if (rb_scan_args(argc, argv, "01", &vflags) == 0) {
03034         flags = TCL_ALL_EVENTS | TCL_DONT_WAIT;
03035     } else {
03036         Check_Type(vflags, T_FIXNUM);
03037         flags = FIX2INT(vflags);
03038     }
03039 
03040     if (rb_safe_level() >= 4 || (rb_safe_level() >=1 && OBJ_TAINTED(vflags))) {
03041       flags |= TCL_DONT_WAIT;
03042     }
03043 
03044     if (is_ip) {
03045         /* check IP */
03046         struct tcltkip *ptr = get_ip(self);
03047 
03048         /* ip is deleted? */
03049         if (deleted_ip(ptr)) {
03050             return Qfalse;
03051         }
03052 
03053         if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
03054             /* slave IP */
03055             flags |= TCL_DONT_WAIT;
03056         }
03057     }
03058 
03059     /* found_event = Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT); */
03060     found_event = Tcl_DoOneEvent(flags);
03061 
03062     if (pending_exception_check0()) {
03063         return Qfalse;
03064     }
03065 
03066     if (found_event) {
03067         return Qtrue;
03068     } else {
03069         return Qfalse;
03070     }
03071 }
03072 
03073 static VALUE
03074 lib_do_one_event(argc, argv, self)
03075     int   argc;
03076     VALUE *argv;
03077     VALUE self;
03078 {
03079     return lib_do_one_event_core(argc, argv, self, 0);
03080 }
03081 
03082 static VALUE
03083 ip_do_one_event(argc, argv, self)
03084     int   argc;
03085     VALUE *argv;
03086     VALUE self;
03087 {
03088     return lib_do_one_event_core(argc, argv, self, 0);
03089 }
03090 
03091 
03092 static void
03093 ip_set_exc_message(interp, exc)
03094     Tcl_Interp *interp;
03095     VALUE exc;
03096 {
03097     char *buf;
03098     Tcl_DString dstr;
03099     volatile VALUE msg;
03100     int thr_crit_bup;
03101 
03102 #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
03103     volatile VALUE enc;
03104     Tcl_Encoding encoding;
03105 #endif
03106 
03107     thr_crit_bup = rb_thread_critical;
03108     rb_thread_critical = Qtrue;
03109 
03110     msg = rb_funcall(exc, ID_message, 0, 0);
03111     StringValue(msg);
03112 
03113 #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
03114     enc = rb_attr_get(exc, ID_at_enc);
03115     if (NIL_P(enc)) {
03116         enc = rb_attr_get(msg, ID_at_enc);
03117     }
03118     if (NIL_P(enc)) {
03119         encoding = (Tcl_Encoding)NULL;
03120     } else if (TYPE(enc) == T_STRING) {
03121         /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
03122         encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(enc));
03123     } else {
03124         enc = rb_funcall(enc, ID_to_s, 0, 0);
03125         /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
03126         encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(enc));
03127     }
03128 
03129     /* to avoid a garbled error message dialog */
03130     /* buf = ALLOC_N(char, (RSTRING(msg)->len)+1);*/
03131     /* memcpy(buf, RSTRING(msg)->ptr, RSTRING(msg)->len);*/
03132     /* buf[RSTRING(msg)->len] = 0; */
03133     buf = ALLOC_N(char, RSTRING_LENINT(msg)+1);
03134     /* buf = ckalloc(RSTRING_LENINT(msg)+1); */
03135     memcpy(buf, RSTRING_PTR(msg), RSTRING_LEN(msg));
03136     buf[RSTRING_LEN(msg)] = 0;
03137 
03138     Tcl_DStringInit(&dstr);
03139     Tcl_DStringFree(&dstr);
03140     Tcl_ExternalToUtfDString(encoding, buf, RSTRING_LENINT(msg), &dstr);
03141 
03142     Tcl_AppendResult(interp, Tcl_DStringValue(&dstr), (char*)NULL);
03143     DUMP2("error message:%s", Tcl_DStringValue(&dstr));
03144     Tcl_DStringFree(&dstr);
03145     xfree(buf);
03146     /* ckfree(buf); */
03147 
03148 #else /* TCL_VERSION <= 8.0 */
03149     Tcl_AppendResult(interp, RSTRING_PTR(msg), (char*)NULL);
03150 #endif
03151 
03152     rb_thread_critical = thr_crit_bup;
03153 }
03154 
03155 static VALUE
03156 TkStringValue(obj)
03157     VALUE obj;
03158 {
03159     switch(TYPE(obj)) {
03160     case T_STRING:
03161         return obj;
03162 
03163     case T_NIL:
03164         return rb_str_new2("");
03165 
03166     case T_TRUE:
03167         return rb_str_new2("1");
03168 
03169     case T_FALSE:
03170         return rb_str_new2("0");
03171 
03172     case T_ARRAY:
03173         return rb_funcall(obj, ID_join, 1, rb_str_new2(" "));
03174 
03175     default:
03176         if (rb_respond_to(obj, ID_to_s)) {
03177             return rb_funcall(obj, ID_to_s, 0, 0);
03178         }
03179     }
03180 
03181     return rb_funcall(obj, ID_inspect, 0, 0);
03182 }
03183 
03184 static int
03185 #ifdef HAVE_PROTOTYPES
03186 tcl_protect_core(Tcl_Interp *interp, VALUE (*proc)(VALUE), VALUE data)
03187 #else
03188 tcl_protect_core(interp, proc, data) /* should not raise exception */
03189     Tcl_Interp *interp;
03190     VALUE (*proc)();
03191     VALUE data;
03192 #endif
03193 {
03194     volatile VALUE ret, exc = Qnil;
03195     int status = 0;
03196     int thr_crit_bup = rb_thread_critical;
03197 
03198     Tcl_ResetResult(interp);
03199 
03200     rb_thread_critical = Qfalse;
03201     ret = rb_protect(proc, data, &status);
03202     rb_thread_critical = Qtrue;
03203     if (status) {
03204         char *buf;
03205         VALUE old_gc;
03206         volatile VALUE type, str;
03207 
03208         old_gc = rb_gc_disable();
03209 
03210         switch(status) {
03211         case TAG_RETURN:
03212             type = eTkCallbackReturn;
03213             goto error;
03214         case TAG_BREAK:
03215             type = eTkCallbackBreak;
03216             goto error;
03217         case TAG_NEXT:
03218             type = eTkCallbackContinue;
03219             goto error;
03220         error:
03221             str = rb_str_new2("LocalJumpError: ");
03222             rb_str_append(str, rb_obj_as_string(rb_errinfo()));
03223             exc = rb_exc_new3(type, str);
03224             break;
03225 
03226         case TAG_RETRY:
03227             if (NIL_P(rb_errinfo())) {
03228                 DUMP1("rb_protect: retry");
03229                 exc = rb_exc_new2(eTkCallbackRetry, "retry jump error");
03230             } else {
03231                 exc = rb_errinfo();
03232             }
03233             break;
03234 
03235         case TAG_REDO:
03236             if (NIL_P(rb_errinfo())) {
03237                 DUMP1("rb_protect: redo");
03238                 exc = rb_exc_new2(eTkCallbackRedo,  "redo jump error");
03239             } else {
03240                 exc = rb_errinfo();
03241             }
03242             break;
03243 
03244         case TAG_RAISE:
03245             if (NIL_P(rb_errinfo())) {
03246                 exc = rb_exc_new2(rb_eException, "unknown exception");
03247             } else {
03248                 exc = rb_errinfo();
03249             }
03250             break;
03251 
03252         case TAG_FATAL:
03253             if (NIL_P(rb_errinfo())) {
03254                 exc = rb_exc_new2(rb_eFatal, "FATAL");
03255             } else {
03256                 exc = rb_errinfo();
03257             }
03258             break;
03259 
03260         case TAG_THROW:
03261             if (NIL_P(rb_errinfo())) {
03262                 DUMP1("rb_protect: throw");
03263                 exc = rb_exc_new2(eTkCallbackThrow,  "throw jump error");
03264             } else {
03265                 exc = rb_errinfo();
03266             }
03267             break;
03268 
03269         default:
03270             buf = ALLOC_N(char, 256);
03271             /* buf = ckalloc(sizeof(char) * 256); */
03272             sprintf(buf, "unknown loncaljmp status %d", status);
03273             exc = rb_exc_new2(rb_eException, buf);
03274             xfree(buf);
03275             /* ckfree(buf); */
03276             break;
03277         }
03278 
03279         if (old_gc == Qfalse) rb_gc_enable();
03280 
03281         ret = Qnil;
03282     }
03283 
03284     rb_thread_critical = thr_crit_bup;
03285 
03286     Tcl_ResetResult(interp);
03287 
03288     /* status check */
03289     if (!NIL_P(exc)) {
03290         volatile VALUE eclass = rb_obj_class(exc);
03291         volatile VALUE backtrace;
03292 
03293         DUMP1("(failed)");
03294 
03295         thr_crit_bup = rb_thread_critical;
03296         rb_thread_critical = Qtrue;
03297 
03298         DUMP1("set backtrace");
03299         if (!NIL_P(backtrace = rb_funcall(exc, ID_backtrace, 0, 0))) {
03300             backtrace = rb_ary_join(backtrace, rb_str_new2("\n"));
03301             Tcl_AddErrorInfo(interp, StringValuePtr(backtrace));
03302         }
03303 
03304         rb_thread_critical = thr_crit_bup;
03305 
03306         ip_set_exc_message(interp, exc);
03307 
03308         if (eclass == eTkCallbackReturn)
03309             return TCL_RETURN;
03310 
03311         if (eclass == eTkCallbackBreak)
03312             return TCL_BREAK;
03313 
03314         if (eclass == eTkCallbackContinue)
03315             return TCL_CONTINUE;
03316 
03317         if (eclass == rb_eSystemExit || eclass == rb_eInterrupt) {
03318             rbtk_pending_exception = exc;
03319             return TCL_RETURN;
03320         }
03321 
03322         if (rb_obj_is_kind_of(exc, eTkLocalJumpError)) {
03323             rbtk_pending_exception = exc;
03324             return TCL_ERROR;
03325         }
03326 
03327         if (rb_obj_is_kind_of(exc, eLocalJumpError)) {
03328             VALUE reason = rb_ivar_get(exc, ID_at_reason);
03329 
03330             if (TYPE(reason) == T_SYMBOL) {
03331                 if (SYM2ID(reason) == ID_return)
03332                     return TCL_RETURN;
03333 
03334                 if (SYM2ID(reason) == ID_break)
03335                     return TCL_BREAK;
03336 
03337                 if (SYM2ID(reason) == ID_next)
03338                     return TCL_CONTINUE;
03339             }
03340         }
03341 
03342         return TCL_ERROR;
03343     }
03344 
03345     /* result must be string or nil */
03346     if (!NIL_P(ret)) {
03347         /* copy result to the tcl interpreter */
03348         thr_crit_bup = rb_thread_critical;
03349         rb_thread_critical = Qtrue;
03350 
03351         ret = TkStringValue(ret);
03352         DUMP1("Tcl_AppendResult");
03353         Tcl_AppendResult(interp, RSTRING_PTR(ret), (char *)NULL);
03354 
03355         rb_thread_critical = thr_crit_bup;
03356     }
03357 
03358     DUMP2("(result) %s", NIL_P(ret) ? "nil" : RSTRING_PTR(ret));
03359 
03360     return TCL_OK;
03361 }
03362 
03363 static int
03364 tcl_protect(interp, proc, data)
03365     Tcl_Interp *interp;
03366     VALUE (*proc)();
03367     VALUE data;
03368 {
03369     int code;
03370 
03371 #ifdef HAVE_NATIVETHREAD
03372 #ifndef RUBY_USE_NATIVE_THREAD
03373     if (!ruby_native_thread_p()) {
03374         rb_bug("cross-thread violation on tcl_protect()");
03375     }
03376 #endif
03377 #endif
03378 
03379 #ifdef RUBY_VM
03380     code = tcl_protect_core(interp, proc, data);
03381 #else
03382     do {
03383       int old_trapflag = rb_trap_immediate;
03384       rb_trap_immediate = 0;
03385       code = tcl_protect_core(interp, proc, data);
03386       rb_trap_immediate = old_trapflag;
03387     } while (0);
03388 #endif
03389 
03390     return code;
03391 }
03392 
03393 static int
03394 #if TCL_MAJOR_VERSION >= 8
03395 ip_ruby_eval(clientData, interp, argc, argv)
03396     ClientData clientData;
03397     Tcl_Interp *interp;
03398     int argc;
03399     Tcl_Obj *CONST argv[];
03400 #else /* TCL_MAJOR_VERSION < 8 */
03401 ip_ruby_eval(clientData, interp, argc, argv)
03402     ClientData clientData;
03403     Tcl_Interp *interp;
03404     int argc;
03405     char *argv[];
03406 #endif
03407 {
03408     char *arg;
03409     int thr_crit_bup;
03410     int code;
03411 
03412     if (interp == (Tcl_Interp*)NULL) {
03413         rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
03414                                              "IP is deleted");
03415         return TCL_ERROR;
03416     }
03417 
03418     /* ruby command has 1 arg. */
03419     if (argc != 2) {
03420 #if 0
03421         rb_raise(rb_eArgError,
03422                  "wrong number of arguments (%d for 1)", argc - 1);
03423 #else
03424         char buf[sizeof(int)*8 + 1];
03425         Tcl_ResetResult(interp);
03426         sprintf(buf, "%d", argc-1);
03427         Tcl_AppendResult(interp, "wrong number of arguments (",
03428                          buf, " for 1)", (char *)NULL);
03429         rbtk_pending_exception = rb_exc_new2(rb_eArgError,
03430                                              Tcl_GetStringResult(interp));
03431         return TCL_ERROR;
03432 #endif
03433     }
03434 
03435     /* get C string from Tcl object */
03436 #if TCL_MAJOR_VERSION >= 8
03437     {
03438       char *str;
03439       int  len;
03440 
03441       thr_crit_bup = rb_thread_critical;
03442       rb_thread_critical = Qtrue;
03443 
03444       str = Tcl_GetStringFromObj(argv[1], &len);
03445       arg = ALLOC_N(char, len + 1);
03446       /* arg = ckalloc(sizeof(char) * (len + 1)); */
03447       memcpy(arg, str, len);
03448       arg[len] = 0;
03449 
03450       rb_thread_critical = thr_crit_bup;
03451 
03452     }
03453 #else /* TCL_MAJOR_VERSION < 8 */
03454     arg = argv[1];
03455 #endif
03456 
03457     /* evaluate the argument string by ruby */
03458     DUMP2("rb_eval_string(%s)", arg);
03459 
03460     code = tcl_protect(interp, rb_eval_string, (VALUE)arg);
03461 
03462 #if TCL_MAJOR_VERSION >= 8
03463     xfree(arg);
03464     /* ckfree(arg); */
03465 #endif
03466 
03467     return code;
03468 }
03469 
03470 
03471 /* Tcl command `ruby_cmd' */
03472 static VALUE
03473 ip_ruby_cmd_core(arg)
03474     struct cmd_body_arg *arg;
03475 {
03476     volatile VALUE ret;
03477     int thr_crit_bup;
03478 
03479     DUMP1("call ip_ruby_cmd_core");
03480     thr_crit_bup = rb_thread_critical;
03481     rb_thread_critical = Qfalse;
03482     ret = rb_apply(arg->receiver, arg->method, arg->args);
03483     DUMP2("rb_apply return:%lx", ret);
03484     rb_thread_critical = thr_crit_bup;
03485     DUMP1("finish ip_ruby_cmd_core");
03486 
03487     return ret;
03488 }
03489 
03490 #define SUPPORT_NESTED_CONST_AS_IP_RUBY_CMD_RECEIVER 1
03491 
03492 static VALUE
03493 ip_ruby_cmd_receiver_const_get(name)
03494      char *name;
03495 {
03496   volatile VALUE klass = rb_cObject;
03497 #if 0
03498   char *head, *tail;
03499 #endif
03500   int state;
03501 
03502 #if SUPPORT_NESTED_CONST_AS_IP_RUBY_CMD_RECEIVER
03503   klass = rb_eval_string_protect(name, &state);
03504   if (state) {
03505     return Qnil;
03506   } else {
03507     return klass;
03508   }
03509 #else
03510   return rb_const_get(klass, rb_intern(name));
03511 #endif
03512 
03513   /* TODO!!!!!! */
03514   /* support nest of classes/modules */
03515 
03516   /* return rb_eval_string(name); */
03517   /* return rb_eval_string_protect(name, &state); */
03518 
03519 #if 0 /* doesn't work!! (fail to autoload?) */
03520   /* duplicate */
03521   head = name = strdup(name);
03522 
03523   /* has '::' at head ? */
03524   if (*head == ':')  head += 2;
03525   tail = head;
03526 
03527   /* search */
03528   while(*tail) {
03529     if (*tail == ':') {
03530       *tail = '\0';
03531       klass = rb_const_get(klass, rb_intern(head));
03532       tail += 2;
03533       head = tail;
03534     } else {
03535       tail++;
03536     }
03537   }
03538 
03539   free(name);
03540   return rb_const_get(klass, rb_intern(head));
03541 #endif
03542 }
03543 
03544 static VALUE
03545 ip_ruby_cmd_receiver_get(str)
03546      char *str;
03547 {
03548   volatile VALUE receiver;
03549 #if !SUPPORT_NESTED_CONST_AS_IP_RUBY_CMD_RECEIVER
03550   int state;
03551 #endif
03552 
03553   if (str[0] == ':' || ('A' <= str[0] && str[0] <= 'Z')) {
03554     /* class | module | constant */
03555 #if SUPPORT_NESTED_CONST_AS_IP_RUBY_CMD_RECEIVER
03556     receiver = ip_ruby_cmd_receiver_const_get(str);
03557 #else
03558     receiver = rb_protect(ip_ruby_cmd_receiver_const_get, (VALUE)str, &state);
03559     if (state) return Qnil;
03560 #endif
03561   } else if (str[0] == '$') {
03562     /* global variable */
03563     receiver = rb_gv_get(str);
03564   } else {
03565     /* global variable omitted '$' */
03566     char *buf;
03567     size_t len;
03568 
03569     len = strlen(str);
03570     buf = ALLOC_N(char, len + 2);
03571     /* buf = ckalloc(sizeof(char) * (len + 2)); */
03572     buf[0] = '$';
03573     memcpy(buf + 1, str, len);
03574     buf[len + 1] = 0;
03575     receiver = rb_gv_get(buf);
03576     xfree(buf);
03577     /* ckfree(buf); */
03578   }
03579 
03580   return receiver;
03581 }
03582 
03583 /* ruby_cmd receiver method arg ... */
03584 static int
03585 #if TCL_MAJOR_VERSION >= 8
03586 ip_ruby_cmd(clientData, interp, argc, argv)
03587     ClientData clientData;
03588     Tcl_Interp *interp;
03589     int argc;
03590     Tcl_Obj *CONST argv[];
03591 #else /* TCL_MAJOR_VERSION < 8 */
03592 ip_ruby_cmd(clientData, interp, argc, argv)
03593     ClientData clientData;
03594     Tcl_Interp *interp;
03595     int argc;
03596     char *argv[];
03597 #endif
03598 {
03599     volatile VALUE receiver;
03600     volatile ID method;
03601     volatile VALUE args;
03602     char *str;
03603     int i;
03604     int  len;
03605     struct cmd_body_arg *arg;
03606     int thr_crit_bup;
03607     VALUE old_gc;
03608     int code;
03609 
03610     if (interp == (Tcl_Interp*)NULL) {
03611         rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
03612                                              "IP is deleted");
03613         return TCL_ERROR;
03614     }
03615 
03616     if (argc < 3) {
03617 #if 0
03618         rb_raise(rb_eArgError, "too few arguments");
03619 #else
03620         Tcl_ResetResult(interp);
03621         Tcl_AppendResult(interp, "too few arguments", (char *)NULL);
03622         rbtk_pending_exception = rb_exc_new2(rb_eArgError,
03623                                              Tcl_GetStringResult(interp));
03624         return TCL_ERROR;
03625 #endif
03626     }
03627 
03628     /* get arguments from Tcl objects */
03629     thr_crit_bup = rb_thread_critical;
03630     rb_thread_critical = Qtrue;
03631     old_gc = rb_gc_disable();
03632 
03633     /* get receiver */
03634 #if TCL_MAJOR_VERSION >= 8
03635     str = Tcl_GetStringFromObj(argv[1], &len);
03636 #else /* TCL_MAJOR_VERSION < 8 */
03637     str = argv[1];
03638 #endif
03639     DUMP2("receiver:%s",str);
03640     /* receiver = rb_protect(ip_ruby_cmd_receiver_get, (VALUE)str, &code); */
03641     receiver = ip_ruby_cmd_receiver_get(str);
03642     if (NIL_P(receiver)) {
03643 #if 0
03644         rb_raise(rb_eArgError,
03645                  "unknown class/module/global-variable '%s'", str);
03646 #else
03647         Tcl_ResetResult(interp);
03648         Tcl_AppendResult(interp, "unknown class/module/global-variable '",
03649                          str, "'", (char *)NULL);
03650         rbtk_pending_exception = rb_exc_new2(rb_eArgError,
03651                                              Tcl_GetStringResult(interp));
03652         if (old_gc == Qfalse) rb_gc_enable();
03653         return TCL_ERROR;
03654 #endif
03655     }
03656 
03657     /* get metrhod */
03658 #if TCL_MAJOR_VERSION >= 8
03659     str = Tcl_GetStringFromObj(argv[2], &len);
03660 #else /* TCL_MAJOR_VERSION < 8 */
03661     str = argv[2];
03662 #endif
03663     method = rb_intern(str);
03664 
03665     /* get args */
03666     args = rb_ary_new2(argc - 2);
03667     for(i = 3; i < argc; i++) {
03668         VALUE s;
03669 #if TCL_MAJOR_VERSION >= 8
03670         str = Tcl_GetStringFromObj(argv[i], &len);
03671         s = rb_tainted_str_new(str, len);
03672 #else /* TCL_MAJOR_VERSION < 8 */
03673         str = argv[i];
03674         s = rb_tainted_str_new2(str);
03675 #endif
03676         DUMP2("arg:%s",str);
03677 #ifndef HAVE_STRUCT_RARRAY_LEN
03678         rb_ary_push(args, s);
03679 #else
03680         RARRAY(args)->ptr[RARRAY(args)->len++] = s;
03681 #endif
03682     }
03683 
03684     if (old_gc == Qfalse) rb_gc_enable();
03685     rb_thread_critical = thr_crit_bup;
03686 
03687     /* allocate */
03688     arg = ALLOC(struct cmd_body_arg);
03689     /* arg = RbTk_ALLOC_N(struct cmd_body_arg, 1); */
03690 
03691     arg->receiver = receiver;
03692     arg->method = method;
03693     arg->args = args;
03694 
03695     /* evaluate the argument string by ruby */
03696     code = tcl_protect(interp, ip_ruby_cmd_core, (VALUE)arg);
03697 
03698     xfree(arg);
03699     /* ckfree((char*)arg); */
03700 
03701     return code;
03702 }
03703 
03704 
03705 /*****************************/
03706 /* relpace of 'exit' command */
03707 /*****************************/
03708 static int
03709 #if TCL_MAJOR_VERSION >= 8
03710 #ifdef HAVE_PROTOTYPES
03711 ip_InterpExitObjCmd(ClientData clientData, Tcl_Interp *interp,
03712                     int argc, Tcl_Obj *CONST argv[])
03713 #else
03714 ip_InterpExitObjCmd(clientData, interp, argc, argv)
03715     ClientData clientData;
03716     Tcl_Interp *interp;
03717     int argc;
03718     Tcl_Obj *CONST argv[];
03719 #endif
03720 #else /* TCL_MAJOR_VERSION < 8 */
03721 #ifdef HAVE_PROTOTYPES
03722 ip_InterpExitCommand(ClientData clientData, Tcl_Interp *interp,
03723                      int argc, char *argv[])
03724 #else
03725 ip_InterpExitCommand(clientData, interp, argc, argv)
03726     ClientData clientData;
03727     Tcl_Interp *interp;
03728     int argc;
03729     char *argv[];
03730 #endif
03731 #endif
03732 {
03733     DUMP1("start ip_InterpExitCommand");
03734     if (interp != (Tcl_Interp*)NULL
03735         && !Tcl_InterpDeleted(interp)
03736 #if TCL_NAMESPACE_DEBUG
03737         && !ip_null_namespace(interp)
03738 #endif
03739         ) {
03740         Tcl_ResetResult(interp);
03741         /* Tcl_Preserve(interp); */
03742         /* Tcl_Eval(interp, "interp eval {} {destroy .}; interp delete {}"); */
03743         if (!Tcl_InterpDeleted(interp)) {
03744           ip_finalize(interp);
03745 
03746           Tcl_DeleteInterp(interp);
03747           Tcl_Release(interp);
03748         }
03749     }
03750     return TCL_OK;
03751 }
03752 
03753 static int
03754 #if TCL_MAJOR_VERSION >= 8
03755 #ifdef HAVE_PROTOTYPES
03756 ip_RubyExitObjCmd(ClientData clientData, Tcl_Interp *interp,
03757                   int argc, Tcl_Obj *CONST argv[])
03758 #else
03759 ip_RubyExitObjCmd(clientData, interp, argc, argv)
03760     ClientData clientData;
03761     Tcl_Interp *interp;
03762     int argc;
03763     Tcl_Obj *CONST argv[];
03764 #endif
03765 #else /* TCL_MAJOR_VERSION < 8 */
03766 #ifdef HAVE_PROTOTYPES
03767 ip_RubyExitCommand(ClientData clientData, Tcl_Interp *interp,
03768                    int argc, char *argv[])
03769 #else
03770 ip_RubyExitCommand(clientData, interp, argc, argv)
03771     ClientData clientData;
03772     Tcl_Interp *interp;
03773     int argc;
03774     char *argv[];
03775 #endif
03776 #endif
03777 {
03778     int state;
03779     char *cmd, *param;
03780 #if TCL_MAJOR_VERSION < 8
03781     char *endptr;
03782     cmd = argv[0];
03783 #endif
03784 
03785     DUMP1("start ip_RubyExitCommand");
03786 
03787 #if TCL_MAJOR_VERSION >= 8
03788     /* cmd = Tcl_GetString(argv[0]); */
03789     cmd = Tcl_GetStringFromObj(argv[0], (int*)NULL);
03790 #endif
03791 
03792     if (argc < 1 || argc > 2) {
03793         /* arguemnt error */
03794         Tcl_AppendResult(interp,
03795                          "wrong number of arguments: should be \"",
03796                          cmd, " ?returnCode?\"", (char *)NULL);
03797         return TCL_ERROR;
03798     }
03799 
03800     if (interp == (Tcl_Interp*)NULL) return TCL_OK;
03801 
03802     Tcl_ResetResult(interp);
03803 
03804     if (rb_safe_level() >= 4 || Tcl_IsSafe(interp)) {
03805         if (!Tcl_InterpDeleted(interp)) {
03806           ip_finalize(interp);
03807 
03808           Tcl_DeleteInterp(interp);
03809           Tcl_Release(interp);
03810         }
03811         return TCL_OK;
03812     }
03813 
03814     switch(argc) {
03815     case 1:
03816         /* rb_exit(0); */ /* not return if succeed */
03817         Tcl_AppendResult(interp,
03818                          "fail to call \"", cmd, "\"", (char *)NULL);
03819 
03820         rbtk_pending_exception = rb_exc_new2(rb_eSystemExit,
03821                                              Tcl_GetStringResult(interp));
03822         rb_iv_set(rbtk_pending_exception, "status", INT2FIX(0));
03823 
03824         return TCL_RETURN;
03825 
03826     case 2:
03827 #if TCL_MAJOR_VERSION >= 8
03828         if (Tcl_GetIntFromObj(interp, argv[1], &state) == TCL_ERROR) {
03829             return TCL_ERROR;
03830         }
03831         /* param = Tcl_GetString(argv[1]); */
03832         param = Tcl_GetStringFromObj(argv[1], (int*)NULL);
03833 #else /* TCL_MAJOR_VERSION < 8 */
03834         state = (int)strtol(argv[1], &endptr, 0);
03835         if (*endptr) {
03836             Tcl_AppendResult(interp,
03837                              "expected integer but got \"",
03838                              argv[1], "\"", (char *)NULL);
03839             return TCL_ERROR;
03840         }
03841         param = argv[1];
03842 #endif
03843         /* rb_exit(state); */ /* not return if succeed */
03844 
03845         Tcl_AppendResult(interp, "fail to call \"", cmd, " ",
03846                          param, "\"", (char *)NULL);
03847 
03848         rbtk_pending_exception = rb_exc_new2(rb_eSystemExit,
03849                                              Tcl_GetStringResult(interp));
03850         rb_iv_set(rbtk_pending_exception, "status", INT2FIX(state));
03851 
03852         return TCL_RETURN;
03853 
03854     default:
03855         /* arguemnt error */
03856         Tcl_AppendResult(interp,
03857                          "wrong number of arguments: should be \"",
03858                          cmd, " ?returnCode?\"", (char *)NULL);
03859         return TCL_ERROR;
03860     }
03861 }
03862 
03863 
03864 /**************************/
03865 /*  based on tclEvent.c   */
03866 /**************************/
03867 
03868 /*********************/
03869 /* replace of update */
03870 /*********************/
03871 #if TCL_MAJOR_VERSION >= 8
03872 static int ip_rbUpdateObjCmd _((ClientData, Tcl_Interp *, int,
03873                                Tcl_Obj *CONST []));
03874 static int
03875 ip_rbUpdateObjCmd(clientData, interp, objc, objv)
03876     ClientData clientData;
03877     Tcl_Interp *interp;
03878     int objc;
03879     Tcl_Obj *CONST objv[];
03880 #else /* TCL_MAJOR_VERSION < 8 */
03881 static int ip_rbUpdateCommand _((ClientData, Tcl_Interp *, int, char *[]));
03882 static int
03883 ip_rbUpdateCommand(clientData, interp, objc, objv)
03884     ClientData clientData;
03885     Tcl_Interp *interp;
03886     int objc;
03887     char *objv[];
03888 #endif
03889 {
03890     int  optionIndex;
03891     int  ret;
03892     int  flags = 0;
03893     static CONST char *updateOptions[] = {"idletasks", (char *) NULL};
03894     enum updateOptions {REGEXP_IDLETASKS};
03895 
03896     DUMP1("Ruby's 'update' is called");
03897     if (interp == (Tcl_Interp*)NULL) {
03898         rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
03899                                              "IP is deleted");
03900         return TCL_ERROR;
03901     }
03902 #ifdef HAVE_NATIVETHREAD
03903 #ifndef RUBY_USE_NATIVE_THREAD
03904     if (!ruby_native_thread_p()) {
03905         rb_bug("cross-thread violation on ip_ruby_eval()");
03906     }
03907 #endif
03908 #endif
03909 
03910     Tcl_ResetResult(interp);
03911 
03912     if (objc == 1) {
03913         flags = TCL_DONT_WAIT;
03914 
03915     } else if (objc == 2) {
03916 #if TCL_MAJOR_VERSION >= 8
03917         if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)updateOptions,
03918                 "option", 0, &optionIndex) != TCL_OK) {
03919             return TCL_ERROR;
03920         }
03921         switch ((enum updateOptions) optionIndex) {
03922             case REGEXP_IDLETASKS: {
03923                 flags = TCL_IDLE_EVENTS;
03924                 break;
03925             }
03926             default: {
03927                 rb_bug("ip_rbUpdateObjCmd: bad option index to UpdateOptions");
03928             }
03929         }
03930 #else
03931         if (strncmp(objv[1], "idletasks", strlen(objv[1])) != 0) {
03932             Tcl_AppendResult(interp, "bad option \"", objv[1],
03933                     "\": must be idletasks", (char *) NULL);
03934             return TCL_ERROR;
03935         }
03936         flags = TCL_IDLE_EVENTS;
03937 #endif
03938     } else {
03939 #ifdef Tcl_WrongNumArgs
03940         Tcl_WrongNumArgs(interp, 1, objv, "[ idletasks ]");
03941 #else
03942 # if TCL_MAJOR_VERSION >= 8
03943         int  dummy;
03944         Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
03945                          Tcl_GetStringFromObj(objv[0], &dummy),
03946                          " [ idletasks ]\"",
03947                          (char *) NULL);
03948 # else /* TCL_MAJOR_VERSION < 8 */
03949         Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
03950                          objv[0], " [ idletasks ]\"", (char *) NULL);
03951 # endif
03952 #endif
03953         return TCL_ERROR;
03954     }
03955 
03956     Tcl_Preserve(interp);
03957 
03958     /* call eventloop */
03959     /* ret = lib_eventloop_core(0, flags, (int *)NULL);*/ /* ignore result */
03960     ret = RTEST(lib_eventloop_launcher(0, flags, (int *)NULL, interp)); /* ignore result */
03961 
03962     /* exception check */
03963     if (!NIL_P(rbtk_pending_exception)) {
03964         Tcl_Release(interp);
03965 
03966         /*
03967         if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)) {
03968         */
03969         if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)
03970             || rb_obj_is_kind_of(rbtk_pending_exception, rb_eInterrupt)) {
03971             return TCL_RETURN;
03972         } else{
03973             return TCL_ERROR;
03974         }
03975     }
03976 
03977     /* trap check */
03978 #ifdef RUBY_VM
03979     if (rb_thread_check_trap_pending()) {
03980 #else
03981     if (rb_trap_pending) {
03982 #endif
03983         Tcl_Release(interp);
03984 
03985         return TCL_RETURN;
03986     }
03987 
03988     /*
03989      * Must clear the interpreter's result because event handlers could
03990      * have executed commands.
03991      */
03992 
03993     DUMP2("last result '%s'", Tcl_GetStringResult(interp));
03994     Tcl_ResetResult(interp);
03995     Tcl_Release(interp);
03996 
03997     DUMP1("finish Ruby's 'update'");
03998     return TCL_OK;
03999 }
04000 
04001 
04002 /**********************/
04003 /* update with thread */
04004 /**********************/
04005 struct th_update_param {
04006     VALUE thread;
04007     int   done;
04008 };
04009 
04010 static void rb_threadUpdateProc _((ClientData));
04011 static void
04012 rb_threadUpdateProc(clientData)
04013     ClientData clientData;      /* Pointer to integer to set to 1. */
04014 {
04015     struct th_update_param *param = (struct th_update_param *) clientData;
04016 
04017     DUMP1("threadUpdateProc is called");
04018     param->done = 1;
04019     rb_thread_wakeup(param->thread);
04020 
04021     return;
04022 }
04023 
04024 #if TCL_MAJOR_VERSION >= 8
04025 static int ip_rb_threadUpdateObjCmd _((ClientData, Tcl_Interp *, int,
04026                                        Tcl_Obj *CONST []));
04027 static int
04028 ip_rb_threadUpdateObjCmd(clientData, interp, objc, objv)
04029     ClientData clientData;
04030     Tcl_Interp *interp;
04031     int objc;
04032     Tcl_Obj *CONST objv[];
04033 #else /* TCL_MAJOR_VERSION < 8 */
04034 static int ip_rb_threadUpdateCommand _((ClientData, Tcl_Interp *, int,
04035                                        char *[]));
04036 static int
04037 ip_rb_threadUpdateCommand(clientData, interp, objc, objv)
04038     ClientData clientData;
04039     Tcl_Interp *interp;
04040     int objc;
04041     char *objv[];
04042 #endif
04043 {
04044     int  optionIndex;
04045     int  flags = 0;
04046     struct th_update_param *param;
04047     static CONST char *updateOptions[] = {"idletasks", (char *) NULL};
04048     enum updateOptions {REGEXP_IDLETASKS};
04049     volatile VALUE current_thread = rb_thread_current();
04050     struct timeval t;
04051 
04052     DUMP1("Ruby's 'thread_update' is called");
04053     if (interp == (Tcl_Interp*)NULL) {
04054         rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
04055                                              "IP is deleted");
04056         return TCL_ERROR;
04057     }
04058 #ifdef HAVE_NATIVETHREAD
04059 #ifndef RUBY_USE_NATIVE_THREAD
04060     if (!ruby_native_thread_p()) {
04061         rb_bug("cross-thread violation on ip_rb_threadUpdateCommand()");
04062     }
04063 #endif
04064 #endif
04065 
04066     if (rb_thread_alone()
04067         || NIL_P(eventloop_thread) || eventloop_thread == current_thread) {
04068 #if TCL_MAJOR_VERSION >= 8
04069         DUMP1("call ip_rbUpdateObjCmd");
04070         return ip_rbUpdateObjCmd(clientData, interp, objc, objv);
04071 #else /* TCL_MAJOR_VERSION < 8 */
04072         DUMP1("call ip_rbUpdateCommand");
04073         return ip_rbUpdateCommand(clientData, interp, objc, objv);
04074 #endif
04075     }
04076 
04077     DUMP1("start Ruby's 'thread_update' body");
04078 
04079     Tcl_ResetResult(interp);
04080 
04081     if (objc == 1) {
04082         flags = TCL_DONT_WAIT;
04083 
04084     } else if (objc == 2) {
04085 #if TCL_MAJOR_VERSION >= 8
04086         if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)updateOptions,
04087                 "option", 0, &optionIndex) != TCL_OK) {
04088             return TCL_ERROR;
04089         }
04090         switch ((enum updateOptions) optionIndex) {
04091             case REGEXP_IDLETASKS: {
04092                 flags = TCL_IDLE_EVENTS;
04093                 break;
04094             }
04095             default: {
04096                 rb_bug("ip_rb_threadUpdateObjCmd: bad option index to UpdateOptions");
04097             }
04098         }
04099 #else
04100         if (strncmp(objv[1], "idletasks", strlen(objv[1])) != 0) {
04101             Tcl_AppendResult(interp, "bad option \"", objv[1],
04102                     "\": must be idletasks", (char *) NULL);
04103             return TCL_ERROR;
04104         }
04105         flags = TCL_IDLE_EVENTS;
04106 #endif
04107     } else {
04108 #ifdef Tcl_WrongNumArgs
04109         Tcl_WrongNumArgs(interp, 1, objv, "[ idletasks ]");
04110 #else
04111 # if TCL_MAJOR_VERSION >= 8
04112         int  dummy;
04113         Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
04114                          Tcl_GetStringFromObj(objv[0], &dummy),
04115                          " [ idletasks ]\"",
04116                          (char *) NULL);
04117 # else /* TCL_MAJOR_VERSION < 8 */
04118         Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
04119                          objv[0], " [ idletasks ]\"", (char *) NULL);
04120 # endif
04121 #endif
04122         return TCL_ERROR;
04123     }
04124 
04125     DUMP1("pass argument check");
04126 
04127     /* param = (struct th_update_param *)Tcl_Alloc(sizeof(struct th_update_param)); */
04128     param = RbTk_ALLOC_N(struct th_update_param, 1);
04129 #if 0 /* use Tcl_Preserve/Release */
04130     Tcl_Preserve((ClientData)param);
04131 #endif
04132     param->thread = current_thread;
04133     param->done = 0;
04134 
04135     DUMP1("set idle proc");
04136     Tcl_DoWhenIdle(rb_threadUpdateProc, (ClientData) param);
04137 
04138     t.tv_sec  = 0;
04139     t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
04140 
04141     while(!param->done) {
04142       DUMP1("wait for complete idle proc");
04143       /* rb_thread_stop(); */
04144       /* rb_thread_sleep_forever(); */
04145       rb_thread_wait_for(t);
04146       if (NIL_P(eventloop_thread)) {
04147         break;
04148       }
04149     }
04150 
04151 #if 0 /* use Tcl_EventuallyFree */
04152         Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
04153 #else
04154 #if 0 /* use Tcl_Preserve/Release */
04155     Tcl_Release((ClientData)param);
04156 #else
04157     /* Tcl_Free((char *)param); */
04158     ckfree((char *)param);
04159 #endif
04160 #endif
04161 
04162     DUMP1("finish Ruby's 'thread_update'");
04163     return TCL_OK;
04164 }
04165 
04166 
04167 /***************************/
04168 /* replace of vwait/tkwait */
04169 /***************************/
04170 #if TCL_MAJOR_VERSION >= 8
04171 static int ip_rbVwaitObjCmd _((ClientData, Tcl_Interp *, int,
04172                                Tcl_Obj *CONST []));
04173 static int ip_rb_threadVwaitObjCmd _((ClientData, Tcl_Interp *, int,
04174                                       Tcl_Obj *CONST []));
04175 static int ip_rbTkWaitObjCmd _((ClientData, Tcl_Interp *, int,
04176                                 Tcl_Obj *CONST []));
04177 static int ip_rb_threadTkWaitObjCmd _((ClientData, Tcl_Interp *, int,
04178                                        Tcl_Obj *CONST []));
04179 #else
04180 static int ip_rbVwaitCommand _((ClientData, Tcl_Interp *, int, char *[]));
04181 static int ip_rb_threadVwaitCommand _((ClientData, Tcl_Interp *, int,
04182                                        char *[]));
04183 static int ip_rbTkWaitCommand _((ClientData, Tcl_Interp *, int, char *[]));
04184 static int ip_rb_threadTkWaitCommand _((ClientData, Tcl_Interp *, int,
04185                                         char *[]));
04186 #endif
04187 
04188 #if TCL_MAJOR_VERSION >= 8
04189 static char *VwaitVarProc _((ClientData, Tcl_Interp *,
04190                              CONST84 char *,CONST84 char *, int));
04191 static char *
04192 VwaitVarProc(clientData, interp, name1, name2, flags)
04193     ClientData clientData;      /* Pointer to integer to set to 1. */
04194     Tcl_Interp *interp;         /* Interpreter containing variable. */
04195     CONST84 char *name1;        /* Name of variable. */
04196     CONST84 char *name2;        /* Second part of variable name. */
04197     int flags;                  /* Information about what happened. */
04198 #else /* TCL_MAJOR_VERSION < 8 */
04199 static char *VwaitVarProc _((ClientData, Tcl_Interp *, char *, char *, int));
04200 static char *
04201 VwaitVarProc(clientData, interp, name1, name2, flags)
04202     ClientData clientData;      /* Pointer to integer to set to 1. */
04203     Tcl_Interp *interp;         /* Interpreter containing variable. */
04204     char *name1;                /* Name of variable. */
04205     char *name2;                /* Second part of variable name. */
04206     int flags;                  /* Information about what happened. */
04207 #endif
04208 {
04209     int *donePtr = (int *) clientData;
04210 
04211     *donePtr = 1;
04212     return (char *) NULL;
04213 }
04214 
04215 #if TCL_MAJOR_VERSION >= 8
04216 static int
04217 ip_rbVwaitObjCmd(clientData, interp, objc, objv)
04218     ClientData clientData; /* Not used */
04219     Tcl_Interp *interp;
04220     int objc;
04221     Tcl_Obj *CONST objv[];
04222 #else /* TCL_MAJOR_VERSION < 8 */
04223 static int
04224 ip_rbVwaitCommand(clientData, interp, objc, objv)
04225     ClientData clientData; /* Not used */
04226     Tcl_Interp *interp;
04227     int objc;
04228     char *objv[];
04229 #endif
04230 {
04231     int  ret, done, foundEvent;
04232     char *nameString;
04233     int  dummy;
04234     int thr_crit_bup;
04235 
04236     DUMP1("Ruby's 'vwait' is called");
04237     if (interp == (Tcl_Interp*)NULL) {
04238         rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
04239                                              "IP is deleted");
04240         return TCL_ERROR;
04241     }
04242 
04243 #if 0
04244     if (!rb_thread_alone()
04245         && eventloop_thread != Qnil
04246         && eventloop_thread != rb_thread_current()) {
04247 #if TCL_MAJOR_VERSION >= 8
04248         DUMP1("call ip_rb_threadVwaitObjCmd");
04249         return ip_rb_threadVwaitObjCmd(clientData, interp, objc, objv);
04250 #else /* TCL_MAJOR_VERSION < 8 */
04251         DUMP1("call ip_rb_threadVwaitCommand");
04252         return ip_rb_threadVwaitCommand(clientData, interp, objc, objv);
04253 #endif
04254     }
04255 #endif
04256 
04257     Tcl_Preserve(interp);
04258 #ifdef HAVE_NATIVETHREAD
04259 #ifndef RUBY_USE_NATIVE_THREAD
04260     if (!ruby_native_thread_p()) {
04261         rb_bug("cross-thread violation on ip_rbVwaitCommand()");
04262     }
04263 #endif
04264 #endif
04265 
04266     Tcl_ResetResult(interp);
04267 
04268     if (objc != 2) {
04269 #ifdef Tcl_WrongNumArgs
04270         Tcl_WrongNumArgs(interp, 1, objv, "name");
04271 #else
04272         thr_crit_bup = rb_thread_critical;
04273         rb_thread_critical = Qtrue;
04274 
04275 #if TCL_MAJOR_VERSION >= 8
04276         /* nameString = Tcl_GetString(objv[0]); */
04277         nameString = Tcl_GetStringFromObj(objv[0], &dummy);
04278 #else /* TCL_MAJOR_VERSION < 8 */
04279         nameString = objv[0];
04280 #endif
04281         Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
04282                          nameString, " name\"", (char *) NULL);
04283 
04284         rb_thread_critical = thr_crit_bup;
04285 #endif
04286 
04287         Tcl_Release(interp);
04288         return TCL_ERROR;
04289     }
04290 
04291     thr_crit_bup = rb_thread_critical;
04292     rb_thread_critical = Qtrue;
04293 
04294 #if TCL_MAJOR_VERSION >= 8
04295     Tcl_IncrRefCount(objv[1]);
04296     /* nameString = Tcl_GetString(objv[1]); */
04297     nameString = Tcl_GetStringFromObj(objv[1], &dummy);
04298 #else /* TCL_MAJOR_VERSION < 8 */
04299     nameString = objv[1];
04300 #endif
04301 
04302     /*
04303     if (Tcl_TraceVar(interp, nameString,
04304                      TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
04305                      VwaitVarProc, (ClientData) &done) != TCL_OK) {
04306         return TCL_ERROR;
04307     }
04308     */
04309     ret = Tcl_TraceVar(interp, nameString,
04310                        TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
04311                        VwaitVarProc, (ClientData) &done);
04312 
04313     rb_thread_critical = thr_crit_bup;
04314 
04315     if (ret != TCL_OK) {
04316 #if TCL_MAJOR_VERSION >= 8
04317         Tcl_DecrRefCount(objv[1]);
04318 #endif
04319         Tcl_Release(interp);
04320         return TCL_ERROR;
04321     }
04322 
04323     done = 0;
04324 
04325     foundEvent = RTEST(lib_eventloop_launcher(/* not check root-widget */0,
04326                                               0, &done, interp));
04327 
04328     thr_crit_bup = rb_thread_critical;
04329     rb_thread_critical = Qtrue;
04330 
04331     Tcl_UntraceVar(interp, nameString,
04332                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
04333                    VwaitVarProc, (ClientData) &done);
04334 
04335     rb_thread_critical = thr_crit_bup;
04336 
04337     /* exception check */
04338     if (!NIL_P(rbtk_pending_exception)) {
04339 #if TCL_MAJOR_VERSION >= 8
04340         Tcl_DecrRefCount(objv[1]);
04341 #endif
04342         Tcl_Release(interp);
04343 
04344 /*
04345         if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)) {
04346 */
04347         if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)
04348             || rb_obj_is_kind_of(rbtk_pending_exception, rb_eInterrupt)) {
04349             return TCL_RETURN;
04350         } else{
04351             return TCL_ERROR;
04352         }
04353     }
04354 
04355     /* trap check */
04356 #ifdef RUBY_VM
04357     if (rb_thread_check_trap_pending()) {
04358 #else
04359     if (rb_trap_pending) {
04360 #endif
04361 #if TCL_MAJOR_VERSION >= 8
04362         Tcl_DecrRefCount(objv[1]);
04363 #endif
04364         Tcl_Release(interp);
04365 
04366         return TCL_RETURN;
04367     }
04368 
04369     /*
04370      * Clear out the interpreter's result, since it may have been set
04371      * by event handlers.
04372      */
04373 
04374     Tcl_ResetResult(interp);
04375     if (!foundEvent) {
04376         thr_crit_bup = rb_thread_critical;
04377         rb_thread_critical = Qtrue;
04378 
04379         Tcl_AppendResult(interp, "can't wait for variable \"", nameString,
04380                          "\":  would wait forever", (char *) NULL);
04381 
04382         rb_thread_critical = thr_crit_bup;
04383 
04384 #if TCL_MAJOR_VERSION >= 8
04385         Tcl_DecrRefCount(objv[1]);
04386 #endif
04387         Tcl_Release(interp);
04388         return TCL_ERROR;
04389     }
04390 
04391 #if TCL_MAJOR_VERSION >= 8
04392     Tcl_DecrRefCount(objv[1]);
04393 #endif
04394     Tcl_Release(interp);
04395     return TCL_OK;
04396 }
04397 
04398 
04399 /**************************/
04400 /*  based on tkCmd.c      */
04401 /**************************/
04402 #if TCL_MAJOR_VERSION >= 8
04403 static char *WaitVariableProc _((ClientData, Tcl_Interp *,
04404                                  CONST84 char *,CONST84 char *, int));
04405 static char *
04406 WaitVariableProc(clientData, interp, name1, name2, flags)
04407     ClientData clientData;      /* Pointer to integer to set to 1. */
04408     Tcl_Interp *interp;         /* Interpreter containing variable. */
04409     CONST84 char *name1;        /* Name of variable. */
04410     CONST84 char *name2;        /* Second part of variable name. */
04411     int flags;                  /* Information about what happened. */
04412 #else /* TCL_MAJOR_VERSION < 8 */
04413 static char *WaitVariableProc _((ClientData, Tcl_Interp *,
04414                                  char *, char *, int));
04415 static char *
04416 WaitVariableProc(clientData, interp, name1, name2, flags)
04417     ClientData clientData;      /* Pointer to integer to set to 1. */
04418     Tcl_Interp *interp;         /* Interpreter containing variable. */
04419     char *name1;                /* Name of variable. */
04420     char *name2;                /* Second part of variable name. */
04421     int flags;                  /* Information about what happened. */
04422 #endif
04423 {
04424     int *donePtr = (int *) clientData;
04425 
04426     *donePtr = 1;
04427     return (char *) NULL;
04428 }
04429 
04430 static void WaitVisibilityProc _((ClientData, XEvent *));
04431 static void
04432 WaitVisibilityProc(clientData, eventPtr)
04433     ClientData clientData;      /* Pointer to integer to set to 1. */
04434     XEvent *eventPtr;           /* Information about event (not used). */
04435 {
04436     int *donePtr = (int *) clientData;
04437 
04438     if (eventPtr->type == VisibilityNotify) {
04439         *donePtr = 1;
04440     }
04441     if (eventPtr->type == DestroyNotify) {
04442         *donePtr = 2;
04443     }
04444 }
04445 
04446 static void WaitWindowProc _((ClientData, XEvent *));
04447 static void
04448 WaitWindowProc(clientData, eventPtr)
04449     ClientData clientData;      /* Pointer to integer to set to 1. */
04450     XEvent *eventPtr;           /* Information about event. */
04451 {
04452     int *donePtr = (int *) clientData;
04453 
04454     if (eventPtr->type == DestroyNotify) {
04455         *donePtr = 1;
04456     }
04457 }
04458 
04459 #if TCL_MAJOR_VERSION >= 8
04460 static int
04461 ip_rbTkWaitObjCmd(clientData, interp, objc, objv)
04462     ClientData clientData;
04463     Tcl_Interp *interp;
04464     int objc;
04465     Tcl_Obj *CONST objv[];
04466 #else /* TCL_MAJOR_VERSION < 8 */
04467 static int
04468 ip_rbTkWaitCommand(clientData, interp, objc, objv)
04469     ClientData clientData;
04470     Tcl_Interp *interp;
04471     int objc;
04472     char *objv[];
04473 #endif
04474 {
04475     Tk_Window tkwin = (Tk_Window) clientData;
04476     Tk_Window window;
04477     int done, index;
04478     static CONST char *optionStrings[] = { "variable", "visibility", "window",
04479                                            (char *) NULL };
04480     enum options { TKWAIT_VARIABLE, TKWAIT_VISIBILITY, TKWAIT_WINDOW };
04481     char *nameString;
04482     int ret, dummy;
04483     int thr_crit_bup;
04484 
04485     DUMP1("Ruby's 'tkwait' is called");
04486     if (interp == (Tcl_Interp*)NULL) {
04487         rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
04488                                              "IP is deleted");
04489         return TCL_ERROR;
04490     }
04491 
04492 #if 0
04493     if (!rb_thread_alone()
04494         && eventloop_thread != Qnil
04495         && eventloop_thread != rb_thread_current()) {
04496 #if TCL_MAJOR_VERSION >= 8
04497         DUMP1("call ip_rb_threadTkWaitObjCmd");
04498         return ip_rb_threadTkWaitObjCmd((ClientData)tkwin, interp, objc, objv);
04499 #else /* TCL_MAJOR_VERSION < 8 */
04500         DUMP1("call ip_rb_threadTkWaitCommand");
04501         return ip_rb_threadTkWwaitCommand((ClientData)tkwin, interp, objc, objv);
04502 #endif
04503     }
04504 #endif
04505 
04506     Tcl_Preserve(interp);
04507     Tcl_ResetResult(interp);
04508 
04509     if (objc != 3) {
04510 #ifdef Tcl_WrongNumArgs
04511         Tcl_WrongNumArgs(interp, 1, objv, "variable|visibility|window name");
04512 #else
04513         thr_crit_bup = rb_thread_critical;
04514         rb_thread_critical = Qtrue;
04515 
04516 #if TCL_MAJOR_VERSION >= 8
04517         Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
04518                          Tcl_GetStringFromObj(objv[0], &dummy),
04519                          " variable|visibility|window name\"",
04520                          (char *) NULL);
04521 #else /* TCL_MAJOR_VERSION < 8 */
04522         Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
04523                          objv[0], " variable|visibility|window name\"",
04524                          (char *) NULL);
04525 #endif
04526 
04527         rb_thread_critical = thr_crit_bup;
04528 #endif
04529 
04530         Tcl_Release(interp);
04531         return TCL_ERROR;
04532     }
04533 
04534 #if TCL_MAJOR_VERSION >= 8
04535     thr_crit_bup = rb_thread_critical;
04536     rb_thread_critical = Qtrue;
04537 
04538     /*
04539     if (Tcl_GetIndexFromObj(interp, objv[1],
04540                             (CONST84 char **)optionStrings,
04541                             "option", 0, &index) != TCL_OK) {
04542         return TCL_ERROR;
04543     }
04544     */
04545     ret = Tcl_GetIndexFromObj(interp, objv[1],
04546                               (CONST84 char **)optionStrings,
04547                               "option", 0, &index);
04548 
04549     rb_thread_critical = thr_crit_bup;
04550 
04551     if (ret != TCL_OK) {
04552         Tcl_Release(interp);
04553         return TCL_ERROR;
04554     }
04555 #else /* TCL_MAJOR_VERSION < 8 */
04556     {
04557         int c = objv[1][0];
04558         size_t length = strlen(objv[1]);
04559 
04560         if ((c == 'v') && (strncmp(objv[1], "variable", length) == 0)
04561             && (length >= 2)) {
04562             index = TKWAIT_VARIABLE;
04563         } else if ((c == 'v') && (strncmp(objv[1], "visibility", length) == 0)
04564                    && (length >= 2)) {
04565             index = TKWAIT_VISIBILITY;
04566         } else if ((c == 'w') && (strncmp(objv[1], "window", length) == 0)) {
04567             index = TKWAIT_WINDOW;
04568         } else {
04569             Tcl_AppendResult(interp, "bad option \"", objv[1],
04570                              "\": must be variable, visibility, or window",
04571                              (char *) NULL);
04572             Tcl_Release(interp);
04573             return TCL_ERROR;
04574         }
04575     }
04576 #endif
04577 
04578     thr_crit_bup = rb_thread_critical;
04579     rb_thread_critical = Qtrue;
04580 
04581 #if TCL_MAJOR_VERSION >= 8
04582     Tcl_IncrRefCount(objv[2]);
04583     /* nameString = Tcl_GetString(objv[2]); */
04584     nameString = Tcl_GetStringFromObj(objv[2], &dummy);
04585 #else /* TCL_MAJOR_VERSION < 8 */
04586     nameString = objv[2];
04587 #endif
04588 
04589     rb_thread_critical = thr_crit_bup;
04590 
04591     switch ((enum options) index) {
04592     case TKWAIT_VARIABLE:
04593         thr_crit_bup = rb_thread_critical;
04594         rb_thread_critical = Qtrue;
04595         /*
04596         if (Tcl_TraceVar(interp, nameString,
04597                          TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
04598                          WaitVariableProc, (ClientData) &done) != TCL_OK) {
04599             return TCL_ERROR;
04600         }
04601         */
04602         ret = Tcl_TraceVar(interp, nameString,
04603                            TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
04604                            WaitVariableProc, (ClientData) &done);
04605 
04606         rb_thread_critical = thr_crit_bup;
04607 
04608         if (ret != TCL_OK) {
04609 #if TCL_MAJOR_VERSION >= 8
04610             Tcl_DecrRefCount(objv[2]);
04611 #endif
04612             Tcl_Release(interp);
04613             return TCL_ERROR;
04614         }
04615 
04616         done = 0;
04617         /* lib_eventloop_core(check_rootwidget_flag, 0, &done); */
04618         lib_eventloop_launcher(check_rootwidget_flag, 0, &done, interp);
04619 
04620         thr_crit_bup = rb_thread_critical;
04621         rb_thread_critical = Qtrue;
04622 
04623         Tcl_UntraceVar(interp, nameString,
04624                        TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
04625                        WaitVariableProc, (ClientData) &done);
04626 
04627 #if TCL_MAJOR_VERSION >= 8
04628         Tcl_DecrRefCount(objv[2]);
04629 #endif
04630 
04631         rb_thread_critical = thr_crit_bup;
04632 
04633         /* exception check */
04634         if (!NIL_P(rbtk_pending_exception)) {
04635             Tcl_Release(interp);
04636 
04637             /*
04638             if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)) {
04639             */
04640             if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)
04641                 || rb_obj_is_kind_of(rbtk_pending_exception, rb_eInterrupt)) {
04642                 return TCL_RETURN;
04643             } else{
04644                 return TCL_ERROR;
04645             }
04646         }
04647 
04648         /* trap check */
04649 #ifdef RUBY_VM
04650         if (rb_thread_check_trap_pending()) {
04651 #else
04652         if (rb_trap_pending) {
04653 #endif
04654             Tcl_Release(interp);
04655 
04656             return TCL_RETURN;
04657         }
04658 
04659         break;
04660 
04661     case TKWAIT_VISIBILITY:
04662         thr_crit_bup = rb_thread_critical;
04663         rb_thread_critical = Qtrue;
04664 
04665         /* This function works on the Tk eventloop thread only. */
04666         if (!tk_stubs_init_p() || Tk_MainWindow(interp) == (Tk_Window)NULL) {
04667             window = NULL;
04668         } else {
04669             window = Tk_NameToWindow(interp, nameString, tkwin);
04670         }
04671 
04672         if (window == NULL) {
04673             Tcl_AppendResult(interp, ": tkwait: ",
04674                              "no main-window (not Tk application?)",
04675                              (char*)NULL);
04676             rb_thread_critical = thr_crit_bup;
04677 #if TCL_MAJOR_VERSION >= 8
04678             Tcl_DecrRefCount(objv[2]);
04679 #endif
04680             Tcl_Release(interp);
04681             return TCL_ERROR;
04682         }
04683 
04684         Tk_CreateEventHandler(window,
04685                               VisibilityChangeMask|StructureNotifyMask,
04686                               WaitVisibilityProc, (ClientData) &done);
04687 
04688         rb_thread_critical = thr_crit_bup;
04689 
04690         done = 0;
04691         /* lib_eventloop_core(check_rootwidget_flag, 0, &done); */
04692         lib_eventloop_launcher(check_rootwidget_flag, 0, &done, interp);
04693 
04694         /* exception check */
04695         if (!NIL_P(rbtk_pending_exception)) {
04696 #if TCL_MAJOR_VERSION >= 8
04697             Tcl_DecrRefCount(objv[2]);
04698 #endif
04699             Tcl_Release(interp);
04700 
04701             /*
04702             if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)) {
04703             */
04704             if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)
04705                 || rb_obj_is_kind_of(rbtk_pending_exception, rb_eInterrupt)) {
04706                 return TCL_RETURN;
04707             } else{
04708                 return TCL_ERROR;
04709             }
04710         }
04711 
04712         /* trap check */
04713 #ifdef RUBY_VM
04714         if (rb_thread_check_trap_pending()) {
04715 #else
04716         if (rb_trap_pending) {
04717 #endif
04718 #if TCL_MAJOR_VERSION >= 8
04719             Tcl_DecrRefCount(objv[2]);
04720 #endif
04721             Tcl_Release(interp);
04722 
04723             return TCL_RETURN;
04724         }
04725 
04726         if (done != 1) {
04727             /*
04728              * Note that we do not delete the event handler because it
04729              * was deleted automatically when the window was destroyed.
04730              */
04731             thr_crit_bup = rb_thread_critical;
04732             rb_thread_critical = Qtrue;
04733 
04734             Tcl_ResetResult(interp);
04735             Tcl_AppendResult(interp, "window \"", nameString,
04736                              "\" was deleted before its visibility changed",
04737                              (char *) NULL);
04738 
04739             rb_thread_critical = thr_crit_bup;
04740 
04741 #if TCL_MAJOR_VERSION >= 8
04742             Tcl_DecrRefCount(objv[2]);
04743 #endif
04744             Tcl_Release(interp);
04745             return TCL_ERROR;
04746         }
04747 
04748         thr_crit_bup = rb_thread_critical;
04749         rb_thread_critical = Qtrue;
04750 
04751 #if TCL_MAJOR_VERSION >= 8
04752         Tcl_DecrRefCount(objv[2]);
04753 #endif
04754 
04755         Tk_DeleteEventHandler(window,
04756                               VisibilityChangeMask|StructureNotifyMask,
04757                               WaitVisibilityProc, (ClientData) &done);
04758 
04759         rb_thread_critical = thr_crit_bup;
04760 
04761         break;
04762 
04763     case TKWAIT_WINDOW:
04764         thr_crit_bup = rb_thread_critical;
04765         rb_thread_critical = Qtrue;
04766 
04767         /* This function works on the Tk eventloop thread only. */
04768         if (!tk_stubs_init_p() || Tk_MainWindow(interp) == (Tk_Window)NULL) {
04769             window = NULL;
04770         } else {
04771             window = Tk_NameToWindow(interp, nameString, tkwin);
04772         }
04773 
04774 #if TCL_MAJOR_VERSION >= 8
04775         Tcl_DecrRefCount(objv[2]);
04776 #endif
04777 
04778         if (window == NULL) {
04779             Tcl_AppendResult(interp, ": tkwait: ",
04780                              "no main-window (not Tk application?)",
04781                              (char*)NULL);
04782             rb_thread_critical = thr_crit_bup;
04783             Tcl_Release(interp);
04784             return TCL_ERROR;
04785         }
04786 
04787         Tk_CreateEventHandler(window, StructureNotifyMask,
04788                               WaitWindowProc, (ClientData) &done);
04789 
04790         rb_thread_critical = thr_crit_bup;
04791 
04792         done = 0;
04793         /* lib_eventloop_core(check_rootwidget_flag, 0, &done); */
04794         lib_eventloop_launcher(check_rootwidget_flag, 0, &done, interp);
04795 
04796         /* exception check */
04797         if (!NIL_P(rbtk_pending_exception)) {
04798             Tcl_Release(interp);
04799 
04800             /*
04801             if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)) {
04802             */
04803             if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)
04804                 || rb_obj_is_kind_of(rbtk_pending_exception, rb_eInterrupt)) {
04805                 return TCL_RETURN;
04806             } else{
04807                 return TCL_ERROR;
04808             }
04809         }
04810 
04811         /* trap check */
04812 #ifdef RUBY_VM
04813         if (rb_thread_check_trap_pending()) {
04814 #else
04815         if (rb_trap_pending) {
04816 #endif
04817             Tcl_Release(interp);
04818 
04819             return TCL_RETURN;
04820         }
04821 
04822         /*
04823          * Note:  there's no need to delete the event handler.  It was
04824          * deleted automatically when the window was destroyed.
04825          */
04826         break;
04827     }
04828 
04829     /*
04830      * Clear out the interpreter's result, since it may have been set
04831      * by event handlers.
04832      */
04833 
04834     Tcl_ResetResult(interp);
04835     Tcl_Release(interp);
04836     return TCL_OK;
04837 }
04838 
04839 /****************************/
04840 /* vwait/tkwait with thread */
04841 /****************************/
04842 struct th_vwait_param {
04843     VALUE thread;
04844     int   done;
04845 };
04846 
04847 #if TCL_MAJOR_VERSION >= 8
04848 static char *rb_threadVwaitProc _((ClientData, Tcl_Interp *,
04849                                    CONST84 char *,CONST84 char *, int));
04850 static char *
04851 rb_threadVwaitProc(clientData, interp, name1, name2, flags)
04852     ClientData clientData;      /* Pointer to integer to set to 1. */
04853     Tcl_Interp *interp;         /* Interpreter containing variable. */
04854     CONST84 char *name1;        /* Name of variable. */
04855     CONST84 char *name2;        /* Second part of variable name. */
04856     int flags;                  /* Information about what happened. */
04857 #else /* TCL_MAJOR_VERSION < 8 */
04858 static char *rb_threadVwaitProc _((ClientData, Tcl_Interp *,
04859                                    char *, char *, int));
04860 static char *
04861 rb_threadVwaitProc(clientData, interp, name1, name2, flags)
04862     ClientData clientData;      /* Pointer to integer to set to 1. */
04863     Tcl_Interp *interp;         /* Interpreter containing variable. */
04864     char *name1;                /* Name of variable. */
04865     char *name2;                /* Second part of variable name. */
04866     int flags;                  /* Information about what happened. */
04867 #endif
04868 {
04869     struct th_vwait_param *param = (struct th_vwait_param *) clientData;
04870 
04871     if (flags & (TCL_INTERP_DESTROYED | TCL_TRACE_DESTROYED)) {
04872         param->done = -1;
04873     } else {
04874         param->done = 1;
04875     }
04876     if (param->done != 0) rb_thread_wakeup(param->thread);
04877 
04878     return (char *)NULL;
04879 }
04880 
04881 #define TKWAIT_MODE_VISIBILITY 1
04882 #define TKWAIT_MODE_DESTROY    2
04883 
04884 static void rb_threadWaitVisibilityProc _((ClientData, XEvent *));
04885 static void
04886 rb_threadWaitVisibilityProc(clientData, eventPtr)
04887     ClientData clientData;      /* Pointer to integer to set to 1. */
04888     XEvent *eventPtr;           /* Information about event (not used). */
04889 {
04890     struct th_vwait_param *param = (struct th_vwait_param *) clientData;
04891 
04892     if (eventPtr->type == VisibilityNotify) {
04893         param->done = TKWAIT_MODE_VISIBILITY;
04894     }
04895     if (eventPtr->type == DestroyNotify) {
04896         param->done = TKWAIT_MODE_DESTROY;
04897     }
04898     if (param->done != 0) rb_thread_wakeup(param->thread);
04899 }
04900 
04901 static void rb_threadWaitWindowProc _((ClientData, XEvent *));
04902 static void
04903 rb_threadWaitWindowProc(clientData, eventPtr)
04904     ClientData clientData;      /* Pointer to integer to set to 1. */
04905     XEvent *eventPtr;           /* Information about event. */
04906 {
04907     struct th_vwait_param *param = (struct th_vwait_param *) clientData;
04908 
04909     if (eventPtr->type == DestroyNotify) {
04910         param->done = TKWAIT_MODE_DESTROY;
04911     }
04912     if (param->done != 0) rb_thread_wakeup(param->thread);
04913 }
04914 
04915 #if TCL_MAJOR_VERSION >= 8
04916 static int
04917 ip_rb_threadVwaitObjCmd(clientData, interp, objc, objv)
04918     ClientData clientData;
04919     Tcl_Interp *interp;
04920     int objc;
04921     Tcl_Obj *CONST objv[];
04922 #else /* TCL_MAJOR_VERSION < 8 */
04923 static int
04924 ip_rb_threadVwaitCommand(clientData, interp, objc, objv)
04925     ClientData clientData; /* Not used */
04926     Tcl_Interp *interp;
04927     int objc;
04928     char *objv[];
04929 #endif
04930 {
04931     struct th_vwait_param *param;
04932     char *nameString;
04933     int ret, dummy;
04934     int thr_crit_bup;
04935     volatile VALUE current_thread = rb_thread_current();
04936     struct timeval t;
04937 
04938     DUMP1("Ruby's 'thread_vwait' is called");
04939     if (interp == (Tcl_Interp*)NULL) {
04940         rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
04941                                              "IP is deleted");
04942         return TCL_ERROR;
04943     }
04944 
04945     if (rb_thread_alone() || eventloop_thread == current_thread) {
04946 #if TCL_MAJOR_VERSION >= 8
04947         DUMP1("call ip_rbVwaitObjCmd");
04948         return ip_rbVwaitObjCmd(clientData, interp, objc, objv);
04949 #else /* TCL_MAJOR_VERSION < 8 */
04950         DUMP1("call ip_rbVwaitCommand");
04951         return ip_rbVwaitCommand(clientData, interp, objc, objv);
04952 #endif
04953     }
04954 
04955     Tcl_Preserve(interp);
04956     Tcl_ResetResult(interp);
04957 
04958     if (objc != 2) {
04959 #ifdef Tcl_WrongNumArgs
04960         Tcl_WrongNumArgs(interp, 1, objv, "name");
04961 #else
04962         thr_crit_bup = rb_thread_critical;
04963         rb_thread_critical = Qtrue;
04964 
04965 #if TCL_MAJOR_VERSION >= 8
04966         /* nameString = Tcl_GetString(objv[0]); */
04967         nameString = Tcl_GetStringFromObj(objv[0], &dummy);
04968 #else /* TCL_MAJOR_VERSION < 8 */
04969         nameString = objv[0];
04970 #endif
04971         Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
04972                          nameString, " name\"", (char *) NULL);
04973 
04974         rb_thread_critical = thr_crit_bup;
04975 #endif
04976 
04977         Tcl_Release(interp);
04978         return TCL_ERROR;
04979     }
04980 
04981 #if TCL_MAJOR_VERSION >= 8
04982     Tcl_IncrRefCount(objv[1]);
04983     /* nameString = Tcl_GetString(objv[1]); */
04984     nameString = Tcl_GetStringFromObj(objv[1], &dummy);
04985 #else /* TCL_MAJOR_VERSION < 8 */
04986     nameString = objv[1];
04987 #endif
04988     thr_crit_bup = rb_thread_critical;
04989     rb_thread_critical = Qtrue;
04990 
04991     /* param = (struct th_vwait_param *)Tcl_Alloc(sizeof(struct th_vwait_param)); */
04992     param = RbTk_ALLOC_N(struct th_vwait_param, 1);
04993 #if 1 /* use Tcl_Preserve/Release */
04994     Tcl_Preserve((ClientData)param);
04995 #endif
04996     param->thread = current_thread;
04997     param->done = 0;
04998 
04999     /*
05000     if (Tcl_TraceVar(interp, nameString,
05001                      TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
05002                      rb_threadVwaitProc, (ClientData) param) != TCL_OK) {
05003         return TCL_ERROR;
05004     }
05005     */
05006     ret = Tcl_TraceVar(interp, nameString,
05007                        TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
05008                        rb_threadVwaitProc, (ClientData) param);
05009 
05010     rb_thread_critical = thr_crit_bup;
05011 
05012     if (ret != TCL_OK) {
05013 #if 0 /* use Tcl_EventuallyFree */
05014         Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
05015 #else
05016 #if 1 /* use Tcl_Preserve/Release */
05017         Tcl_Release((ClientData)param);
05018 #else
05019         /* Tcl_Free((char *)param); */
05020         ckfree((char *)param);
05021 #endif
05022 #endif
05023 
05024 #if TCL_MAJOR_VERSION >= 8
05025         Tcl_DecrRefCount(objv[1]);
05026 #endif
05027         Tcl_Release(interp);
05028         return TCL_ERROR;
05029     }
05030 
05031     t.tv_sec  = 0;
05032     t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
05033 
05034     while(!param->done) {
05035       /* rb_thread_stop(); */
05036       /* rb_thread_sleep_forever(); */
05037       rb_thread_wait_for(t);
05038       if (NIL_P(eventloop_thread)) {
05039         break;
05040       }
05041     }
05042 
05043     thr_crit_bup = rb_thread_critical;
05044     rb_thread_critical = Qtrue;
05045 
05046     if (param->done > 0) {
05047         Tcl_UntraceVar(interp, nameString,
05048                        TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
05049                        rb_threadVwaitProc, (ClientData) param);
05050     }
05051 
05052 #if 0 /* use Tcl_EventuallyFree */
05053     Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
05054 #else
05055 #if 1 /* use Tcl_Preserve/Release */
05056     Tcl_Release((ClientData)param);
05057 #else
05058     /* Tcl_Free((char *)param); */
05059     ckfree((char *)param);
05060 #endif
05061 #endif
05062 
05063     rb_thread_critical = thr_crit_bup;
05064 
05065 #if TCL_MAJOR_VERSION >= 8
05066     Tcl_DecrRefCount(objv[1]);
05067 #endif
05068     Tcl_Release(interp);
05069     return TCL_OK;
05070 }
05071 
05072 #if TCL_MAJOR_VERSION >= 8
05073 static int
05074 ip_rb_threadTkWaitObjCmd(clientData, interp, objc, objv)
05075     ClientData clientData;
05076     Tcl_Interp *interp;
05077     int objc;
05078     Tcl_Obj *CONST objv[];
05079 #else /* TCL_MAJOR_VERSION < 8 */
05080 static int
05081 ip_rb_threadTkWaitCommand(clientData, interp, objc, objv)
05082     ClientData clientData;
05083     Tcl_Interp *interp;
05084     int objc;
05085     char *objv[];
05086 #endif
05087 {
05088     struct th_vwait_param *param;
05089     Tk_Window tkwin = (Tk_Window) clientData;
05090     Tk_Window window;
05091     int index;
05092     static CONST char *optionStrings[] = { "variable", "visibility", "window",
05093                                            (char *) NULL };
05094     enum options { TKWAIT_VARIABLE, TKWAIT_VISIBILITY, TKWAIT_WINDOW };
05095     char *nameString;
05096     int ret, dummy;
05097     int thr_crit_bup;
05098     volatile VALUE current_thread = rb_thread_current();
05099     struct timeval t;
05100 
05101     DUMP1("Ruby's 'thread_tkwait' is called");
05102     if (interp == (Tcl_Interp*)NULL) {
05103         rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
05104                                              "IP is deleted");
05105         return TCL_ERROR;
05106     }
05107 
05108     if (rb_thread_alone() || eventloop_thread == current_thread) {
05109 #if TCL_MAJOR_VERSION >= 8
05110         DUMP1("call ip_rbTkWaitObjCmd");
05111         DUMP2("eventloop_thread %lx", eventloop_thread);
05112         DUMP2("current_thread %lx", current_thread);
05113         return ip_rbTkWaitObjCmd(clientData, interp, objc, objv);
05114 #else /* TCL_MAJOR_VERSION < 8 */
05115         DUMP1("call rb_VwaitCommand");
05116         return ip_rbTkWaitCommand(clientData, interp, objc, objv);
05117 #endif
05118     }
05119 
05120     Tcl_Preserve(interp);
05121     Tcl_Preserve(tkwin);
05122 
05123     Tcl_ResetResult(interp);
05124 
05125     if (objc != 3) {
05126 #ifdef Tcl_WrongNumArgs
05127         Tcl_WrongNumArgs(interp, 1, objv, "variable|visibility|window name");
05128 #else
05129         thr_crit_bup = rb_thread_critical;
05130         rb_thread_critical = Qtrue;
05131 
05132 #if TCL_MAJOR_VERSION >= 8
05133         Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
05134                          Tcl_GetStringFromObj(objv[0], &dummy),
05135                          " variable|visibility|window name\"",
05136                          (char *) NULL);
05137 #else /* TCL_MAJOR_VERSION < 8 */
05138         Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
05139                          objv[0], " variable|visibility|window name\"",
05140                          (char *) NULL);
05141 #endif
05142 
05143         rb_thread_critical = thr_crit_bup;
05144 #endif
05145 
05146         Tcl_Release(tkwin);
05147         Tcl_Release(interp);
05148         return TCL_ERROR;
05149     }
05150 
05151 #if TCL_MAJOR_VERSION >= 8
05152     thr_crit_bup = rb_thread_critical;
05153     rb_thread_critical = Qtrue;
05154     /*
05155     if (Tcl_GetIndexFromObj(interp, objv[1],
05156                             (CONST84 char **)optionStrings,
05157                             "option", 0, &index) != TCL_OK) {
05158         return TCL_ERROR;
05159     }
05160     */
05161     ret = Tcl_GetIndexFromObj(interp, objv[1],
05162                               (CONST84 char **)optionStrings,
05163                               "option", 0, &index);
05164 
05165     rb_thread_critical = thr_crit_bup;
05166 
05167     if (ret != TCL_OK) {
05168         Tcl_Release(tkwin);
05169         Tcl_Release(interp);
05170         return TCL_ERROR;
05171     }
05172 #else /* TCL_MAJOR_VERSION < 8 */
05173     {
05174         int c = objv[1][0];
05175         size_t length = strlen(objv[1]);
05176 
05177         if ((c == 'v') && (strncmp(objv[1], "variable", length) == 0)
05178             && (length >= 2)) {
05179             index = TKWAIT_VARIABLE;
05180         } else if ((c == 'v') && (strncmp(objv[1], "visibility", length) == 0)
05181                    && (length >= 2)) {
05182             index = TKWAIT_VISIBILITY;
05183         } else if ((c == 'w') && (strncmp(objv[1], "window", length) == 0)) {
05184             index = TKWAIT_WINDOW;
05185         } else {
05186             Tcl_AppendResult(interp, "bad option \"", objv[1],
05187                              "\": must be variable, visibility, or window",
05188                              (char *) NULL);
05189             Tcl_Release(tkwin);
05190             Tcl_Release(interp);
05191             return TCL_ERROR;
05192         }
05193     }
05194 #endif
05195 
05196     thr_crit_bup = rb_thread_critical;
05197     rb_thread_critical = Qtrue;
05198 
05199 #if TCL_MAJOR_VERSION >= 8
05200     Tcl_IncrRefCount(objv[2]);
05201     /* nameString = Tcl_GetString(objv[2]); */
05202     nameString = Tcl_GetStringFromObj(objv[2], &dummy);
05203 #else /* TCL_MAJOR_VERSION < 8 */
05204     nameString = objv[2];
05205 #endif
05206 
05207     /* param = (struct th_vwait_param *)Tcl_Alloc(sizeof(struct th_vwait_param)); */
05208     param = RbTk_ALLOC_N(struct th_vwait_param, 1);
05209 #if 1 /* use Tcl_Preserve/Release */
05210     Tcl_Preserve((ClientData)param);
05211 #endif
05212     param->thread = current_thread;
05213     param->done = 0;
05214 
05215     rb_thread_critical = thr_crit_bup;
05216 
05217     switch ((enum options) index) {
05218     case TKWAIT_VARIABLE:
05219         thr_crit_bup = rb_thread_critical;
05220         rb_thread_critical = Qtrue;
05221         /*
05222         if (Tcl_TraceVar(interp, nameString,
05223                          TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
05224                          rb_threadVwaitProc, (ClientData) param) != TCL_OK) {
05225             return TCL_ERROR;
05226         }
05227         */
05228         ret = Tcl_TraceVar(interp, nameString,
05229                          TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
05230                          rb_threadVwaitProc, (ClientData) param);
05231 
05232         rb_thread_critical = thr_crit_bup;
05233 
05234         if (ret != TCL_OK) {
05235 #if 0 /* use Tcl_EventuallyFree */
05236             Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
05237 #else
05238 #if 1 /* use Tcl_Preserve/Release */
05239             Tcl_Release(param);
05240 #else
05241             /* Tcl_Free((char *)param); */
05242             ckfree((char *)param);
05243 #endif
05244 #endif
05245 
05246 #if TCL_MAJOR_VERSION >= 8
05247             Tcl_DecrRefCount(objv[2]);
05248 #endif
05249 
05250             Tcl_Release(tkwin);
05251             Tcl_Release(interp);
05252             return TCL_ERROR;
05253         }
05254 
05255         t.tv_sec  = 0;
05256         t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
05257 
05258         while(!param->done) {
05259           /* rb_thread_stop(); */
05260           /* rb_thread_sleep_forever(); */
05261           rb_thread_wait_for(t);
05262           if (NIL_P(eventloop_thread)) {
05263             break;
05264           }
05265         }
05266 
05267         thr_crit_bup = rb_thread_critical;
05268         rb_thread_critical = Qtrue;
05269 
05270         if (param->done > 0) {
05271             Tcl_UntraceVar(interp, nameString,
05272                            TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
05273                            rb_threadVwaitProc, (ClientData) param);
05274         }
05275 
05276 #if TCL_MAJOR_VERSION >= 8
05277         Tcl_DecrRefCount(objv[2]);
05278 #endif
05279 
05280         rb_thread_critical = thr_crit_bup;
05281 
05282         break;
05283 
05284     case TKWAIT_VISIBILITY:
05285         thr_crit_bup = rb_thread_critical;
05286         rb_thread_critical = Qtrue;
05287 
05288 #if 0 /* variable 'tkwin' must keep the token of MainWindow */
05289         if (!tk_stubs_init_p() || Tk_MainWindow(interp) == (Tk_Window)NULL) {
05290             window = NULL;
05291         } else {
05292             window = Tk_NameToWindow(interp, nameString, tkwin);
05293         }
05294 #else
05295         if (!tk_stubs_init_p() || tkwin == (Tk_Window)NULL) {
05296             window = NULL;
05297         } else {
05298             /* Tk_NameToWindow() returns right token on non-eventloop thread */
05299             Tcl_CmdInfo info;
05300             if (Tcl_GetCommandInfo(interp, ".", &info)) { /* check root */
05301                 window = Tk_NameToWindow(interp, nameString, tkwin);
05302             } else {
05303                 window = NULL;
05304             }
05305         }
05306 #endif
05307 
05308         if (window == NULL) {
05309             Tcl_AppendResult(interp, ": thread_tkwait: ",
05310                              "no main-window (not Tk application?)",
05311                              (char*)NULL);
05312 
05313             rb_thread_critical = thr_crit_bup;
05314 
05315 #if 0 /* use Tcl_EventuallyFree */
05316             Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
05317 #else
05318 #if 1 /* use Tcl_Preserve/Release */
05319             Tcl_Release(param);
05320 #else
05321             /* Tcl_Free((char *)param); */
05322             ckfree((char *)param);
05323 #endif
05324 #endif
05325 
05326 #if TCL_MAJOR_VERSION >= 8
05327             Tcl_DecrRefCount(objv[2]);
05328 #endif
05329             Tcl_Release(tkwin);
05330             Tcl_Release(interp);
05331             return TCL_ERROR;
05332         }
05333         Tcl_Preserve(window);
05334 
05335         Tk_CreateEventHandler(window,
05336                               VisibilityChangeMask|StructureNotifyMask,
05337                               rb_threadWaitVisibilityProc, (ClientData) param);
05338 
05339         rb_thread_critical = thr_crit_bup;
05340 
05341         t.tv_sec  = 0;
05342         t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
05343 
05344         while(param->done != TKWAIT_MODE_VISIBILITY) {
05345           if (param->done == TKWAIT_MODE_DESTROY) break;
05346           /* rb_thread_stop(); */
05347           /* rb_thread_sleep_forever(); */
05348           rb_thread_wait_for(t);
05349           if (NIL_P(eventloop_thread)) {
05350             break;
05351           }
05352         }
05353 
05354         thr_crit_bup = rb_thread_critical;
05355         rb_thread_critical = Qtrue;
05356 
05357         /* when a window is destroyed, no need to call Tk_DeleteEventHandler */
05358         if (param->done != TKWAIT_MODE_DESTROY) {
05359             Tk_DeleteEventHandler(window,
05360                                   VisibilityChangeMask|StructureNotifyMask,
05361                                   rb_threadWaitVisibilityProc,
05362                                   (ClientData) param);
05363         }
05364 
05365         if (param->done != 1) {
05366             Tcl_ResetResult(interp);
05367             Tcl_AppendResult(interp, "window \"", nameString,
05368                              "\" was deleted before its visibility changed",
05369                              (char *) NULL);
05370 
05371             rb_thread_critical = thr_crit_bup;
05372 
05373             Tcl_Release(window);
05374 
05375 #if 0 /* use Tcl_EventuallyFree */
05376             Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
05377 #else
05378 #if 1 /* use Tcl_Preserve/Release */
05379             Tcl_Release(param);
05380 #else
05381             /* Tcl_Free((char *)param); */
05382             ckfree((char *)param);
05383 #endif
05384 #endif
05385 
05386 #if TCL_MAJOR_VERSION >= 8
05387             Tcl_DecrRefCount(objv[2]);
05388 #endif
05389 
05390             Tcl_Release(tkwin);
05391             Tcl_Release(interp);
05392             return TCL_ERROR;
05393         }
05394 
05395         Tcl_Release(window);
05396 
05397 #if TCL_MAJOR_VERSION >= 8
05398         Tcl_DecrRefCount(objv[2]);
05399 #endif
05400 
05401         rb_thread_critical = thr_crit_bup;
05402 
05403         break;
05404 
05405     case TKWAIT_WINDOW:
05406         thr_crit_bup = rb_thread_critical;
05407         rb_thread_critical = Qtrue;
05408 
05409 #if 0 /* variable 'tkwin' must keep the token of MainWindow */
05410         if (!tk_stubs_init_p() || Tk_MainWindow(interp) == (Tk_Window)NULL) {
05411             window = NULL;
05412         } else {
05413             window = Tk_NameToWindow(interp, nameString, tkwin);
05414         }
05415 #else
05416         if (!tk_stubs_init_p() || tkwin == (Tk_Window)NULL) {
05417             window = NULL;
05418         } else {
05419             /* Tk_NameToWindow() returns right token on non-eventloop thread */
05420             Tcl_CmdInfo info;
05421             if (Tcl_GetCommandInfo(interp, ".", &info)) { /* check root */
05422                 window = Tk_NameToWindow(interp, nameString, tkwin);
05423             } else {
05424                 window = NULL;
05425             }
05426         }
05427 #endif
05428 
05429 #if TCL_MAJOR_VERSION >= 8
05430         Tcl_DecrRefCount(objv[2]);
05431 #endif
05432 
05433         if (window == NULL) {
05434             Tcl_AppendResult(interp, ": thread_tkwait: ",
05435                              "no main-window (not Tk application?)",
05436                              (char*)NULL);
05437 
05438             rb_thread_critical = thr_crit_bup;
05439 
05440 #if 0 /* use Tcl_EventuallyFree */
05441             Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
05442 #else
05443 #if 1 /* use Tcl_Preserve/Release */
05444             Tcl_Release(param);
05445 #else
05446             /* Tcl_Free((char *)param); */
05447             ckfree((char *)param);
05448 #endif
05449 #endif
05450 
05451             Tcl_Release(tkwin);
05452             Tcl_Release(interp);
05453             return TCL_ERROR;
05454         }
05455 
05456         Tcl_Preserve(window);
05457 
05458         Tk_CreateEventHandler(window, StructureNotifyMask,
05459                               rb_threadWaitWindowProc, (ClientData) param);
05460 
05461         rb_thread_critical = thr_crit_bup;
05462 
05463         t.tv_sec  = 0;
05464         t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
05465 
05466         while(param->done != TKWAIT_MODE_DESTROY) {
05467           /* rb_thread_stop(); */
05468           /* rb_thread_sleep_forever(); */
05469           rb_thread_wait_for(t);
05470           if (NIL_P(eventloop_thread)) {
05471             break;
05472           }
05473         }
05474 
05475         Tcl_Release(window);
05476 
05477         /* when a window is destroyed, no need to call Tk_DeleteEventHandler
05478         thr_crit_bup = rb_thread_critical;
05479         rb_thread_critical = Qtrue;
05480 
05481         Tk_DeleteEventHandler(window, StructureNotifyMask,
05482                               rb_threadWaitWindowProc, (ClientData) param);
05483 
05484         rb_thread_critical = thr_crit_bup;
05485         */
05486 
05487         break;
05488     } /* end of 'switch' statement */
05489 
05490 #if 0 /* use Tcl_EventuallyFree */
05491     Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
05492 #else
05493 #if 1 /* use Tcl_Preserve/Release */
05494     Tcl_Release((ClientData)param);
05495 #else
05496     /* Tcl_Free((char *)param); */
05497     ckfree((char *)param);
05498 #endif
05499 #endif
05500 
05501     /*
05502      * Clear out the interpreter's result, since it may have been set
05503      * by event handlers.
05504      */
05505 
05506     Tcl_ResetResult(interp);
05507 
05508     Tcl_Release(tkwin);
05509     Tcl_Release(interp);
05510     return TCL_OK;
05511 }
05512 
05513 static VALUE
05514 ip_thread_vwait(self, var)
05515     VALUE self;
05516     VALUE var;
05517 {
05518     VALUE argv[2];
05519     volatile VALUE cmd_str = rb_str_new2("thread_vwait");
05520 
05521     argv[0] = cmd_str;
05522     argv[1] = var;
05523 
05524     return ip_invoke_with_position(2, argv, self, TCL_QUEUE_TAIL);
05525 }
05526 
05527 static VALUE
05528 ip_thread_tkwait(self, mode, target)
05529     VALUE self;
05530     VALUE mode;
05531     VALUE target;
05532 {
05533     VALUE argv[3];
05534     volatile VALUE cmd_str = rb_str_new2("thread_tkwait");
05535 
05536     argv[0] = cmd_str;
05537     argv[1] = mode;
05538     argv[2] = target;
05539 
05540     return ip_invoke_with_position(3, argv, self, TCL_QUEUE_TAIL);
05541 }
05542 
05543 
05544 /* delete slave interpreters */
05545 #if TCL_MAJOR_VERSION >= 8
05546 static void
05547 delete_slaves(ip)
05548     Tcl_Interp *ip;
05549 {
05550     int  thr_crit_bup;
05551     Tcl_Interp *slave;
05552     Tcl_Obj *slave_list, *elem;
05553     char *slave_name;
05554     int i, len;
05555 
05556     DUMP1("delete slaves");
05557     thr_crit_bup = rb_thread_critical;
05558     rb_thread_critical = Qtrue;
05559 
05560     if (!Tcl_InterpDeleted(ip) && Tcl_Eval(ip, "interp slaves") == TCL_OK) {
05561         slave_list = Tcl_GetObjResult(ip);
05562         Tcl_IncrRefCount(slave_list);
05563 
05564         if (Tcl_ListObjLength((Tcl_Interp*)NULL, slave_list, &len) == TCL_OK) {
05565             for(i = 0; i < len; i++) {
05566                 Tcl_ListObjIndex((Tcl_Interp*)NULL, slave_list, i, &elem);
05567 
05568                 if (elem == (Tcl_Obj*)NULL) continue;
05569 
05570                 Tcl_IncrRefCount(elem);
05571 
05572                 /* get slave */
05573                 /* slave_name = Tcl_GetString(elem); */
05574                 slave_name = Tcl_GetStringFromObj(elem, (int*)NULL);
05575                 DUMP2("delete slave:'%s'", slave_name);
05576 
05577                 Tcl_DecrRefCount(elem);
05578 
05579                 slave = Tcl_GetSlave(ip, slave_name);
05580                 if (slave == (Tcl_Interp*)NULL) continue;
05581 
05582                 if (!Tcl_InterpDeleted(slave)) {
05583                   /* call ip_finalize */
05584                   ip_finalize(slave);
05585 
05586                   Tcl_DeleteInterp(slave);
05587                   /* Tcl_Release(slave); */
05588                 }
05589             }
05590         }
05591 
05592         Tcl_DecrRefCount(slave_list);
05593     }
05594 
05595     rb_thread_critical = thr_crit_bup;
05596 }
05597 #else /* TCL_MAJOR_VERSION < 8 */
05598 static void
05599 delete_slaves(ip)
05600     Tcl_Interp *ip;
05601 {
05602     int  thr_crit_bup;
05603     Tcl_Interp *slave;
05604     int argc;
05605     char **argv;
05606     char *slave_list;
05607     char *slave_name;
05608     int i, len;
05609 
05610     DUMP1("delete slaves");
05611     thr_crit_bup = rb_thread_critical;
05612     rb_thread_critical = Qtrue;
05613 
05614     if (!Tcl_InterpDeleted(ip) && Tcl_Eval(ip, "interp slaves") == TCL_OK) {
05615         slave_list = ip->result;
05616         if (Tcl_SplitList((Tcl_Interp*)NULL,
05617                           slave_list, &argc, &argv) == TCL_OK) {
05618             for(i = 0; i < argc; i++) {
05619                 slave_name = argv[i];
05620 
05621                 DUMP2("delete slave:'%s'", slave_name);
05622 
05623                 slave = Tcl_GetSlave(ip, slave_name);
05624                 if (slave == (Tcl_Interp*)NULL) continue;
05625 
05626                 if (!Tcl_InterpDeleted(slave)) {
05627                   /* call ip_finalize */
05628                   ip_finalize(slave);
05629 
05630                   Tcl_DeleteInterp(slave);
05631                 }
05632             }
05633         }
05634     }
05635 
05636     rb_thread_critical = thr_crit_bup;
05637 }
05638 #endif
05639 
05640 
05641 /* finalize operation */
05642 static void
05643 #ifdef HAVE_PROTOTYPES
05644 lib_mark_at_exit(VALUE self)
05645 #else
05646 lib_mark_at_exit(self)
05647     VALUE self;
05648 #endif
05649 {
05650     at_exit = 1;
05651 }
05652 
05653 static int
05654 #if TCL_MAJOR_VERSION >= 8
05655 #ifdef HAVE_PROTOTYPES
05656 ip_null_proc(ClientData clientData, Tcl_Interp *interp,
05657              int argc, Tcl_Obj *CONST argv[])
05658 #else
05659 ip_null_proc(clientData, interp, argc, argv)
05660     ClientData clientData;
05661     Tcl_Interp *interp;
05662     int argc;
05663     Tcl_Obj *CONST argv[];
05664 #endif
05665 #else /* TCL_MAJOR_VERSION < 8 */
05666 #ifdef HAVE_PROTOTYPES
05667 ip_null_proc(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
05668 #else
05669 ip_null_proc(clientData, interp, argc, argv)
05670     ClientData clientData;
05671     Tcl_Interp *interp;
05672     int argc;
05673     char *argv[];
05674 #endif
05675 #endif
05676 {
05677     Tcl_ResetResult(interp);
05678     return TCL_OK;
05679 }
05680 
05681 static void
05682 ip_finalize(ip)
05683     Tcl_Interp *ip;
05684 {
05685     Tcl_CmdInfo info;
05686     int  thr_crit_bup;
05687 
05688     VALUE rb_debug_bup, rb_verbose_bup;
05689           /* When ruby is exiting, printing debug messages in some callback
05690              operations from Tcl-IP sometimes cause SEGV. I don't know the
05691              reason. But I got SEGV when calling "rb_io_write(rb_stdout, ...)".
05692              So, in some part of this function, debug mode and verbose mode
05693              are disabled. If you know the reason, please fix it.
05694                            --  Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)  */
05695 
05696     DUMP1("start ip_finalize");
05697 
05698     if (ip == (Tcl_Interp*)NULL) {
05699         DUMP1("ip is NULL");
05700         return;
05701     }
05702 
05703     if (Tcl_InterpDeleted(ip)) {
05704         DUMP2("ip(%p) is already deleted", ip);
05705         return;
05706     }
05707 
05708 #if TCL_NAMESPACE_DEBUG
05709     if (ip_null_namespace(ip)) {
05710         DUMP2("ip(%p) has null namespace", ip);
05711         return;
05712     }
05713 #endif
05714 
05715     thr_crit_bup = rb_thread_critical;
05716     rb_thread_critical = Qtrue;
05717 
05718     rb_debug_bup   = ruby_debug;
05719     rb_verbose_bup = ruby_verbose;
05720 
05721     Tcl_Preserve(ip);
05722 
05723     /* delete slaves */
05724     delete_slaves(ip);
05725 
05726     /* shut off some connections from Tcl-proc to Ruby */
05727     if (at_exit) {
05728         /* NOTE: Only when at exit.
05729            Because, ruby removes objects, which depends on the deleted
05730            interpreter, on some callback operations.
05731            It is important for GC. */
05732 #if TCL_MAJOR_VERSION >= 8
05733         Tcl_CreateObjCommand(ip, "ruby", ip_null_proc,
05734                              (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
05735         Tcl_CreateObjCommand(ip, "ruby_eval", ip_null_proc,
05736                              (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
05737         Tcl_CreateObjCommand(ip, "ruby_cmd", ip_null_proc,
05738                              (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
05739 #else /* TCL_MAJOR_VERSION < 8 */
05740         Tcl_CreateCommand(ip, "ruby", ip_null_proc,
05741                           (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
05742         Tcl_CreateCommand(ip, "ruby_eval", ip_null_proc,
05743                           (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
05744         Tcl_CreateCommand(ip, "ruby_cmd", ip_null_proc,
05745                           (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
05746 #endif
05747         /*
05748           rb_thread_critical = thr_crit_bup;
05749           return;
05750         */
05751     }
05752 
05753     /* delete root widget */
05754 #ifdef RUBY_VM
05755     /* cause SEGV on Ruby 1.9 */
05756 #else
05757     DUMP1("check `destroy'");
05758     if (Tcl_GetCommandInfo(ip, "destroy", &info)) {
05759         DUMP1("call `destroy .'");
05760         Tcl_GlobalEval(ip, "catch {destroy .}");
05761     }
05762 #endif
05763 #if 1
05764     DUMP1("destroy root widget");
05765     if (tk_stubs_init_p() && Tk_MainWindow(ip) != (Tk_Window)NULL) {
05766         /*
05767          *  On Ruby VM, this code piece may be not called, because
05768          *  Tk_MainWindow() returns NULL on a native thread except
05769          *  the thread which initialize Tk environment.
05770          *  Of course, that is a problem. But maybe not so serious.
05771          *  All widgets are destroyed when the Tcl interp is deleted.
05772          *  At then, Ruby may raise exceptions on the delete hook
05773          *  callbacks which registered for the deleted widgets, and
05774          *  may fail to clear objects which depends on the widgets.
05775          *  Although it is the problem, it is possibly avoidable by
05776          *  rescuing exceptions and the finalize hook of the interp.
05777          */
05778         Tk_Window win = Tk_MainWindow(ip);
05779 
05780         DUMP1("call Tk_DestroyWindow");
05781         ruby_debug   = Qfalse;
05782         ruby_verbose = Qnil;
05783         if (! (((Tk_FakeWin*)win)->flags & TK_ALREADY_DEAD)) {
05784           Tk_DestroyWindow(win);
05785         }
05786         ruby_debug   = rb_debug_bup;
05787         ruby_verbose = rb_verbose_bup;
05788     }
05789 #endif
05790 
05791     /* call finalize-hook-proc */
05792     DUMP1("check `finalize-hook-proc'");
05793     if ( Tcl_GetCommandInfo(ip, finalize_hook_name, &info)) {
05794         DUMP2("call finalize hook proc '%s'", finalize_hook_name);
05795         ruby_debug   = Qfalse;
05796         ruby_verbose = Qnil;
05797         Tcl_GlobalEval(ip, finalize_hook_name);
05798         ruby_debug   = rb_debug_bup;
05799         ruby_verbose = rb_verbose_bup;
05800     }
05801 
05802     DUMP1("check `foreach' & `after'");
05803     if ( Tcl_GetCommandInfo(ip, "foreach", &info)
05804          && Tcl_GetCommandInfo(ip, "after", &info) ) {
05805         DUMP1("cancel after callbacks");
05806         ruby_debug   = Qfalse;
05807         ruby_verbose = Qnil;
05808         Tcl_GlobalEval(ip, "catch {foreach id [after info] {after cancel $id}}");
05809         ruby_debug   = rb_debug_bup;
05810         ruby_verbose = rb_verbose_bup;
05811     }
05812 
05813     Tcl_Release(ip);
05814 
05815     DUMP1("finish ip_finalize");
05816     ruby_debug   = rb_debug_bup;
05817     ruby_verbose = rb_verbose_bup;
05818     rb_thread_critical = thr_crit_bup;
05819 }
05820 
05821 
05822 /* destroy interpreter */
05823 static void
05824 ip_free(ptr)
05825     struct tcltkip *ptr;
05826 {
05827     int  thr_crit_bup;
05828 
05829     DUMP2("free Tcl Interp %lx", (unsigned long)ptr->ip);
05830     if (ptr) {
05831         thr_crit_bup = rb_thread_critical;
05832         rb_thread_critical = Qtrue;
05833 
05834         if ( ptr->ip != (Tcl_Interp*)NULL
05835              && !Tcl_InterpDeleted(ptr->ip)
05836              && Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL
05837              && !Tcl_InterpDeleted(Tcl_GetMaster(ptr->ip)) ) {
05838             DUMP2("parent IP(%lx) is not deleted",
05839                   (unsigned long)Tcl_GetMaster(ptr->ip));
05840             DUMP2("slave IP(%lx) should not be deleted",
05841                   (unsigned long)ptr->ip);
05842             xfree(ptr);
05843             /* ckfree((char*)ptr); */
05844             rb_thread_critical = thr_crit_bup;
05845             return;
05846         }
05847 
05848         if (ptr->ip == (Tcl_Interp*)NULL) {
05849             DUMP1("ip_free is called for deleted IP");
05850             xfree(ptr);
05851             /* ckfree((char*)ptr); */
05852             rb_thread_critical = thr_crit_bup;
05853             return;
05854         }
05855 
05856         if (!Tcl_InterpDeleted(ptr->ip)) {
05857           ip_finalize(ptr->ip);
05858 
05859           Tcl_DeleteInterp(ptr->ip);
05860           Tcl_Release(ptr->ip);
05861         }
05862 
05863         ptr->ip = (Tcl_Interp*)NULL;
05864         xfree(ptr);
05865         /* ckfree((char*)ptr); */
05866 
05867         rb_thread_critical = thr_crit_bup;
05868     }
05869 
05870     DUMP1("complete freeing Tcl Interp");
05871 }
05872 
05873 
05874 /* create and initialize interpreter */
05875 static VALUE ip_alloc _((VALUE));
05876 static VALUE
05877 ip_alloc(self)
05878     VALUE self;
05879 {
05880     return Data_Wrap_Struct(self, 0, ip_free, 0);
05881 }
05882 
05883 static void
05884 ip_replace_wait_commands(interp, mainWin)
05885     Tcl_Interp *interp;
05886     Tk_Window mainWin;
05887 {
05888     /* replace 'vwait' command */
05889 #if TCL_MAJOR_VERSION >= 8
05890     DUMP1("Tcl_CreateObjCommand(\"vwait\")");
05891     Tcl_CreateObjCommand(interp, "vwait", ip_rbVwaitObjCmd,
05892                          (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
05893 #else /* TCL_MAJOR_VERSION < 8 */
05894     DUMP1("Tcl_CreateCommand(\"vwait\")");
05895     Tcl_CreateCommand(interp, "vwait", ip_rbVwaitCommand,
05896                       (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
05897 #endif
05898 
05899     /* replace 'tkwait' command */
05900 #if TCL_MAJOR_VERSION >= 8
05901     DUMP1("Tcl_CreateObjCommand(\"tkwait\")");
05902     Tcl_CreateObjCommand(interp, "tkwait", ip_rbTkWaitObjCmd,
05903                          (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
05904 #else /* TCL_MAJOR_VERSION < 8 */
05905     DUMP1("Tcl_CreateCommand(\"tkwait\")");
05906     Tcl_CreateCommand(interp, "tkwait", ip_rbTkWaitCommand,
05907                       (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
05908 #endif
05909 
05910     /* add 'thread_vwait' command */
05911 #if TCL_MAJOR_VERSION >= 8
05912     DUMP1("Tcl_CreateObjCommand(\"thread_vwait\")");
05913     Tcl_CreateObjCommand(interp, "thread_vwait", ip_rb_threadVwaitObjCmd,
05914                          (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
05915 #else /* TCL_MAJOR_VERSION < 8 */
05916     DUMP1("Tcl_CreateCommand(\"thread_vwait\")");
05917     Tcl_CreateCommand(interp, "thread_vwait", ip_rb_threadVwaitCommand,
05918                       (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
05919 #endif
05920 
05921     /* add 'thread_tkwait' command */
05922 #if TCL_MAJOR_VERSION >= 8
05923     DUMP1("Tcl_CreateObjCommand(\"thread_tkwait\")");
05924     Tcl_CreateObjCommand(interp, "thread_tkwait", ip_rb_threadTkWaitObjCmd,
05925                          (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
05926 #else /* TCL_MAJOR_VERSION < 8 */
05927     DUMP1("Tcl_CreateCommand(\"thread_tkwait\")");
05928     Tcl_CreateCommand(interp, "thread_tkwait", ip_rb_threadTkWaitCommand,
05929                       (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
05930 #endif
05931 
05932     /* replace 'update' command */
05933 #if TCL_MAJOR_VERSION >= 8
05934     DUMP1("Tcl_CreateObjCommand(\"update\")");
05935     Tcl_CreateObjCommand(interp, "update", ip_rbUpdateObjCmd,
05936                          (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
05937 #else /* TCL_MAJOR_VERSION < 8 */
05938     DUMP1("Tcl_CreateCommand(\"update\")");
05939     Tcl_CreateCommand(interp, "update", ip_rbUpdateCommand,
05940                       (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
05941 #endif
05942 
05943     /* add 'thread_update' command */
05944 #if TCL_MAJOR_VERSION >= 8
05945     DUMP1("Tcl_CreateObjCommand(\"thread_update\")");
05946     Tcl_CreateObjCommand(interp, "thread_update", ip_rb_threadUpdateObjCmd,
05947                          (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
05948 #else /* TCL_MAJOR_VERSION < 8 */
05949     DUMP1("Tcl_CreateCommand(\"thread_update\")");
05950     Tcl_CreateCommand(interp, "thread_update", ip_rb_threadUpdateCommand,
05951                       (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
05952 #endif
05953 }
05954 
05955 
05956 #if TCL_MAJOR_VERSION >= 8
05957 static int
05958 ip_rb_replaceSlaveTkCmdsObjCmd(clientData, interp, objc, objv)
05959     ClientData clientData;
05960     Tcl_Interp *interp;
05961     int objc;
05962     Tcl_Obj *CONST objv[];
05963 #else /* TCL_MAJOR_VERSION < 8 */
05964 static int
05965 ip_rb_replaceSlaveTkCmdsCommand(clientData, interp, objc, objv)
05966     ClientData clientData;
05967     Tcl_Interp *interp;
05968     int objc;
05969     char *objv[];
05970 #endif
05971 {
05972     char *slave_name;
05973     Tcl_Interp *slave;
05974     Tk_Window mainWin;
05975 
05976     if (objc != 2) {
05977 #ifdef Tcl_WrongNumArgs
05978         Tcl_WrongNumArgs(interp, 1, objv, "slave_name");
05979 #else
05980         char *nameString;
05981 #if TCL_MAJOR_VERSION >= 8
05982         nameString = Tcl_GetStringFromObj(objv[0], (int*)NULL);
05983 #else /* TCL_MAJOR_VERSION < 8 */
05984         nameString = objv[0];
05985 #endif
05986         Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
05987                          nameString, " slave_name\"", (char *) NULL);
05988 #endif
05989     }
05990 
05991 #if TCL_MAJOR_VERSION >= 8
05992     slave_name = Tcl_GetStringFromObj(objv[1], (int*)NULL);
05993 #else
05994     slave_name = objv[1];
05995 #endif
05996 
05997     slave = Tcl_GetSlave(interp, slave_name);
05998     if (slave == NULL) {
05999         Tcl_AppendResult(interp, "cannot find slave \"",
06000                          slave_name, "\"", (char *)NULL);
06001         return TCL_ERROR;
06002     }
06003     mainWin = Tk_MainWindow(slave);
06004 
06005     /* replace 'exit' command --> 'interp_exit' command */
06006 #if TCL_MAJOR_VERSION >= 8
06007     DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"interp_exit\"");
06008     Tcl_CreateObjCommand(slave, "exit", ip_InterpExitObjCmd,
06009                          (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06010 #else /* TCL_MAJOR_VERSION < 8 */
06011     DUMP1("Tcl_CreateCommand(\"exit\") --> \"interp_exit\"");
06012     Tcl_CreateCommand(slave, "exit", ip_InterpExitCommand,
06013                       (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06014 #endif
06015 
06016     /* replace vwait and tkwait */
06017     ip_replace_wait_commands(slave, mainWin);
06018 
06019     return TCL_OK;
06020 }
06021 
06022 
06023 #if TCL_MAJOR_VERSION >= 8
06024 static int ip_rbNamespaceObjCmd _((ClientData, Tcl_Interp *, int,
06025                                    Tcl_Obj *CONST []));
06026 static int
06027 ip_rbNamespaceObjCmd(clientData, interp, objc, objv)
06028     ClientData clientData;
06029     Tcl_Interp *interp;
06030     int objc;
06031     Tcl_Obj *CONST objv[];
06032 {
06033     Tcl_CmdInfo info;
06034     int ret;
06035 
06036     if (!Tcl_GetCommandInfo(interp, "__orig_namespace_command__", &(info))) {
06037         Tcl_ResetResult(interp);
06038         Tcl_AppendResult(interp,
06039                          "invalid command name \"namespace\"", (char*)NULL);
06040         return TCL_ERROR;
06041     }
06042 
06043     rbtk_eventloop_depth++;
06044     /* DUMP2("namespace wrapper enter depth == %d", rbtk_eventloop_depth); */
06045 
06046     if (info.isNativeObjectProc) {
06047         ret = (*(info.objProc))(info.objClientData, interp, objc, objv);
06048     } else {
06049         /* string interface */
06050         int i;
06051         char **argv;
06052 
06053         /* argv = (char **)Tcl_Alloc(sizeof(char *) * (objc + 1)); */
06054         argv = RbTk_ALLOC_N(char *, (objc + 1));
06055 #if 0 /* use Tcl_Preserve/Release */
06056         Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
06057 #endif
06058 
06059         for(i = 0; i < objc; i++) {
06060             /* argv[i] = Tcl_GetString(objv[i]); */
06061             argv[i] = Tcl_GetStringFromObj(objv[i], (int*)NULL);
06062         }
06063         argv[objc] = (char *)NULL;
06064 
06065         ret = (*(info.proc))(info.clientData, interp,
06066                               objc, (CONST84 char **)argv);
06067 
06068 #if 0 /* use Tcl_EventuallyFree */
06069         Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
06070 #else
06071 #if 0 /* use Tcl_Preserve/Release */
06072         Tcl_Release((ClientData)argv); /* XXXXXXXX */
06073 #else
06074         /* Tcl_Free((char*)argv); */
06075         ckfree((char*)argv);
06076 #endif
06077 #endif
06078     }
06079 
06080     /* DUMP2("namespace wrapper exit depth == %d", rbtk_eventloop_depth); */
06081     rbtk_eventloop_depth--;
06082 
06083     return ret;
06084 }
06085 #endif
06086 
06087 static void
06088 ip_wrap_namespace_command(interp)
06089     Tcl_Interp *interp;
06090 {
06091 #if TCL_MAJOR_VERSION >= 8
06092     Tcl_CmdInfo orig_info;
06093 
06094     if (!Tcl_GetCommandInfo(interp, "namespace", &(orig_info))) {
06095         return;
06096     }
06097 
06098     if (orig_info.isNativeObjectProc) {
06099         Tcl_CreateObjCommand(interp, "__orig_namespace_command__",
06100                              orig_info.objProc, orig_info.objClientData,
06101                              orig_info.deleteProc);
06102     } else {
06103         Tcl_CreateCommand(interp, "__orig_namespace_command__",
06104                           orig_info.proc, orig_info.clientData,
06105                           orig_info.deleteProc);
06106     }
06107 
06108     Tcl_CreateObjCommand(interp, "namespace", ip_rbNamespaceObjCmd,
06109                          (ClientData) 0, (Tcl_CmdDeleteProc *)NULL);
06110 #endif
06111 }
06112 
06113 
06114 /* call when interpreter is deleted */
06115 static void
06116 #ifdef HAVE_PROTOTYPES
06117 ip_CallWhenDeleted(ClientData clientData, Tcl_Interp *ip)
06118 #else
06119 ip_CallWhenDeleted(clientData, ip)
06120     ClientData clientData;
06121     Tcl_Interp *ip;
06122 #endif
06123 {
06124     int  thr_crit_bup;
06125     /* Tk_Window main_win = (Tk_Window) clientData; */
06126 
06127     DUMP1("start ip_CallWhenDeleted");
06128     thr_crit_bup = rb_thread_critical;
06129     rb_thread_critical = Qtrue;
06130 
06131     ip_finalize(ip);
06132 
06133     DUMP1("finish ip_CallWhenDeleted");
06134     rb_thread_critical = thr_crit_bup;
06135 }
06136 
06137 /*--------------------------------------------------------*/
06138 
06139 /* initialize interpreter */
06140 static VALUE
06141 ip_init(argc, argv, self)
06142     int   argc;
06143     VALUE *argv;
06144     VALUE self;
06145 {
06146     struct tcltkip *ptr;        /* tcltkip data struct */
06147     VALUE argv0, opts;
06148     int cnt;
06149     int st;
06150     int with_tk = 1;
06151     Tk_Window mainWin = (Tk_Window)NULL;
06152 
06153     /* security check */
06154     if (rb_safe_level() >= 4) {
06155         rb_raise(rb_eSecurityError,
06156                  "Cannot create a TclTkIp object at level %d",
06157                  rb_safe_level());
06158     }
06159 
06160     /* create object */
06161     Data_Get_Struct(self, struct tcltkip, ptr);
06162     ptr = ALLOC(struct tcltkip);
06163     /* ptr = RbTk_ALLOC_N(struct tcltkip, 1); */
06164     DATA_PTR(self) = ptr;
06165 #ifdef RUBY_USE_NATIVE_THREAD
06166     ptr->tk_thread_id = 0;
06167 #endif
06168     ptr->ref_count = 0;
06169     ptr->allow_ruby_exit = 1;
06170     ptr->return_value = 0;
06171 
06172     /* from Tk_Main() */
06173     DUMP1("Tcl_CreateInterp");
06174     ptr->ip = ruby_tcl_create_ip_and_stubs_init(&st);
06175     if (ptr->ip == NULL) {
06176         switch(st) {
06177         case TCLTK_STUBS_OK:
06178             break;
06179         case NO_TCL_DLL:
06180             rb_raise(rb_eLoadError, "tcltklib: fail to open tcl_dll");
06181         case NO_FindExecutable:
06182             rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_FindExecutable");
06183         case NO_CreateInterp:
06184             rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_CreateInterp()");
06185         case NO_DeleteInterp:
06186             rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_DeleteInterp()");
06187         case FAIL_CreateInterp:
06188             rb_raise(rb_eRuntimeError, "tcltklib: fail to create a new IP");
06189         case FAIL_Tcl_InitStubs:
06190             rb_raise(rb_eRuntimeError, "tcltklib: fail to Tcl_InitStubs()");
06191         default:
06192             rb_raise(rb_eRuntimeError, "tcltklib: unknown error(%d) on ruby_tcl_create_ip_and_stubs_init", st);
06193         }
06194     }
06195 
06196 #if TCL_MAJOR_VERSION >= 8
06197 #if TCL_NAMESPACE_DEBUG
06198     DUMP1("get current namespace");
06199     if ((ptr->default_ns = Tcl_GetCurrentNamespace(ptr->ip))
06200         == (Tcl_Namespace*)NULL) {
06201       rb_raise(rb_eRuntimeError, "a new Tk interpreter has a NULL namespace");
06202     }
06203 #endif
06204 #endif
06205 
06206     rbtk_preserve_ip(ptr);
06207     DUMP2("IP ref_count = %d", ptr->ref_count);
06208     current_interp = ptr->ip;
06209 
06210     ptr->has_orig_exit
06211         = Tcl_GetCommandInfo(ptr->ip, "exit", &(ptr->orig_exit_info));
06212 
06213 #if defined CREATE_RUBYTK_KIT || defined CREATE_RUBYKIT
06214     call_tclkit_init_script(current_interp);
06215 
06216 # if 10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION > 84
06217     {
06218       Tcl_DString encodingName;
06219       Tcl_GetEncodingNameFromEnvironment(&encodingName);
06220       if (strcmp(Tcl_DStringValue(&encodingName), Tcl_GetEncodingName(NULL))) {
06221         /* fails, so we set a variable and do it in the boot.tcl script */
06222         Tcl_SetSystemEncoding(NULL, Tcl_DStringValue(&encodingName));
06223       }
06224       Tcl_SetVar(current_interp, "tclkit_system_encoding", Tcl_DStringValue(&encodingName), 0);
06225       Tcl_DStringFree(&encodingName);
06226     }
06227 # endif
06228 #endif
06229 
06230     /* set variables */
06231     Tcl_Eval(ptr->ip, "set argc 0; set argv {}; set argv0 tcltklib.so");
06232 
06233     cnt = rb_scan_args(argc, argv, "02", &argv0, &opts);
06234     switch(cnt) {
06235     case 2:
06236         /* options */
06237         if (NIL_P(opts) || opts == Qfalse) {
06238             /* without Tk */
06239             with_tk = 0;
06240         } else {
06241             /* Tcl_SetVar(ptr->ip, "argv", StringValuePtr(opts), 0); */
06242             Tcl_SetVar(ptr->ip, "argv", StringValuePtr(opts), TCL_GLOBAL_ONLY);
06243             Tcl_Eval(ptr->ip, "set argc [llength $argv]");
06244         }
06245     case 1:
06246         /* argv0 */
06247         if (!NIL_P(argv0)) {
06248             if (strncmp(StringValuePtr(argv0), "-e", 3) == 0
06249                 || strncmp(StringValuePtr(argv0), "-", 2) == 0) {
06250                 Tcl_SetVar(ptr->ip, "argv0", "ruby", TCL_GLOBAL_ONLY);
06251             } else {
06252                 /* Tcl_SetVar(ptr->ip, "argv0", StringValuePtr(argv0), 0); */
06253                 Tcl_SetVar(ptr->ip, "argv0", StringValuePtr(argv0),
06254                            TCL_GLOBAL_ONLY);
06255             }
06256         }
06257     case 0:
06258         /* no args */
06259         ;
06260     }
06261 
06262     /* from Tcl_AppInit() */
06263     DUMP1("Tcl_Init");
06264 #if (defined CREATE_RUBYTK_KIT || defined CREATE_RUBYKIT) && (!defined KIT_LITE) && (10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION == 85)
06265     /*************************************************************************/
06266     /*  FIX ME (2010/06/28)                                                  */
06267     /*    Don't use ::chan command for Mk4tcl + tclvfs-1.4 on Tcl8.5.        */
06268     /*    It fails to access VFS files because of vfs::zstream.              */
06269     /*    So, force to use ::rechan by temporaly hiding ::chan.              */
06270     /*************************************************************************/
06271     Tcl_Eval(ptr->ip, "catch {rename ::chan ::_tmp_chan}");
06272     if (Tcl_Init(ptr->ip) == TCL_ERROR) {
06273         rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip));
06274     }
06275     Tcl_Eval(ptr->ip, "catch {rename ::_tmp_chan ::chan}");
06276 #else
06277     if (Tcl_Init(ptr->ip) == TCL_ERROR) {
06278         rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip));
06279     }
06280 #endif
06281 
06282     st = ruby_tcl_stubs_init();
06283     /* from Tcl_AppInit() */
06284     if (with_tk) {
06285         DUMP1("Tk_Init");
06286         st = ruby_tk_stubs_init(ptr->ip);
06287         switch(st) {
06288         case TCLTK_STUBS_OK:
06289             break;
06290         case NO_Tk_Init:
06291             rb_raise(rb_eLoadError, "tcltklib: can't find Tk_Init()");
06292         case FAIL_Tk_Init:
06293             rb_raise(rb_eRuntimeError, "tcltklib: fail to Tk_Init(). %s",
06294                      Tcl_GetStringResult(ptr->ip));
06295         case FAIL_Tk_InitStubs:
06296             rb_raise(rb_eRuntimeError, "tcltklib: fail to Tk_InitStubs(). %s",
06297                      Tcl_GetStringResult(ptr->ip));
06298         default:
06299             rb_raise(rb_eRuntimeError, "tcltklib: unknown error(%d) on ruby_tk_stubs_init", st);
06300         }
06301 
06302         DUMP1("Tcl_StaticPackage(\"Tk\")");
06303 #if TCL_MAJOR_VERSION >= 8
06304         Tcl_StaticPackage(ptr->ip, "Tk", Tk_Init, Tk_SafeInit);
06305 #else /* TCL_MAJOR_VERSION < 8 */
06306         Tcl_StaticPackage(ptr->ip, "Tk", Tk_Init,
06307                           (Tcl_PackageInitProc *) NULL);
06308 #endif
06309 
06310 #ifdef RUBY_USE_NATIVE_THREAD
06311         /* set Tk thread ID */
06312         ptr->tk_thread_id = Tcl_GetCurrentThread();
06313 #endif
06314         /* get main window */
06315         mainWin = Tk_MainWindow(ptr->ip);
06316         Tk_Preserve((ClientData)mainWin);
06317     }
06318 
06319     /* add ruby command to the interpreter */
06320 #if TCL_MAJOR_VERSION >= 8
06321     DUMP1("Tcl_CreateObjCommand(\"ruby\")");
06322     Tcl_CreateObjCommand(ptr->ip, "ruby", ip_ruby_eval, (ClientData)NULL,
06323                          (Tcl_CmdDeleteProc *)NULL);
06324     DUMP1("Tcl_CreateObjCommand(\"ruby_eval\")");
06325     Tcl_CreateObjCommand(ptr->ip, "ruby_eval", ip_ruby_eval, (ClientData)NULL,
06326                          (Tcl_CmdDeleteProc *)NULL);
06327     DUMP1("Tcl_CreateObjCommand(\"ruby_cmd\")");
06328     Tcl_CreateObjCommand(ptr->ip, "ruby_cmd", ip_ruby_cmd, (ClientData)NULL,
06329                          (Tcl_CmdDeleteProc *)NULL);
06330 #else /* TCL_MAJOR_VERSION < 8 */
06331     DUMP1("Tcl_CreateCommand(\"ruby\")");
06332     Tcl_CreateCommand(ptr->ip, "ruby", ip_ruby_eval, (ClientData)NULL,
06333                       (Tcl_CmdDeleteProc *)NULL);
06334     DUMP1("Tcl_CreateCommand(\"ruby_eval\")");
06335     Tcl_CreateCommand(ptr->ip, "ruby_eval", ip_ruby_eval, (ClientData)NULL,
06336                       (Tcl_CmdDeleteProc *)NULL);
06337     DUMP1("Tcl_CreateCommand(\"ruby_cmd\")");
06338     Tcl_CreateCommand(ptr->ip, "ruby_cmd", ip_ruby_cmd, (ClientData)NULL,
06339                       (Tcl_CmdDeleteProc *)NULL);
06340 #endif
06341 
06342     /* add 'interp_exit', 'ruby_exit' and replace 'exit' command */
06343 #if TCL_MAJOR_VERSION >= 8
06344     DUMP1("Tcl_CreateObjCommand(\"interp_exit\")");
06345     Tcl_CreateObjCommand(ptr->ip, "interp_exit", ip_InterpExitObjCmd,
06346                          (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06347     DUMP1("Tcl_CreateObjCommand(\"ruby_exit\")");
06348     Tcl_CreateObjCommand(ptr->ip, "ruby_exit", ip_RubyExitObjCmd,
06349                          (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06350     DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"ruby_exit\"");
06351     Tcl_CreateObjCommand(ptr->ip, "exit", ip_RubyExitObjCmd,
06352                          (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06353 #else /* TCL_MAJOR_VERSION < 8 */
06354     DUMP1("Tcl_CreateCommand(\"interp_exit\")");
06355     Tcl_CreateCommand(ptr->ip, "interp_exit", ip_InterpExitCommand,
06356                       (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06357     DUMP1("Tcl_CreateCommand(\"ruby_exit\")");
06358     Tcl_CreateCommand(ptr->ip, "ruby_exit", ip_RubyExitCommand,
06359                       (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06360     DUMP1("Tcl_CreateCommand(\"exit\") --> \"ruby_exit\"");
06361     Tcl_CreateCommand(ptr->ip, "exit", ip_RubyExitCommand,
06362                       (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06363 #endif
06364 
06365     /* replace vwait and tkwait */
06366     ip_replace_wait_commands(ptr->ip, mainWin);
06367 
06368     /* wrap namespace command */
06369     ip_wrap_namespace_command(ptr->ip);
06370 
06371     /* define command to replace commands which depend on slave's MainWindow */
06372 #if TCL_MAJOR_VERSION >= 8
06373     Tcl_CreateObjCommand(ptr->ip, "__replace_slave_tk_commands__",
06374                          ip_rb_replaceSlaveTkCmdsObjCmd,
06375                          (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
06376 #else /* TCL_MAJOR_VERSION < 8 */
06377     Tcl_CreateCommand(ptr->ip, "__replace_slave_tk_commands__",
06378                       ip_rb_replaceSlaveTkCmdsCommand,
06379                       (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
06380 #endif
06381 
06382     /* set finalizer */
06383     Tcl_CallWhenDeleted(ptr->ip, ip_CallWhenDeleted, (ClientData)mainWin);
06384 
06385     if (mainWin != (Tk_Window)NULL) {
06386         Tk_Release((ClientData)mainWin);
06387     }
06388 
06389     return self;
06390 }
06391 
06392 static VALUE
06393 ip_create_slave_core(interp, argc, argv)
06394     VALUE interp;
06395     int   argc;
06396     VALUE *argv;
06397 {
06398     struct tcltkip *master = get_ip(interp);
06399     struct tcltkip *slave = ALLOC(struct tcltkip);
06400     /* struct tcltkip *slave = RbTk_ALLOC_N(struct tcltkip, 1); */
06401     VALUE safemode;
06402     VALUE name;
06403     int safe;
06404     int thr_crit_bup;
06405     Tk_Window mainWin;
06406 
06407     /* ip is deleted? */
06408     if (deleted_ip(master)) {
06409         return rb_exc_new2(rb_eRuntimeError,
06410                            "deleted master cannot create a new slave");
06411     }
06412 
06413     name     = argv[0];
06414     safemode = argv[1];
06415 
06416     if (Tcl_IsSafe(master->ip) == 1) {
06417         safe = 1;
06418     } else if (safemode == Qfalse || NIL_P(safemode)) {
06419         safe = 0;
06420         /* rb_secure(4); */ /* already checked */
06421     } else {
06422         safe = 1;
06423     }
06424 
06425     thr_crit_bup = rb_thread_critical;
06426     rb_thread_critical = Qtrue;
06427 
06428 #if 0
06429     /* init Tk */
06430     if (RTEST(with_tk)) {
06431         volatile VALUE exc;
06432         if (!tk_stubs_init_p()) {
06433             exc = tcltkip_init_tk(interp);
06434             if (!NIL_P(exc)) {
06435                 rb_thread_critical = thr_crit_bup;
06436                 return exc;
06437             }
06438         }
06439     }
06440 #endif
06441 
06442     /* create slave-ip */
06443 #ifdef RUBY_USE_NATIVE_THREAD
06444     /* slave->tk_thread_id = 0; */
06445     slave->tk_thread_id = master->tk_thread_id; /* == current thread */
06446 #endif
06447     slave->ref_count = 0;
06448     slave->allow_ruby_exit = 0;
06449     slave->return_value = 0;
06450 
06451     slave->ip = Tcl_CreateSlave(master->ip, StringValuePtr(name), safe);
06452     if (slave->ip == NULL) {
06453         rb_thread_critical = thr_crit_bup;
06454         return rb_exc_new2(rb_eRuntimeError,
06455                            "fail to create the new slave interpreter");
06456     }
06457 #if TCL_MAJOR_VERSION >= 8
06458 #if TCL_NAMESPACE_DEBUG
06459     slave->default_ns = Tcl_GetCurrentNamespace(slave->ip);
06460 #endif
06461 #endif
06462     rbtk_preserve_ip(slave);
06463 
06464     slave->has_orig_exit
06465         = Tcl_GetCommandInfo(slave->ip, "exit", &(slave->orig_exit_info));
06466 
06467     /* replace 'exit' command --> 'interp_exit' command */
06468     mainWin = (tk_stubs_init_p())? Tk_MainWindow(slave->ip): (Tk_Window)NULL;
06469 #if TCL_MAJOR_VERSION >= 8
06470     DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"interp_exit\"");
06471     Tcl_CreateObjCommand(slave->ip, "exit", ip_InterpExitObjCmd,
06472                          (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06473 #else /* TCL_MAJOR_VERSION < 8 */
06474     DUMP1("Tcl_CreateCommand(\"exit\") --> \"interp_exit\"");
06475     Tcl_CreateCommand(slave->ip, "exit", ip_InterpExitCommand,
06476                       (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06477 #endif
06478 
06479     /* replace vwait and tkwait */
06480     ip_replace_wait_commands(slave->ip, mainWin);
06481 
06482     /* wrap namespace command */
06483     ip_wrap_namespace_command(slave->ip);
06484 
06485     /* define command to replace cmds which depend on slave-slave's MainWin */
06486 #if TCL_MAJOR_VERSION >= 8
06487     Tcl_CreateObjCommand(slave->ip, "__replace_slave_tk_commands__",
06488                          ip_rb_replaceSlaveTkCmdsObjCmd,
06489                          (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
06490 #else /* TCL_MAJOR_VERSION < 8 */
06491     Tcl_CreateCommand(slave->ip, "__replace_slave_tk_commands__",
06492                       ip_rb_replaceSlaveTkCmdsCommand,
06493                       (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
06494 #endif
06495 
06496     /* set finalizer */
06497     Tcl_CallWhenDeleted(slave->ip, ip_CallWhenDeleted, (ClientData)mainWin);
06498 
06499     rb_thread_critical = thr_crit_bup;
06500 
06501     return Data_Wrap_Struct(CLASS_OF(interp), 0, ip_free, slave);
06502 }
06503 
06504 static VALUE
06505 ip_create_slave(argc, argv, self)
06506     int   argc;
06507     VALUE *argv;
06508     VALUE self;
06509 {
06510     struct tcltkip *master = get_ip(self);
06511     VALUE safemode;
06512     VALUE name;
06513     VALUE callargv[2];
06514 
06515     /* ip is deleted? */
06516     if (deleted_ip(master)) {
06517         rb_raise(rb_eRuntimeError,
06518                  "deleted master cannot create a new slave interpreter");
06519     }
06520 
06521     /* argument check */
06522     if (rb_scan_args(argc, argv, "11", &name, &safemode) == 1) {
06523         safemode = Qfalse;
06524     }
06525     if (Tcl_IsSafe(master->ip) != 1
06526         && (safemode == Qfalse || NIL_P(safemode))) {
06527         rb_secure(4);
06528     }
06529 
06530     StringValue(name);
06531     callargv[0] = name;
06532     callargv[1] = safemode;
06533 
06534     return tk_funcall(ip_create_slave_core, 2, callargv, self);
06535 }
06536 
06537 
06538 /* self is slave of master? */
06539 static VALUE
06540 ip_is_slave_of_p(self, master)
06541     VALUE self, master;
06542 {
06543     if (!rb_obj_is_kind_of(master, tcltkip_class)) {
06544         rb_raise(rb_eArgError, "expected TclTkIp object");
06545     }
06546 
06547     if (Tcl_GetMaster(get_ip(self)->ip) == get_ip(master)->ip) {
06548       return Qtrue;
06549     } else {
06550       return Qfalse;
06551     }
06552 }
06553 
06554 
06555 /* create console (if supported) */
06556 #if defined(MAC_TCL) || defined(__WIN32__)
06557 #if TCL_MAJOR_VERSION < 8 \
06558     || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0) \
06559     || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 1 \
06560         && (TCL_RELEASE_LEVEL == TCL_ALPHA_RELEASE \
06561            || (TCL_RELEASE_LEVEL == TCL_BETA_RELEASE \
06562                && TCL_RELEASE_SERIAL < 2) ) )
06563 EXTERN void TkConsoleCreate _((void));
06564 #endif
06565 #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 1 \
06566     && ( (TCL_RELEASE_LEVEL == TCL_FINAL_RELEASE \
06567           && TCL_RELEASE_SERIAL == 0) \
06568        || (TCL_RELEASE_LEVEL == TCL_BETA_RELEASE \
06569            && TCL_RELEASE_SERIAL >= 2) )
06570 EXTERN void TkConsoleCreate_ _((void));
06571 #endif
06572 #endif
06573 static VALUE
06574 ip_create_console_core(interp, argc, argv)
06575     VALUE interp;
06576     int   argc;   /* dummy */
06577     VALUE *argv;  /* dummy */
06578 {
06579     struct tcltkip *ptr = get_ip(interp);
06580 
06581     if (!tk_stubs_init_p()) {
06582         tcltkip_init_tk(interp);
06583     }
06584 
06585     if (Tcl_GetVar(ptr->ip,"tcl_interactive",TCL_GLOBAL_ONLY) == (char*)NULL) {
06586         Tcl_SetVar(ptr->ip, "tcl_interactive", "0", TCL_GLOBAL_ONLY);
06587     }
06588 
06589 #if TCL_MAJOR_VERSION > 8 \
06590     || (TCL_MAJOR_VERSION == 8 \
06591         && (TCL_MINOR_VERSION > 1 \
06592             || (TCL_MINOR_VERSION == 1 \
06593                  && TCL_RELEASE_LEVEL == TCL_FINAL_RELEASE \
06594                  && TCL_RELEASE_SERIAL >= 1) ) )
06595     Tk_InitConsoleChannels(ptr->ip);
06596 
06597     if (Tk_CreateConsoleWindow(ptr->ip) != TCL_OK) {
06598         rb_raise(rb_eRuntimeError, "fail to create console-window");
06599     }
06600 #else
06601 #if defined(MAC_TCL) || defined(__WIN32__)
06602 #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 1 \
06603     && ( (TCL_RELEASE_LEVEL == TCL_FINAL_RELEASE && TCL_RELEASE_SERIAL == 0) \
06604         || (TCL_RELEASE_LEVEL == TCL_BETA_RELEASE && TCL_RELEASE_SERIAL >= 2) )
06605     TkConsoleCreate_();
06606 #else
06607     TkConsoleCreate();
06608 #endif
06609 
06610     if (TkConsoleInit(ptr->ip) != TCL_OK) {
06611         rb_raise(rb_eRuntimeError, "fail to create console-window");
06612     }
06613 #else
06614     rb_notimplement();
06615 #endif
06616 #endif
06617 
06618     return interp;
06619 }
06620 
06621 static VALUE
06622 ip_create_console(self)
06623     VALUE self;
06624 {
06625     struct tcltkip *ptr = get_ip(self);
06626 
06627     /* ip is deleted? */
06628     if (deleted_ip(ptr)) {
06629         rb_raise(rb_eRuntimeError, "interpreter is deleted");
06630     }
06631 
06632     return tk_funcall(ip_create_console_core, 0, (VALUE*)NULL, self);
06633 }
06634 
06635 /* make ip "safe" */
06636 static VALUE
06637 ip_make_safe_core(interp, argc, argv)
06638     VALUE interp;
06639     int   argc;   /* dummy */
06640     VALUE *argv;  /* dummy */
06641 {
06642     struct tcltkip *ptr = get_ip(interp);
06643     Tk_Window mainWin;
06644 
06645     /* ip is deleted? */
06646     if (deleted_ip(ptr)) {
06647         return rb_exc_new2(rb_eRuntimeError, "interpreter is deleted");
06648     }
06649 
06650     if (Tcl_MakeSafe(ptr->ip) == TCL_ERROR) {
06651         /* return rb_exc_new2(rb_eRuntimeError,
06652                               Tcl_GetStringResult(ptr->ip)); */
06653         return create_ip_exc(interp, rb_eRuntimeError, "%s",
06654                              Tcl_GetStringResult(ptr->ip));
06655     }
06656 
06657     ptr->allow_ruby_exit = 0;
06658 
06659     /* replace 'exit' command --> 'interp_exit' command */
06660     mainWin = (tk_stubs_init_p())? Tk_MainWindow(ptr->ip): (Tk_Window)NULL;
06661 #if TCL_MAJOR_VERSION >= 8
06662     DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"interp_exit\"");
06663     Tcl_CreateObjCommand(ptr->ip, "exit", ip_InterpExitObjCmd,
06664                          (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06665 #else /* TCL_MAJOR_VERSION < 8 */
06666     DUMP1("Tcl_CreateCommand(\"exit\") --> \"interp_exit\"");
06667     Tcl_CreateCommand(ptr->ip, "exit", ip_InterpExitCommand,
06668                       (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06669 #endif
06670 
06671     return interp;
06672 }
06673 
06674 static VALUE
06675 ip_make_safe(self)
06676     VALUE self;
06677 {
06678     struct tcltkip *ptr = get_ip(self);
06679 
06680     /* ip is deleted? */
06681     if (deleted_ip(ptr)) {
06682         rb_raise(rb_eRuntimeError, "interpreter is deleted");
06683     }
06684 
06685     return tk_funcall(ip_make_safe_core, 0, (VALUE*)NULL, self);
06686 }
06687 
06688 /* is safe? */
06689 static VALUE
06690 ip_is_safe_p(self)
06691     VALUE self;
06692 {
06693     struct tcltkip *ptr = get_ip(self);
06694 
06695     /* ip is deleted? */
06696     if (deleted_ip(ptr)) {
06697         rb_raise(rb_eRuntimeError, "interpreter is deleted");
06698     }
06699 
06700     if (Tcl_IsSafe(ptr->ip)) {
06701         return Qtrue;
06702     } else {
06703         return Qfalse;
06704     }
06705 }
06706 
06707 /* allow_ruby_exit? */
06708 static VALUE
06709 ip_allow_ruby_exit_p(self)
06710     VALUE self;
06711 {
06712     struct tcltkip *ptr = get_ip(self);
06713 
06714     /* ip is deleted? */
06715     if (deleted_ip(ptr)) {
06716         rb_raise(rb_eRuntimeError, "interpreter is deleted");
06717     }
06718 
06719     if (ptr->allow_ruby_exit) {
06720         return Qtrue;
06721     } else {
06722         return Qfalse;
06723     }
06724 }
06725 
06726 /* allow_ruby_exit = mode */
06727 static VALUE
06728 ip_allow_ruby_exit_set(self, val)
06729     VALUE self, val;
06730 {
06731     struct tcltkip *ptr = get_ip(self);
06732     Tk_Window mainWin;
06733 
06734     rb_secure(4);
06735 
06736     /* ip is deleted? */
06737     if (deleted_ip(ptr)) {
06738         rb_raise(rb_eRuntimeError, "interpreter is deleted");
06739     }
06740 
06741     if (Tcl_IsSafe(ptr->ip)) {
06742         rb_raise(rb_eSecurityError,
06743                  "insecure operation on a safe interpreter");
06744     }
06745 
06746     /*
06747      *  Because of cross-threading, the following line may fail to find
06748      *  the MainWindow, even if the Tcl/Tk interpreter has one or more.
06749      *  But it has no problem. Current implementation of both type of
06750      *  the "exit" command don't need maiinWin token.
06751      */
06752     mainWin = (tk_stubs_init_p())? Tk_MainWindow(ptr->ip): (Tk_Window)NULL;
06753 
06754     if (RTEST(val)) {
06755         ptr->allow_ruby_exit = 1;
06756 #if TCL_MAJOR_VERSION >= 8
06757         DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"ruby_exit\"");
06758         Tcl_CreateObjCommand(ptr->ip, "exit", ip_RubyExitObjCmd,
06759                              (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06760 #else /* TCL_MAJOR_VERSION < 8 */
06761         DUMP1("Tcl_CreateCommand(\"exit\") --> \"ruby_exit\"");
06762         Tcl_CreateCommand(ptr->ip, "exit", ip_RubyExitCommand,
06763                           (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06764 #endif
06765         return Qtrue;
06766 
06767     } else {
06768         ptr->allow_ruby_exit = 0;
06769 #if TCL_MAJOR_VERSION >= 8
06770         DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"interp_exit\"");
06771         Tcl_CreateObjCommand(ptr->ip, "exit", ip_InterpExitObjCmd,
06772                              (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06773 #else /* TCL_MAJOR_VERSION < 8 */
06774         DUMP1("Tcl_CreateCommand(\"exit\") --> \"interp_exit\"");
06775         Tcl_CreateCommand(ptr->ip, "exit", ip_InterpExitCommand,
06776                           (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
06777 #endif
06778         return Qfalse;
06779     }
06780 }
06781 
06782 /* delete interpreter */
06783 static VALUE
06784 ip_delete(self)
06785     VALUE self;
06786 {
06787     int  thr_crit_bup;
06788     struct tcltkip *ptr = get_ip(self);
06789 
06790     /* if (ptr == (struct tcltkip *)NULL || ptr->ip == (Tcl_Interp*)NULL) { */
06791     if (deleted_ip(ptr)) {
06792         DUMP1("delete deleted IP");
06793         return Qnil;
06794     }
06795 
06796     thr_crit_bup = rb_thread_critical;
06797     rb_thread_critical = Qtrue;
06798 
06799     DUMP1("delete interp");
06800     if (!Tcl_InterpDeleted(ptr->ip)) {
06801       DUMP1("call ip_finalize");
06802       ip_finalize(ptr->ip);
06803 
06804       Tcl_DeleteInterp(ptr->ip);
06805       Tcl_Release(ptr->ip);
06806     }
06807 
06808     rb_thread_critical = thr_crit_bup;
06809 
06810     return Qnil;
06811 }
06812 
06813 
06814 /* is deleted? */
06815 static VALUE
06816 ip_has_invalid_namespace_p(self)
06817     VALUE self;
06818 {
06819     struct tcltkip *ptr = get_ip(self);
06820 
06821     if (ptr == (struct tcltkip *)NULL || ptr->ip == (Tcl_Interp *)NULL) {
06822         /* deleted IP */
06823         return Qtrue;
06824     }
06825 
06826 #if TCL_NAMESPACE_DEBUG
06827     if (rbtk_invalid_namespace(ptr)) {
06828         return Qtrue;
06829     } else {
06830         return Qfalse;
06831     }
06832 #else
06833     return Qfalse;
06834 #endif
06835 }
06836 
06837 static VALUE
06838 ip_is_deleted_p(self)
06839     VALUE self;
06840 {
06841     struct tcltkip *ptr = get_ip(self);
06842 
06843     if (deleted_ip(ptr)) {
06844         return Qtrue;
06845     } else {
06846         return Qfalse;
06847     }
06848 }
06849 
06850 static VALUE
06851 ip_has_mainwindow_p_core(self, argc, argv)
06852     VALUE self;
06853     int   argc;   /* dummy */
06854     VALUE *argv;  /* dummy */
06855 {
06856     struct tcltkip *ptr = get_ip(self);
06857 
06858     if (deleted_ip(ptr) || !tk_stubs_init_p()) {
06859         return Qnil;
06860     } else if (Tk_MainWindow(ptr->ip) == (Tk_Window)NULL) {
06861         return Qfalse;
06862     } else {
06863         return Qtrue;
06864     }
06865 }
06866 
06867 static VALUE
06868 ip_has_mainwindow_p(self)
06869     VALUE self;
06870 {
06871     return tk_funcall(ip_has_mainwindow_p_core, 0, (VALUE*)NULL, self);
06872 }
06873 
06874 
06875 /*** ruby string <=> tcl object ***/
06876 #if TCL_MAJOR_VERSION >= 8
06877 static VALUE
06878 get_str_from_obj(obj)
06879     Tcl_Obj *obj;
06880 {
06881     int len, binary = 0;
06882     const char *s;
06883     volatile VALUE str;
06884 
06885 #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0
06886     s = Tcl_GetStringFromObj(obj, &len);
06887 #else
06888 #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 3
06889      /* TCL_VERSION 8.1 -- 8.3 */
06890     if (Tcl_GetCharLength(obj) != Tcl_UniCharLen(Tcl_GetUnicode(obj))) {
06891         /* possibly binary string */
06892         s = (char *)Tcl_GetByteArrayFromObj(obj, &len);
06893         binary = 1;
06894     } else {
06895         /* possibly text string */
06896         s = Tcl_GetStringFromObj(obj, &len);
06897     }
06898 #else /* TCL_VERSION >= 8.4 */
06899     if (IS_TCL_BYTEARRAY(obj)) {
06900       s = (char *)Tcl_GetByteArrayFromObj(obj, &len);
06901       binary = 1;
06902     } else {
06903       s = Tcl_GetStringFromObj(obj, &len);
06904     }
06905 
06906 #endif
06907 #endif
06908     str = s ? rb_str_new(s, len) : rb_str_new2("");
06909     if (binary) {
06910 #ifdef HAVE_RUBY_ENCODING_H
06911       rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
06912 #endif
06913       rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
06914 #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1)
06915     } else {
06916 #ifdef HAVE_RUBY_ENCODING_H
06917       rb_enc_associate_index(str, ENCODING_INDEX_UTF8);
06918 #endif
06919       rb_ivar_set(str, ID_at_enc, ENCODING_NAME_UTF8);
06920 #endif
06921     }
06922     return str;
06923 }
06924 
06925 static Tcl_Obj *
06926 get_obj_from_str(str)
06927     VALUE str;
06928 {
06929     const char *s = StringValuePtr(str);
06930 
06931 #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0
06932     return Tcl_NewStringObj((char*)s, RSTRING_LEN(str));
06933 #else /* TCL_VERSION >= 8.1 */
06934     VALUE enc = rb_attr_get(str, ID_at_enc);
06935 
06936     if (!NIL_P(enc)) {
06937         StringValue(enc);
06938         if (strcmp(RSTRING_PTR(enc), "binary") == 0) {
06939             /* binary string */
06940             return Tcl_NewByteArrayObj((const unsigned char *)s, RSTRING_LENINT(str));
06941         } else {
06942             /* text string */
06943             return Tcl_NewStringObj(s, RSTRING_LENINT(str));
06944         }
06945 #ifdef HAVE_RUBY_ENCODING_H
06946     } else if (rb_enc_get_index(str) == ENCODING_INDEX_BINARY) {
06947         /* binary string */
06948         return Tcl_NewByteArrayObj((const unsigned char *)s, RSTRING_LENINT(str));
06949 #endif
06950     } else if (memchr(s, 0, RSTRING_LEN(str))) {
06951         /* probably binary string */
06952         return Tcl_NewByteArrayObj((const unsigned char *)s, RSTRING_LENINT(str));
06953     } else {
06954         /* probably text string */
06955         return Tcl_NewStringObj(s, RSTRING_LENINT(str));
06956     }
06957 #endif
06958 }
06959 #endif /* ruby string <=> tcl object */
06960 
06961 static VALUE
06962 ip_get_result_string_obj(interp)
06963     Tcl_Interp *interp;
06964 {
06965 #if TCL_MAJOR_VERSION >= 8
06966     Tcl_Obj *retObj;
06967     volatile VALUE strval;
06968 
06969     retObj = Tcl_GetObjResult(interp);
06970     Tcl_IncrRefCount(retObj);
06971     strval = get_str_from_obj(retObj);
06972     RbTk_OBJ_UNTRUST(strval);
06973     Tcl_ResetResult(interp);
06974     Tcl_DecrRefCount(retObj);
06975     return strval;
06976 #else
06977     return rb_tainted_str_new2(interp->result);
06978 #endif
06979 }
06980 
06981 /* call Tcl/Tk functions on the eventloop thread */
06982 static VALUE
06983 callq_safelevel_handler(arg, callq)
06984     VALUE arg;
06985     VALUE callq;
06986 {
06987     struct call_queue *q;
06988 
06989     Data_Get_Struct(callq, struct call_queue, q);
06990     DUMP2("(safe-level handler) $SAFE = %d", q->safe_level);
06991     rb_set_safe_level(q->safe_level);
06992     return((q->func)(q->interp, q->argc, q->argv));
06993 }
06994 
06995 static int call_queue_handler _((Tcl_Event *, int));
06996 static int
06997 call_queue_handler(evPtr, flags)
06998     Tcl_Event *evPtr;
06999     int flags;
07000 {
07001     struct call_queue *q = (struct call_queue *)evPtr;
07002     volatile VALUE ret;
07003     volatile VALUE q_dat;
07004     volatile VALUE thread = q->thread;
07005     struct tcltkip *ptr;
07006 
07007     DUMP2("do_call_queue_handler : evPtr = %p", evPtr);
07008     DUMP2("call_queue_handler thread : %lx", rb_thread_current());
07009     DUMP2("added by thread : %lx", thread);
07010 
07011     if (*(q->done)) {
07012         DUMP1("processed by another event-loop");
07013         return 0;
07014     } else {
07015         DUMP1("process it on current event-loop");
07016     }
07017 
07018 #ifdef RUBY_VM
07019     if (RTEST(rb_funcall(thread, ID_alive_p, 0))
07020         && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
07021 #else
07022     if (RTEST(rb_thread_alive_p(thread))
07023         && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
07024 #endif
07025       DUMP1("caller is not yet ready to receive the result -> pending");
07026       return 0;
07027     }
07028 
07029     /* process it */
07030     *(q->done) = 1;
07031 
07032     /* deleted ipterp ? */
07033     ptr = get_ip(q->interp);
07034     if (deleted_ip(ptr)) {
07035         /* deleted IP --> ignore */
07036         return 1;
07037     }
07038 
07039     /* incr internal handler mark */
07040     rbtk_internal_eventloop_handler++;
07041 
07042     /* check safe-level */
07043     if (rb_safe_level() != q->safe_level) {
07044         /* q_dat = Data_Wrap_Struct(rb_cData,0,-1,q); */
07045         q_dat = Data_Wrap_Struct(rb_cData,call_queue_mark,-1,q);
07046         ret = rb_funcall(rb_proc_new(callq_safelevel_handler, q_dat),
07047                          ID_call, 0);
07048         rb_gc_force_recycle(q_dat);
07049         q_dat = (VALUE)NULL;
07050     } else {
07051         DUMP2("call function (for caller thread:%lx)", thread);
07052         DUMP2("call function (current thread:%lx)", rb_thread_current());
07053         ret = (q->func)(q->interp, q->argc, q->argv);
07054     }
07055 
07056     /* set result */
07057     RARRAY_PTR(q->result)[0] = ret;
07058     ret = (VALUE)NULL;
07059 
07060     /* decr internal handler mark */
07061     rbtk_internal_eventloop_handler--;
07062 
07063     /* complete */
07064     *(q->done) = -1;
07065 
07066     /* unlink ruby objects */
07067     q->argv = (VALUE*)NULL;
07068     q->interp = (VALUE)NULL;
07069     q->result = (VALUE)NULL;
07070     q->thread = (VALUE)NULL;
07071 
07072     /* back to caller */
07073 #ifdef RUBY_VM
07074     if (RTEST(rb_funcall(thread, ID_alive_p, 0, 0))) {
07075 #else
07076     if (RTEST(rb_thread_alive_p(thread))) {
07077 #endif
07078       DUMP2("back to caller (caller thread:%lx)", thread);
07079       DUMP2("               (current thread:%lx)", rb_thread_current());
07080 #if CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE
07081       have_rb_thread_waiting_for_value = 1;
07082       rb_thread_wakeup(thread);
07083 #else
07084       rb_thread_run(thread);
07085 #endif
07086       DUMP1("finish back to caller");
07087 #if DO_THREAD_SCHEDULE_AT_CALLBACK_DONE
07088       rb_thread_schedule();
07089 #endif
07090     } else {
07091       DUMP2("caller is dead (caller thread:%lx)", thread);
07092       DUMP2("               (current thread:%lx)", rb_thread_current());
07093     }
07094 
07095     /* end of handler : remove it */
07096     return 1;
07097 }
07098 
07099 static VALUE
07100 tk_funcall(func, argc, argv, obj)
07101     VALUE (*func)();
07102     int argc;
07103     VALUE *argv;
07104     VALUE obj;
07105 {
07106     struct call_queue *callq;
07107     struct tcltkip *ptr;
07108     int  *alloc_done;
07109     int  thr_crit_bup;
07110     int  is_tk_evloop_thread;
07111     volatile VALUE current = rb_thread_current();
07112     volatile VALUE ip_obj = obj;
07113     volatile VALUE result;
07114     volatile VALUE ret;
07115     struct timeval t;
07116 
07117     if (!NIL_P(ip_obj) && rb_obj_is_kind_of(ip_obj, tcltkip_class)) {
07118         ptr = get_ip(ip_obj);
07119         if (deleted_ip(ptr)) return Qnil;
07120     } else {
07121         ptr = (struct tcltkip *)NULL;
07122     }
07123 
07124 #ifdef RUBY_USE_NATIVE_THREAD
07125     if (ptr) {
07126       /* on Tcl interpreter */
07127       is_tk_evloop_thread = (ptr->tk_thread_id == (Tcl_ThreadId) 0
07128                              || ptr->tk_thread_id == Tcl_GetCurrentThread());
07129     } else {
07130       /* on Tcl/Tk library */
07131       is_tk_evloop_thread = (tk_eventloop_thread_id == (Tcl_ThreadId) 0
07132                              || tk_eventloop_thread_id == Tcl_GetCurrentThread());
07133     }
07134 #else
07135     is_tk_evloop_thread = 1;
07136 #endif
07137 
07138     if (is_tk_evloop_thread
07139         && (NIL_P(eventloop_thread) || current == eventloop_thread)
07140         ) {
07141         if (NIL_P(eventloop_thread)) {
07142             DUMP2("tk_funcall from thread:%lx but no eventloop", current);
07143         } else {
07144             DUMP2("tk_funcall from current eventloop %lx", current);
07145         }
07146         result = (func)(ip_obj, argc, argv);
07147         if (rb_obj_is_kind_of(result, rb_eException)) {
07148             rb_exc_raise(result);
07149         }
07150         return result;
07151     }
07152 
07153     DUMP2("tk_funcall from thread %lx (NOT current eventloop)", current);
07154 
07155     thr_crit_bup = rb_thread_critical;
07156     rb_thread_critical = Qtrue;
07157 
07158     /* allocate memory (argv cross over thread : must be in heap) */
07159     if (argv) {
07160         /* VALUE *temp = ALLOC_N(VALUE, argc); */
07161         VALUE *temp = RbTk_ALLOC_N(VALUE, argc);
07162 #if 0 /* use Tcl_Preserve/Release */
07163         Tcl_Preserve((ClientData)temp); /* XXXXXXXX */
07164 #endif
07165         MEMCPY(temp, argv, VALUE, argc);
07166         argv = temp;
07167     }
07168 
07169     /* allocate memory (keep result) */
07170     /* alloc_done = (int*)ALLOC(int); */
07171     alloc_done = RbTk_ALLOC_N(int, 1);
07172 #if 0 /* use Tcl_Preserve/Release */
07173     Tcl_Preserve((ClientData)alloc_done); /* XXXXXXXX */
07174 #endif
07175     *alloc_done = 0;
07176 
07177     /* allocate memory (freed by Tcl_ServiceEvent) */
07178     /* callq = (struct call_queue *)Tcl_Alloc(sizeof(struct call_queue)); */
07179     callq = RbTk_ALLOC_N(struct call_queue, 1);
07180 #if 0 /* use Tcl_Preserve/Release */
07181     Tcl_Preserve(callq);
07182 #endif
07183 
07184     /* allocate result obj */
07185     result = rb_ary_new3(1, Qnil);
07186 
07187     /* construct event data */
07188     callq->done = alloc_done;
07189     callq->func = func;
07190     callq->argc = argc;
07191     callq->argv = argv;
07192     callq->interp = ip_obj;
07193     callq->result = result;
07194     callq->thread = current;
07195     callq->safe_level = rb_safe_level();
07196     callq->ev.proc = call_queue_handler;
07197 
07198     /* add the handler to Tcl event queue */
07199     DUMP1("add handler");
07200 #ifdef RUBY_USE_NATIVE_THREAD
07201     if (ptr && ptr->tk_thread_id) {
07202       /* Tcl_ThreadQueueEvent(ptr->tk_thread_id,
07203                            &(callq->ev), TCL_QUEUE_HEAD); */
07204       Tcl_ThreadQueueEvent(ptr->tk_thread_id,
07205                            (Tcl_Event*)callq, TCL_QUEUE_HEAD);
07206       Tcl_ThreadAlert(ptr->tk_thread_id);
07207     } else if (tk_eventloop_thread_id) {
07208       /* Tcl_ThreadQueueEvent(tk_eventloop_thread_id,
07209                            &(callq->ev), TCL_QUEUE_HEAD); */
07210       Tcl_ThreadQueueEvent(tk_eventloop_thread_id,
07211                            (Tcl_Event*)callq, TCL_QUEUE_HEAD);
07212       Tcl_ThreadAlert(tk_eventloop_thread_id);
07213     } else {
07214       /* Tcl_QueueEvent(&(callq->ev), TCL_QUEUE_HEAD); */
07215       Tcl_QueueEvent((Tcl_Event*)callq, TCL_QUEUE_HEAD);
07216     }
07217 #else
07218     /* Tcl_QueueEvent(&(callq->ev), TCL_QUEUE_HEAD); */
07219     Tcl_QueueEvent((Tcl_Event*)callq, TCL_QUEUE_HEAD);
07220 #endif
07221 
07222     rb_thread_critical = thr_crit_bup;
07223 
07224     /* wait for the handler to be processed */
07225     t.tv_sec  = 0;
07226     t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
07227 
07228     DUMP2("callq wait for handler (current thread:%lx)", current);
07229     while(*alloc_done >= 0) {
07230       DUMP2("*** callq wait for handler (current thread:%lx)", current);
07231       /* rb_thread_stop(); */
07232       /* rb_thread_sleep_forever(); */
07233       rb_thread_wait_for(t);
07234       DUMP2("*** callq wakeup (current thread:%lx)", current);
07235       DUMP2("***            (eventloop thread:%lx)", eventloop_thread);
07236       if (NIL_P(eventloop_thread)) {
07237         DUMP1("*** callq lost eventloop thread");
07238         break;
07239       }
07240     }
07241     DUMP2("back from handler (current thread:%lx)", current);
07242 
07243     /* get result & free allocated memory */
07244     ret = RARRAY_PTR(result)[0];
07245 #if 0 /* use Tcl_EventuallyFree */
07246     Tcl_EventuallyFree((ClientData)alloc_done, TCL_DYNAMIC); /* XXXXXXXX */
07247 #else
07248 #if 0 /* use Tcl_Preserve/Release */
07249     Tcl_Release((ClientData)alloc_done); /* XXXXXXXX */
07250 #else
07251     /* free(alloc_done); */
07252     ckfree((char*)alloc_done);
07253 #endif
07254 #endif
07255     /* if (argv) free(argv); */
07256     if (argv) {
07257       /* if argv != NULL, alloc as 'temp' */
07258       int i;
07259       for(i = 0; i < argc; i++) { argv[i] = (VALUE)NULL; }
07260 
07261 #if 0 /* use Tcl_EventuallyFree */
07262       Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
07263 #else
07264 #if 0 /* use Tcl_Preserve/Release */
07265       Tcl_Release((ClientData)argv); /* XXXXXXXX */
07266 #else
07267       ckfree((char*)argv);
07268 #endif
07269 #endif
07270     }
07271 
07272 #if 0 /* callq is freed by Tcl_ServiceEvent */
07273 #if 0 /* use Tcl_Preserve/Release */
07274     Tcl_Release(callq);
07275 #else
07276     ckfree((char*)callq);
07277 #endif
07278 #endif
07279 
07280     /* exception? */
07281     if (rb_obj_is_kind_of(ret, rb_eException)) {
07282         DUMP1("raise exception");
07283         /* rb_exc_raise(ret); */
07284         rb_exc_raise(rb_exc_new3(rb_obj_class(ret),
07285                                  rb_funcall(ret, ID_to_s, 0, 0)));
07286     }
07287 
07288     DUMP1("exit tk_funcall");
07289     return ret;
07290 }
07291 
07292 
07293 /* eval string in tcl by Tcl_Eval() */
07294 #if TCL_MAJOR_VERSION >= 8
07295 struct call_eval_info {
07296     struct tcltkip *ptr;
07297     Tcl_Obj *cmd;
07298 };
07299 
07300 static VALUE
07301 #ifdef HAVE_PROTOTYPES
07302 call_tcl_eval(VALUE arg)
07303 #else
07304 call_tcl_eval(arg)
07305     VALUE arg;
07306 #endif
07307 {
07308     struct call_eval_info *inf = (struct call_eval_info *)arg;
07309 
07310     Tcl_AllowExceptions(inf->ptr->ip);
07311     inf->ptr->return_value = Tcl_EvalObj(inf->ptr->ip, inf->cmd);
07312 
07313     return Qnil;
07314 }
07315 #endif
07316 
07317 static VALUE
07318 ip_eval_real(self, cmd_str, cmd_len)
07319     VALUE self;
07320     char *cmd_str;
07321     int  cmd_len;
07322 {
07323     volatile VALUE ret;
07324     struct tcltkip *ptr = get_ip(self);
07325     int thr_crit_bup;
07326 
07327 #if TCL_MAJOR_VERSION >= 8
07328     /* call Tcl_EvalObj() */
07329     {
07330       Tcl_Obj *cmd;
07331 
07332       thr_crit_bup = rb_thread_critical;
07333       rb_thread_critical = Qtrue;
07334 
07335       cmd = Tcl_NewStringObj(cmd_str, cmd_len);
07336       Tcl_IncrRefCount(cmd);
07337 
07338       /* ip is deleted? */
07339       if (deleted_ip(ptr)) {
07340           Tcl_DecrRefCount(cmd);
07341           rb_thread_critical = thr_crit_bup;
07342           ptr->return_value = TCL_OK;
07343           return rb_tainted_str_new2("");
07344       } else {
07345           int status;
07346           struct call_eval_info inf;
07347 
07348           /* Tcl_Preserve(ptr->ip); */
07349           rbtk_preserve_ip(ptr);
07350 
07351 #if 0
07352           ptr->return_value = Tcl_EvalObj(ptr->ip, cmd);
07353           /* ptr->return_value = Tcl_GlobalEvalObj(ptr->ip, cmd); */
07354 #else
07355           inf.ptr = ptr;
07356           inf.cmd = cmd;
07357           ret = rb_protect(call_tcl_eval, (VALUE)&inf, &status);
07358           switch(status) {
07359           case TAG_RAISE:
07360               if (NIL_P(rb_errinfo())) {
07361                   rbtk_pending_exception = rb_exc_new2(rb_eException,
07362                                                        "unknown exception");
07363               } else {
07364                   rbtk_pending_exception = rb_errinfo();
07365               }
07366               break;
07367 
07368           case TAG_FATAL:
07369               if (NIL_P(rb_errinfo())) {
07370                   rbtk_pending_exception = rb_exc_new2(rb_eFatal, "FATAL");
07371               } else {
07372                   rbtk_pending_exception = rb_errinfo();
07373               }
07374           }
07375 #endif
07376       }
07377 
07378       Tcl_DecrRefCount(cmd);
07379 
07380     }
07381 
07382     if (pending_exception_check1(thr_crit_bup, ptr)) {
07383         rbtk_release_ip(ptr);
07384         return rbtk_pending_exception;
07385     }
07386 
07387     /* if (ptr->return_value == TCL_ERROR) { */
07388     if (ptr->return_value != TCL_OK) {
07389         if (event_loop_abort_on_exc > 0 && !Tcl_InterpDeleted(ptr->ip)) {
07390             volatile VALUE exc;
07391 
07392             switch (ptr->return_value) {
07393             case TCL_RETURN:
07394               exc = create_ip_exc(self, eTkCallbackReturn,
07395                                   "ip_eval_real receives TCL_RETURN");
07396             case TCL_BREAK:
07397               exc = create_ip_exc(self, eTkCallbackBreak,
07398                                   "ip_eval_real receives TCL_BREAK");
07399             case TCL_CONTINUE:
07400               exc = create_ip_exc(self, eTkCallbackContinue,
07401                                   "ip_eval_real receives TCL_CONTINUE");
07402             default:
07403               exc = create_ip_exc(self, rb_eRuntimeError, "%s",
07404                                   Tcl_GetStringResult(ptr->ip));
07405             }
07406 
07407             rbtk_release_ip(ptr);
07408             rb_thread_critical = thr_crit_bup;
07409             return exc;
07410         } else {
07411             if (event_loop_abort_on_exc < 0) {
07412                 rb_warning("%s (ignore)", Tcl_GetStringResult(ptr->ip));
07413             } else {
07414                 rb_warn("%s (ignore)", Tcl_GetStringResult(ptr->ip));
07415             }
07416             Tcl_ResetResult(ptr->ip);
07417             rbtk_release_ip(ptr);
07418             rb_thread_critical = thr_crit_bup;
07419             return rb_tainted_str_new2("");
07420         }
07421     }
07422 
07423     /* pass back the result (as string) */
07424     ret =  ip_get_result_string_obj(ptr->ip);
07425     rbtk_release_ip(ptr);
07426     rb_thread_critical = thr_crit_bup;
07427     return ret;
07428 
07429 #else /* TCL_MAJOR_VERSION < 8 */
07430     DUMP2("Tcl_Eval(%s)", cmd_str);
07431 
07432     /* ip is deleted? */
07433     if (deleted_ip(ptr)) {
07434         ptr->return_value = TCL_OK;
07435         return rb_tainted_str_new2("");
07436     } else {
07437         /* Tcl_Preserve(ptr->ip); */
07438         rbtk_preserve_ip(ptr);
07439         ptr->return_value = Tcl_Eval(ptr->ip, cmd_str);
07440         /* ptr->return_value = Tcl_GlobalEval(ptr->ip, cmd_str); */
07441     }
07442 
07443     if (pending_exception_check1(thr_crit_bup, ptr)) {
07444         rbtk_release_ip(ptr);
07445         return rbtk_pending_exception;
07446     }
07447 
07448     /* if (ptr->return_value == TCL_ERROR) { */
07449     if (ptr->return_value != TCL_OK) {
07450         volatile VALUE exc;
07451 
07452         switch (ptr->return_value) {
07453         case TCL_RETURN:
07454           exc = create_ip_exc(self, eTkCallbackReturn,
07455                               "ip_eval_real receives TCL_RETURN");
07456         case TCL_BREAK:
07457           exc = create_ip_exc(self, eTkCallbackBreak,
07458                               "ip_eval_real receives TCL_BREAK");
07459         case TCL_CONTINUE:
07460           exc = create_ip_exc(self, eTkCallbackContinue,
07461                                "ip_eval_real receives TCL_CONTINUE");
07462         default:
07463           exc = create_ip_exc(self, rb_eRuntimeError, "%s", ptr->ip->result);
07464         }
07465 
07466         rbtk_release_ip(ptr);
07467         return exc;
07468     }
07469     DUMP2("(TCL_Eval result) %d", ptr->return_value);
07470 
07471     /* pass back the result (as string) */
07472     ret =  ip_get_result_string_obj(ptr->ip);
07473     rbtk_release_ip(ptr);
07474     return ret;
07475 #endif
07476 }
07477 
07478 static VALUE
07479 evq_safelevel_handler(arg, evq)
07480     VALUE arg;
07481     VALUE evq;
07482 {
07483     struct eval_queue *q;
07484 
07485     Data_Get_Struct(evq, struct eval_queue, q);
07486     DUMP2("(safe-level handler) $SAFE = %d", q->safe_level);
07487     rb_set_safe_level(q->safe_level);
07488     return ip_eval_real(q->interp, q->str, q->len);
07489 }
07490 
07491 int eval_queue_handler _((Tcl_Event *, int));
07492 int
07493 eval_queue_handler(evPtr, flags)
07494     Tcl_Event *evPtr;
07495     int flags;
07496 {
07497     struct eval_queue *q = (struct eval_queue *)evPtr;
07498     volatile VALUE ret;
07499     volatile VALUE q_dat;
07500     volatile VALUE thread = q->thread;
07501     struct tcltkip *ptr;
07502 
07503     DUMP2("do_eval_queue_handler : evPtr = %p", evPtr);
07504     DUMP2("eval_queue_thread : %lx", rb_thread_current());
07505     DUMP2("added by thread : %lx", thread);
07506 
07507     if (*(q->done)) {
07508         DUMP1("processed by another event-loop");
07509         return 0;
07510     } else {
07511         DUMP1("process it on current event-loop");
07512     }
07513 
07514 #ifdef RUBY_VM
07515     if (RTEST(rb_funcall(thread, ID_alive_p, 0))
07516         && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
07517 #else
07518     if (RTEST(rb_thread_alive_p(thread))
07519         && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
07520 #endif
07521       DUMP1("caller is not yet ready to receive the result -> pending");
07522       return 0;
07523     }
07524 
07525     /* process it */
07526     *(q->done) = 1;
07527 
07528     /* deleted ipterp ? */
07529     ptr = get_ip(q->interp);
07530     if (deleted_ip(ptr)) {
07531         /* deleted IP --> ignore */
07532         return 1;
07533     }
07534 
07535     /* incr internal handler mark */
07536     rbtk_internal_eventloop_handler++;
07537 
07538     /* check safe-level */
07539     if (rb_safe_level() != q->safe_level) {
07540 #ifdef HAVE_NATIVETHREAD
07541 #ifndef RUBY_USE_NATIVE_THREAD
07542     if (!ruby_native_thread_p()) {
07543       rb_bug("cross-thread violation on eval_queue_handler()");
07544     }
07545 #endif
07546 #endif
07547         /* q_dat = Data_Wrap_Struct(rb_cData,0,-1,q); */
07548         q_dat = Data_Wrap_Struct(rb_cData,eval_queue_mark,-1,q);
07549         ret = rb_funcall(rb_proc_new(evq_safelevel_handler, q_dat),
07550                          ID_call, 0);
07551         rb_gc_force_recycle(q_dat);
07552         q_dat = (VALUE)NULL;
07553     } else {
07554         ret = ip_eval_real(q->interp, q->str, q->len);
07555     }
07556 
07557     /* set result */
07558     RARRAY_PTR(q->result)[0] = ret;
07559     ret = (VALUE)NULL;
07560 
07561     /* decr internal handler mark */
07562     rbtk_internal_eventloop_handler--;
07563 
07564     /* complete */
07565     *(q->done) = -1;
07566 
07567     /* unlink ruby objects */
07568     q->interp = (VALUE)NULL;
07569     q->result = (VALUE)NULL;
07570     q->thread = (VALUE)NULL;
07571 
07572     /* back to caller */
07573 #ifdef RUBY_VM
07574     if (RTEST(rb_funcall(thread, ID_alive_p, 0, 0))) {
07575 #else
07576     if (RTEST(rb_thread_alive_p(thread))) {
07577 #endif
07578       DUMP2("back to caller (caller thread:%lx)", thread);
07579       DUMP2("               (current thread:%lx)", rb_thread_current());
07580 #if CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE
07581       have_rb_thread_waiting_for_value = 1;
07582       rb_thread_wakeup(thread);
07583 #else
07584       rb_thread_run(thread);
07585 #endif
07586       DUMP1("finish back to caller");
07587 #if DO_THREAD_SCHEDULE_AT_CALLBACK_DONE
07588       rb_thread_schedule();
07589 #endif
07590     } else {
07591       DUMP2("caller is dead (caller thread:%lx)", thread);
07592       DUMP2("               (current thread:%lx)", rb_thread_current());
07593     }
07594 
07595     /* end of handler : remove it */
07596     return 1;
07597 }
07598 
07599 static VALUE
07600 ip_eval(self, str)
07601     VALUE self;
07602     VALUE str;
07603 {
07604     struct eval_queue *evq;
07605 #ifdef RUBY_USE_NATIVE_THREAD
07606     struct tcltkip *ptr;
07607 #endif
07608     char *eval_str;
07609     int  *alloc_done;
07610     int  thr_crit_bup;
07611     volatile VALUE current = rb_thread_current();
07612     volatile VALUE ip_obj = self;
07613     volatile VALUE result;
07614     volatile VALUE ret;
07615     Tcl_QueuePosition position;
07616     struct timeval t;
07617 
07618     thr_crit_bup = rb_thread_critical;
07619     rb_thread_critical = Qtrue;
07620     StringValue(str);
07621     rb_thread_critical = thr_crit_bup;
07622 
07623 #ifdef RUBY_USE_NATIVE_THREAD
07624     ptr = get_ip(ip_obj);
07625     DUMP2("eval status: ptr->tk_thread_id %p", ptr->tk_thread_id);
07626     DUMP2("eval status: Tcl_GetCurrentThread %p", Tcl_GetCurrentThread());
07627 #else
07628     DUMP2("status: Tcl_GetCurrentThread %p", Tcl_GetCurrentThread());
07629 #endif
07630     DUMP2("status: eventloopt_thread %lx", eventloop_thread);
07631 
07632     if (
07633 #ifdef RUBY_USE_NATIVE_THREAD
07634         (ptr->tk_thread_id == 0 || ptr->tk_thread_id == Tcl_GetCurrentThread())
07635         &&
07636 #endif
07637         (NIL_P(eventloop_thread) || current == eventloop_thread)
07638         ) {
07639         if (NIL_P(eventloop_thread)) {
07640             DUMP2("eval from thread:%lx but no eventloop", current);
07641         } else {
07642             DUMP2("eval from current eventloop %lx", current);
07643         }
07644         result = ip_eval_real(self, RSTRING_PTR(str), RSTRING_LENINT(str));
07645         if (rb_obj_is_kind_of(result, rb_eException)) {
07646             rb_exc_raise(result);
07647         }
07648         return result;
07649     }
07650 
07651     DUMP2("eval from thread %lx (NOT current eventloop)", current);
07652 
07653     thr_crit_bup = rb_thread_critical;
07654     rb_thread_critical = Qtrue;
07655 
07656     /* allocate memory (keep result) */
07657     /* alloc_done = (int*)ALLOC(int); */
07658     alloc_done = RbTk_ALLOC_N(int, 1);
07659 #if 0 /* use Tcl_Preserve/Release */
07660     Tcl_Preserve((ClientData)alloc_done); /* XXXXXXXX */
07661 #endif
07662     *alloc_done = 0;
07663 
07664     /* eval_str = ALLOC_N(char, RSTRING_LEN(str) + 1); */
07665     eval_str = ckalloc(RSTRING_LENINT(str) + 1);
07666 #if 0 /* use Tcl_Preserve/Release */
07667     Tcl_Preserve((ClientData)eval_str); /* XXXXXXXX */
07668 #endif
07669     memcpy(eval_str, RSTRING_PTR(str), RSTRING_LEN(str));
07670     eval_str[RSTRING_LEN(str)] = 0;
07671 
07672     /* allocate memory (freed by Tcl_ServiceEvent) */
07673     /* evq = (struct eval_queue *)Tcl_Alloc(sizeof(struct eval_queue)); */
07674     evq = RbTk_ALLOC_N(struct eval_queue, 1);
07675 #if 0 /* use Tcl_Preserve/Release */
07676     Tcl_Preserve(evq);
07677 #endif
07678 
07679     /* allocate result obj */
07680     result = rb_ary_new3(1, Qnil);
07681 
07682     /* construct event data */
07683     evq->done = alloc_done;
07684     evq->str = eval_str;
07685     evq->len = RSTRING_LENINT(str);
07686     evq->interp = ip_obj;
07687     evq->result = result;
07688     evq->thread = current;
07689     evq->safe_level = rb_safe_level();
07690     evq->ev.proc = eval_queue_handler;
07691 
07692     position = TCL_QUEUE_TAIL;
07693 
07694     /* add the handler to Tcl event queue */
07695     DUMP1("add handler");
07696 #ifdef RUBY_USE_NATIVE_THREAD
07697     if (ptr->tk_thread_id) {
07698       /* Tcl_ThreadQueueEvent(ptr->tk_thread_id, &(evq->ev), position); */
07699       Tcl_ThreadQueueEvent(ptr->tk_thread_id, (Tcl_Event*)evq, position);
07700       Tcl_ThreadAlert(ptr->tk_thread_id);
07701     } else if (tk_eventloop_thread_id) {
07702       Tcl_ThreadQueueEvent(tk_eventloop_thread_id, (Tcl_Event*)evq, position);
07703       /* Tcl_ThreadQueueEvent(tk_eventloop_thread_id,
07704                            &(evq->ev), position); */
07705       Tcl_ThreadAlert(tk_eventloop_thread_id);
07706     } else {
07707       /* Tcl_QueueEvent(&(evq->ev), position); */
07708       Tcl_QueueEvent((Tcl_Event*)evq, position);
07709     }
07710 #else
07711     /* Tcl_QueueEvent(&(evq->ev), position); */
07712     Tcl_QueueEvent((Tcl_Event*)evq, position);
07713 #endif
07714 
07715     rb_thread_critical = thr_crit_bup;
07716 
07717     /* wait for the handler to be processed */
07718     t.tv_sec  = 0;
07719     t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
07720 
07721     DUMP2("evq wait for handler (current thread:%lx)", current);
07722     while(*alloc_done >= 0) {
07723       DUMP2("*** evq wait for handler (current thread:%lx)", current);
07724       /* rb_thread_stop(); */
07725       /* rb_thread_sleep_forever(); */
07726       rb_thread_wait_for(t);
07727       DUMP2("*** evq wakeup (current thread:%lx)", current);
07728       DUMP2("***          (eventloop thread:%lx)", eventloop_thread);
07729       if (NIL_P(eventloop_thread)) {
07730         DUMP1("*** evq lost eventloop thread");
07731         break;
07732       }
07733     }
07734     DUMP2("back from handler (current thread:%lx)", current);
07735 
07736     /* get result & free allocated memory */
07737     ret = RARRAY_PTR(result)[0];
07738 
07739 #if 0 /* use Tcl_EventuallyFree */
07740     Tcl_EventuallyFree((ClientData)alloc_done, TCL_DYNAMIC); /* XXXXXXXX */
07741 #else
07742 #if 0 /* use Tcl_Preserve/Release */
07743     Tcl_Release((ClientData)alloc_done); /* XXXXXXXX */
07744 #else
07745     /* free(alloc_done); */
07746     ckfree((char*)alloc_done);
07747 #endif
07748 #endif
07749 #if 0 /* use Tcl_EventuallyFree */
07750     Tcl_EventuallyFree((ClientData)eval_str, TCL_DYNAMIC); /* XXXXXXXX */
07751 #else
07752 #if 0 /* use Tcl_Preserve/Release */
07753     Tcl_Release((ClientData)eval_str); /* XXXXXXXX */
07754 #else
07755     /* free(eval_str); */
07756     ckfree(eval_str);
07757 #endif
07758 #endif
07759 #if 0 /* evq is freed by Tcl_ServiceEvent */
07760 #if 0 /* use Tcl_Preserve/Release */
07761     Tcl_Release(evq);
07762 #else
07763     ckfree((char*)evq);
07764 #endif
07765 #endif
07766 
07767     if (rb_obj_is_kind_of(ret, rb_eException)) {
07768         DUMP1("raise exception");
07769         /* rb_exc_raise(ret); */
07770         rb_exc_raise(rb_exc_new3(rb_obj_class(ret),
07771                                  rb_funcall(ret, ID_to_s, 0, 0)));
07772     }
07773 
07774     return ret;
07775 }
07776 
07777 
07778 static int
07779 ip_cancel_eval_core(interp, msg, flag)
07780     Tcl_Interp *interp;
07781     VALUE msg;
07782     int flag;
07783 {
07784 #if TCL_MAJOR_VERSION < 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION < 6)
07785     rb_raise(rb_eNotImpError,
07786              "cancel_eval is supported Tcl/Tk8.6 or later.");
07787 
07788     UNREACHABLE;
07789 #else
07790     Tcl_Obj *msg_obj;
07791 
07792     if (NIL_P(msg)) {
07793       msg_obj = NULL;
07794     } else {
07795       msg_obj = Tcl_NewStringObj(RSTRING_PTR(msg), RSTRING_LEN(msg));
07796       Tcl_IncrRefCount(msg_obj);
07797     }
07798 
07799     return Tcl_CancelEval(interp, msg_obj, 0, flag);
07800 #endif
07801 }
07802 
07803 static VALUE
07804 ip_cancel_eval(argc, argv, self)
07805     int   argc;
07806     VALUE *argv;
07807     VALUE self;
07808 {
07809     VALUE retval;
07810 
07811     if (rb_scan_args(argc, argv, "01", &retval) == 0) {
07812         retval = Qnil;
07813     }
07814     if (ip_cancel_eval_core(get_ip(self)->ip, retval, 0) == TCL_OK) {
07815       return Qtrue;
07816     } else {
07817       return Qfalse;
07818     }
07819 }
07820 
07821 #ifndef TCL_CANCEL_UNWIND
07822 #define TCL_CANCEL_UNWIND 0x100000
07823 #endif
07824 static VALUE
07825 ip_cancel_eval_unwind(argc, argv, self)
07826     int   argc;
07827     VALUE *argv;
07828     VALUE self;
07829 {
07830     int flag = 0;
07831     VALUE retval;
07832 
07833     if (rb_scan_args(argc, argv, "01", &retval) == 0) {
07834         retval = Qnil;
07835     }
07836 
07837     flag |= TCL_CANCEL_UNWIND;
07838     if (ip_cancel_eval_core(get_ip(self)->ip, retval, flag) == TCL_OK) {
07839       return Qtrue;
07840     } else {
07841       return Qfalse;
07842     }
07843 }
07844 
07845 /* restart Tk */
07846 static VALUE
07847 lib_restart_core(interp, argc, argv)
07848     VALUE interp;
07849     int   argc;   /* dummy */
07850     VALUE *argv;  /* dummy */
07851 {
07852     volatile VALUE exc;
07853     struct tcltkip *ptr = get_ip(interp);
07854     int  thr_crit_bup;
07855 
07856     /* rb_secure(4); */ /* already checked */
07857 
07858     /* tcl_stubs_check(); */ /* already checked */
07859 
07860     /* ip is deleted? */
07861     if (deleted_ip(ptr)) {
07862         return rb_exc_new2(rb_eRuntimeError, "interpreter is deleted");
07863     }
07864 
07865     thr_crit_bup = rb_thread_critical;
07866     rb_thread_critical = Qtrue;
07867 
07868     /* Tcl_Preserve(ptr->ip); */
07869     rbtk_preserve_ip(ptr);
07870 
07871     /* destroy the root wdiget */
07872     ptr->return_value = Tcl_Eval(ptr->ip, "destroy .");
07873     /* ignore ERROR */
07874     DUMP2("(TCL_Eval result) %d", ptr->return_value);
07875     Tcl_ResetResult(ptr->ip);
07876 
07877 #if TCL_MAJOR_VERSION >= 8
07878     /* delete namespace ( tested on tk8.4.5 ) */
07879     ptr->return_value = Tcl_Eval(ptr->ip, "namespace delete ::tk::msgcat");
07880     /* ignore ERROR */
07881     DUMP2("(TCL_Eval result) %d", ptr->return_value);
07882     Tcl_ResetResult(ptr->ip);
07883 #endif
07884 
07885     /* delete trace proc ( tested on tk8.4.5 ) */
07886     ptr->return_value = Tcl_Eval(ptr->ip, "trace vdelete ::tk_strictMotif w ::tk::EventMotifBindings");
07887     /* ignore ERROR */
07888     DUMP2("(TCL_Eval result) %d", ptr->return_value);
07889     Tcl_ResetResult(ptr->ip);
07890 
07891     /* execute Tk_Init or Tk_SafeInit */
07892     exc = tcltkip_init_tk(interp);
07893     if (!NIL_P(exc)) {
07894         rb_thread_critical = thr_crit_bup;
07895         rbtk_release_ip(ptr);
07896         return exc;
07897     }
07898 
07899     /* Tcl_Release(ptr->ip); */
07900     rbtk_release_ip(ptr);
07901 
07902     rb_thread_critical = thr_crit_bup;
07903 
07904     /* return Qnil; */
07905     return interp;
07906 }
07907 
07908 static VALUE
07909 lib_restart(self)
07910     VALUE self;
07911 {
07912     struct tcltkip *ptr = get_ip(self);
07913 
07914     rb_secure(4);
07915 
07916     tcl_stubs_check();
07917 
07918     /* ip is deleted? */
07919     if (deleted_ip(ptr)) {
07920         rb_raise(rb_eRuntimeError, "interpreter is deleted");
07921     }
07922 
07923     return tk_funcall(lib_restart_core, 0, (VALUE*)NULL, self);
07924 }
07925 
07926 
07927 static VALUE
07928 ip_restart(self)
07929     VALUE self;
07930 {
07931     struct tcltkip *ptr = get_ip(self);
07932 
07933     rb_secure(4);
07934 
07935     tcl_stubs_check();
07936 
07937     /* ip is deleted? */
07938     if (deleted_ip(ptr)) {
07939         rb_raise(rb_eRuntimeError, "interpreter is deleted");
07940     }
07941 
07942     if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
07943         /* slave IP */
07944         return Qnil;
07945     }
07946     return lib_restart(self);
07947 }
07948 
07949 static VALUE
07950 lib_toUTF8_core(ip_obj, src, encodename)
07951     VALUE ip_obj;
07952     VALUE src;
07953     VALUE encodename;
07954 {
07955     volatile VALUE str = src;
07956 
07957 #ifdef TCL_UTF_MAX
07958     Tcl_Interp *interp;
07959     Tcl_Encoding encoding;
07960     Tcl_DString dstr;
07961     int taint_flag = OBJ_TAINTED(str);
07962     struct tcltkip *ptr;
07963     char *buf;
07964     int thr_crit_bup;
07965 #endif
07966 
07967     tcl_stubs_check();
07968 
07969     if (NIL_P(src)) {
07970       return rb_str_new2("");
07971     }
07972 
07973 #ifdef TCL_UTF_MAX
07974     if (NIL_P(ip_obj)) {
07975         interp = (Tcl_Interp *)NULL;
07976     } else {
07977         ptr = get_ip(ip_obj);
07978 
07979         /* ip is deleted? */
07980         if (deleted_ip(ptr)) {
07981             interp = (Tcl_Interp *)NULL;
07982         } else {
07983             interp = ptr->ip;
07984         }
07985     }
07986 
07987     thr_crit_bup = rb_thread_critical;
07988     rb_thread_critical = Qtrue;
07989 
07990     if (NIL_P(encodename)) {
07991         if (TYPE(str) == T_STRING) {
07992             volatile VALUE enc;
07993 
07994 #ifdef HAVE_RUBY_ENCODING_H
07995             enc = rb_funcall(rb_obj_encoding(str), ID_to_s, 0, 0);
07996 #else
07997             enc = rb_attr_get(str, ID_at_enc);
07998 #endif
07999             if (NIL_P(enc)) {
08000                 if (NIL_P(ip_obj)) {
08001                     encoding = (Tcl_Encoding)NULL;
08002                 } else {
08003                     enc = rb_attr_get(ip_obj, ID_at_enc);
08004                     if (NIL_P(enc)) {
08005                         encoding = (Tcl_Encoding)NULL;
08006                     } else {
08007                         /* StringValue(enc); */
08008                         enc = rb_funcall(enc, ID_to_s, 0, 0);
08009                         /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
08010                         if (!RSTRING_LEN(enc)) {
08011                           encoding = (Tcl_Encoding)NULL;
08012                         } else {
08013                           encoding = Tcl_GetEncoding((Tcl_Interp*)NULL,
08014                                                      RSTRING_PTR(enc));
08015                           if (encoding == (Tcl_Encoding)NULL) {
08016                             rb_warning("Tk-interp has unknown encoding information (@encoding:'%s')", RSTRING_PTR(enc));
08017                           }
08018                         }
08019                     }
08020                 }
08021             } else {
08022                 StringValue(enc);
08023                 if (strcmp(RSTRING_PTR(enc), "binary") == 0) {
08024 #ifdef HAVE_RUBY_ENCODING_H
08025                     rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
08026 #endif
08027                     rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
08028                     rb_thread_critical = thr_crit_bup;
08029                     return str;
08030                 }
08031                 /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
08032                 encoding = Tcl_GetEncoding((Tcl_Interp*)NULL,
08033                                            RSTRING_PTR(enc));
08034                 if (encoding == (Tcl_Encoding)NULL) {
08035                     rb_warning("string has unknown encoding information (@encoding:'%s')", RSTRING_PTR(enc));
08036                 }
08037             }
08038         } else {
08039             encoding = (Tcl_Encoding)NULL;
08040         }
08041     } else {
08042         StringValue(encodename);
08043         if (strcmp(RSTRING_PTR(encodename), "binary") == 0) {
08044 #ifdef HAVE_RUBY_ENCODING_H
08045           rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
08046 #endif
08047           rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
08048           rb_thread_critical = thr_crit_bup;
08049           return str;
08050         }
08051         /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(encodename)); */
08052         encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(encodename));
08053         if (encoding == (Tcl_Encoding)NULL) {
08054             /*
08055             rb_warning("unknown encoding name '%s'",
08056                        RSTRING_PTR(encodename));
08057             */
08058             rb_raise(rb_eArgError, "unknown encoding name '%s'",
08059                      RSTRING_PTR(encodename));
08060         }
08061     }
08062 
08063     StringValue(str);
08064     if (!RSTRING_LEN(str)) {
08065         rb_thread_critical = thr_crit_bup;
08066         return str;
08067     }
08068     buf = ALLOC_N(char, RSTRING_LEN(str)+1);
08069     /* buf = ckalloc(sizeof(char) * (RSTRING_LENINT(str)+1)); */
08070     memcpy(buf, RSTRING_PTR(str), RSTRING_LEN(str));
08071     buf[RSTRING_LEN(str)] = 0;
08072 
08073     Tcl_DStringInit(&dstr);
08074     Tcl_DStringFree(&dstr);
08075     /* Tcl_ExternalToUtfDString(encoding,buf,strlen(buf),&dstr); */
08076     Tcl_ExternalToUtfDString(encoding, buf, RSTRING_LENINT(str), &dstr);
08077 
08078     /* str = rb_tainted_str_new2(Tcl_DStringValue(&dstr)); */
08079     /* str = rb_str_new2(Tcl_DStringValue(&dstr)); */
08080     str = rb_str_new(Tcl_DStringValue(&dstr), Tcl_DStringLength(&dstr));
08081 #ifdef HAVE_RUBY_ENCODING_H
08082     rb_enc_associate_index(str, ENCODING_INDEX_UTF8);
08083 #endif
08084     if (taint_flag) RbTk_OBJ_UNTRUST(str);
08085     rb_ivar_set(str, ID_at_enc, ENCODING_NAME_UTF8);
08086 
08087     /*
08088     if (encoding != (Tcl_Encoding)NULL) {
08089         Tcl_FreeEncoding(encoding);
08090     }
08091     */
08092     Tcl_DStringFree(&dstr);
08093 
08094     xfree(buf);
08095     /* ckfree(buf); */
08096 
08097     rb_thread_critical = thr_crit_bup;
08098 #endif
08099 
08100     return str;
08101 }
08102 
08103 static VALUE
08104 lib_toUTF8(argc, argv, self)
08105     int   argc;
08106     VALUE *argv;
08107     VALUE self;
08108 {
08109     VALUE str, encodename;
08110 
08111     if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) {
08112         encodename = Qnil;
08113     }
08114     return lib_toUTF8_core(Qnil, str, encodename);
08115 }
08116 
08117 static VALUE
08118 ip_toUTF8(argc, argv, self)
08119     int   argc;
08120     VALUE *argv;
08121     VALUE self;
08122 {
08123     VALUE str, encodename;
08124 
08125     if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) {
08126         encodename = Qnil;
08127     }
08128     return lib_toUTF8_core(self, str, encodename);
08129 }
08130 
08131 static VALUE
08132 lib_fromUTF8_core(ip_obj, src, encodename)
08133     VALUE ip_obj;
08134     VALUE src;
08135     VALUE encodename;
08136 {
08137     volatile VALUE str = src;
08138 
08139 #ifdef TCL_UTF_MAX
08140     Tcl_Interp *interp;
08141     Tcl_Encoding encoding;
08142     Tcl_DString dstr;
08143     int taint_flag = OBJ_TAINTED(str);
08144     char *buf;
08145     int thr_crit_bup;
08146 #endif
08147 
08148     tcl_stubs_check();
08149 
08150     if (NIL_P(src)) {
08151       return rb_str_new2("");
08152     }
08153 
08154 #ifdef TCL_UTF_MAX
08155     if (NIL_P(ip_obj)) {
08156         interp = (Tcl_Interp *)NULL;
08157     } else if (get_ip(ip_obj) == (struct tcltkip *)NULL) {
08158         interp = (Tcl_Interp *)NULL;
08159     } else {
08160         interp = get_ip(ip_obj)->ip;
08161     }
08162 
08163     thr_crit_bup = rb_thread_critical;
08164     rb_thread_critical = Qtrue;
08165 
08166     if (NIL_P(encodename)) {
08167         volatile VALUE enc;
08168 
08169         if (TYPE(str) == T_STRING) {
08170             enc = rb_attr_get(str, ID_at_enc);
08171             if (!NIL_P(enc)) {
08172                 StringValue(enc);
08173                 if (strcmp(RSTRING_PTR(enc), "binary") == 0) {
08174 #ifdef HAVE_RUBY_ENCODING_H
08175                     rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
08176 #endif
08177                     rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
08178                     rb_thread_critical = thr_crit_bup;
08179                     return str;
08180                 }
08181 #ifdef HAVE_RUBY_ENCODING_H
08182             } else if (rb_enc_get_index(str) == ENCODING_INDEX_BINARY) {
08183                 rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
08184                 rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
08185                 rb_thread_critical = thr_crit_bup;
08186                 return str;
08187 #endif
08188             }
08189         }
08190 
08191         if (NIL_P(ip_obj)) {
08192             encoding = (Tcl_Encoding)NULL;
08193         } else {
08194             enc = rb_attr_get(ip_obj, ID_at_enc);
08195             if (NIL_P(enc)) {
08196                 encoding = (Tcl_Encoding)NULL;
08197             } else {
08198                 /* StringValue(enc); */
08199                 enc = rb_funcall(enc, ID_to_s, 0, 0);
08200                 /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
08201                 if (!RSTRING_LEN(enc)) {
08202                   encoding = (Tcl_Encoding)NULL;
08203                 } else {
08204                   encoding = Tcl_GetEncoding((Tcl_Interp*)NULL,
08205                                              RSTRING_PTR(enc));
08206                   if (encoding == (Tcl_Encoding)NULL) {
08207                     rb_warning("Tk-interp has unknown encoding information (@encoding:'%s')", RSTRING_PTR(enc));
08208                   } else {
08209                     encodename = rb_obj_dup(enc);
08210                   }
08211                 }
08212             }
08213         }
08214 
08215     } else {
08216         StringValue(encodename);
08217 
08218         if (strcmp(RSTRING_PTR(encodename), "binary") == 0) {
08219             Tcl_Obj *tclstr;
08220             char *s;
08221             int  len;
08222 
08223             StringValue(str);
08224             tclstr = Tcl_NewStringObj(RSTRING_PTR(str), RSTRING_LENINT(str));
08225             Tcl_IncrRefCount(tclstr);
08226             s = (char*)Tcl_GetByteArrayFromObj(tclstr, &len);
08227             str = rb_tainted_str_new(s, len);
08228             s = (char*)NULL;
08229             Tcl_DecrRefCount(tclstr);
08230 #ifdef HAVE_RUBY_ENCODING_H
08231             rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
08232 #endif
08233             rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
08234 
08235             rb_thread_critical = thr_crit_bup;
08236             return str;
08237         }
08238 
08239         /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(encodename)); */
08240         encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(encodename));
08241         if (encoding == (Tcl_Encoding)NULL) {
08242             /*
08243             rb_warning("unknown encoding name '%s'",
08244                        RSTRING_PTR(encodename));
08245             encodename = Qnil;
08246             */
08247             rb_raise(rb_eArgError, "unknown encoding name '%s'",
08248                      RSTRING_PTR(encodename));
08249         }
08250     }
08251 
08252     StringValue(str);
08253 
08254     if (RSTRING_LEN(str) == 0) {
08255         rb_thread_critical = thr_crit_bup;
08256         return rb_tainted_str_new2("");
08257     }
08258 
08259     buf = ALLOC_N(char, RSTRING_LEN(str)+1);
08260     /* buf = ckalloc(sizeof(char) * (RSTRING_LENINT(str)+1)); */
08261     memcpy(buf, RSTRING_PTR(str), RSTRING_LEN(str));
08262     buf[RSTRING_LEN(str)] = 0;
08263 
08264     Tcl_DStringInit(&dstr);
08265     Tcl_DStringFree(&dstr);
08266     /* Tcl_UtfToExternalDString(encoding,buf,strlen(buf),&dstr); */
08267     Tcl_UtfToExternalDString(encoding,buf,RSTRING_LENINT(str),&dstr);
08268 
08269     /* str = rb_tainted_str_new2(Tcl_DStringValue(&dstr)); */
08270     /* str = rb_str_new2(Tcl_DStringValue(&dstr)); */
08271     str = rb_str_new(Tcl_DStringValue(&dstr), Tcl_DStringLength(&dstr));
08272 #ifdef HAVE_RUBY_ENCODING_H
08273     if (interp) {
08274       /* can access encoding_table of TclTkIp */
08275       /*   ->  try to use encoding_table      */
08276       VALUE tbl = ip_get_encoding_table(ip_obj);
08277       VALUE encobj = encoding_table_get_obj(tbl, encodename);
08278       rb_enc_associate_index(str, rb_to_encoding_index(encobj));
08279     } else {
08280       /* cannot access encoding_table of TclTkIp */
08281       /*   ->  try to find on Ruby Encoding      */
08282       rb_enc_associate_index(str, rb_enc_find_index(RSTRING_PTR(encodename)));
08283     }
08284 #endif
08285 
08286     if (taint_flag) RbTk_OBJ_UNTRUST(str);
08287     rb_ivar_set(str, ID_at_enc, encodename);
08288 
08289     /*
08290     if (encoding != (Tcl_Encoding)NULL) {
08291         Tcl_FreeEncoding(encoding);
08292     }
08293     */
08294     Tcl_DStringFree(&dstr);
08295 
08296     xfree(buf);
08297     /* ckfree(buf); */
08298 
08299     rb_thread_critical = thr_crit_bup;
08300 #endif
08301 
08302     return str;
08303 }
08304 
08305 static VALUE
08306 lib_fromUTF8(argc, argv, self)
08307     int   argc;
08308     VALUE *argv;
08309     VALUE self;
08310 {
08311     VALUE str, encodename;
08312 
08313     if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) {
08314         encodename = Qnil;
08315     }
08316     return lib_fromUTF8_core(Qnil, str, encodename);
08317 }
08318 
08319 static VALUE
08320 ip_fromUTF8(argc, argv, self)
08321     int   argc;
08322     VALUE *argv;
08323     VALUE self;
08324 {
08325     VALUE str, encodename;
08326 
08327     if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) {
08328         encodename = Qnil;
08329     }
08330     return lib_fromUTF8_core(self, str, encodename);
08331 }
08332 
08333 static VALUE
08334 lib_UTF_backslash_core(self, str, all_bs)
08335     VALUE self;
08336     VALUE str;
08337     int all_bs;
08338 {
08339 #ifdef TCL_UTF_MAX
08340     char *src_buf, *dst_buf, *ptr;
08341     int read_len = 0, dst_len = 0;
08342     int taint_flag = OBJ_TAINTED(str);
08343     int thr_crit_bup;
08344 
08345     tcl_stubs_check();
08346 
08347     StringValue(str);
08348     if (!RSTRING_LEN(str)) {
08349         return str;
08350     }
08351 
08352     thr_crit_bup = rb_thread_critical;
08353     rb_thread_critical = Qtrue;
08354 
08355     /* src_buf = ALLOC_N(char, RSTRING_LEN(str)+1); */
08356     src_buf = ckalloc(RSTRING_LENINT(str)+1);
08357 #if 0 /* use Tcl_Preserve/Release */
08358     Tcl_Preserve((ClientData)src_buf); /* XXXXXXXX */
08359 #endif
08360     memcpy(src_buf, RSTRING_PTR(str), RSTRING_LEN(str));
08361     src_buf[RSTRING_LEN(str)] = 0;
08362 
08363     /* dst_buf = ALLOC_N(char, RSTRING_LEN(str)+1); */
08364     dst_buf = ckalloc(RSTRING_LENINT(str)+1);
08365 #if 0 /* use Tcl_Preserve/Release */
08366     Tcl_Preserve((ClientData)dst_buf); /* XXXXXXXX */
08367 #endif
08368 
08369     ptr = src_buf;
08370     while(RSTRING_LEN(str) > ptr - src_buf) {
08371         if (*ptr == '\\' && (all_bs || *(ptr + 1) == 'u')) {
08372             dst_len += Tcl_UtfBackslash(ptr, &read_len, (dst_buf + dst_len));
08373             ptr += read_len;
08374         } else {
08375             *(dst_buf + (dst_len++)) = *(ptr++);
08376         }
08377     }
08378 
08379     str = rb_str_new(dst_buf, dst_len);
08380     if (taint_flag) RbTk_OBJ_UNTRUST(str);
08381 #ifdef HAVE_RUBY_ENCODING_H
08382     rb_enc_associate_index(str, ENCODING_INDEX_UTF8);
08383 #endif
08384     rb_ivar_set(str, ID_at_enc, ENCODING_NAME_UTF8);
08385 
08386 #if 0 /* use Tcl_EventuallyFree */
08387     Tcl_EventuallyFree((ClientData)src_buf, TCL_DYNAMIC); /* XXXXXXXX */
08388 #else
08389 #if 0 /* use Tcl_Preserve/Release */
08390     Tcl_Release((ClientData)src_buf); /* XXXXXXXX */
08391 #else
08392     /* free(src_buf); */
08393     ckfree(src_buf);
08394 #endif
08395 #endif
08396 #if 0 /* use Tcl_EventuallyFree */
08397     Tcl_EventuallyFree((ClientData)dst_buf, TCL_DYNAMIC); /* XXXXXXXX */
08398 #else
08399 #if 0 /* use Tcl_Preserve/Release */
08400     Tcl_Release((ClientData)dst_buf); /* XXXXXXXX */
08401 #else
08402     /* free(dst_buf); */
08403     ckfree(dst_buf);
08404 #endif
08405 #endif
08406 
08407     rb_thread_critical = thr_crit_bup;
08408 #endif
08409 
08410     return str;
08411 }
08412 
08413 static VALUE
08414 lib_UTF_backslash(self, str)
08415     VALUE self;
08416     VALUE str;
08417 {
08418     return lib_UTF_backslash_core(self, str, 0);
08419 }
08420 
08421 static VALUE
08422 lib_Tcl_backslash(self, str)
08423     VALUE self;
08424     VALUE str;
08425 {
08426     return lib_UTF_backslash_core(self, str, 1);
08427 }
08428 
08429 static VALUE
08430 lib_get_system_encoding(self)
08431     VALUE self;
08432 {
08433 #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
08434     tcl_stubs_check();
08435     return rb_str_new2(Tcl_GetEncodingName((Tcl_Encoding)NULL));
08436 #else
08437     return Qnil;
08438 #endif
08439 }
08440 
08441 static VALUE
08442 lib_set_system_encoding(self, enc_name)
08443     VALUE self;
08444     VALUE enc_name;
08445 {
08446 #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
08447     tcl_stubs_check();
08448 
08449     if (NIL_P(enc_name)) {
08450         Tcl_SetSystemEncoding((Tcl_Interp *)NULL, (CONST char *)NULL);
08451         return lib_get_system_encoding(self);
08452     }
08453 
08454     enc_name = rb_funcall(enc_name, ID_to_s, 0, 0);
08455     if (Tcl_SetSystemEncoding((Tcl_Interp *)NULL,
08456                               StringValuePtr(enc_name)) != TCL_OK) {
08457         rb_raise(rb_eArgError, "unknown encoding name '%s'",
08458                  RSTRING_PTR(enc_name));
08459     }
08460 
08461     return enc_name;
08462 #else
08463     return Qnil;
08464 #endif
08465 }
08466 
08467 
08468 /* invoke Tcl proc */
08469 struct invoke_info {
08470     struct tcltkip *ptr;
08471     Tcl_CmdInfo cmdinfo;
08472 #if TCL_MAJOR_VERSION >= 8
08473     int objc;
08474     Tcl_Obj **objv;
08475 #else
08476     int argc;
08477     char **argv;
08478 #endif
08479 };
08480 
08481 static VALUE
08482 #ifdef HAVE_PROTOTYPES
08483 invoke_tcl_proc(VALUE arg)
08484 #else
08485 invoke_tcl_proc(arg)
08486     VALUE arg;
08487 #endif
08488 {
08489     struct invoke_info *inf = (struct invoke_info *)arg;
08490     int i, len;
08491 #if TCL_MAJOR_VERSION >= 8
08492     int argc = inf->objc;
08493     char **argv = (char **)NULL;
08494 #endif
08495 
08496     /* memory allocation for arguments of this command */
08497 #if TCL_MAJOR_VERSION >= 8
08498     if (!inf->cmdinfo.isNativeObjectProc) {
08499         /* string interface */
08500         /* argv = (char **)ALLOC_N(char *, argc+1);*/ /* XXXXXXXXXX */
08501         argv = RbTk_ALLOC_N(char *, (argc+1));
08502 #if 0 /* use Tcl_Preserve/Release */
08503         Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
08504 #endif
08505         for (i = 0; i < argc; ++i) {
08506             argv[i] = Tcl_GetStringFromObj(inf->objv[i], &len);
08507         }
08508         argv[argc] = (char *)NULL;
08509     }
08510 #endif
08511 
08512     Tcl_ResetResult(inf->ptr->ip);
08513 
08514     /* Invoke the C procedure */
08515 #if TCL_MAJOR_VERSION >= 8
08516     if (inf->cmdinfo.isNativeObjectProc) {
08517         inf->ptr->return_value
08518             = (*(inf->cmdinfo.objProc))(inf->cmdinfo.objClientData,
08519                                         inf->ptr->ip, inf->objc, inf->objv);
08520     }
08521     else
08522 #endif
08523     {
08524 #if TCL_MAJOR_VERSION >= 8
08525         inf->ptr->return_value
08526             = (*(inf->cmdinfo.proc))(inf->cmdinfo.clientData, inf->ptr->ip,
08527                                      argc, (CONST84 char **)argv);
08528 
08529 #if 0 /* use Tcl_EventuallyFree */
08530     Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
08531 #else
08532 #if 0 /* use Tcl_Preserve/Release */
08533         Tcl_Release((ClientData)argv); /* XXXXXXXX */
08534 #else
08535         /* free(argv); */
08536         ckfree((char*)argv);
08537 #endif
08538 #endif
08539 
08540 #else /* TCL_MAJOR_VERSION < 8 */
08541         inf->ptr->return_value
08542             = (*(inf->cmdinfo.proc))(inf->cmdinfo.clientData, inf->ptr->ip,
08543                                      inf->argc, inf->argv);
08544 #endif
08545     }
08546 
08547     return Qnil;
08548 }
08549 
08550 
08551 #if TCL_MAJOR_VERSION >= 8
08552 static VALUE
08553 ip_invoke_core(interp, objc, objv)
08554     VALUE interp;
08555     int objc;
08556     Tcl_Obj **objv;
08557 #else
08558 static VALUE
08559 ip_invoke_core(interp, argc, argv)
08560     VALUE interp;
08561     int argc;
08562     char **argv;
08563 #endif
08564 {
08565     struct tcltkip *ptr;
08566     Tcl_CmdInfo info;
08567     char *cmd;
08568     int  len;
08569     int  thr_crit_bup;
08570     int unknown_flag = 0;
08571 
08572 #if 1 /* wrap tcl-proc call */
08573     struct invoke_info inf;
08574     int status;
08575     VALUE ret;
08576 #else
08577 #if TCL_MAJOR_VERSION >= 8
08578     int argc = objc;
08579     char **argv = (char **)NULL;
08580     /* Tcl_Obj *resultPtr; */
08581 #endif
08582 #endif
08583 
08584     /* get the data struct */
08585     ptr = get_ip(interp);
08586 
08587     /* get the command name string */
08588 #if TCL_MAJOR_VERSION >= 8
08589     cmd = Tcl_GetStringFromObj(objv[0], &len);
08590 #else /* TCL_MAJOR_VERSION < 8 */
08591     cmd = argv[0];
08592 #endif
08593 
08594     /* get the data struct */
08595     ptr = get_ip(interp);
08596 
08597     /* ip is deleted? */
08598     if (deleted_ip(ptr)) {
08599         return rb_tainted_str_new2("");
08600     }
08601 
08602     /* Tcl_Preserve(ptr->ip); */
08603     rbtk_preserve_ip(ptr);
08604 
08605     /* map from the command name to a C procedure */
08606     DUMP2("call Tcl_GetCommandInfo, %s", cmd);
08607     if (!Tcl_GetCommandInfo(ptr->ip, cmd, &info)) {
08608         DUMP1("error Tcl_GetCommandInfo");
08609         DUMP1("try auto_load (call 'unknown' command)");
08610         if (!Tcl_GetCommandInfo(ptr->ip,
08611 #if TCL_MAJOR_VERSION >= 8
08612                                 "::unknown",
08613 #else
08614                                 "unknown",
08615 #endif
08616                                 &info)) {
08617             DUMP1("fail to get 'unknown' command");
08618             /* if (event_loop_abort_on_exc || cmd[0] != '.') { */
08619             if (event_loop_abort_on_exc > 0) {
08620                 /* Tcl_Release(ptr->ip); */
08621                 rbtk_release_ip(ptr);
08622                 /*rb_ip_raise(obj,rb_eNameError,"invalid command name `%s'",cmd);*/
08623                 return create_ip_exc(interp, rb_eNameError,
08624                                      "invalid command name `%s'", cmd);
08625             } else {
08626                 if (event_loop_abort_on_exc < 0) {
08627                     rb_warning("invalid command name `%s' (ignore)", cmd);
08628                 } else {
08629                     rb_warn("invalid command name `%s' (ignore)", cmd);
08630                 }
08631                 Tcl_ResetResult(ptr->ip);
08632                 /* Tcl_Release(ptr->ip); */
08633                 rbtk_release_ip(ptr);
08634                 return rb_tainted_str_new2("");
08635             }
08636         } else {
08637 #if TCL_MAJOR_VERSION >= 8
08638             Tcl_Obj **unknown_objv;
08639 #else
08640             char **unknown_argv;
08641 #endif
08642             DUMP1("find 'unknown' command -> set arguemnts");
08643             unknown_flag = 1;
08644 
08645 #if TCL_MAJOR_VERSION >= 8
08646             /* unknown_objv = (Tcl_Obj **)ALLOC_N(Tcl_Obj *, objc+2); */
08647             unknown_objv = RbTk_ALLOC_N(Tcl_Obj *, (objc+2));
08648 #if 0 /* use Tcl_Preserve/Release */
08649             Tcl_Preserve((ClientData)unknown_objv); /* XXXXXXXX */
08650 #endif
08651             unknown_objv[0] = Tcl_NewStringObj("::unknown", 9);
08652             Tcl_IncrRefCount(unknown_objv[0]);
08653             memcpy(unknown_objv + 1, objv, sizeof(Tcl_Obj *)*objc);
08654             unknown_objv[++objc] = (Tcl_Obj*)NULL;
08655             objv = unknown_objv;
08656 #else
08657             /* unknown_argv = (char **)ALLOC_N(char *, argc+2); */
08658             unknown_argv = RbTk_ALLOC_N(char *, (argc+2));
08659 #if 0 /* use Tcl_Preserve/Release */
08660             Tcl_Preserve((ClientData)unknown_argv); /* XXXXXXXX */
08661 #endif
08662             unknown_argv[0] = strdup("unknown");
08663             memcpy(unknown_argv + 1, argv, sizeof(char *)*argc);
08664             unknown_argv[++argc] = (char *)NULL;
08665             argv = unknown_argv;
08666 #endif
08667         }
08668     }
08669     DUMP1("end Tcl_GetCommandInfo");
08670 
08671     thr_crit_bup = rb_thread_critical;
08672     rb_thread_critical = Qtrue;
08673 
08674 #if 1 /* wrap tcl-proc call */
08675     /* setup params */
08676     inf.ptr = ptr;
08677     inf.cmdinfo = info;
08678 #if TCL_MAJOR_VERSION >= 8
08679     inf.objc = objc;
08680     inf.objv = objv;
08681 #else
08682     inf.argc = argc;
08683     inf.argv = argv;
08684 #endif
08685 
08686     /* invoke tcl-proc */
08687     ret = rb_protect(invoke_tcl_proc, (VALUE)&inf, &status);
08688     switch(status) {
08689     case TAG_RAISE:
08690         if (NIL_P(rb_errinfo())) {
08691             rbtk_pending_exception = rb_exc_new2(rb_eException,
08692                                                  "unknown exception");
08693         } else {
08694             rbtk_pending_exception = rb_errinfo();
08695         }
08696         break;
08697 
08698     case TAG_FATAL:
08699         if (NIL_P(rb_errinfo())) {
08700             rbtk_pending_exception = rb_exc_new2(rb_eFatal, "FATAL");
08701         } else {
08702             rbtk_pending_exception = rb_errinfo();
08703         }
08704     }
08705 
08706 #else /* !wrap tcl-proc call */
08707 
08708     /* memory allocation for arguments of this command */
08709 #if TCL_MAJOR_VERSION >= 8
08710     if (!info.isNativeObjectProc) {
08711         int i;
08712 
08713         /* string interface */
08714         /* argv = (char **)ALLOC_N(char *, argc+1); */
08715         argv = RbTk_ALLOC_N(char *, (argc+1));
08716 #if 0 /* use Tcl_Preserve/Release */
08717         Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
08718 #endif
08719         for (i = 0; i < argc; ++i) {
08720             argv[i] = Tcl_GetStringFromObj(objv[i], &len);
08721         }
08722         argv[argc] = (char *)NULL;
08723     }
08724 #endif
08725 
08726     Tcl_ResetResult(ptr->ip);
08727 
08728     /* Invoke the C procedure */
08729 #if TCL_MAJOR_VERSION >= 8
08730     if (info.isNativeObjectProc) {
08731         ptr->return_value = (*info.objProc)(info.objClientData, ptr->ip,
08732                                             objc, objv);
08733 #if 0
08734         /* get the string value from the result object */
08735         resultPtr = Tcl_GetObjResult(ptr->ip);
08736         Tcl_SetResult(ptr->ip, Tcl_GetStringFromObj(resultPtr, &len),
08737                       TCL_VOLATILE);
08738 #endif
08739     }
08740     else
08741 #endif
08742     {
08743 #if TCL_MAJOR_VERSION >= 8
08744         ptr->return_value = (*info.proc)(info.clientData, ptr->ip,
08745                                          argc, (CONST84 char **)argv);
08746 
08747 #if 0 /* use Tcl_EventuallyFree */
08748     Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
08749 #else
08750 #if 0 /* use Tcl_Preserve/Release */
08751         Tcl_Release((ClientData)argv); /* XXXXXXXX */
08752 #else
08753         /* free(argv); */
08754         ckfree((char*)argv);
08755 #endif
08756 #endif
08757 
08758 #else /* TCL_MAJOR_VERSION < 8 */
08759         ptr->return_value = (*info.proc)(info.clientData, ptr->ip,
08760                                          argc, argv);
08761 #endif
08762     }
08763 #endif /* ! wrap tcl-proc call */
08764 
08765     /* free allocated memory for calling 'unknown' command */
08766     if (unknown_flag) {
08767 #if TCL_MAJOR_VERSION >= 8
08768         Tcl_DecrRefCount(objv[0]);
08769 #if 0 /* use Tcl_EventuallyFree */
08770         Tcl_EventuallyFree((ClientData)objv, TCL_DYNAMIC); /* XXXXXXXX */
08771 #else
08772 #if 0 /* use Tcl_Preserve/Release */
08773         Tcl_Release((ClientData)objv); /* XXXXXXXX */
08774 #else
08775         /* free(objv); */
08776         ckfree((char*)objv);
08777 #endif
08778 #endif
08779 #else /* TCL_MAJOR_VERSION < 8 */
08780         free(argv[0]);
08781         /* ckfree(argv[0]); */
08782 #if 0 /* use Tcl_EventuallyFree */
08783         Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
08784 #else
08785 #if 0 /* use Tcl_Preserve/Release */
08786         Tcl_Release((ClientData)argv); /* XXXXXXXX */
08787 #else
08788         /* free(argv); */
08789         ckfree((char*)argv);
08790 #endif
08791 #endif
08792 #endif
08793     }
08794 
08795     /* exception on mainloop */
08796     if (pending_exception_check1(thr_crit_bup, ptr)) {
08797         return rbtk_pending_exception;
08798     }
08799 
08800     rb_thread_critical = thr_crit_bup;
08801 
08802     /* if (ptr->return_value == TCL_ERROR) { */
08803     if (ptr->return_value != TCL_OK) {
08804         if (event_loop_abort_on_exc > 0 && !Tcl_InterpDeleted(ptr->ip)) {
08805             switch (ptr->return_value) {
08806             case TCL_RETURN:
08807               return create_ip_exc(interp, eTkCallbackReturn,
08808                                    "ip_invoke_core receives TCL_RETURN");
08809             case TCL_BREAK:
08810               return create_ip_exc(interp, eTkCallbackBreak,
08811                                    "ip_invoke_core receives TCL_BREAK");
08812             case TCL_CONTINUE:
08813               return create_ip_exc(interp, eTkCallbackContinue,
08814                                    "ip_invoke_core receives TCL_CONTINUE");
08815             default:
08816               return create_ip_exc(interp, rb_eRuntimeError, "%s",
08817                                    Tcl_GetStringResult(ptr->ip));
08818             }
08819 
08820         } else {
08821             if (event_loop_abort_on_exc < 0) {
08822                 rb_warning("%s (ignore)", Tcl_GetStringResult(ptr->ip));
08823             } else {
08824                 rb_warn("%s (ignore)", Tcl_GetStringResult(ptr->ip));
08825             }
08826             Tcl_ResetResult(ptr->ip);
08827             return rb_tainted_str_new2("");
08828         }
08829     }
08830 
08831     /* pass back the result (as string) */
08832     return ip_get_result_string_obj(ptr->ip);
08833 }
08834 
08835 
08836 #if TCL_MAJOR_VERSION >= 8
08837 static Tcl_Obj **
08838 #else /* TCL_MAJOR_VERSION < 8 */
08839 static char **
08840 #endif
08841 alloc_invoke_arguments(argc, argv)
08842     int argc;
08843     VALUE *argv;
08844 {
08845     int i;
08846     int thr_crit_bup;
08847 
08848 #if TCL_MAJOR_VERSION >= 8
08849     Tcl_Obj **av;
08850 #else /* TCL_MAJOR_VERSION < 8 */
08851     char **av;
08852 #endif
08853 
08854     thr_crit_bup = rb_thread_critical;
08855     rb_thread_critical = Qtrue;
08856 
08857     /* memory allocation */
08858 #if TCL_MAJOR_VERSION >= 8
08859     /* av = ALLOC_N(Tcl_Obj *, argc+1);*/ /* XXXXXXXXXX */
08860     av = RbTk_ALLOC_N(Tcl_Obj *, (argc+1));
08861 #if 0 /* use Tcl_Preserve/Release */
08862     Tcl_Preserve((ClientData)av); /* XXXXXXXX */
08863 #endif
08864     for (i = 0; i < argc; ++i) {
08865         av[i] = get_obj_from_str(argv[i]);
08866         Tcl_IncrRefCount(av[i]);
08867     }
08868     av[argc] = NULL;
08869 
08870 #else /* TCL_MAJOR_VERSION < 8 */
08871     /* string interface */
08872     /* av = ALLOC_N(char *, argc+1); */
08873     av = RbTk_ALLOC_N(char *, (argc+1));
08874 #if 0 /* use Tcl_Preserve/Release */
08875     Tcl_Preserve((ClientData)av); /* XXXXXXXX */
08876 #endif
08877     for (i = 0; i < argc; ++i) {
08878         av[i] = strdup(StringValuePtr(argv[i]));
08879     }
08880     av[argc] = NULL;
08881 #endif
08882 
08883     rb_thread_critical = thr_crit_bup;
08884 
08885     return av;
08886 }
08887 
08888 static void
08889 free_invoke_arguments(argc, av)
08890     int argc;
08891 #if TCL_MAJOR_VERSION >= 8
08892     Tcl_Obj **av;
08893 #else /* TCL_MAJOR_VERSION < 8 */
08894     char **av;
08895 #endif
08896 {
08897     int i;
08898 
08899     for (i = 0; i < argc; ++i) {
08900 #if TCL_MAJOR_VERSION >= 8
08901         Tcl_DecrRefCount(av[i]);
08902         av[i] = (Tcl_Obj*)NULL;
08903 #else /* TCL_MAJOR_VERSION < 8 */
08904         free(av[i]);
08905         av[i] = (char*)NULL;
08906 #endif
08907     }
08908 #if TCL_MAJOR_VERSION >= 8
08909 #if 0 /* use Tcl_EventuallyFree */
08910     Tcl_EventuallyFree((ClientData)av, TCL_DYNAMIC); /* XXXXXXXX */
08911 #else
08912 #if 0 /* use Tcl_Preserve/Release */
08913     Tcl_Release((ClientData)av); /* XXXXXXXX */
08914 #else
08915     ckfree((char*)av);
08916 #endif
08917 #endif
08918 #else /* TCL_MAJOR_VERSION < 8 */
08919 #if 0 /* use Tcl_EventuallyFree */
08920     Tcl_EventuallyFree((ClientData)av, TCL_DYNAMIC); /* XXXXXXXX */
08921 #else
08922 #if 0 /* use Tcl_Preserve/Release */
08923     Tcl_Release((ClientData)av); /* XXXXXXXX */
08924 #else
08925     /* free(av); */
08926     ckfree((char*)av);
08927 #endif
08928 #endif
08929 #endif
08930 }
08931 
08932 static VALUE
08933 ip_invoke_real(argc, argv, interp)
08934     int argc;
08935     VALUE *argv;
08936     VALUE interp;
08937 {
08938     VALUE v;
08939     struct tcltkip *ptr;        /* tcltkip data struct */
08940 
08941 #if TCL_MAJOR_VERSION >= 8
08942     Tcl_Obj **av = (Tcl_Obj **)NULL;
08943 #else /* TCL_MAJOR_VERSION < 8 */
08944     char **av = (char **)NULL;
08945 #endif
08946 
08947     DUMP2("invoke_real called by thread:%lx", rb_thread_current());
08948 
08949     /* get the data struct */
08950     ptr = get_ip(interp);
08951 
08952     /* ip is deleted? */
08953     if (deleted_ip(ptr)) {
08954         return rb_tainted_str_new2("");
08955     }
08956 
08957     /* allocate memory for arguments */
08958     av = alloc_invoke_arguments(argc, argv);
08959 
08960     /* Invoke the C procedure */
08961     Tcl_ResetResult(ptr->ip);
08962     v = ip_invoke_core(interp, argc, av);
08963 
08964     /* free allocated memory */
08965     free_invoke_arguments(argc, av);
08966 
08967     return v;
08968 }
08969 
08970 VALUE
08971 ivq_safelevel_handler(arg, ivq)
08972     VALUE arg;
08973     VALUE ivq;
08974 {
08975     struct invoke_queue *q;
08976 
08977     Data_Get_Struct(ivq, struct invoke_queue, q);
08978     DUMP2("(safe-level handler) $SAFE = %d", q->safe_level);
08979     rb_set_safe_level(q->safe_level);
08980     return ip_invoke_core(q->interp, q->argc, q->argv);
08981 }
08982 
08983 int invoke_queue_handler _((Tcl_Event *, int));
08984 int
08985 invoke_queue_handler(evPtr, flags)
08986     Tcl_Event *evPtr;
08987     int flags;
08988 {
08989     struct invoke_queue *q = (struct invoke_queue *)evPtr;
08990     volatile VALUE ret;
08991     volatile VALUE q_dat;
08992     volatile VALUE thread = q->thread;
08993     struct tcltkip *ptr;
08994 
08995     DUMP2("do_invoke_queue_handler : evPtr = %p", evPtr);
08996     DUMP2("invoke queue_thread : %lx", rb_thread_current());
08997     DUMP2("added by thread : %lx", thread);
08998 
08999     if (*(q->done)) {
09000         DUMP1("processed by another event-loop");
09001         return 0;
09002     } else {
09003         DUMP1("process it on current event-loop");
09004     }
09005 
09006 #ifdef RUBY_VM
09007     if (RTEST(rb_funcall(thread, ID_alive_p, 0))
09008         && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
09009 #else
09010     if (RTEST(rb_thread_alive_p(thread))
09011         && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
09012 #endif
09013       DUMP1("caller is not yet ready to receive the result -> pending");
09014       return 0;
09015     }
09016 
09017     /* process it */
09018     *(q->done) = 1;
09019 
09020     /* deleted ipterp ? */
09021     ptr = get_ip(q->interp);
09022     if (deleted_ip(ptr)) {
09023         /* deleted IP --> ignore */
09024         return 1;
09025     }
09026 
09027     /* incr internal handler mark */
09028     rbtk_internal_eventloop_handler++;
09029 
09030     /* check safe-level */
09031     if (rb_safe_level() != q->safe_level) {
09032         /* q_dat = Data_Wrap_Struct(rb_cData,0,0,q); */
09033         q_dat = Data_Wrap_Struct(rb_cData,invoke_queue_mark,-1,q);
09034         ret = rb_funcall(rb_proc_new(ivq_safelevel_handler, q_dat),
09035                          ID_call, 0);
09036         rb_gc_force_recycle(q_dat);
09037         q_dat = (VALUE)NULL;
09038     } else {
09039         DUMP2("call invoke_real (for caller thread:%lx)", thread);
09040         DUMP2("call invoke_real (current thread:%lx)", rb_thread_current());
09041         ret = ip_invoke_core(q->interp, q->argc, q->argv);
09042     }
09043 
09044     /* set result */
09045     RARRAY_PTR(q->result)[0] = ret;
09046     ret = (VALUE)NULL;
09047 
09048     /* decr internal handler mark */
09049     rbtk_internal_eventloop_handler--;
09050 
09051     /* complete */
09052     *(q->done) = -1;
09053 
09054     /* unlink ruby objects */
09055     q->interp = (VALUE)NULL;
09056     q->result = (VALUE)NULL;
09057     q->thread = (VALUE)NULL;
09058 
09059     /* back to caller */
09060 #ifdef RUBY_VM
09061     if (RTEST(rb_funcall(thread, ID_alive_p, 0, 0))) {
09062 #else
09063     if (RTEST(rb_thread_alive_p(thread))) {
09064 #endif
09065       DUMP2("back to caller (caller thread:%lx)", thread);
09066       DUMP2("               (current thread:%lx)", rb_thread_current());
09067 #if CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE
09068       have_rb_thread_waiting_for_value = 1;
09069       rb_thread_wakeup(thread);
09070 #else
09071       rb_thread_run(thread);
09072 #endif
09073       DUMP1("finish back to caller");
09074 #if DO_THREAD_SCHEDULE_AT_CALLBACK_DONE
09075       rb_thread_schedule();
09076 #endif
09077     } else {
09078       DUMP2("caller is dead (caller thread:%lx)", thread);
09079       DUMP2("               (current thread:%lx)", rb_thread_current());
09080     }
09081 
09082     /* end of handler : remove it */
09083     return 1;
09084 }
09085 
09086 static VALUE
09087 ip_invoke_with_position(argc, argv, obj, position)
09088     int argc;
09089     VALUE *argv;
09090     VALUE obj;
09091     Tcl_QueuePosition position;
09092 {
09093     struct invoke_queue *ivq;
09094 #ifdef RUBY_USE_NATIVE_THREAD
09095     struct tcltkip *ptr;
09096 #endif
09097     int  *alloc_done;
09098     int  thr_crit_bup;
09099     volatile VALUE current = rb_thread_current();
09100     volatile VALUE ip_obj = obj;
09101     volatile VALUE result;
09102     volatile VALUE ret;
09103     struct timeval t;
09104 
09105 #if TCL_MAJOR_VERSION >= 8
09106     Tcl_Obj **av = (Tcl_Obj **)NULL;
09107 #else /* TCL_MAJOR_VERSION < 8 */
09108     char **av = (char **)NULL;
09109 #endif
09110 
09111     if (argc < 1) {
09112         rb_raise(rb_eArgError, "command name missing");
09113     }
09114 
09115 #ifdef RUBY_USE_NATIVE_THREAD
09116     ptr = get_ip(ip_obj);
09117     DUMP2("invoke status: ptr->tk_thread_id %p", ptr->tk_thread_id);
09118     DUMP2("invoke status: Tcl_GetCurrentThread %p", Tcl_GetCurrentThread());
09119 #else
09120     DUMP2("status: Tcl_GetCurrentThread %p", Tcl_GetCurrentThread());
09121 #endif
09122     DUMP2("status: eventloopt_thread %lx", eventloop_thread);
09123 
09124     if (
09125 #ifdef RUBY_USE_NATIVE_THREAD
09126         (ptr->tk_thread_id == 0 || ptr->tk_thread_id == Tcl_GetCurrentThread())
09127         &&
09128 #endif
09129         (NIL_P(eventloop_thread) || current == eventloop_thread)
09130         ) {
09131         if (NIL_P(eventloop_thread)) {
09132             DUMP2("invoke from thread:%lx but no eventloop", current);
09133         } else {
09134             DUMP2("invoke from current eventloop %lx", current);
09135         }
09136         result = ip_invoke_real(argc, argv, ip_obj);
09137         if (rb_obj_is_kind_of(result, rb_eException)) {
09138             rb_exc_raise(result);
09139         }
09140         return result;
09141     }
09142 
09143     DUMP2("invoke from thread %lx (NOT current eventloop)", current);
09144 
09145     thr_crit_bup = rb_thread_critical;
09146     rb_thread_critical = Qtrue;
09147 
09148     /* allocate memory (for arguments) */
09149     av = alloc_invoke_arguments(argc, argv);
09150 
09151     /* allocate memory (keep result) */
09152     /* alloc_done = (int*)ALLOC(int); */
09153     alloc_done = RbTk_ALLOC_N(int, 1);
09154 #if 0 /* use Tcl_Preserve/Release */
09155     Tcl_Preserve((ClientData)alloc_done); /* XXXXXXXX */
09156 #endif
09157     *alloc_done = 0;
09158 
09159     /* allocate memory (freed by Tcl_ServiceEvent) */
09160     /* ivq = (struct invoke_queue *)Tcl_Alloc(sizeof(struct invoke_queue)); */
09161     ivq = RbTk_ALLOC_N(struct invoke_queue, 1);
09162 #if 0 /* use Tcl_Preserve/Release */
09163     Tcl_Preserve((ClientData)ivq); /* XXXXXXXX */
09164 #endif
09165 
09166     /* allocate result obj */
09167     result = rb_ary_new3(1, Qnil);
09168 
09169     /* construct event data */
09170     ivq->done = alloc_done;
09171     ivq->argc = argc;
09172     ivq->argv = av;
09173     ivq->interp = ip_obj;
09174     ivq->result = result;
09175     ivq->thread = current;
09176     ivq->safe_level = rb_safe_level();
09177     ivq->ev.proc = invoke_queue_handler;
09178 
09179     /* add the handler to Tcl event queue */
09180     DUMP1("add handler");
09181 #ifdef RUBY_USE_NATIVE_THREAD
09182     if (ptr->tk_thread_id) {
09183       /* Tcl_ThreadQueueEvent(ptr->tk_thread_id, &(ivq->ev), position); */
09184       Tcl_ThreadQueueEvent(ptr->tk_thread_id, (Tcl_Event*)ivq, position);
09185       Tcl_ThreadAlert(ptr->tk_thread_id);
09186     } else if (tk_eventloop_thread_id) {
09187       /* Tcl_ThreadQueueEvent(tk_eventloop_thread_id,
09188                            &(ivq->ev), position); */
09189       Tcl_ThreadQueueEvent(tk_eventloop_thread_id,
09190                            (Tcl_Event*)ivq, position);
09191       Tcl_ThreadAlert(tk_eventloop_thread_id);
09192     } else {
09193       /* Tcl_QueueEvent(&(ivq->ev), position); */
09194       Tcl_QueueEvent((Tcl_Event*)ivq, position);
09195     }
09196 #else
09197     /* Tcl_QueueEvent(&(ivq->ev), position); */
09198     Tcl_QueueEvent((Tcl_Event*)ivq, position);
09199 #endif
09200 
09201     rb_thread_critical = thr_crit_bup;
09202 
09203     /* wait for the handler to be processed */
09204     t.tv_sec  = 0;
09205     t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
09206 
09207     DUMP2("ivq wait for handler (current thread:%lx)", current);
09208     while(*alloc_done >= 0) {
09209       /* rb_thread_stop(); */
09210       /* rb_thread_sleep_forever(); */
09211       rb_thread_wait_for(t);
09212       DUMP2("*** ivq wakeup (current thread:%lx)", current);
09213       DUMP2("***          (eventloop thread:%lx)", eventloop_thread);
09214       if (NIL_P(eventloop_thread)) {
09215         DUMP1("*** ivq lost eventloop thread");
09216         break;
09217       }
09218     }
09219     DUMP2("back from handler (current thread:%lx)", current);
09220 
09221     /* get result & free allocated memory */
09222     ret = RARRAY_PTR(result)[0];
09223 #if 0 /* use Tcl_EventuallyFree */
09224     Tcl_EventuallyFree((ClientData)alloc_done, TCL_DYNAMIC); /* XXXXXXXX */
09225 #else
09226 #if 0 /* use Tcl_Preserve/Release */
09227     Tcl_Release((ClientData)alloc_done); /* XXXXXXXX */
09228 #else
09229     /* free(alloc_done); */
09230     ckfree((char*)alloc_done);
09231 #endif
09232 #endif
09233 
09234 #if 0 /* ivq is freed by Tcl_ServiceEvent */
09235 #if 0 /* use Tcl_EventuallyFree */
09236     Tcl_EventuallyFree((ClientData)ivq, TCL_DYNAMIC); /* XXXXXXXX */
09237 #else
09238 #if 0 /* use Tcl_Preserve/Release */
09239     Tcl_Release(ivq);
09240 #else
09241     ckfree((char*)ivq);
09242 #endif
09243 #endif
09244 #endif
09245 
09246     /* free allocated memory */
09247     free_invoke_arguments(argc, av);
09248 
09249     /* exception? */
09250     if (rb_obj_is_kind_of(ret, rb_eException)) {
09251         DUMP1("raise exception");
09252         /* rb_exc_raise(ret); */
09253         rb_exc_raise(rb_exc_new3(rb_obj_class(ret),
09254                                  rb_funcall(ret, ID_to_s, 0, 0)));
09255     }
09256 
09257     DUMP1("exit ip_invoke");
09258     return ret;
09259 }
09260 
09261 
09262 /* get return code from Tcl_Eval() */
09263 static VALUE
09264 ip_retval(self)
09265     VALUE self;
09266 {
09267     struct tcltkip *ptr;        /* tcltkip data struct */
09268 
09269     /* get the data strcut */
09270     ptr = get_ip(self);
09271 
09272     /* ip is deleted? */
09273     if (deleted_ip(ptr)) {
09274         return rb_tainted_str_new2("");
09275     }
09276 
09277     return (INT2FIX(ptr->return_value));
09278 }
09279 
09280 static VALUE
09281 ip_invoke(argc, argv, obj)
09282     int argc;
09283     VALUE *argv;
09284     VALUE obj;
09285 {
09286     return ip_invoke_with_position(argc, argv, obj, TCL_QUEUE_TAIL);
09287 }
09288 
09289 static VALUE
09290 ip_invoke_immediate(argc, argv, obj)
09291     int argc;
09292     VALUE *argv;
09293     VALUE obj;
09294 {
09295     /* POTENTIALY INSECURE : can create infinite loop */
09296     rb_secure(4);
09297     return ip_invoke_with_position(argc, argv, obj, TCL_QUEUE_HEAD);
09298 }
09299 
09300 
09301 /* access Tcl variables */
09302 static VALUE
09303 ip_get_variable2_core(interp, argc, argv)
09304     VALUE interp;
09305     int   argc;
09306     VALUE *argv;
09307 {
09308     struct tcltkip *ptr = get_ip(interp);
09309     int thr_crit_bup;
09310     volatile VALUE varname, index, flag;
09311 
09312     varname = argv[0];
09313     index   = argv[1];
09314     flag    = argv[2];
09315 
09316     /*
09317     StringValue(varname);
09318     if (!NIL_P(index)) StringValue(index);
09319     */
09320 
09321 #if TCL_MAJOR_VERSION >= 8
09322     {
09323         Tcl_Obj *ret;
09324         volatile VALUE strval;
09325 
09326         thr_crit_bup = rb_thread_critical;
09327         rb_thread_critical = Qtrue;
09328 
09329         /* ip is deleted? */
09330         if (deleted_ip(ptr)) {
09331             rb_thread_critical = thr_crit_bup;
09332             return rb_tainted_str_new2("");
09333         } else {
09334             /* Tcl_Preserve(ptr->ip); */
09335             rbtk_preserve_ip(ptr);
09336             ret = Tcl_GetVar2Ex(ptr->ip, RSTRING_PTR(varname),
09337                                 NIL_P(index) ? NULL : RSTRING_PTR(index),
09338                                 FIX2INT(flag));
09339         }
09340 
09341         if (ret == (Tcl_Obj*)NULL) {
09342             volatile VALUE exc;
09343             /* exc = rb_exc_new2(rb_eRuntimeError,
09344                                  Tcl_GetStringResult(ptr->ip)); */
09345             exc = create_ip_exc(interp, rb_eRuntimeError, "%s",
09346                                 Tcl_GetStringResult(ptr->ip));
09347             /* Tcl_Release(ptr->ip); */
09348             rbtk_release_ip(ptr);
09349             rb_thread_critical = thr_crit_bup;
09350             return exc;
09351         }
09352 
09353         Tcl_IncrRefCount(ret);
09354         strval = get_str_from_obj(ret);
09355         RbTk_OBJ_UNTRUST(strval);
09356         Tcl_DecrRefCount(ret);
09357 
09358         /* Tcl_Release(ptr->ip); */
09359         rbtk_release_ip(ptr);
09360         rb_thread_critical = thr_crit_bup;
09361         return(strval);
09362     }
09363 #else /* TCL_MAJOR_VERSION < 8 */
09364     {
09365         char *ret;
09366         volatile VALUE strval;
09367 
09368         /* ip is deleted? */
09369         if (deleted_ip(ptr)) {
09370             return rb_tainted_str_new2("");
09371         } else {
09372             /* Tcl_Preserve(ptr->ip); */
09373             rbtk_preserve_ip(ptr);
09374             ret = Tcl_GetVar2(ptr->ip, RSTRING_PTR(varname),
09375                               NIL_P(index) ? NULL : RSTRING_PTR(index),
09376                               FIX2INT(flag));
09377         }
09378 
09379         if (ret == (char*)NULL) {
09380             volatile VALUE exc;
09381             exc = rb_exc_new2(rb_eRuntimeError, Tcl_GetStringResult(ptr->ip));
09382             /* Tcl_Release(ptr->ip); */
09383             rbtk_release_ip(ptr);
09384             rb_thread_critical = thr_crit_bup;
09385             return exc;
09386         }
09387 
09388         strval = rb_tainted_str_new2(ret);
09389         /* Tcl_Release(ptr->ip); */
09390         rbtk_release_ip(ptr);
09391         rb_thread_critical = thr_crit_bup;
09392 
09393         return(strval);
09394     }
09395 #endif
09396 }
09397 
09398 static VALUE
09399 ip_get_variable2(self, varname, index, flag)
09400     VALUE self;
09401     VALUE varname;
09402     VALUE index;
09403     VALUE flag;
09404 {
09405     VALUE argv[3];
09406     VALUE retval;
09407 
09408     StringValue(varname);
09409     if (!NIL_P(index)) StringValue(index);
09410 
09411     argv[0] = varname;
09412     argv[1] = index;
09413     argv[2] = flag;
09414 
09415     retval = tk_funcall(ip_get_variable2_core, 3, argv, self);
09416 
09417     if (NIL_P(retval)) {
09418         return rb_tainted_str_new2("");
09419     } else {
09420         return retval;
09421     }
09422 }
09423 
09424 static VALUE
09425 ip_get_variable(self, varname, flag)
09426     VALUE self;
09427     VALUE varname;
09428     VALUE flag;
09429 {
09430     return ip_get_variable2(self, varname, Qnil, flag);
09431 }
09432 
09433 static VALUE
09434 ip_set_variable2_core(interp, argc, argv)
09435     VALUE interp;
09436     int   argc;
09437     VALUE *argv;
09438 {
09439     struct tcltkip *ptr = get_ip(interp);
09440     int thr_crit_bup;
09441     volatile VALUE varname, index, value, flag;
09442 
09443     varname = argv[0];
09444     index   = argv[1];
09445     value   = argv[2];
09446     flag    = argv[3];
09447 
09448     /*
09449     StringValue(varname);
09450     if (!NIL_P(index)) StringValue(index);
09451     StringValue(value);
09452     */
09453 
09454 #if TCL_MAJOR_VERSION >= 8
09455     {
09456         Tcl_Obj *valobj, *ret;
09457         volatile VALUE strval;
09458 
09459         thr_crit_bup = rb_thread_critical;
09460         rb_thread_critical = Qtrue;
09461 
09462         valobj = get_obj_from_str(value);
09463         Tcl_IncrRefCount(valobj);
09464 
09465         /* ip is deleted? */
09466         if (deleted_ip(ptr)) {
09467             Tcl_DecrRefCount(valobj);
09468             rb_thread_critical = thr_crit_bup;
09469             return rb_tainted_str_new2("");
09470         } else {
09471             /* Tcl_Preserve(ptr->ip); */
09472             rbtk_preserve_ip(ptr);
09473             ret = Tcl_SetVar2Ex(ptr->ip, RSTRING_PTR(varname),
09474                                 NIL_P(index) ? NULL : RSTRING_PTR(index),
09475                                 valobj, FIX2INT(flag));
09476         }
09477 
09478         Tcl_DecrRefCount(valobj);
09479 
09480         if (ret == (Tcl_Obj*)NULL) {
09481             volatile VALUE exc;
09482             /* exc = rb_exc_new2(rb_eRuntimeError,
09483                                  Tcl_GetStringResult(ptr->ip)); */
09484             exc = create_ip_exc(interp, rb_eRuntimeError, "%s",
09485                                 Tcl_GetStringResult(ptr->ip));
09486             /* Tcl_Release(ptr->ip); */
09487             rbtk_release_ip(ptr);
09488             rb_thread_critical = thr_crit_bup;
09489             return exc;
09490         }
09491 
09492         Tcl_IncrRefCount(ret);
09493         strval = get_str_from_obj(ret);
09494         RbTk_OBJ_UNTRUST(strval);
09495         Tcl_DecrRefCount(ret);
09496 
09497         /* Tcl_Release(ptr->ip); */
09498         rbtk_release_ip(ptr);
09499         rb_thread_critical = thr_crit_bup;
09500 
09501         return(strval);
09502     }
09503 #else /* TCL_MAJOR_VERSION < 8 */
09504     {
09505         CONST char *ret;
09506         volatile VALUE strval;
09507 
09508         /* ip is deleted? */
09509         if (deleted_ip(ptr)) {
09510             return rb_tainted_str_new2("");
09511         } else {
09512             /* Tcl_Preserve(ptr->ip); */
09513             rbtk_preserve_ip(ptr);
09514             ret = Tcl_SetVar2(ptr->ip, RSTRING_PTR(varname),
09515                               NIL_P(index) ? NULL : RSTRING_PTR(index),
09516                               RSTRING_PTR(value), FIX2INT(flag));
09517         }
09518 
09519         if (ret == (char*)NULL) {
09520             return rb_exc_new2(rb_eRuntimeError, ptr->ip->result);
09521         }
09522 
09523         strval = rb_tainted_str_new2(ret);
09524 
09525         /* Tcl_Release(ptr->ip); */
09526         rbtk_release_ip(ptr);
09527         rb_thread_critical = thr_crit_bup;
09528 
09529         return(strval);
09530     }
09531 #endif
09532 }
09533 
09534 static VALUE
09535 ip_set_variable2(self, varname, index, value, flag)
09536     VALUE self;
09537     VALUE varname;
09538     VALUE index;
09539     VALUE value;
09540     VALUE flag;
09541 {
09542     VALUE argv[4];
09543     VALUE retval;
09544 
09545     StringValue(varname);
09546     if (!NIL_P(index)) StringValue(index);
09547     StringValue(value);
09548 
09549     argv[0] = varname;
09550     argv[1] = index;
09551     argv[2] = value;
09552     argv[3] = flag;
09553 
09554     retval = tk_funcall(ip_set_variable2_core, 4, argv, self);
09555 
09556     if (NIL_P(retval)) {
09557         return rb_tainted_str_new2("");
09558     } else {
09559         return retval;
09560     }
09561 }
09562 
09563 static VALUE
09564 ip_set_variable(self, varname, value, flag)
09565     VALUE self;
09566     VALUE varname;
09567     VALUE value;
09568     VALUE flag;
09569 {
09570     return ip_set_variable2(self, varname, Qnil, value, flag);
09571 }
09572 
09573 static VALUE
09574 ip_unset_variable2_core(interp, argc, argv)
09575     VALUE interp;
09576     int   argc;
09577     VALUE *argv;
09578 {
09579     struct tcltkip *ptr = get_ip(interp);
09580     volatile VALUE varname, index, flag;
09581 
09582     varname = argv[0];
09583     index   = argv[1];
09584     flag    = argv[2];
09585 
09586     /*
09587     StringValue(varname);
09588     if (!NIL_P(index)) StringValue(index);
09589     */
09590 
09591     /* ip is deleted? */
09592     if (deleted_ip(ptr)) {
09593         return Qtrue;
09594     }
09595 
09596     ptr->return_value = Tcl_UnsetVar2(ptr->ip, RSTRING_PTR(varname),
09597                                       NIL_P(index) ? NULL : RSTRING_PTR(index),
09598                                       FIX2INT(flag));
09599 
09600     if (ptr->return_value == TCL_ERROR) {
09601         if (FIX2INT(flag) & TCL_LEAVE_ERR_MSG) {
09602             /* return rb_exc_new2(rb_eRuntimeError,
09603                                   Tcl_GetStringResult(ptr->ip)); */
09604             return create_ip_exc(interp, rb_eRuntimeError, "%s",
09605                                  Tcl_GetStringResult(ptr->ip));
09606         }
09607         return Qfalse;
09608     }
09609     return Qtrue;
09610 }
09611 
09612 static VALUE
09613 ip_unset_variable2(self, varname, index, flag)
09614     VALUE self;
09615     VALUE varname;
09616     VALUE index;
09617     VALUE flag;
09618 {
09619     VALUE argv[3];
09620     VALUE retval;
09621 
09622     StringValue(varname);
09623     if (!NIL_P(index)) StringValue(index);
09624 
09625     argv[0] = varname;
09626     argv[1] = index;
09627     argv[2] = flag;
09628 
09629     retval = tk_funcall(ip_unset_variable2_core, 3, argv, self);
09630 
09631     if (NIL_P(retval)) {
09632         return rb_tainted_str_new2("");
09633     } else {
09634         return retval;
09635     }
09636 }
09637 
09638 static VALUE
09639 ip_unset_variable(self, varname, flag)
09640     VALUE self;
09641     VALUE varname;
09642     VALUE flag;
09643 {
09644     return ip_unset_variable2(self, varname, Qnil, flag);
09645 }
09646 
09647 static VALUE
09648 ip_get_global_var(self, varname)
09649     VALUE self;
09650     VALUE varname;
09651 {
09652     return ip_get_variable(self, varname,
09653                            INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
09654 }
09655 
09656 static VALUE
09657 ip_get_global_var2(self, varname, index)
09658     VALUE self;
09659     VALUE varname;
09660     VALUE index;
09661 {
09662     return ip_get_variable2(self, varname, index,
09663                             INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
09664 }
09665 
09666 static VALUE
09667 ip_set_global_var(self, varname, value)
09668     VALUE self;
09669     VALUE varname;
09670     VALUE value;
09671 {
09672     return ip_set_variable(self, varname, value,
09673                            INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
09674 }
09675 
09676 static VALUE
09677 ip_set_global_var2(self, varname, index, value)
09678     VALUE self;
09679     VALUE varname;
09680     VALUE index;
09681     VALUE value;
09682 {
09683     return ip_set_variable2(self, varname, index, value,
09684                             INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
09685 }
09686 
09687 static VALUE
09688 ip_unset_global_var(self, varname)
09689     VALUE self;
09690     VALUE varname;
09691 {
09692     return ip_unset_variable(self, varname,
09693                              INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
09694 }
09695 
09696 static VALUE
09697 ip_unset_global_var2(self, varname, index)
09698     VALUE self;
09699     VALUE varname;
09700     VALUE index;
09701 {
09702     return ip_unset_variable2(self, varname, index,
09703                               INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
09704 }
09705 
09706 
09707 /* treat Tcl_List */
09708 static VALUE
09709 lib_split_tklist_core(ip_obj, list_str)
09710     VALUE ip_obj;
09711     VALUE list_str;
09712 {
09713     Tcl_Interp *interp;
09714     volatile VALUE ary, elem;
09715     int idx;
09716     int taint_flag = OBJ_TAINTED(list_str);
09717 #ifdef HAVE_RUBY_ENCODING_H
09718     int list_enc_idx;
09719     volatile VALUE list_ivar_enc;
09720 #endif
09721     int result;
09722     VALUE old_gc;
09723 
09724     tcl_stubs_check();
09725 
09726     if (NIL_P(ip_obj)) {
09727         interp = (Tcl_Interp *)NULL;
09728     } else if (get_ip(ip_obj) == (struct tcltkip *)NULL) {
09729         interp = (Tcl_Interp *)NULL;
09730     } else {
09731         interp = get_ip(ip_obj)->ip;
09732     }
09733 
09734     StringValue(list_str);
09735 #ifdef HAVE_RUBY_ENCODING_H
09736     list_enc_idx = rb_enc_get_index(list_str);
09737     list_ivar_enc = rb_ivar_get(list_str, ID_at_enc);
09738 #endif
09739 
09740     {
09741 #if TCL_MAJOR_VERSION >= 8
09742         /* object style interface */
09743         Tcl_Obj *listobj;
09744         int     objc;
09745         Tcl_Obj **objv;
09746         int thr_crit_bup;
09747 
09748         listobj = get_obj_from_str(list_str);
09749 
09750         Tcl_IncrRefCount(listobj);
09751 
09752         result = Tcl_ListObjGetElements(interp, listobj, &objc, &objv);
09753 
09754         if (result == TCL_ERROR) {
09755             Tcl_DecrRefCount(listobj);
09756             if (interp == (Tcl_Interp*)NULL) {
09757                 rb_raise(rb_eRuntimeError, "can't get elements from list");
09758             } else {
09759                 rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(interp));
09760             }
09761         }
09762 
09763         for(idx = 0; idx < objc; idx++) {
09764             Tcl_IncrRefCount(objv[idx]);
09765         }
09766 
09767         thr_crit_bup = rb_thread_critical;
09768         rb_thread_critical = Qtrue;
09769 
09770         ary = rb_ary_new2(objc);
09771         if (taint_flag) RbTk_OBJ_UNTRUST(ary);
09772 
09773         old_gc = rb_gc_disable();
09774 
09775         for(idx = 0; idx < objc; idx++) {
09776             elem = get_str_from_obj(objv[idx]);
09777             if (taint_flag) RbTk_OBJ_UNTRUST(elem);
09778 
09779 #ifdef HAVE_RUBY_ENCODING_H
09780             if (rb_enc_get_index(elem) == ENCODING_INDEX_BINARY) {
09781                 rb_enc_associate_index(elem, ENCODING_INDEX_BINARY);
09782                 rb_ivar_set(elem, ID_at_enc, ENCODING_NAME_BINARY);
09783             } else {
09784                 rb_enc_associate_index(elem, list_enc_idx);
09785                 rb_ivar_set(elem, ID_at_enc, list_ivar_enc);
09786             }
09787 #endif
09788             /* RARRAY(ary)->ptr[idx] = elem; */
09789             rb_ary_push(ary, elem);
09790         }
09791 
09792         /* RARRAY(ary)->len = objc; */
09793 
09794         if (old_gc == Qfalse) rb_gc_enable();
09795 
09796         rb_thread_critical = thr_crit_bup;
09797 
09798         for(idx = 0; idx < objc; idx++) {
09799             Tcl_DecrRefCount(objv[idx]);
09800         }
09801 
09802         Tcl_DecrRefCount(listobj);
09803 
09804 #else /* TCL_MAJOR_VERSION < 8 */
09805         /* string style interface */
09806         int  argc;
09807         char **argv;
09808 
09809         if (Tcl_SplitList(interp, RSTRING_PTR(list_str),
09810                           &argc, &argv) == TCL_ERROR) {
09811             if (interp == (Tcl_Interp*)NULL) {
09812                 rb_raise(rb_eRuntimeError, "can't get elements from list");
09813             } else {
09814                 rb_raise(rb_eRuntimeError, "%s", interp->result);
09815             }
09816         }
09817 
09818         ary = rb_ary_new2(argc);
09819         if (taint_flag) RbTk_OBJ_UNTRUST(ary);
09820 
09821         old_gc = rb_gc_disable();
09822 
09823         for(idx = 0; idx < argc; idx++) {
09824             if (taint_flag) {
09825                 elem = rb_tainted_str_new2(argv[idx]);
09826             } else {
09827                 elem = rb_str_new2(argv[idx]);
09828             }
09829             /* rb_ivar_set(elem, ID_at_enc, rb_str_new2("binary")); */
09830             /* RARRAY(ary)->ptr[idx] = elem; */
09831             rb_ary_push(ary, elem)
09832         }
09833         /* RARRAY(ary)->len = argc; */
09834 
09835         if (old_gc == Qfalse) rb_gc_enable();
09836 #endif
09837     }
09838 
09839     return ary;
09840 }
09841 
09842 static VALUE
09843 lib_split_tklist(self, list_str)
09844     VALUE self;
09845     VALUE list_str;
09846 {
09847     return lib_split_tklist_core(Qnil, list_str);
09848 }
09849 
09850 
09851 static VALUE
09852 ip_split_tklist(self, list_str)
09853     VALUE self;
09854     VALUE list_str;
09855 {
09856     return lib_split_tklist_core(self, list_str);
09857 }
09858 
09859 static VALUE
09860 lib_merge_tklist(argc, argv, obj)
09861     int argc;
09862     VALUE *argv;
09863     VALUE obj;
09864 {
09865     int  num, len;
09866     int  *flagPtr;
09867     char *dst, *result;
09868     volatile VALUE str;
09869     int taint_flag = 0;
09870     int thr_crit_bup;
09871     VALUE old_gc;
09872 
09873     if (argc == 0) return rb_str_new2("");
09874 
09875     tcl_stubs_check();
09876 
09877     thr_crit_bup = rb_thread_critical;
09878     rb_thread_critical = Qtrue;
09879     old_gc = rb_gc_disable();
09880 
09881     /* based on Tcl/Tk's Tcl_Merge() */
09882     /* flagPtr = ALLOC_N(int, argc); */
09883     flagPtr = RbTk_ALLOC_N(int, argc);
09884 #if 0 /* use Tcl_Preserve/Release */
09885     Tcl_Preserve((ClientData)flagPtr); /* XXXXXXXXXX */
09886 #endif
09887 
09888     /* pass 1 */
09889     len = 1;
09890     for(num = 0; num < argc; num++) {
09891         if (OBJ_TAINTED(argv[num])) taint_flag = 1;
09892         dst = StringValuePtr(argv[num]);
09893 #if TCL_MAJOR_VERSION >= 8
09894         len += Tcl_ScanCountedElement(dst, RSTRING_LENINT(argv[num]),
09895                                       &flagPtr[num]) + 1;
09896 #else /* TCL_MAJOR_VERSION < 8 */
09897         len += Tcl_ScanElement(dst, &flagPtr[num]) + 1;
09898 #endif
09899     }
09900 
09901     /* pass 2 */
09902     /* result = (char *)Tcl_Alloc(len); */
09903     result = (char *)ckalloc(len);
09904 #if 0 /* use Tcl_Preserve/Release */
09905     Tcl_Preserve((ClientData)result);
09906 #endif
09907     dst = result;
09908     for(num = 0; num < argc; num++) {
09909 #if TCL_MAJOR_VERSION >= 8
09910         len = Tcl_ConvertCountedElement(RSTRING_PTR(argv[num]),
09911                                         RSTRING_LENINT(argv[num]),
09912                                         dst, flagPtr[num]);
09913 #else /* TCL_MAJOR_VERSION < 8 */
09914         len = Tcl_ConvertElement(RSTRING_PTR(argv[num]), dst, flagPtr[num]);
09915 #endif
09916         dst += len;
09917         *dst = ' ';
09918         dst++;
09919     }
09920     if (dst == result) {
09921         *dst = 0;
09922     } else {
09923         dst[-1] = 0;
09924     }
09925 
09926 #if 0 /* use Tcl_EventuallyFree */
09927     Tcl_EventuallyFree((ClientData)flagPtr, TCL_DYNAMIC); /* XXXXXXXX */
09928 #else
09929 #if 0 /* use Tcl_Preserve/Release */
09930     Tcl_Release((ClientData)flagPtr);
09931 #else
09932     /* free(flagPtr); */
09933     ckfree((char*)flagPtr);
09934 #endif
09935 #endif
09936 
09937     /* create object */
09938     str = rb_str_new(result, dst - result - 1);
09939     if (taint_flag) RbTk_OBJ_UNTRUST(str);
09940 #if 0 /* use Tcl_EventuallyFree */
09941     Tcl_EventuallyFree((ClientData)result, TCL_DYNAMIC); /* XXXXXXXX */
09942 #else
09943 #if 0 /* use Tcl_Preserve/Release */
09944     Tcl_Release((ClientData)result); /* XXXXXXXXXXX */
09945 #else
09946     /* Tcl_Free(result); */
09947     ckfree(result);
09948 #endif
09949 #endif
09950 
09951     if (old_gc == Qfalse) rb_gc_enable();
09952     rb_thread_critical = thr_crit_bup;
09953 
09954     return str;
09955 }
09956 
09957 static VALUE
09958 lib_conv_listelement(self, src)
09959     VALUE self;
09960     VALUE src;
09961 {
09962     int   len, scan_flag;
09963     volatile VALUE dst;
09964     int   taint_flag = OBJ_TAINTED(src);
09965     int thr_crit_bup;
09966 
09967     tcl_stubs_check();
09968 
09969     thr_crit_bup = rb_thread_critical;
09970     rb_thread_critical = Qtrue;
09971 
09972     StringValue(src);
09973 
09974 #if TCL_MAJOR_VERSION >= 8
09975     len = Tcl_ScanCountedElement(RSTRING_PTR(src), RSTRING_LENINT(src),
09976                                  &scan_flag);
09977     dst = rb_str_new(0, len + 1);
09978     len = Tcl_ConvertCountedElement(RSTRING_PTR(src), RSTRING_LENINT(src),
09979                                     RSTRING_PTR(dst), scan_flag);
09980 #else /* TCL_MAJOR_VERSION < 8 */
09981     len = Tcl_ScanElement(RSTRING_PTR(src), &scan_flag);
09982     dst = rb_str_new(0, len + 1);
09983     len = Tcl_ConvertElement(RSTRING_PTR(src), RSTRING_PTR(dst), scan_flag);
09984 #endif
09985 
09986     rb_str_resize(dst, len);
09987     if (taint_flag) RbTk_OBJ_UNTRUST(dst);
09988 
09989     rb_thread_critical = thr_crit_bup;
09990 
09991     return dst;
09992 }
09993 
09994 static VALUE
09995 lib_getversion(self)
09996     VALUE self;
09997 {
09998     set_tcltk_version();
09999 
10000     return rb_ary_new3(4, INT2NUM(tcltk_version.major),
10001                           INT2NUM(tcltk_version.minor),
10002                           INT2NUM(tcltk_version.type),
10003                           INT2NUM(tcltk_version.patchlevel));
10004 }
10005 
10006 static VALUE
10007 lib_get_reltype_name(self)
10008     VALUE self;
10009 {
10010     set_tcltk_version();
10011 
10012     switch(tcltk_version.type) {
10013     case TCL_ALPHA_RELEASE:
10014       return rb_str_new2("alpha");
10015     case TCL_BETA_RELEASE:
10016       return rb_str_new2("beta");
10017     case TCL_FINAL_RELEASE:
10018       return rb_str_new2("final");
10019     default:
10020       rb_raise(rb_eRuntimeError, "tcltklib has invalid release type number");
10021     }
10022 
10023     UNREACHABLE;
10024 }
10025 
10026 
10027 static VALUE
10028 tcltklib_compile_info()
10029 {
10030     volatile VALUE ret;
10031     size_t size;
10032     static CONST char form[]
10033       = "tcltklib %s :: Ruby%s (%s) %s pthread :: Tcl%s(%s)/Tk%s(%s) %s";
10034     char *info;
10035 
10036     size = strlen(form)
10037         + strlen(TCLTKLIB_RELEASE_DATE)
10038         + strlen(RUBY_VERSION)
10039         + strlen(RUBY_RELEASE_DATE)
10040         + strlen("without")
10041         + strlen(TCL_PATCH_LEVEL)
10042         + strlen("without stub")
10043         + strlen(TK_PATCH_LEVEL)
10044         + strlen("without stub")
10045         + strlen("unknown tcl_threads");
10046 
10047     info = ALLOC_N(char, size);
10048     /* info = ckalloc(sizeof(char) * size); */ /* SEGV */
10049 
10050     sprintf(info, form,
10051             TCLTKLIB_RELEASE_DATE,
10052             RUBY_VERSION, RUBY_RELEASE_DATE,
10053 #ifdef HAVE_NATIVETHREAD
10054             "with",
10055 #else
10056             "without",
10057 #endif
10058             TCL_PATCH_LEVEL,
10059 #ifdef USE_TCL_STUBS
10060             "with stub",
10061 #else
10062             "without stub",
10063 #endif
10064             TK_PATCH_LEVEL,
10065 #ifdef USE_TK_STUBS
10066             "with stub",
10067 #else
10068             "without stub",
10069 #endif
10070 #ifdef WITH_TCL_ENABLE_THREAD
10071 # if WITH_TCL_ENABLE_THREAD
10072             "with tcl_threads"
10073 # else
10074             "without tcl_threads"
10075 # endif
10076 #else
10077             "unknown tcl_threads"
10078 #endif
10079         );
10080 
10081     ret = rb_obj_freeze(rb_str_new2(info));
10082 
10083     xfree(info);
10084     /* ckfree(info); */
10085 
10086     return ret;
10087 }
10088 
10089 
10090 /*###############################################*/
10091 
10092 static VALUE
10093 create_dummy_encoding_for_tk_core(interp, name, error_mode)
10094      VALUE interp;
10095      VALUE name;
10096      VALUE error_mode;
10097 {
10098   get_ip(interp);
10099 
10100   rb_secure(4);
10101 
10102   StringValue(name);
10103 
10104 #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1)
10105   if (Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(name)) == (Tcl_Encoding)NULL) {
10106     if (RTEST(error_mode)) {
10107       rb_raise(rb_eArgError, "invalid Tk encoding name '%s'",
10108                RSTRING_PTR(name));
10109     } else {
10110       return Qnil;
10111     }
10112   }
10113 #endif
10114 
10115 #ifdef HAVE_RUBY_ENCODING_H
10116   if (RTEST(rb_define_dummy_encoding(RSTRING_PTR(name)))) {
10117     int idx = rb_enc_find_index(StringValueCStr(name));
10118     return rb_enc_from_encoding(rb_enc_from_index(idx));
10119   } else {
10120     if (RTEST(error_mode)) {
10121       rb_raise(rb_eRuntimeError, "fail to create dummy encoding for '%s'",
10122                RSTRING_PTR(name));
10123     } else {
10124       return Qnil;
10125     }
10126   }
10127 
10128   UNREACHABLE;
10129 #else
10130     return name;
10131 #endif
10132 }
10133 static VALUE
10134 create_dummy_encoding_for_tk(interp, name)
10135      VALUE interp;
10136      VALUE name;
10137 {
10138   return create_dummy_encoding_for_tk_core(interp, name, Qtrue);
10139 }
10140 
10141 
10142 #ifdef HAVE_RUBY_ENCODING_H
10143 static int
10144 update_encoding_table(table, interp, error_mode)
10145      VALUE table;
10146      VALUE interp;
10147      VALUE error_mode;
10148 {
10149   struct tcltkip *ptr;
10150   int retry = 0;
10151   int i, idx, objc;
10152   Tcl_Obj **objv;
10153   Tcl_Obj *enc_list;
10154   volatile VALUE encname = Qnil;
10155   volatile VALUE encobj = Qnil;
10156 
10157   /* interpreter check */
10158   if (NIL_P(interp)) return 0;
10159   ptr = get_ip(interp);
10160   if (ptr == (struct tcltkip *) NULL)  return 0;
10161   if (deleted_ip(ptr)) return 0;
10162 
10163   /* get Tcl's encoding list */
10164   Tcl_GetEncodingNames(ptr->ip);
10165   enc_list = Tcl_GetObjResult(ptr->ip);
10166   Tcl_IncrRefCount(enc_list);
10167 
10168   if (Tcl_ListObjGetElements(ptr->ip, enc_list,
10169                              &objc, &objv) != TCL_OK) {
10170     Tcl_DecrRefCount(enc_list);
10171     /* rb_raise(rb_eRuntimeError, "failt to get Tcl's encoding names");*/
10172     return 0;
10173   }
10174 
10175   /* check each encoding name */
10176   for(i = 0; i < objc; i++) {
10177     encname = rb_str_new2(Tcl_GetString(objv[i]));
10178     if (NIL_P(rb_hash_lookup(table, encname))) {
10179       /* new Tk encoding -> add to table */
10180       idx = rb_enc_find_index(StringValueCStr(encname));
10181       if (idx < 0) {
10182         encobj = create_dummy_encoding_for_tk_core(interp,encname,error_mode);
10183       } else {
10184         encobj = rb_enc_from_encoding(rb_enc_from_index(idx));
10185       }
10186       encname = rb_obj_freeze(encname);
10187       rb_hash_aset(table, encname, encobj);
10188       if (!NIL_P(encobj) && NIL_P(rb_hash_lookup(table, encobj))) {
10189         rb_hash_aset(table, encobj, encname);
10190       }
10191       retry = 1;
10192     }
10193   }
10194 
10195   Tcl_DecrRefCount(enc_list);
10196 
10197   return retry;
10198 }
10199 
10200 static VALUE
10201 encoding_table_get_name_core(table, enc_arg, error_mode)
10202      VALUE table;
10203      VALUE enc_arg;
10204      VALUE error_mode;
10205 {
10206   volatile VALUE enc = enc_arg;
10207   volatile VALUE name = Qnil;
10208   volatile VALUE tmp = Qnil;
10209   volatile VALUE interp = rb_ivar_get(table, ID_at_interp);
10210   struct tcltkip *ptr = (struct tcltkip *) NULL;
10211   int idx;
10212 
10213   /* deleted interp ? */
10214   if (!NIL_P(interp)) {
10215     ptr = get_ip(interp);
10216     if (deleted_ip(ptr)) {
10217       ptr = (struct tcltkip *) NULL;
10218     }
10219   }
10220 
10221   /* encoding argument check */
10222   /* 1st: default encoding setting of interp */
10223   if (ptr && NIL_P(enc)) {
10224     if (rb_respond_to(interp, ID_encoding_name)) {
10225       enc = rb_funcall(interp, ID_encoding_name, 0, 0);
10226     }
10227   }
10228   /* 2nd: Encoding.default_internal */
10229   if (NIL_P(enc)) {
10230     enc = rb_enc_default_internal();
10231   }
10232   /* 3rd: encoding system of Tcl/Tk */
10233   if (NIL_P(enc)) {
10234     enc = rb_str_new2(Tcl_GetEncodingName((Tcl_Encoding)NULL));
10235   }
10236   /* 4th: Encoding.default_external */
10237   if (NIL_P(enc)) {
10238     enc = rb_enc_default_external();
10239   }
10240   /* 5th: Encoding.locale_charmap */
10241   if (NIL_P(enc)) {
10242     enc = rb_locale_charmap(rb_cEncoding);
10243   }
10244 
10245   if (RTEST(rb_obj_is_kind_of(enc, cRubyEncoding))) {
10246     /* Ruby's Encoding object */
10247     name = rb_hash_lookup(table, enc);
10248     if (!NIL_P(name)) {
10249       /* find */
10250       return name;
10251     }
10252 
10253     /* is it new ? */
10254     /* update check of Tk encoding names */
10255     if (update_encoding_table(table, interp, error_mode)) {
10256       /* add new relations to the table   */
10257       /* RETRY: registered Ruby encoding? */
10258       name = rb_hash_lookup(table, enc);
10259       if (!NIL_P(name)) {
10260         /* find */
10261         return name;
10262       }
10263     }
10264     /* fail to find */
10265 
10266   } else {
10267     /* String or Symbol? */
10268     name = rb_funcall(enc, ID_to_s, 0, 0);
10269 
10270     if (!NIL_P(rb_hash_lookup(table, name))) {
10271       /* find */
10272       return name;
10273     }
10274 
10275     /* is it new ? */
10276     idx = rb_enc_find_index(StringValueCStr(name));
10277     if (idx >= 0) {
10278       enc = rb_enc_from_encoding(rb_enc_from_index(idx));
10279 
10280       /* registered Ruby encoding? */
10281       tmp = rb_hash_lookup(table, enc);
10282       if (!NIL_P(tmp)) {
10283         /* find */
10284         return tmp;
10285       }
10286 
10287       /* update check of Tk encoding names */
10288       if (update_encoding_table(table, interp, error_mode)) {
10289         /* add new relations to the table   */
10290         /* RETRY: registered Ruby encoding? */
10291         tmp = rb_hash_lookup(table, enc);
10292         if (!NIL_P(tmp)) {
10293           /* find */
10294           return tmp;
10295         }
10296       }
10297     }
10298     /* fail to find */
10299   }
10300 
10301   if (RTEST(error_mode)) {
10302     enc = rb_funcall(enc_arg, ID_to_s, 0, 0);
10303     rb_raise(rb_eArgError, "unsupported Tk encoding '%s'", RSTRING_PTR(enc));
10304   }
10305   return Qnil;
10306 }
10307 static VALUE
10308 encoding_table_get_obj_core(table, enc, error_mode)
10309      VALUE table;
10310      VALUE enc;
10311      VALUE error_mode;
10312 {
10313   volatile VALUE obj = Qnil;
10314 
10315   obj = rb_hash_lookup(table,
10316                        encoding_table_get_name_core(table, enc, error_mode));
10317   if (RTEST(rb_obj_is_kind_of(obj, cRubyEncoding))) {
10318     return obj;
10319   } else {
10320     return Qnil;
10321   }
10322 }
10323 
10324 #else /* ! HAVE_RUBY_ENCODING_H */
10325 #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1)
10326 static int
10327 update_encoding_table(table, interp, error_mode)
10328      VALUE table;
10329      VALUE interp;
10330      VALUE error_mode;
10331 {
10332   struct tcltkip *ptr;
10333   int retry = 0;
10334   int i, objc;
10335   Tcl_Obj **objv;
10336   Tcl_Obj *enc_list;
10337   volatile VALUE encname = Qnil;
10338 
10339   /* interpreter check */
10340   if (NIL_P(interp)) return 0;
10341   ptr = get_ip(interp);
10342   if (ptr == (struct tcltkip *) NULL)  return 0;
10343   if (deleted_ip(ptr)) return 0;
10344 
10345   /* get Tcl's encoding list */
10346   Tcl_GetEncodingNames(ptr->ip);
10347   enc_list = Tcl_GetObjResult(ptr->ip);
10348   Tcl_IncrRefCount(enc_list);
10349 
10350   if (Tcl_ListObjGetElements(ptr->ip, enc_list, &objc, &objv) != TCL_OK) {
10351     Tcl_DecrRefCount(enc_list);
10352     /* rb_raise(rb_eRuntimeError, "failt to get Tcl's encoding names"); */
10353     return 0;
10354   }
10355 
10356   /* get encoding name and set it to table */
10357   for(i = 0; i < objc; i++) {
10358     encname = rb_str_new2(Tcl_GetString(objv[i]));
10359     if (NIL_P(rb_hash_lookup(table, encname))) {
10360       /* new Tk encoding -> add to table */
10361       encname = rb_obj_freeze(encname);
10362       rb_hash_aset(table, encname, encname);
10363       retry = 1;
10364     }
10365   }
10366 
10367   Tcl_DecrRefCount(enc_list);
10368 
10369   return retry;
10370 }
10371 
10372 static VALUE
10373 encoding_table_get_name_core(table, enc, error_mode)
10374      VALUE table;
10375      VALUE enc;
10376      VALUE error_mode;
10377 {
10378   volatile VALUE name = Qnil;
10379 
10380   enc = rb_funcall(enc, ID_to_s, 0, 0);
10381   name = rb_hash_lookup(table, enc);
10382 
10383   if (!NIL_P(name)) {
10384     /* find */
10385     return name;
10386   }
10387 
10388   /* update check */
10389   if (update_encoding_table(table, rb_ivar_get(table, ID_at_interp),
10390                                                error_mode)) {
10391     /* add new relations to the table   */
10392     /* RETRY: registered Ruby encoding? */
10393     name = rb_hash_lookup(table, enc);
10394     if (!NIL_P(name)) {
10395       /* find */
10396       return name;
10397     }
10398   }
10399 
10400   if (RTEST(error_mode)) {
10401     rb_raise(rb_eArgError, "unsupported Tk encoding '%s'", RSTRING_PTR(enc));
10402   }
10403   return Qnil;
10404 }
10405 static VALUE
10406 encoding_table_get_obj_core(table, enc, error_mode)
10407      VALUE table;
10408      VALUE enc;
10409      VALUE error_mode;
10410 {
10411   return encoding_table_get_name_core(table, enc, error_mode);
10412 }
10413 
10414 #else /* Tcl/Tk 7.x or 8.0 */
10415 static VALUE
10416 encoding_table_get_name_core(table, enc, error_mode)
10417      VALUE table;
10418      VALUE enc;
10419      VALUE error_mode;
10420 {
10421   return Qnil;
10422 }
10423 static VALUE
10424 encoding_table_get_obj_core(table, enc, error_mode)
10425      VALUE table;
10426      VALUE enc;
10427      VALUE error_mode;
10428 {
10429   return Qnil;
10430 }
10431 #endif /* end of dependency for the version of Tcl/Tk */
10432 #endif
10433 
10434 static VALUE
10435 encoding_table_get_name(table, enc)
10436      VALUE table;
10437      VALUE enc;
10438 {
10439   return encoding_table_get_name_core(table, enc, Qtrue);
10440 }
10441 static VALUE
10442 encoding_table_get_obj(table, enc)
10443      VALUE table;
10444      VALUE enc;
10445 {
10446   return encoding_table_get_obj_core(table, enc, Qtrue);
10447 }
10448 
10449 #ifdef HAVE_RUBY_ENCODING_H
10450 static VALUE
10451 create_encoding_table_core(arg, interp)
10452      VALUE arg;
10453      VALUE interp;
10454 {
10455   struct tcltkip *ptr = get_ip(interp);
10456   volatile VALUE table = rb_hash_new();
10457   volatile VALUE encname = Qnil;
10458   volatile VALUE encobj = Qnil;
10459   int i, idx, objc;
10460   Tcl_Obj **objv;
10461   Tcl_Obj *enc_list;
10462 
10463 #ifdef HAVE_RB_SET_SAFE_LEVEL_FORCE
10464   rb_set_safe_level_force(0);
10465 #else
10466   rb_set_safe_level(0);
10467 #endif
10468 
10469   /* set 'binary' encoding */
10470   encobj = rb_enc_from_encoding(rb_enc_from_index(ENCODING_INDEX_BINARY));
10471   rb_hash_aset(table, ENCODING_NAME_BINARY, encobj);
10472   rb_hash_aset(table, encobj, ENCODING_NAME_BINARY);
10473 
10474 
10475   /* Tcl stub check */
10476   tcl_stubs_check();
10477 
10478   /* get Tcl's encoding list */
10479   Tcl_GetEncodingNames(ptr->ip);
10480   enc_list = Tcl_GetObjResult(ptr->ip);
10481   Tcl_IncrRefCount(enc_list);
10482 
10483   if (Tcl_ListObjGetElements(ptr->ip, enc_list, &objc, &objv) != TCL_OK) {
10484     Tcl_DecrRefCount(enc_list);
10485     rb_raise(rb_eRuntimeError, "failt to get Tcl's encoding names");
10486   }
10487 
10488   /* get encoding name and set it to table */
10489   for(i = 0; i < objc; i++) {
10490     int name2obj, obj2name;
10491 
10492     name2obj = 1; obj2name = 1;
10493     encname = rb_obj_freeze(rb_str_new2(Tcl_GetString(objv[i])));
10494     idx = rb_enc_find_index(StringValueCStr(encname));
10495     if (idx < 0) {
10496       /* fail to find ruby encoding -> check known encoding */
10497       if (strcmp(RSTRING_PTR(encname), "identity") == 0) {
10498         name2obj = 1; obj2name = 0;
10499         idx = ENCODING_INDEX_BINARY;
10500 
10501       } else if (strcmp(RSTRING_PTR(encname), "shiftjis") == 0) {
10502         name2obj = 1; obj2name = 0;
10503         idx = rb_enc_find_index("Shift_JIS");
10504 
10505       } else if (strcmp(RSTRING_PTR(encname), "unicode") == 0) {
10506         name2obj = 1; obj2name = 0;
10507         idx = ENCODING_INDEX_UTF8;
10508 
10509       } else if (strcmp(RSTRING_PTR(encname), "symbol") == 0) {
10510         name2obj = 1; obj2name = 0;
10511         idx = rb_enc_find_index("ASCII-8BIT");
10512 
10513       } else {
10514         /* regist dummy encoding */
10515         name2obj = 1; obj2name = 1;
10516       }
10517     }
10518 
10519     if (idx < 0) {
10520       /* unknown encoding -> create dummy */
10521       encobj = create_dummy_encoding_for_tk(interp, encname);
10522     } else {
10523       encobj = rb_enc_from_encoding(rb_enc_from_index(idx));
10524     }
10525 
10526     if (name2obj) {
10527       DUMP2("create_encoding_table: name2obj: %s", RSTRING_PTR(encname));
10528       rb_hash_aset(table, encname, encobj);
10529     }
10530     if (obj2name) {
10531       DUMP2("create_encoding_table: obj2name: %s", RSTRING_PTR(encname));
10532       rb_hash_aset(table, encobj, encname);
10533     }
10534   }
10535 
10536   Tcl_DecrRefCount(enc_list);
10537 
10538   rb_ivar_set(table, ID_at_interp, interp);
10539   rb_ivar_set(interp, ID_encoding_table, table);
10540 
10541   return table;
10542 }
10543 
10544 #else /* ! HAVE_RUBY_ENCODING_H */
10545 #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1)
10546 static VALUE
10547 create_encoding_table_core(arg, interp)
10548      VALUE arg;
10549      VALUE interp;
10550 {
10551   struct tcltkip *ptr = get_ip(interp);
10552   volatile VALUE table = rb_hash_new();
10553   volatile VALUE encname = Qnil;
10554   int i, objc;
10555   Tcl_Obj **objv;
10556   Tcl_Obj *enc_list;
10557 
10558   rb_secure(4);
10559 
10560   /* set 'binary' encoding */
10561   rb_hash_aset(table, ENCODING_NAME_BINARY, ENCODING_NAME_BINARY);
10562 
10563   /* get Tcl's encoding list */
10564   Tcl_GetEncodingNames(ptr->ip);
10565   enc_list = Tcl_GetObjResult(ptr->ip);
10566   Tcl_IncrRefCount(enc_list);
10567 
10568   if (Tcl_ListObjGetElements(ptr->ip, enc_list, &objc, &objv) != TCL_OK) {
10569     Tcl_DecrRefCount(enc_list);
10570     rb_raise(rb_eRuntimeError, "failt to get Tcl's encoding names");
10571   }
10572 
10573   /* get encoding name and set it to table */
10574   for(i = 0; i < objc; i++) {
10575     encname = rb_obj_freeze(rb_str_new2(Tcl_GetString(objv[i])));
10576     rb_hash_aset(table, encname, encname);
10577   }
10578 
10579   Tcl_DecrRefCount(enc_list);
10580 
10581   rb_ivar_set(table, ID_at_interp, interp);
10582   rb_ivar_set(interp, ID_encoding_table, table);
10583 
10584   return table;
10585 }
10586 
10587 #else /* Tcl/Tk 7.x or 8.0 */
10588 static VALUE
10589 create_encoding_table_core(arg, interp)
10590      VALUE arg;
10591      VALUE interp;
10592 {
10593   volatile VALUE table = rb_hash_new();
10594   rb_secure(4);
10595   rb_ivar_set(interp, ID_encoding_table, table);
10596   return table;
10597 }
10598 #endif
10599 #endif
10600 
10601 static VALUE
10602 create_encoding_table(interp)
10603      VALUE interp;
10604 {
10605   return rb_funcall(rb_proc_new(create_encoding_table_core, interp),
10606                     ID_call, 0);
10607 }
10608 
10609 static VALUE
10610 ip_get_encoding_table(interp)
10611      VALUE interp;
10612 {
10613   volatile VALUE table = Qnil;
10614 
10615   table = rb_ivar_get(interp, ID_encoding_table);
10616 
10617   if (NIL_P(table)) {
10618     /* initialize encoding_table */
10619     table = create_encoding_table(interp);
10620     rb_define_singleton_method(table, "get_name", encoding_table_get_name, 1);
10621     rb_define_singleton_method(table, "get_obj",  encoding_table_get_obj,  1);
10622   }
10623 
10624   return table;
10625 }
10626 
10627 
10628 /*###############################################*/
10629 
10630 /*
10631  *   The following is based on tkMenu.[ch]
10632  *   of Tcl/Tk (Tk8.0 -- Tk8.5b1) source code.
10633  */
10634 #if TCL_MAJOR_VERSION >= 8
10635 
10636 #define MASTER_MENU             0
10637 #define TEAROFF_MENU            1
10638 #define MENUBAR                 2
10639 
10640 struct dummy_TkMenuEntry {
10641     int type;
10642     struct dummy_TkMenu *menuPtr;
10643     /* , and etc.   */
10644 };
10645 
10646 struct dummy_TkMenu {
10647     Tk_Window tkwin;
10648     Display *display;
10649     Tcl_Interp *interp;
10650     Tcl_Command widgetCmd;
10651     struct dummy_TkMenuEntry **entries;
10652     int numEntries;
10653     int active;
10654     int menuType;     /* MASTER_MENU, TEAROFF_MENU, or MENUBAR */
10655     Tcl_Obj *menuTypePtr;
10656     /* , and etc.   */
10657 };
10658 
10659 struct dummy_TkMenuRef {
10660     struct dummy_TkMenu *menuPtr;
10661     char *dummy1;
10662     char *dummy2;
10663     char *dummy3;
10664 };
10665 
10666 #if 0 /* was available on Tk8.0 -- Tk8.4 */
10667 EXTERN struct dummy_TkMenuRef *TkFindMenuReferences(Tcl_Interp*, char*);
10668 #else /* based on Tk8.0 -- Tk8.5.0 */
10669 #define MENU_HASH_KEY "tkMenus"
10670 #endif
10671 
10672 #endif
10673 
10674 static VALUE
10675 ip_make_menu_embeddable_core(interp, argc, argv)
10676     VALUE interp;
10677     int   argc;
10678     VALUE *argv;
10679 {
10680 #if TCL_MAJOR_VERSION >= 8
10681     volatile VALUE menu_path;
10682     struct tcltkip *ptr = get_ip(interp);
10683     struct dummy_TkMenuRef *menuRefPtr = NULL;
10684     XEvent event;
10685     Tcl_HashTable *menuTablePtr;
10686     Tcl_HashEntry *hashEntryPtr;
10687 
10688     menu_path = argv[0];
10689     StringValue(menu_path);
10690 
10691 #if 0 /* was available on Tk8.0 -- Tk8.4 */
10692     menuRefPtr = TkFindMenuReferences(ptr->ip, RSTRING_PTR(menu_path));
10693 #else /* based on Tk8.0 -- Tk8.5b1 */
10694     if ((menuTablePtr
10695          = (Tcl_HashTable *) Tcl_GetAssocData(ptr->ip, MENU_HASH_KEY, NULL))
10696         != NULL) {
10697       if ((hashEntryPtr
10698            = Tcl_FindHashEntry(menuTablePtr, RSTRING_PTR(menu_path)))
10699           != NULL) {
10700         menuRefPtr = (struct dummy_TkMenuRef *) Tcl_GetHashValue(hashEntryPtr);
10701       }
10702     }
10703 #endif
10704 
10705     if (menuRefPtr == (struct dummy_TkMenuRef *) NULL) {
10706         rb_raise(rb_eArgError, "not a menu widget, or invalid widget path");
10707     }
10708 
10709     if (menuRefPtr->menuPtr == (struct dummy_TkMenu *) NULL) {
10710         rb_raise(rb_eRuntimeError,
10711                  "invalid menu widget (maybe already destroyed)");
10712     }
10713 
10714     if ((menuRefPtr->menuPtr)->menuType != MENUBAR) {
10715         rb_raise(rb_eRuntimeError,
10716                  "target menu widget must be a MENUBAR type");
10717     }
10718 
10719     (menuRefPtr->menuPtr)->menuType = TEAROFF_MENU;
10720 #if 0  /* cause SEGV */
10721     {
10722        /* char *s = "tearoff"; */
10723        char *s = "normal";
10724        /* Tcl_SetStringObj((menuRefPtr->menuPtr)->menuTypePtr, s, strlen(s));*/
10725        (menuRefPtr->menuPtr)->menuTypePtr = Tcl_NewStringObj(s, strlen(s));
10726        /* Tcl_IncrRefCount((menuRefPtr->menuPtr)->menuTypePtr); */
10727        /* (menuRefPtr->menuPtr)->menuType = TEAROFF_MENU; */
10728        (menuRefPtr->menuPtr)->menuType = MASTER_MENU;
10729     }
10730 #endif
10731 
10732 #if 0 /* was available on Tk8.0 -- Tk8.4 */
10733     TkEventuallyRecomputeMenu(menuRefPtr->menuPtr);
10734     TkEventuallyRedrawMenu(menuRefPtr->menuPtr,
10735                            (struct dummy_TkMenuEntry *)NULL);
10736 #else /* based on Tk8.0 -- Tk8.5b1 */
10737     memset((void *) &event, 0, sizeof(event));
10738     event.xany.type = ConfigureNotify;
10739     event.xany.serial = NextRequest(Tk_Display((menuRefPtr->menuPtr)->tkwin));
10740     event.xany.send_event = 0; /* FALSE */
10741     event.xany.window = Tk_WindowId((menuRefPtr->menuPtr)->tkwin);
10742     event.xany.display = Tk_Display((menuRefPtr->menuPtr)->tkwin);
10743     event.xconfigure.window = event.xany.window;
10744     Tk_HandleEvent(&event);
10745 #endif
10746 
10747 #else /* TCL_MAJOR_VERSION <= 7 */
10748     rb_notimplement();
10749 #endif
10750 
10751     return interp;
10752 }
10753 
10754 static VALUE
10755 ip_make_menu_embeddable(interp, menu_path)
10756     VALUE interp;
10757     VALUE menu_path;
10758 {
10759     VALUE argv[1];
10760 
10761     argv[0] = menu_path;
10762     return tk_funcall(ip_make_menu_embeddable_core, 1, argv, interp);
10763 }
10764 
10765 
10766 /*###############################################*/
10767 
10768 /*---- initialization ----*/
10769 void
10770 Init_tcltklib()
10771 {
10772     int  ret;
10773 
10774     VALUE lib = rb_define_module("TclTkLib");
10775     VALUE ip = rb_define_class("TclTkIp", rb_cObject);
10776 
10777     VALUE ev_flag = rb_define_module_under(lib, "EventFlag");
10778     VALUE var_flag = rb_define_module_under(lib, "VarAccessFlag");
10779     VALUE release_type = rb_define_module_under(lib, "RELEASE_TYPE");
10780 
10781     /* --------------------------------------------------------------- */
10782 
10783     tcltkip_class = ip;
10784 
10785     /* --------------------------------------------------------------- */
10786 
10787 #ifdef HAVE_RUBY_ENCODING_H
10788     rb_global_variable(&cRubyEncoding);
10789     cRubyEncoding = rb_path2class("Encoding");
10790 
10791     ENCODING_INDEX_UTF8   = rb_enc_to_index(rb_utf8_encoding());
10792     ENCODING_INDEX_BINARY = rb_enc_find_index("binary");
10793 #endif
10794 
10795     rb_global_variable(&ENCODING_NAME_UTF8);
10796     rb_global_variable(&ENCODING_NAME_BINARY);
10797 
10798     ENCODING_NAME_UTF8   = rb_obj_freeze(rb_str_new2("utf-8"));
10799     ENCODING_NAME_BINARY = rb_obj_freeze(rb_str_new2("binary"));
10800 
10801     /* --------------------------------------------------------------- */
10802 
10803     rb_global_variable(&eTkCallbackReturn);
10804     rb_global_variable(&eTkCallbackBreak);
10805     rb_global_variable(&eTkCallbackContinue);
10806 
10807     rb_global_variable(&eventloop_thread);
10808     rb_global_variable(&eventloop_stack);
10809     rb_global_variable(&watchdog_thread);
10810 
10811     rb_global_variable(&rbtk_pending_exception);
10812 
10813    /* --------------------------------------------------------------- */
10814 
10815     rb_define_const(lib, "COMPILE_INFO", tcltklib_compile_info());
10816 
10817     rb_define_const(lib, "RELEASE_DATE",
10818                     rb_obj_freeze(rb_str_new2(tcltklib_release_date)));
10819 
10820     rb_define_const(lib, "FINALIZE_PROC_NAME",
10821                     rb_str_new2(finalize_hook_name));
10822 
10823    /* --------------------------------------------------------------- */
10824 
10825 #ifdef __WIN32__
10826 #  define TK_WINDOWING_SYSTEM "win32"
10827 #else
10828 #  ifdef MAC_TCL
10829 #    define TK_WINDOWING_SYSTEM "classic"
10830 #  else
10831 #    ifdef MAC_OSX_TK
10832 #      define TK_WINDOWING_SYSTEM "aqua"
10833 #    else
10834 #      define TK_WINDOWING_SYSTEM "x11"
10835 #    endif
10836 #  endif
10837 #endif
10838     rb_define_const(lib, "WINDOWING_SYSTEM",
10839                     rb_obj_freeze(rb_str_new2(TK_WINDOWING_SYSTEM)));
10840 
10841    /* --------------------------------------------------------------- */
10842 
10843     rb_define_const(ev_flag, "NONE",      INT2FIX(0));
10844     rb_define_const(ev_flag, "WINDOW",    INT2FIX(TCL_WINDOW_EVENTS));
10845     rb_define_const(ev_flag, "FILE",      INT2FIX(TCL_FILE_EVENTS));
10846     rb_define_const(ev_flag, "TIMER",     INT2FIX(TCL_TIMER_EVENTS));
10847     rb_define_const(ev_flag, "IDLE",      INT2FIX(TCL_IDLE_EVENTS));
10848     rb_define_const(ev_flag, "ALL",       INT2FIX(TCL_ALL_EVENTS));
10849     rb_define_const(ev_flag, "DONT_WAIT", INT2FIX(TCL_DONT_WAIT));
10850 
10851     /* --------------------------------------------------------------- */
10852 
10853     rb_define_const(var_flag, "NONE",           INT2FIX(0));
10854     rb_define_const(var_flag, "GLOBAL_ONLY",    INT2FIX(TCL_GLOBAL_ONLY));
10855 #ifdef TCL_NAMESPACE_ONLY
10856     rb_define_const(var_flag, "NAMESPACE_ONLY", INT2FIX(TCL_NAMESPACE_ONLY));
10857 #else /* probably Tcl7.6 */
10858     rb_define_const(var_flag, "NAMESPACE_ONLY", INT2FIX(0));
10859 #endif
10860     rb_define_const(var_flag, "LEAVE_ERR_MSG",  INT2FIX(TCL_LEAVE_ERR_MSG));
10861     rb_define_const(var_flag, "APPEND_VALUE",   INT2FIX(TCL_APPEND_VALUE));
10862     rb_define_const(var_flag, "LIST_ELEMENT",   INT2FIX(TCL_LIST_ELEMENT));
10863 #ifdef TCL_PARSE_PART1
10864     rb_define_const(var_flag, "PARSE_VARNAME",  INT2FIX(TCL_PARSE_PART1));
10865 #else /* probably Tcl7.6 */
10866     rb_define_const(var_flag, "PARSE_VARNAME",  INT2FIX(0));
10867 #endif
10868 
10869     /* --------------------------------------------------------------- */
10870 
10871     rb_define_module_function(lib, "get_version", lib_getversion, -1);
10872     rb_define_module_function(lib, "get_release_type_name",
10873                               lib_get_reltype_name, -1);
10874 
10875     rb_define_const(release_type, "ALPHA", INT2FIX(TCL_ALPHA_RELEASE));
10876     rb_define_const(release_type, "BETA",  INT2FIX(TCL_BETA_RELEASE));
10877     rb_define_const(release_type, "FINAL", INT2FIX(TCL_FINAL_RELEASE));
10878 
10879     /* --------------------------------------------------------------- */
10880 
10881     eTkCallbackReturn = rb_define_class("TkCallbackReturn", rb_eStandardError);
10882     eTkCallbackBreak = rb_define_class("TkCallbackBreak", rb_eStandardError);
10883     eTkCallbackContinue = rb_define_class("TkCallbackContinue",
10884                                           rb_eStandardError);
10885 
10886     /* --------------------------------------------------------------- */
10887 
10888     eLocalJumpError = rb_const_get(rb_cObject, rb_intern("LocalJumpError"));
10889 
10890     eTkLocalJumpError = rb_define_class("TkLocalJumpError", eLocalJumpError);
10891 
10892     eTkCallbackRetry  = rb_define_class("TkCallbackRetry", eTkLocalJumpError);
10893     eTkCallbackRedo   = rb_define_class("TkCallbackRedo",  eTkLocalJumpError);
10894     eTkCallbackThrow  = rb_define_class("TkCallbackThrow", eTkLocalJumpError);
10895 
10896     /* --------------------------------------------------------------- */
10897 
10898     ID_at_enc = rb_intern("@encoding");
10899     ID_at_interp = rb_intern("@interp");
10900     ID_encoding_name = rb_intern("encoding_name");
10901     ID_encoding_table = rb_intern("encoding_table");
10902 
10903     ID_stop_p = rb_intern("stop?");
10904     ID_alive_p = rb_intern("alive?");
10905     ID_kill = rb_intern("kill");
10906     ID_join = rb_intern("join");
10907     ID_value = rb_intern("value");
10908 
10909     ID_call = rb_intern("call");
10910     ID_backtrace = rb_intern("backtrace");
10911     ID_message = rb_intern("message");
10912 
10913     ID_at_reason = rb_intern("@reason");
10914     ID_return = rb_intern("return");
10915     ID_break = rb_intern("break");
10916     ID_next = rb_intern("next");
10917 
10918     ID_to_s = rb_intern("to_s");
10919     ID_inspect = rb_intern("inspect");
10920 
10921     /* --------------------------------------------------------------- */
10922 
10923     rb_define_module_function(lib, "mainloop", lib_mainloop, -1);
10924     rb_define_module_function(lib, "mainloop_thread?",
10925                               lib_evloop_thread_p, 0);
10926     rb_define_module_function(lib, "mainloop_watchdog",
10927                               lib_mainloop_watchdog, -1);
10928     rb_define_module_function(lib, "do_thread_callback",
10929                               lib_thread_callback, -1);
10930     rb_define_module_function(lib, "do_one_event", lib_do_one_event, -1);
10931     rb_define_module_function(lib, "mainloop_abort_on_exception",
10932                              lib_evloop_abort_on_exc, 0);
10933     rb_define_module_function(lib, "mainloop_abort_on_exception=",
10934                              lib_evloop_abort_on_exc_set, 1);
10935     rb_define_module_function(lib, "set_eventloop_window_mode",
10936                               set_eventloop_window_mode, 1);
10937     rb_define_module_function(lib, "get_eventloop_window_mode",
10938                               get_eventloop_window_mode, 0);
10939     rb_define_module_function(lib, "set_eventloop_tick",set_eventloop_tick,1);
10940     rb_define_module_function(lib, "get_eventloop_tick",get_eventloop_tick,0);
10941     rb_define_module_function(lib, "set_no_event_wait", set_no_event_wait, 1);
10942     rb_define_module_function(lib, "get_no_event_wait", get_no_event_wait, 0);
10943     rb_define_module_function(lib, "set_eventloop_weight",
10944                               set_eventloop_weight, 2);
10945     rb_define_module_function(lib, "set_max_block_time", set_max_block_time,1);
10946     rb_define_module_function(lib, "get_eventloop_weight",
10947                               get_eventloop_weight, 0);
10948     rb_define_module_function(lib, "num_of_mainwindows",
10949                               lib_num_of_mainwindows, 0);
10950 
10951     /* --------------------------------------------------------------- */
10952 
10953     rb_define_module_function(lib, "_split_tklist", lib_split_tklist, 1);
10954     rb_define_module_function(lib, "_merge_tklist", lib_merge_tklist, -1);
10955     rb_define_module_function(lib, "_conv_listelement",
10956                               lib_conv_listelement, 1);
10957     rb_define_module_function(lib, "_toUTF8", lib_toUTF8, -1);
10958     rb_define_module_function(lib, "_fromUTF8", lib_fromUTF8, -1);
10959     rb_define_module_function(lib, "_subst_UTF_backslash",
10960                               lib_UTF_backslash, 1);
10961     rb_define_module_function(lib, "_subst_Tcl_backslash",
10962                               lib_Tcl_backslash, 1);
10963 
10964     rb_define_module_function(lib, "encoding_system",
10965                               lib_get_system_encoding, 0);
10966     rb_define_module_function(lib, "encoding_system=",
10967                               lib_set_system_encoding, 1);
10968     rb_define_module_function(lib, "encoding",
10969                               lib_get_system_encoding, 0);
10970     rb_define_module_function(lib, "encoding=",
10971                               lib_set_system_encoding, 1);
10972 
10973     /* --------------------------------------------------------------- */
10974 
10975     rb_define_alloc_func(ip, ip_alloc);
10976     rb_define_method(ip, "initialize", ip_init, -1);
10977     rb_define_method(ip, "create_slave", ip_create_slave, -1);
10978     rb_define_method(ip, "slave_of?", ip_is_slave_of_p, 1);
10979     rb_define_method(ip, "make_safe", ip_make_safe, 0);
10980     rb_define_method(ip, "safe?", ip_is_safe_p, 0);
10981     rb_define_method(ip, "allow_ruby_exit?", ip_allow_ruby_exit_p, 0);
10982     rb_define_method(ip, "allow_ruby_exit=", ip_allow_ruby_exit_set, 1);
10983     rb_define_method(ip, "delete", ip_delete, 0);
10984     rb_define_method(ip, "deleted?", ip_is_deleted_p, 0);
10985     rb_define_method(ip, "has_mainwindow?", ip_has_mainwindow_p, 0);
10986     rb_define_method(ip, "invalid_namespace?", ip_has_invalid_namespace_p, 0);
10987     rb_define_method(ip, "_eval", ip_eval, 1);
10988     rb_define_method(ip, "_cancel_eval", ip_cancel_eval, -1);
10989     rb_define_method(ip, "_cancel_eval_unwind", ip_cancel_eval_unwind, -1);
10990     rb_define_method(ip, "_toUTF8", ip_toUTF8, -1);
10991     rb_define_method(ip, "_fromUTF8", ip_fromUTF8, -1);
10992     rb_define_method(ip, "_thread_vwait", ip_thread_vwait, 1);
10993     rb_define_method(ip, "_thread_tkwait", ip_thread_tkwait, 2);
10994     rb_define_method(ip, "_invoke", ip_invoke, -1);
10995     rb_define_method(ip, "_immediate_invoke", ip_invoke_immediate, -1);
10996     rb_define_method(ip, "_return_value", ip_retval, 0);
10997 
10998     rb_define_method(ip, "_create_console", ip_create_console, 0);
10999 
11000     /* --------------------------------------------------------------- */
11001 
11002     rb_define_method(ip, "create_dummy_encoding_for_tk",
11003                      create_dummy_encoding_for_tk, 1);
11004     rb_define_method(ip, "encoding_table", ip_get_encoding_table, 0);
11005 
11006     /* --------------------------------------------------------------- */
11007 
11008     rb_define_method(ip, "_get_variable", ip_get_variable, 2);
11009     rb_define_method(ip, "_get_variable2", ip_get_variable2, 3);
11010     rb_define_method(ip, "_set_variable", ip_set_variable, 3);
11011     rb_define_method(ip, "_set_variable2", ip_set_variable2, 4);
11012     rb_define_method(ip, "_unset_variable", ip_unset_variable, 2);
11013     rb_define_method(ip, "_unset_variable2", ip_unset_variable2, 3);
11014     rb_define_method(ip, "_get_global_var", ip_get_global_var, 1);
11015     rb_define_method(ip, "_get_global_var2", ip_get_global_var2, 2);
11016     rb_define_method(ip, "_set_global_var", ip_set_global_var, 2);
11017     rb_define_method(ip, "_set_global_var2", ip_set_global_var2, 3);
11018     rb_define_method(ip, "_unset_global_var", ip_unset_global_var, 1);
11019     rb_define_method(ip, "_unset_global_var2", ip_unset_global_var2, 2);
11020 
11021     /* --------------------------------------------------------------- */
11022 
11023     rb_define_method(ip, "_make_menu_embeddable", ip_make_menu_embeddable, 1);
11024 
11025     /* --------------------------------------------------------------- */
11026 
11027     rb_define_method(ip, "_split_tklist", ip_split_tklist, 1);
11028     rb_define_method(ip, "_merge_tklist", lib_merge_tklist, -1);
11029     rb_define_method(ip, "_conv_listelement", lib_conv_listelement, 1);
11030 
11031     /* --------------------------------------------------------------- */
11032 
11033     rb_define_method(ip, "mainloop", ip_mainloop, -1);
11034     rb_define_method(ip, "mainloop_watchdog", ip_mainloop_watchdog, -1);
11035     rb_define_method(ip, "do_one_event", ip_do_one_event, -1);
11036     rb_define_method(ip, "mainloop_abort_on_exception",
11037                     ip_evloop_abort_on_exc, 0);
11038     rb_define_method(ip, "mainloop_abort_on_exception=",
11039                     ip_evloop_abort_on_exc_set, 1);
11040     rb_define_method(ip, "set_eventloop_tick", ip_set_eventloop_tick, 1);
11041     rb_define_method(ip, "get_eventloop_tick", ip_get_eventloop_tick, 0);
11042     rb_define_method(ip, "set_no_event_wait", ip_set_no_event_wait, 1);
11043     rb_define_method(ip, "get_no_event_wait", ip_get_no_event_wait, 0);
11044     rb_define_method(ip, "set_eventloop_weight", ip_set_eventloop_weight, 2);
11045     rb_define_method(ip, "get_eventloop_weight", ip_get_eventloop_weight, 0);
11046     rb_define_method(ip, "set_max_block_time", set_max_block_time, 1);
11047     rb_define_method(ip, "restart", ip_restart, 0);
11048 
11049     /* --------------------------------------------------------------- */
11050 
11051     eventloop_thread = Qnil;
11052     eventloop_interp = (Tcl_Interp*)NULL;
11053 
11054 #ifndef DEFAULT_EVENTLOOP_DEPTH
11055 #define DEFAULT_EVENTLOOP_DEPTH 7
11056 #endif
11057     eventloop_stack = rb_ary_new2(DEFAULT_EVENTLOOP_DEPTH);
11058     RbTk_OBJ_UNTRUST(eventloop_stack);
11059 
11060     watchdog_thread  = Qnil;
11061 
11062     rbtk_pending_exception = Qnil;
11063 
11064     /* --------------------------------------------------------------- */
11065 
11066 #ifdef HAVE_NATIVETHREAD
11067     /* if ruby->nativethread-supprt and tcltklib->doen't,
11068        the following will cause link-error. */
11069     ruby_native_thread_p();
11070 #endif
11071 
11072     /* --------------------------------------------------------------- */
11073 
11074     rb_set_end_proc(lib_mark_at_exit, 0);
11075 
11076     /* --------------------------------------------------------------- */
11077 
11078     ret = ruby_open_tcl_dll(rb_argv0 ? RSTRING_PTR(rb_argv0) : 0);
11079     switch(ret) {
11080     case TCLTK_STUBS_OK:
11081         break;
11082     case NO_TCL_DLL:
11083         rb_raise(rb_eLoadError, "tcltklib: fail to open tcl_dll");
11084     case NO_FindExecutable:
11085         rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_FindExecutable");
11086     default:
11087         rb_raise(rb_eLoadError, "tcltklib: unknown error(%d) on ruby_open_tcl_dll", ret);
11088     }
11089 
11090     /* --------------------------------------------------------------- */
11091 
11092 #if defined CREATE_RUBYTK_KIT || defined CREATE_RUBYKIT
11093     setup_rubytkkit();
11094 #endif
11095 
11096     /* --------------------------------------------------------------- */
11097 
11098     /* Tcl stub check */
11099     tcl_stubs_check();
11100 
11101     Tcl_ObjType_ByteArray = Tcl_GetObjType(Tcl_ObjTypeName_ByteArray);
11102     Tcl_ObjType_String    = Tcl_GetObjType(Tcl_ObjTypeName_String);
11103 
11104     /* --------------------------------------------------------------- */
11105 
11106     (void)call_original_exit;
11107 }
11108 
11109 /* eof */
11110