1/************************************************
2
3  tkutil.c -
4
5  $Author: nagachika $
6  created at: Fri Nov  3 00:47:54 JST 1995
7
8************************************************/
9
10#define TKUTIL_RELEASE_DATE "2010-03-26"
11
12#include "ruby.h"
13
14#ifdef RUBY_VM
15static int rb_thread_critical; /* dummy */
16#else
17/* On Ruby 1.8.x, use rb_thread_critical (defined at rubysig.h) */
18#include "rubysig.h"
19#endif
20#ifdef HAVE_RUBY_ST_H
21#include "ruby/st.h"
22#else
23#include "st.h"
24#endif
25
26#if !defined(RHASH_TBL)
27#define RHASH_TBL(h) (RHASH(h)->tbl)
28#endif
29#if !defined(RSTRING_PTR)
30#define RSTRING_PTR(s) (RSTRING(s)->ptr)
31#define RSTRING_LEN(s) (RSTRING(s)->len)
32#endif
33#if !defined(RARRAY_PTR)
34#define RARRAY_PTR(s) (RARRAY(s)->ptr)
35#define RARRAY_LEN(s) (RARRAY(s)->len)
36#endif
37
38#if defined(HAVE_STRNDUP) && !defined(_GNU_SOURCE)
39extern char *strndup(const char* _ptr, size_t _len);
40#endif
41
42static VALUE cMethod;
43
44static VALUE cTclTkLib;
45
46static VALUE cTkObject;
47static VALUE cTkCallbackEntry;
48
49static VALUE TK_None;
50
51static VALUE cCB_SUBST;
52static VALUE cSUBST_INFO;
53
54static VALUE ENCODING_NAME_UTF8; /* for saving GC cost */
55
56static ID ID_split_tklist;
57static ID ID_toUTF8;
58static ID ID_fromUTF8;
59static ID ID_path;
60static ID ID_at_path;
61static ID ID_at_enc;
62static ID ID_to_eval;
63static ID ID_to_s;
64static ID ID_source;
65static ID ID_downcase;
66static ID ID_install_cmd;
67static ID ID_merge_tklist;
68static ID ID_encoding;
69static ID ID_encoding_system;
70static ID ID_call;
71
72static ID ID_SUBST_INFO;
73
74static VALUE CALLBACK_TABLE;
75static unsigned long CALLBACK_ID_NUM = 0;
76
77/*************************************/
78
79#if defined(HAVE_RB_OBJ_INSTANCE_EXEC) && !defined(RUBY_VM)
80extern VALUE rb_obj_instance_exec _((int, VALUE*, VALUE));
81#endif
82static VALUE
83tk_s_new(argc, argv, klass)
84    int argc;
85    VALUE *argv;
86    VALUE klass;
87{
88    VALUE obj = rb_class_new_instance(argc, argv, klass);
89
90    if (rb_block_given_p()) {
91#ifndef HAVE_RB_OBJ_INSTANCE_EXEC
92      rb_obj_instance_eval(0, 0, obj);
93#else
94      rb_obj_instance_exec(1, &obj, obj);
95#endif
96    }
97    return obj;
98}
99
100/*************************************/
101
102static VALUE
103tkNone_to_s(self)
104    VALUE self;
105{
106    return rb_str_new2("");
107}
108
109static VALUE
110tkNone_inspect(self)
111    VALUE self;
112{
113    return rb_str_new2("None");
114}
115
116/*************************************/
117
118static VALUE
119tk_obj_untrust(self, obj)
120    VALUE self;
121    VALUE obj;
122{
123#ifdef HAVE_RB_OBJ_TAINT
124  rb_obj_taint(obj);
125#endif
126#ifdef HAVE_RB_OBJ_UNTRUST
127  rb_obj_untrust(obj);
128#endif
129
130  return obj;
131}
132
133static VALUE
134tk_eval_cmd(argc, argv, self)
135    int argc;
136    VALUE argv[];
137    VALUE self;
138{
139    volatile VALUE cmd, rest;
140
141    rb_scan_args(argc, argv, "1*", &cmd, &rest);
142    return rb_eval_cmd(cmd, rest, 0);
143}
144
145static VALUE
146tk_do_callback(argc, argv, self)
147    int   argc;
148    VALUE *argv;
149    VALUE self;
150{
151#if 0
152    volatile VALUE id;
153    volatile VALUE rest;
154
155    rb_scan_args(argc, argv, "1*", &id, &rest);
156    return rb_apply(rb_hash_aref(CALLBACK_TABLE, id), ID_call, rest);
157#endif
158    return rb_funcall2(rb_hash_aref(CALLBACK_TABLE, argv[0]),
159                       ID_call, argc - 1, argv + 1);
160}
161
162static const char cmd_id_head[] = "ruby_cmd TkUtil callback ";
163static const char cmd_id_prefix[] = "cmd";
164
165static VALUE
166tk_install_cmd_core(cmd)
167    VALUE cmd;
168{
169    volatile VALUE id_num;
170
171    id_num = ULONG2NUM(CALLBACK_ID_NUM++);
172    id_num = rb_funcall(id_num, ID_to_s, 0, 0);
173    id_num = rb_str_append(rb_str_new2(cmd_id_prefix), id_num);
174    rb_hash_aset(CALLBACK_TABLE, id_num, cmd);
175    return rb_str_append(rb_str_new2(cmd_id_head), id_num);
176}
177
178static VALUE
179tk_install_cmd(argc, argv, self)
180    int   argc;
181    VALUE *argv;
182    VALUE self;
183{
184    volatile VALUE cmd;
185
186#if 0
187    if (rb_scan_args(argc, argv, "01", &cmd) == 0) {
188        cmd = rb_block_proc();
189    }
190    return tk_install_cmd_core(cmd);
191#endif
192    if (argc == 0) {
193        cmd = rb_block_proc();
194    } else {
195        cmd = argv[0];
196    }
197    return tk_install_cmd_core(cmd);
198}
199
200static VALUE
201tk_uninstall_cmd(self, cmd_id)
202    VALUE self;
203    VALUE cmd_id;
204{
205    size_t head_len = strlen(cmd_id_head);
206    size_t prefix_len = strlen(cmd_id_prefix);
207
208    StringValue(cmd_id);
209    if (strncmp(cmd_id_head, RSTRING_PTR(cmd_id), head_len) != 0) {
210        return Qnil;
211    }
212    if (strncmp(cmd_id_prefix,
213                RSTRING_PTR(cmd_id) + head_len, prefix_len) != 0) {
214        return Qnil;
215    }
216
217    return rb_hash_delete(CALLBACK_TABLE,
218                          rb_str_new2(RSTRING_PTR(cmd_id) + head_len));
219}
220
221static VALUE
222tk_toUTF8(argc, argv, self)
223    int   argc;
224    VALUE *argv;
225    VALUE self;
226{
227    return rb_funcall2(cTclTkLib, ID_toUTF8, argc, argv);
228}
229
230static VALUE
231tk_fromUTF8(argc, argv, self)
232    int   argc;
233    VALUE *argv;
234    VALUE self;
235{
236    return rb_funcall2(cTclTkLib, ID_fromUTF8, argc, argv);
237}
238
239static VALUE
240fromDefaultEnc_toUTF8(str, self)
241    VALUE str;
242    VALUE self;
243{
244    VALUE argv[1];
245
246    argv[0] = str;
247    return tk_toUTF8(1, argv, self);
248}
249
250#if 0
251static VALUE
252fromUTF8_toDefaultEnc(str, self)
253    VALUE str;
254    VALUE self;
255{
256    VALUE argv[1];
257
258    argv[0] = str;
259    return tk_fromUTF8(1, argv, self);
260}
261#endif
262
263static int
264to_strkey(key, value, hash)
265    VALUE key;
266    VALUE value;
267    VALUE hash;
268{
269    rb_hash_aset(hash, rb_funcall(key, ID_to_s, 0, 0), value);
270    return ST_CHECK;
271}
272
273static VALUE
274tk_symbolkey2str(self, keys)
275    VALUE self;
276    VALUE keys;
277{
278    volatile VALUE new_keys = rb_hash_new();
279
280    if (NIL_P(keys)) return new_keys;
281    keys = rb_convert_type(keys, T_HASH, "Hash", "to_hash");
282    st_foreach_check(RHASH_TBL(keys), to_strkey, new_keys, Qundef);
283    return new_keys;
284}
285
286static VALUE get_eval_string_core _((VALUE, VALUE, VALUE));
287static VALUE ary2list _((VALUE, VALUE, VALUE));
288static VALUE ary2list2 _((VALUE, VALUE, VALUE));
289static VALUE hash2list _((VALUE, VALUE));
290static VALUE hash2list_enc _((VALUE, VALUE));
291static VALUE hash2kv _((VALUE, VALUE, VALUE));
292static VALUE hash2kv_enc _((VALUE, VALUE, VALUE));
293
294static VALUE
295ary2list(ary, enc_flag, self)
296    VALUE ary;
297    VALUE enc_flag;
298    VALUE self;
299{
300    long idx, idx2, size, size2;
301    int req_chk_flag;
302    volatile VALUE val, val2, str_val;
303    volatile VALUE dst;
304    volatile VALUE sys_enc, dst_enc, str_enc;
305
306    sys_enc = rb_funcall(cTclTkLib, ID_encoding, 0, 0);
307    if (NIL_P(sys_enc)) {
308      sys_enc = rb_funcall(cTclTkLib, ID_encoding_system, 0, 0);
309      sys_enc = rb_funcall(sys_enc, ID_to_s, 0, 0);
310    }
311
312    if (NIL_P(enc_flag)) {
313        dst_enc = sys_enc;
314        req_chk_flag = 1;
315    } else if (TYPE(enc_flag) == T_TRUE || TYPE(enc_flag) == T_FALSE) {
316        dst_enc = enc_flag;
317        req_chk_flag = 0;
318    } else {
319        dst_enc = rb_funcall(enc_flag, ID_to_s, 0, 0);
320        req_chk_flag = 0;
321    }
322
323    /* size = RARRAY_LEN(ary); */
324    size = 0;
325    for(idx = 0; idx < RARRAY_LEN(ary); idx++) {
326        if (TYPE(RARRAY_PTR(ary)[idx]) == T_HASH) {
327            size += 2 * RHASH_SIZE(RARRAY_PTR(ary)[idx]);
328        } else {
329            size++;
330        }
331    }
332
333    dst = rb_ary_new2(size);
334    for(idx = 0; idx < RARRAY_LEN(ary); idx++) {
335        val = RARRAY_PTR(ary)[idx];
336        str_val = Qnil;
337        switch(TYPE(val)) {
338        case T_ARRAY:
339            str_val = ary2list(val, enc_flag, self);
340            rb_ary_push(dst, str_val);
341
342            if (req_chk_flag) {
343                str_enc = rb_ivar_get(str_val, ID_at_enc);
344                if (!NIL_P(str_enc)) {
345                    str_enc = rb_funcall(str_enc, ID_to_s, 0, 0);
346                } else {
347                    str_enc = sys_enc;
348                }
349                if (!rb_str_cmp(str_enc, dst_enc)) {
350                    dst_enc = Qtrue;
351                    req_chk_flag = 0;
352                }
353            }
354
355            break;
356
357        case T_HASH:
358	    /* rb_ary_push(dst, hash2list(val, self)); */
359            if (RTEST(enc_flag)) {
360                val = hash2kv_enc(val, Qnil, self);
361            } else {
362                val = hash2kv(val, Qnil, self);
363            }
364            size2 = RARRAY_LEN(val);
365            for(idx2 = 0; idx2 < size2; idx2++) {
366                val2 = RARRAY_PTR(val)[idx2];
367                switch(TYPE(val2)) {
368                case T_ARRAY:
369                    str_val = ary2list(val2, enc_flag, self);
370                    rb_ary_push(dst, str_val);
371                    break;
372
373                case T_HASH:
374                    if (RTEST(enc_flag)) {
375                        str_val = hash2list_enc(val2, self);
376                    } else {
377                        str_val = hash2list(val2, self);
378                    }
379                    rb_ary_push(dst, str_val);
380                    break;
381
382                default:
383                    if (val2 != TK_None) {
384                        str_val = get_eval_string_core(val2, enc_flag, self);
385                        rb_ary_push(dst, str_val);
386                    }
387                }
388
389                if (req_chk_flag) {
390                    str_enc = rb_ivar_get(str_val, ID_at_enc);
391                    if (!NIL_P(str_enc)) {
392                        str_enc = rb_funcall(str_enc, ID_to_s, 0, 0);
393                    } else {
394                        str_enc = sys_enc;
395                    }
396                    if (!rb_str_cmp(str_enc, dst_enc)) {
397                        dst_enc = Qtrue;
398                        req_chk_flag = 0;
399                    }
400                }
401            }
402            break;
403
404        default:
405            if (val != TK_None) {
406                str_val = get_eval_string_core(val, enc_flag, self);
407                rb_ary_push(dst, str_val);
408
409                if (req_chk_flag) {
410                    str_enc = rb_ivar_get(str_val, ID_at_enc);
411                    if (!NIL_P(str_enc)) {
412                        str_enc = rb_funcall(str_enc, ID_to_s, 0, 0);
413                    } else {
414                        str_enc = sys_enc;
415                    }
416                    if (!rb_str_cmp(str_enc, dst_enc)) {
417                        dst_enc = Qtrue;
418                        req_chk_flag = 0;
419                    }
420                }
421            }
422        }
423    }
424
425    if (RTEST(dst_enc) && !NIL_P(sys_enc)) {
426        for(idx = 0; idx < RARRAY_LEN(dst); idx++) {
427            str_val = RARRAY_PTR(dst)[idx];
428            if (rb_obj_respond_to(self, ID_toUTF8, Qtrue)) {
429                str_val = rb_funcall(self, ID_toUTF8, 1, str_val);
430            } else {
431                str_val = rb_funcall(cTclTkLib, ID_toUTF8, 1, str_val);
432            }
433            RARRAY_PTR(dst)[idx] = str_val;
434        }
435        val = rb_apply(cTclTkLib, ID_merge_tklist, dst);
436        if (TYPE(dst_enc) == T_STRING) {
437            val = rb_funcall(cTclTkLib, ID_fromUTF8, 2, val, dst_enc);
438            rb_ivar_set(val, ID_at_enc, dst_enc);
439        } else {
440            rb_ivar_set(val, ID_at_enc, ENCODING_NAME_UTF8);
441        }
442        return val;
443    } else {
444        return rb_apply(cTclTkLib, ID_merge_tklist, dst);
445    }
446}
447
448static VALUE
449ary2list2(ary, enc_flag, self)
450    VALUE ary;
451    VALUE enc_flag;
452    VALUE self;
453{
454    long idx, size;
455    int req_chk_flag;
456    volatile VALUE val, str_val;
457    volatile VALUE dst;
458    volatile VALUE sys_enc, dst_enc, str_enc;
459
460    sys_enc = rb_funcall(cTclTkLib, ID_encoding, 0, 0);
461    if (NIL_P(sys_enc)) {
462      sys_enc = rb_funcall(cTclTkLib, ID_encoding_system, 0, 0);
463      sys_enc = rb_funcall(sys_enc, ID_to_s, 0, 0);
464    }
465
466    if (NIL_P(enc_flag)) {
467        dst_enc = sys_enc;
468        req_chk_flag = 1;
469    } else if (TYPE(enc_flag) == T_TRUE || TYPE(enc_flag) == T_FALSE) {
470        dst_enc = enc_flag;
471        req_chk_flag = 0;
472    } else {
473        dst_enc = rb_funcall(enc_flag, ID_to_s, 0, 0);
474        req_chk_flag = 0;
475    }
476
477    size = RARRAY_LEN(ary);
478    dst = rb_ary_new2(size);
479    for(idx = 0; idx < RARRAY_LEN(ary); idx++) {
480        val = RARRAY_PTR(ary)[idx];
481        str_val = Qnil;
482        switch(TYPE(val)) {
483        case T_ARRAY:
484            str_val = ary2list(val, enc_flag, self);
485            break;
486
487        case T_HASH:
488            if (RTEST(enc_flag)) {
489                str_val = hash2list(val, self);
490            } else {
491                str_val = hash2list_enc(val, self);
492            }
493            break;
494
495        default:
496            if (val != TK_None) {
497                str_val = get_eval_string_core(val, enc_flag, self);
498            }
499        }
500
501        if (!NIL_P(str_val)) {
502            rb_ary_push(dst, str_val);
503
504            if (req_chk_flag) {
505                str_enc = rb_ivar_get(str_val, ID_at_enc);
506                if (!NIL_P(str_enc)) {
507                    str_enc = rb_funcall(str_enc, ID_to_s, 0, 0);
508                } else {
509                    str_enc = sys_enc;
510                }
511                if (!rb_str_cmp(str_enc, dst_enc)) {
512                    dst_enc = Qtrue;
513                    req_chk_flag = 0;
514                }
515            }
516        }
517    }
518
519    if (RTEST(dst_enc) && !NIL_P(sys_enc)) {
520        for(idx = 0; idx < RARRAY_LEN(dst); idx++) {
521            str_val = RARRAY_PTR(dst)[idx];
522            if (rb_obj_respond_to(self, ID_toUTF8, Qtrue)) {
523                str_val = rb_funcall(self, ID_toUTF8, 1, str_val);
524            } else {
525                str_val = rb_funcall(cTclTkLib, ID_toUTF8, 1, str_val);
526            }
527            RARRAY_PTR(dst)[idx] = str_val;
528        }
529        val = rb_apply(cTclTkLib, ID_merge_tklist, dst);
530        if (TYPE(dst_enc) == T_STRING) {
531            val = rb_funcall(cTclTkLib, ID_fromUTF8, 2, val, dst_enc);
532            rb_ivar_set(val, ID_at_enc, dst_enc);
533        } else {
534            rb_ivar_set(val, ID_at_enc, ENCODING_NAME_UTF8);
535        }
536        return val;
537    } else {
538        return rb_apply(cTclTkLib, ID_merge_tklist, dst);
539    }
540}
541
542static VALUE
543key2keyname(key)
544    VALUE key;
545{
546    return rb_str_append(rb_str_new2("-"), rb_funcall(key, ID_to_s, 0, 0));
547}
548
549static VALUE
550assoc2kv(assoc, ary, self)
551    VALUE assoc;
552    VALUE ary;
553    VALUE self;
554{
555    long i, j, len;
556    volatile VALUE pair;
557    volatile VALUE val;
558    volatile VALUE dst = rb_ary_new2(2 * RARRAY_LEN(assoc));
559
560    len = RARRAY_LEN(assoc);
561
562    for(i = 0; i < len; i++) {
563        pair = RARRAY_PTR(assoc)[i];
564        if (TYPE(pair) != T_ARRAY) {
565            rb_ary_push(dst, key2keyname(pair));
566            continue;
567        }
568        switch(RARRAY_LEN(assoc)) {
569        case 2:
570            rb_ary_push(dst, RARRAY_PTR(pair)[2]);
571
572        case 1:
573            rb_ary_push(dst, key2keyname(RARRAY_PTR(pair)[0]));
574
575        case 0:
576            continue;
577
578        default:
579            rb_ary_push(dst, key2keyname(RARRAY_PTR(pair)[0]));
580
581            val = rb_ary_new2(RARRAY_LEN(pair) - 1);
582            for(j = 1; j < RARRAY_LEN(pair); j++) {
583                rb_ary_push(val, RARRAY_PTR(pair)[j]);
584            }
585
586            rb_ary_push(dst, val);
587        }
588    }
589
590    if (NIL_P(ary)) {
591        return dst;
592    } else {
593        return rb_ary_plus(ary, dst);
594    }
595}
596
597static VALUE
598assoc2kv_enc(assoc, ary, self)
599    VALUE assoc;
600    VALUE ary;
601    VALUE self;
602{
603    long i, j, len;
604    volatile VALUE pair;
605    volatile VALUE val;
606    volatile VALUE dst = rb_ary_new2(2 * RARRAY_LEN(assoc));
607
608    len = RARRAY_LEN(assoc);
609
610    for(i = 0; i < len; i++) {
611        pair = RARRAY_PTR(assoc)[i];
612        if (TYPE(pair) != T_ARRAY) {
613            rb_ary_push(dst, key2keyname(pair));
614            continue;
615        }
616        switch(RARRAY_LEN(assoc)) {
617        case 2:
618            rb_ary_push(dst, get_eval_string_core(RARRAY_PTR(pair)[2], Qtrue, self));
619
620        case 1:
621            rb_ary_push(dst, key2keyname(RARRAY_PTR(pair)[0]));
622
623        case 0:
624            continue;
625
626        default:
627            rb_ary_push(dst, key2keyname(RARRAY_PTR(pair)[0]));
628
629            val = rb_ary_new2(RARRAY_LEN(pair) - 1);
630            for(j = 1; j < RARRAY_LEN(pair); j++) {
631                rb_ary_push(val, RARRAY_PTR(pair)[j]);
632            }
633
634            rb_ary_push(dst, get_eval_string_core(val, Qtrue, self));
635        }
636    }
637
638    if (NIL_P(ary)) {
639        return dst;
640    } else {
641        return rb_ary_plus(ary, dst);
642    }
643}
644
645static int
646push_kv(key, val, args)
647    VALUE key;
648    VALUE val;
649    VALUE args;
650{
651    volatile VALUE ary;
652
653    ary = RARRAY_PTR(args)[0];
654
655#if 0
656    rb_ary_push(ary, key2keyname(key));
657    if (val != TK_None) rb_ary_push(ary, val);
658#endif
659    rb_ary_push(ary, key2keyname(key));
660
661    if (val == TK_None) return ST_CHECK;
662
663    rb_ary_push(ary, get_eval_string_core(val, Qnil, RARRAY_PTR(args)[1]));
664
665    return ST_CHECK;
666}
667
668static VALUE
669hash2kv(hash, ary, self)
670    VALUE hash;
671    VALUE ary;
672    VALUE self;
673{
674    volatile VALUE dst = rb_ary_new2(2 * RHASH_SIZE(hash));
675    volatile VALUE args = rb_ary_new3(2, dst, self);
676
677    st_foreach_check(RHASH_TBL(hash), push_kv, args, Qundef);
678
679    if (NIL_P(ary)) {
680        return dst;
681    } else {
682        return rb_ary_concat(ary, dst);
683    }
684}
685
686static int
687push_kv_enc(key, val, args)
688    VALUE key;
689    VALUE val;
690    VALUE args;
691{
692    volatile VALUE ary;
693
694    ary = RARRAY_PTR(args)[0];
695
696#if 0
697    rb_ary_push(ary, key2keyname(key));
698    if (val != TK_None) {
699        rb_ary_push(ary, get_eval_string_core(val, Qtrue,
700                                              RARRAY_PTR(args)[1]));
701    }
702#endif
703    rb_ary_push(ary, key2keyname(key));
704
705    if (val == TK_None) return ST_CHECK;
706
707    rb_ary_push(ary, get_eval_string_core(val, Qtrue, RARRAY_PTR(args)[1]));
708
709    return ST_CHECK;
710}
711
712static VALUE
713hash2kv_enc(hash, ary, self)
714    VALUE hash;
715    VALUE ary;
716    VALUE self;
717{
718    volatile VALUE dst = rb_ary_new2(2 * RHASH_SIZE(hash));
719    volatile VALUE args = rb_ary_new3(2, dst, self);
720
721    st_foreach_check(RHASH_TBL(hash), push_kv_enc, args, Qundef);
722
723    if (NIL_P(ary)) {
724        return dst;
725    } else {
726        return rb_ary_concat(ary, dst);
727    }
728}
729
730static VALUE
731hash2list(hash, self)
732    VALUE hash;
733    VALUE self;
734{
735    return ary2list2(hash2kv(hash, Qnil, self), Qfalse, self);
736}
737
738
739static VALUE
740hash2list_enc(hash, self)
741    VALUE hash;
742    VALUE self;
743{
744    return ary2list2(hash2kv_enc(hash, Qnil, self), Qfalse, self);
745}
746
747static VALUE
748tk_hash_kv(argc, argv, self)
749    int   argc;
750    VALUE *argv;
751    VALUE self;
752{
753    volatile VALUE hash, enc_flag, ary;
754
755    ary = Qnil;
756    enc_flag = Qnil;
757    switch(argc) {
758    case 3:
759        ary = argv[2];
760    case 2:
761        enc_flag = argv[1];
762    case 1:
763        hash = argv[0];
764        break;
765    case 0:
766        rb_raise(rb_eArgError, "too few arguments");
767    default: /* >= 3 */
768        rb_raise(rb_eArgError, "too many arguments");
769    }
770
771    switch(TYPE(hash)) {
772    case T_ARRAY:
773        if (RTEST(enc_flag)) {
774            return assoc2kv_enc(hash, ary, self);
775        } else {
776            return assoc2kv(hash, ary, self);
777        }
778
779    case T_HASH:
780        if (RTEST(enc_flag)) {
781            return hash2kv_enc(hash, ary, self);
782        } else {
783            return hash2kv(hash, ary, self);
784        }
785
786    case T_NIL:
787        if (NIL_P(ary)) {
788            return rb_ary_new();
789        } else {
790            return ary;
791        }
792
793    default:
794        if (hash == TK_None) {
795            if (NIL_P(ary)) {
796                return rb_ary_new();
797            } else {
798                return ary;
799            }
800        }
801        rb_raise(rb_eArgError, "Hash is expected for 1st argument");
802    }
803
804    UNREACHABLE;
805}
806
807static VALUE
808get_eval_string_core(obj, enc_flag, self)
809    VALUE obj;
810    VALUE enc_flag;
811    VALUE self;
812{
813    switch(TYPE(obj)) {
814    case T_FLOAT:
815    case T_FIXNUM:
816    case T_BIGNUM:
817        return rb_funcall(obj, ID_to_s, 0, 0);
818
819    case T_STRING:
820        if (RTEST(enc_flag)) {
821            if (rb_obj_respond_to(self, ID_toUTF8, Qtrue)) {
822                return rb_funcall(self, ID_toUTF8, 1, obj);
823            } else {
824                return fromDefaultEnc_toUTF8(obj, self);
825            }
826        } else {
827            return obj;
828        }
829
830    case T_SYMBOL:
831        if (RTEST(enc_flag)) {
832            if (rb_obj_respond_to(self, ID_toUTF8, Qtrue)) {
833                return rb_funcall(self, ID_toUTF8, 1,
834                                  rb_str_new2(rb_id2name(SYM2ID(obj))));
835            } else {
836                return fromDefaultEnc_toUTF8(rb_str_new2(rb_id2name(SYM2ID(obj))), self);
837            }
838        } else {
839#ifdef HAVE_RB_SYM_TO_S
840            return rb_sym_to_s(obj);
841#else
842            return rb_str_new2(rb_id2name(SYM2ID(obj)));
843#endif
844        }
845
846    case T_HASH:
847        if (RTEST(enc_flag)) {
848            return hash2list_enc(obj, self);
849        } else {
850            return hash2list(obj, self);
851        }
852
853    case T_ARRAY:
854        return ary2list(obj, enc_flag, self);
855
856    case T_FALSE:
857        return rb_str_new2("0");
858
859    case T_TRUE:
860        return rb_str_new2("1");
861
862    case T_NIL:
863        return rb_str_new2("");
864
865    case T_REGEXP:
866        return rb_funcall(obj, ID_source, 0, 0);
867
868    default:
869        if (rb_obj_is_kind_of(obj, cTkObject)) {
870            /* return rb_str_new3(rb_funcall(obj, ID_path, 0, 0)); */
871            return get_eval_string_core(rb_funcall(obj, ID_path, 0, 0),
872                                        enc_flag, self);
873        }
874
875        if (rb_obj_is_kind_of(obj, rb_cProc)
876            || rb_obj_is_kind_of(obj, cMethod)
877            || rb_obj_is_kind_of(obj, cTkCallbackEntry)) {
878            if (rb_obj_respond_to(self, ID_install_cmd, Qtrue)) {
879                return rb_funcall(self, ID_install_cmd, 1, obj);
880            } else {
881                return tk_install_cmd_core(obj);
882            }
883        }
884
885        if (obj == TK_None)  return Qnil;
886
887        if (rb_obj_respond_to(obj, ID_to_eval, Qtrue)) {
888            /* return rb_funcall(obj, ID_to_eval, 0, 0); */
889            return get_eval_string_core(rb_funcall(obj, ID_to_eval, 0, 0),
890                                        enc_flag, self);
891        } else if (rb_obj_respond_to(obj, ID_path, Qtrue)) {
892            /* return rb_funcall(obj, ID_path, 0, 0); */
893            return get_eval_string_core(rb_funcall(obj, ID_path, 0, 0),
894                                        enc_flag, self);
895        } else if (rb_obj_respond_to(obj, ID_to_s, Qtrue)) {
896            return rb_funcall(obj, ID_to_s, 0, 0);
897        }
898    }
899
900    rb_warning("fail to convert '%+"PRIsVALUE"' to string for Tk", obj);
901
902    return obj;
903}
904
905static VALUE
906tk_get_eval_string(argc, argv, self)
907    int   argc;
908    VALUE *argv;
909    VALUE self;
910{
911    volatile VALUE obj, enc_flag;
912
913    if (rb_scan_args(argc, argv, "11", &obj, &enc_flag) == 1) {
914        enc_flag = Qnil;
915    }
916
917    return get_eval_string_core(obj, enc_flag, self);
918}
919
920static VALUE
921tk_get_eval_enc_str(self, obj)
922    VALUE self;
923    VALUE obj;
924{
925    if (obj == TK_None) {
926        return obj;
927    } else {
928        return get_eval_string_core(obj, Qtrue, self);
929    }
930}
931
932static VALUE
933tk_conv_args(argc, argv, self)
934    int   argc;
935    VALUE *argv; /* [0]:base_array, [1]:enc_mode, [2]..[n]:args */
936    VALUE self;
937{
938    int idx, size;
939    volatile VALUE dst;
940    int thr_crit_bup;
941    VALUE old_gc;
942
943    if (argc < 2) {
944      rb_raise(rb_eArgError, "too few arguments");
945    }
946
947    thr_crit_bup = rb_thread_critical;
948    rb_thread_critical = Qtrue;
949    old_gc = rb_gc_disable();
950
951    for(size = 0, idx = 2; idx < argc; idx++) {
952        if (TYPE(argv[idx]) == T_HASH) {
953            size += 2 * RHASH_SIZE(argv[idx]);
954        } else {
955            size++;
956        }
957    }
958    /* dst = rb_ary_new2(argc - 2); */
959    dst = rb_ary_new2(size);
960    for(idx = 2; idx < argc; idx++) {
961        if (TYPE(argv[idx]) == T_HASH) {
962            if (RTEST(argv[1])) {
963                hash2kv_enc(argv[idx], dst, self);
964            } else {
965                hash2kv(argv[idx], dst, self);
966            }
967        } else if (argv[idx] != TK_None) {
968            rb_ary_push(dst, get_eval_string_core(argv[idx], argv[1], self));
969        }
970    }
971
972    if (old_gc == Qfalse) rb_gc_enable();
973    rb_thread_critical = thr_crit_bup;
974
975    return rb_ary_plus(argv[0], dst);
976}
977
978
979/*************************************/
980
981static VALUE
982tcl2rb_bool(self, value)
983    VALUE self;
984    VALUE value;
985{
986    if (TYPE(value) == T_FIXNUM) {
987        if (NUM2INT(value) == 0) {
988            return Qfalse;
989        } else {
990            return Qtrue;
991        }
992    }
993
994    if (TYPE(value) == T_TRUE || TYPE(value) == T_FALSE) {
995        return value;
996    }
997
998    rb_check_type(value, T_STRING);
999
1000    value = rb_funcall(value, ID_downcase, 0);
1001
1002    if (RSTRING_PTR(value) == (char*)NULL) return Qnil;
1003
1004    if (RSTRING_PTR(value)[0] == '\0'
1005        || strcmp(RSTRING_PTR(value), "0") == 0
1006        || strcmp(RSTRING_PTR(value), "no") == 0
1007        || strcmp(RSTRING_PTR(value), "off") == 0
1008        || strcmp(RSTRING_PTR(value), "false") == 0) {
1009        return Qfalse;
1010    } else {
1011        return Qtrue;
1012    }
1013}
1014
1015#if 0
1016static VALUE
1017tkstr_to_dec(value)
1018    VALUE value;
1019{
1020    return rb_cstr_to_inum(RSTRING_PTR(value), 10, 1);
1021}
1022#endif
1023
1024static VALUE
1025tkstr_to_int(value)
1026    VALUE value;
1027{
1028    return rb_cstr_to_inum(RSTRING_PTR(value), 0, 1);
1029}
1030
1031static VALUE
1032tkstr_to_float(value)
1033    VALUE value;
1034{
1035    return rb_float_new(rb_cstr_to_dbl(RSTRING_PTR(value), 1));
1036}
1037
1038static VALUE
1039tkstr_invalid_numstr(value)
1040    VALUE value;
1041{
1042    rb_raise(rb_eArgError,
1043             "invalid value for Number: '%s'", RSTRING_PTR(value));
1044    return Qnil; /*dummy*/
1045}
1046
1047static VALUE
1048tkstr_rescue_float(value)
1049    VALUE value;
1050{
1051    return rb_rescue2(tkstr_to_float, value,
1052                      tkstr_invalid_numstr, value,
1053                      rb_eArgError, 0);
1054}
1055
1056static VALUE
1057tkstr_to_number(value)
1058    VALUE value;
1059{
1060    rb_check_type(value, T_STRING);
1061
1062    if (RSTRING_PTR(value) == (char*)NULL) return INT2FIX(0);
1063
1064    return rb_rescue2(tkstr_to_int, value,
1065                      tkstr_rescue_float, value,
1066                      rb_eArgError, 0);
1067}
1068
1069static VALUE
1070tcl2rb_number(self, value)
1071    VALUE self;
1072    VALUE value;
1073{
1074    return tkstr_to_number(value);
1075}
1076
1077static VALUE
1078tkstr_to_str(value)
1079    VALUE value;
1080{
1081    char * ptr;
1082    long len;
1083
1084    ptr = RSTRING_PTR(value);
1085    len = RSTRING_LEN(value);
1086
1087    if (len > 1 && *ptr == '{' && *(ptr + len - 1) == '}') {
1088        return rb_str_new(ptr + 1, len - 2);
1089    }
1090    return value;
1091}
1092
1093static VALUE
1094tcl2rb_string(self, value)
1095    VALUE self;
1096    VALUE value;
1097{
1098    rb_check_type(value, T_STRING);
1099
1100    if (RSTRING_PTR(value) == (char*)NULL) return rb_tainted_str_new2("");
1101
1102    return tkstr_to_str(value);
1103}
1104
1105static VALUE
1106tcl2rb_num_or_str(self, value)
1107    VALUE self;
1108    VALUE value;
1109{
1110    rb_check_type(value, T_STRING);
1111
1112    if (RSTRING_PTR(value) == (char*)NULL) return rb_tainted_str_new2("");
1113
1114    return rb_rescue2(tkstr_to_number, value,
1115                      tkstr_to_str, value,
1116                      rb_eArgError, 0);
1117}
1118
1119static VALUE
1120tcl2rb_num_or_nil(self, value)
1121    VALUE self;
1122    VALUE value;
1123{
1124    rb_check_type(value, T_STRING);
1125
1126    if (RSTRING_LEN(value) == 0) return Qnil;
1127
1128    return tkstr_to_number(value);
1129}
1130
1131
1132/*************************************/
1133
1134#define CBSUBST_TBL_MAX (256)
1135struct cbsubst_info {
1136    long  full_subst_length;
1137    long  keylen[CBSUBST_TBL_MAX];
1138    char  *key[CBSUBST_TBL_MAX];
1139    char  type[CBSUBST_TBL_MAX];
1140    ID    ivar[CBSUBST_TBL_MAX];
1141    VALUE proc;
1142    VALUE aliases;
1143};
1144
1145static void
1146subst_mark(ptr)
1147    struct cbsubst_info *ptr;
1148{
1149    rb_gc_mark(ptr->proc);
1150    rb_gc_mark(ptr->aliases);
1151}
1152
1153static void
1154subst_free(ptr)
1155    struct cbsubst_info *ptr;
1156{
1157    int i;
1158
1159    if (ptr) {
1160      for(i = 0; i < CBSUBST_TBL_MAX; i++) {
1161	if (ptr->key[i] != NULL) {
1162	  free(ptr->key[i]); /* allocated by malloc */
1163	  ptr->key[i] = NULL;
1164	}
1165      }
1166      xfree(ptr); /* allocated by ALLOC */
1167    }
1168}
1169
1170static VALUE
1171allocate_cbsubst_info(struct cbsubst_info **inf_ptr)
1172{
1173  struct cbsubst_info *inf;
1174  volatile VALUE proc, aliases;
1175  int idx;
1176
1177  inf = ALLOC(struct cbsubst_info);
1178
1179  inf->full_subst_length = 0;
1180
1181  for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) {
1182    inf->keylen[idx] = 0;
1183    inf->key[idx]    = NULL;
1184    inf->type[idx]   = '\0';
1185    inf->ivar[idx]   = (ID) 0;
1186  }
1187
1188  proc = rb_hash_new();
1189  inf->proc = proc;
1190
1191  aliases = rb_hash_new();
1192  inf->aliases = aliases;
1193
1194  if (inf_ptr != (struct cbsubst_info **)NULL) *inf_ptr = inf;
1195
1196  return Data_Wrap_Struct(cSUBST_INFO, subst_mark, subst_free, inf);
1197}
1198
1199static void
1200cbsubst_init()
1201{
1202  rb_const_set(cCB_SUBST, ID_SUBST_INFO,
1203	       allocate_cbsubst_info((struct cbsubst_info **)NULL));
1204}
1205
1206static VALUE
1207cbsubst_initialize(argc, argv, self)
1208    int   argc;
1209    VALUE *argv;
1210    VALUE self;
1211{
1212    struct cbsubst_info *inf;
1213    int idx, iv_idx;
1214
1215    Data_Get_Struct(rb_const_get(rb_obj_class(self), ID_SUBST_INFO),
1216                    struct cbsubst_info, inf);
1217
1218   idx = 0;
1219    for(iv_idx = 0; iv_idx < CBSUBST_TBL_MAX; iv_idx++) {
1220      if ( inf->ivar[iv_idx] == (ID) 0 ) continue;
1221      rb_ivar_set(self, inf->ivar[iv_idx], argv[idx++]);
1222      if (idx >= argc) break;
1223    }
1224
1225    return self;
1226}
1227
1228static VALUE
1229cbsubst_ret_val(self, val)
1230    VALUE self;
1231    VALUE val;
1232{
1233    /* This method may be overwritten on some sub-classes.                  */
1234    /* This method is used for converting from ruby's callback-return-value */
1235    /* to tcl's value (e.g. validation procedure of entry widget).          */
1236    return val;
1237}
1238
1239static int
1240each_attr_def(key, value, klass)
1241    VALUE key, value, klass;
1242{
1243    ID key_id, value_id;
1244
1245    if (key == Qundef) return ST_CONTINUE;
1246
1247    switch(TYPE(key)) {
1248    case T_STRING:
1249        key_id = rb_intern_str(key);
1250        break;
1251    case T_SYMBOL:
1252        key_id = SYM2ID(key);
1253        break;
1254    default:
1255        rb_raise(rb_eArgError,
1256                 "includes invalid key(s). expected a String or a Symbol");
1257    }
1258
1259    switch(TYPE(value)) {
1260    case T_STRING:
1261        value_id = rb_intern_str(value);
1262        break;
1263    case T_SYMBOL:
1264        value_id = SYM2ID(value);
1265        break;
1266    default:
1267        rb_raise(rb_eArgError,
1268                 "includes invalid value(s). expected a String or a Symbol");
1269    }
1270
1271    rb_alias(klass, key_id, value_id);
1272
1273    return ST_CONTINUE;
1274}
1275
1276static VALUE
1277cbsubst_def_attr_aliases(self, tbl)
1278    VALUE self;
1279    VALUE tbl;
1280{
1281    struct cbsubst_info *inf;
1282
1283    if (TYPE(tbl) != T_HASH) {
1284        rb_raise(rb_eArgError, "expected a Hash");
1285    }
1286
1287    Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO),
1288                    struct cbsubst_info, inf);
1289
1290    rb_hash_foreach(tbl, each_attr_def, self);
1291
1292    return rb_funcall(inf->aliases, rb_intern("update"), 1, tbl);
1293}
1294
1295static VALUE
1296cbsubst_sym_to_subst(self, sym)
1297    VALUE self;
1298    VALUE sym;
1299{
1300    struct cbsubst_info *inf;
1301    VALUE str;
1302    char *buf, *ptr;
1303    int idx;
1304    long len;
1305    ID id;
1306    volatile VALUE ret;
1307
1308    if (TYPE(sym) != T_SYMBOL) return sym;
1309
1310    Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO),
1311                    struct cbsubst_info, inf);
1312
1313    if (!NIL_P(ret = rb_hash_aref(inf->aliases, sym))) {
1314	str = rb_id2str(SYM2ID(ret));
1315    } else {
1316	str = rb_id2str(SYM2ID(sym));
1317    }
1318
1319    id = rb_intern_str(rb_sprintf("@%"PRIsVALUE, str));
1320
1321    for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) {
1322      if (inf->ivar[idx] == id) break;
1323    }
1324    if (idx >= CBSUBST_TBL_MAX)  return sym;
1325
1326    ptr = buf = ALLOC_N(char, inf->full_subst_length + 1);
1327
1328    *(ptr++) = '%';
1329
1330    if (len = inf->keylen[idx]) {
1331      /* longname */
1332      strncpy(ptr, inf->key[idx], len);
1333      ptr += len;
1334    } else {
1335      /* single char */
1336      *(ptr++) = (unsigned char)idx;
1337    }
1338
1339    *(ptr++) = ' ';
1340    *(ptr++) = '\0';
1341
1342    ret = rb_str_new2(buf);
1343
1344    xfree(buf);
1345
1346    return ret;
1347}
1348
1349static VALUE
1350cbsubst_get_subst_arg(argc, argv, self)
1351    int   argc;
1352    VALUE *argv;
1353    VALUE self;
1354{
1355    struct cbsubst_info *inf;
1356    VALUE str;
1357    char *buf, *ptr;
1358    int i, idx;
1359    long len;
1360    ID id;
1361    volatile VALUE arg_sym, ret;
1362
1363    Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO),
1364                    struct cbsubst_info, inf);
1365
1366    ptr = buf = ALLOC_N(char, inf->full_subst_length + 1);
1367
1368    for(i = 0; i < argc; i++) {
1369        switch(TYPE(argv[i])) {
1370        case T_STRING:
1371            str = argv[i];
1372            arg_sym = ID2SYM(rb_intern_str(argv[i]));
1373            break;
1374        case T_SYMBOL:
1375            arg_sym = argv[i];
1376            str = rb_id2str(SYM2ID(arg_sym));
1377            break;
1378        default:
1379            rb_raise(rb_eArgError, "arg #%d is not a String or a Symbol", i);
1380        }
1381
1382        if (!NIL_P(ret = rb_hash_aref(inf->aliases, arg_sym))) {
1383            str = rb_id2str(SYM2ID(ret));
1384        }
1385
1386        id = rb_intern_str(rb_sprintf("@%"PRIsVALUE, str));
1387
1388	for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) {
1389	  if (inf->ivar[idx] == id) break;
1390	}
1391        if (idx >= CBSUBST_TBL_MAX) {
1392            rb_raise(rb_eArgError, "cannot find attribute :%"PRIsVALUE, str);
1393        }
1394
1395	*(ptr++) = '%';
1396
1397	if (len = inf->keylen[idx]) {
1398	  /* longname */
1399	  strncpy(ptr, inf->key[idx], len);
1400	  ptr += len;
1401	} else {
1402	  /* single char */
1403	  *(ptr++) = (unsigned char)idx;
1404	}
1405
1406	*(ptr++) = ' ';
1407    }
1408
1409    *ptr = '\0';
1410
1411    ret = rb_str_new2(buf);
1412
1413    xfree(buf);
1414
1415    return ret;
1416}
1417
1418static VALUE
1419cbsubst_get_subst_key(self, str)
1420    VALUE self;
1421    VALUE str;
1422{
1423    struct cbsubst_info *inf;
1424    volatile VALUE list;
1425    volatile VALUE ret;
1426    VALUE keyval;
1427    long i, len, keylen;
1428    int idx;
1429    char *buf, *ptr, *key;
1430
1431    list = rb_funcall(cTclTkLib, ID_split_tklist, 1, str);
1432    len = RARRAY_LEN(list);
1433
1434    Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO),
1435                    struct cbsubst_info, inf);
1436
1437    ptr = buf = ALLOC_N(char, inf->full_subst_length + len + 1);
1438
1439    for(i = 0; i < len; i++) {
1440      keyval = RARRAY_PTR(list)[i];
1441      key = RSTRING_PTR(keyval);
1442      if (*key == '%') {
1443	if (*(key + 2) == '\0') {
1444	  /* single char */
1445	  *(ptr++) = *(key + 1);
1446	} else {
1447	  /* search longname-key */
1448	  keylen = RSTRING_LEN(keyval) - 1;
1449	  for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) {
1450	    if (inf->keylen[idx] != keylen) continue;
1451	    if ((unsigned char)inf->key[idx][0] != (unsigned char)*(key + 1)) continue;
1452	    if (strncmp(inf->key[idx], key + 1, keylen)) continue;
1453	    break;
1454	  }
1455	  if (idx < CBSUBST_TBL_MAX) {
1456	    *(ptr++) = (unsigned char)idx;
1457	  } else {
1458	    *(ptr++) = ' ';
1459	  }
1460	}
1461      } else {
1462	*(ptr++) = ' ';
1463      }
1464    }
1465    *ptr = '\0';
1466
1467    ret = rb_str_new2(buf);
1468    xfree(buf);
1469    return ret;
1470}
1471
1472static VALUE
1473cbsubst_get_all_subst_keys(self)
1474    VALUE self;
1475{
1476    struct cbsubst_info *inf;
1477    char *buf, *ptr;
1478    char *keys_buf, *keys_ptr;
1479    int idx;
1480    long len;
1481    volatile VALUE ret;
1482
1483    Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO),
1484                    struct cbsubst_info, inf);
1485
1486    ptr = buf = ALLOC_N(char, inf->full_subst_length + 1);
1487    keys_ptr = keys_buf = ALLOC_N(char, CBSUBST_TBL_MAX + 1);
1488
1489    for(idx = 0; idx < CBSUBST_TBL_MAX; idx++) {
1490      if (inf->ivar[idx] == (ID) 0) continue;
1491
1492      *(keys_ptr++) = (unsigned char)idx;
1493
1494      *(ptr++) = '%';
1495
1496      if (len = inf->keylen[idx]) {
1497	/* longname */
1498	strncpy(ptr, inf->key[idx], len);
1499	ptr += len;
1500      } else {
1501	/* single char */
1502	*(ptr++) = (unsigned char)idx;
1503      }
1504
1505      *(ptr++) = ' ';
1506    }
1507
1508    *ptr = '\0';
1509    *keys_ptr = '\0';
1510
1511    ret = rb_ary_new3(2, rb_str_new2(keys_buf), rb_str_new2(buf));
1512
1513    xfree(buf);
1514    xfree(keys_buf);
1515
1516    return ret;
1517}
1518
1519static VALUE
1520cbsubst_table_setup(argc, argv, self)
1521     int   argc;
1522     VALUE *argv;
1523     VALUE self;
1524{
1525  volatile VALUE cbsubst_obj;
1526  volatile VALUE key_inf;
1527  volatile VALUE longkey_inf;
1528  volatile VALUE proc_inf;
1529  VALUE inf;
1530  ID id;
1531  struct cbsubst_info *subst_inf;
1532  long idx, len;
1533  unsigned char chr;
1534
1535  /* accept (key_inf, proc_inf) or (key_inf, longkey_inf, procinf) */
1536  if (rb_scan_args(argc, argv, "21", &key_inf, &longkey_inf, &proc_inf) == 2) {
1537    proc_inf = longkey_inf;
1538    longkey_inf = rb_ary_new();
1539  }
1540
1541  /* check the number of longkeys */
1542  if (RARRAY_LEN(longkey_inf) > 125 /* from 0x80 to 0xFD */) {
1543    rb_raise(rb_eArgError, "too many longname-key definitions");
1544  }
1545
1546  /* init */
1547  cbsubst_obj = allocate_cbsubst_info(&subst_inf);
1548
1549  /*
1550   * keys : array of [subst, type, ivar]
1551   *         subst ==> char code or string
1552   *         type  ==> char code or string
1553   *         ivar  ==> symbol
1554   */
1555  len = RARRAY_LEN(key_inf);
1556  for(idx = 0; idx < len; idx++) {
1557    inf = RARRAY_PTR(key_inf)[idx];
1558    if (TYPE(inf) != T_ARRAY) continue;
1559
1560    if (TYPE(RARRAY_PTR(inf)[0]) == T_STRING) {
1561      chr = *(RSTRING_PTR(RARRAY_PTR(inf)[0]));
1562    } else {
1563      chr = NUM2CHR(RARRAY_PTR(inf)[0]);
1564    }
1565    if (TYPE(RARRAY_PTR(inf)[1]) == T_STRING) {
1566      subst_inf->type[chr] = *(RSTRING_PTR(RARRAY_PTR(inf)[1]));
1567    } else {
1568      subst_inf->type[chr] = NUM2CHR(RARRAY_PTR(inf)[1]);
1569    }
1570
1571    subst_inf->full_subst_length += 3;
1572
1573    id = SYM2ID(RARRAY_PTR(inf)[2]);
1574    subst_inf->ivar[chr] = rb_intern_str(rb_sprintf("@%"PRIsVALUE, rb_id2str(id)));
1575
1576    rb_attr(self, id, 1, 0, Qtrue);
1577  }
1578
1579
1580  /*
1581   * longkeys : array of [name, type, ivar]
1582   *         name ==> longname key string
1583   *         type ==> char code or string
1584   *         ivar ==> symbol
1585   */
1586  len = RARRAY_LEN(longkey_inf);
1587  for(idx = 0; idx < len; idx++) {
1588    inf = RARRAY_PTR(longkey_inf)[idx];
1589    if (TYPE(inf) != T_ARRAY) continue;
1590
1591    chr = (unsigned char)(0x80 + idx);
1592    subst_inf->keylen[chr] = RSTRING_LEN(RARRAY_PTR(inf)[0]);
1593#if HAVE_STRNDUP
1594    subst_inf->key[chr] = strndup(RSTRING_PTR(RARRAY_PTR(inf)[0]),
1595				  RSTRING_LEN(RARRAY_PTR(inf)[0]));
1596#else
1597    subst_inf->key[chr] = malloc(RSTRING_LEN(RARRAY_PTR(inf)[0]) + 1);
1598    if (subst_inf->key[chr]) {
1599      strncpy(subst_inf->key[chr], RSTRING_PTR(RARRAY_PTR(inf)[0]),
1600	      RSTRING_LEN(RARRAY_PTR(inf)[0]) + 1);
1601      subst_inf->key[chr][RSTRING_LEN(RARRAY_PTR(inf)[0])] = '\0';
1602    }
1603#endif
1604    if (TYPE(RARRAY_PTR(inf)[1]) == T_STRING) {
1605      subst_inf->type[chr] = *(RSTRING_PTR(RARRAY_PTR(inf)[1]));
1606    } else {
1607      subst_inf->type[chr] = NUM2CHR(RARRAY_PTR(inf)[1]);
1608    }
1609
1610    subst_inf->full_subst_length += (subst_inf->keylen[chr] + 2);
1611
1612    id = SYM2ID(RARRAY_PTR(inf)[2]);
1613    subst_inf->ivar[chr] = rb_intern_str(rb_sprintf("@%"PRIsVALUE, rb_id2str(id)));
1614
1615    rb_attr(self, id, 1, 0, Qtrue);
1616  }
1617
1618  /*
1619   * procs : array of [type, proc]
1620   *         type  ==> char code or string
1621   *         proc  ==> proc/method/obj (must respond to 'call')
1622   */
1623  len = RARRAY_LEN(proc_inf);
1624  for(idx = 0; idx < len; idx++) {
1625    inf = RARRAY_PTR(proc_inf)[idx];
1626    if (TYPE(inf) != T_ARRAY) continue;
1627    rb_hash_aset(subst_inf->proc,
1628		 ((TYPE(RARRAY_PTR(inf)[0]) == T_STRING)?
1629		  INT2FIX(*(RSTRING_PTR(RARRAY_PTR(inf)[0]))) :
1630		  RARRAY_PTR(inf)[0]),
1631		 RARRAY_PTR(inf)[1]);
1632  }
1633
1634  rb_const_set(self, ID_SUBST_INFO, cbsubst_obj);
1635
1636  return self;
1637}
1638
1639static VALUE
1640cbsubst_get_extra_args_tbl(self)
1641    VALUE self;
1642{
1643  return rb_ary_new();
1644}
1645
1646static VALUE
1647cbsubst_scan_args(self, arg_key, val_ary)
1648    VALUE self;
1649    VALUE arg_key;
1650    VALUE val_ary;
1651{
1652    struct cbsubst_info *inf;
1653    long idx;
1654    unsigned char *keyptr = (unsigned char*)RSTRING_PTR(arg_key);
1655    long keylen = RSTRING_LEN(arg_key);
1656    long vallen = RARRAY_LEN(val_ary);
1657    unsigned char type_chr;
1658    volatile VALUE dst = rb_ary_new2(vallen);
1659    volatile VALUE proc;
1660    int thr_crit_bup;
1661    VALUE old_gc;
1662
1663    thr_crit_bup = rb_thread_critical;
1664    rb_thread_critical = Qtrue;
1665
1666    old_gc = rb_gc_disable();
1667
1668    Data_Get_Struct(rb_const_get(self, ID_SUBST_INFO),
1669                    struct cbsubst_info, inf);
1670
1671    for(idx = 0; idx < vallen; idx++) {
1672      if (idx >= keylen) {
1673	proc = Qnil;
1674      } else if (*(keyptr + idx) == ' ') {
1675	proc = Qnil;
1676      } else {
1677	if (type_chr = inf->type[*(keyptr + idx)]) {
1678	  proc = rb_hash_aref(inf->proc, INT2FIX((int)type_chr));
1679	} else {
1680	  proc = Qnil;
1681	}
1682      }
1683
1684      if (NIL_P(proc)) {
1685	rb_ary_push(dst, RARRAY_PTR(val_ary)[idx]);
1686      } else {
1687	rb_ary_push(dst, rb_funcall(proc, ID_call, 1,
1688				    RARRAY_PTR(val_ary)[idx]));
1689      }
1690    }
1691
1692    if (old_gc == Qfalse) rb_gc_enable();
1693    rb_thread_critical = thr_crit_bup;
1694
1695    return dst;
1696}
1697
1698static VALUE
1699cbsubst_inspect(self)
1700    VALUE self;
1701{
1702    return rb_str_new2("CallbackSubst");
1703}
1704
1705static VALUE
1706substinfo_inspect(self)
1707    VALUE self;
1708{
1709    return rb_str_new2("SubstInfo");
1710}
1711
1712/*************************************/
1713
1714static VALUE
1715tk_cbe_inspect(self)
1716    VALUE self;
1717{
1718    return rb_str_new2("TkCallbackEntry");
1719}
1720
1721/*************************************/
1722
1723static VALUE
1724tkobj_path(self)
1725    VALUE self;
1726{
1727    return rb_ivar_get(self, ID_at_path);
1728}
1729
1730
1731/*************************************/
1732/* release date */
1733const char tkutil_release_date[] = TKUTIL_RELEASE_DATE;
1734
1735void
1736Init_tkutil()
1737{
1738    VALUE cTK = rb_define_class("TkKernel", rb_cObject);
1739    VALUE mTK = rb_define_module("TkUtil");
1740
1741    /* --------------------- */
1742
1743    rb_define_const(mTK, "RELEASE_DATE",
1744                    rb_obj_freeze(rb_str_new2(tkutil_release_date)));
1745
1746    /* --------------------- */
1747    rb_global_variable(&cMethod);
1748    cMethod = rb_const_get(rb_cObject, rb_intern("Method"));
1749
1750    ID_path = rb_intern("path");
1751    ID_at_path = rb_intern("@path");
1752    ID_at_enc = rb_intern("@encoding");
1753    ID_to_eval = rb_intern("to_eval");
1754    ID_to_s = rb_intern("to_s");
1755    ID_source = rb_intern("source");
1756    ID_downcase = rb_intern("downcase");
1757    ID_install_cmd = rb_intern("install_cmd");
1758    ID_merge_tklist = rb_intern("_merge_tklist");
1759    ID_encoding = rb_intern("encoding");
1760    ID_encoding_system = rb_intern("encoding_system");
1761    ID_call = rb_intern("call");
1762
1763    /* --------------------- */
1764    cCB_SUBST = rb_define_class_under(mTK, "CallbackSubst", rb_cObject);
1765    rb_define_singleton_method(cCB_SUBST, "inspect", cbsubst_inspect, 0);
1766
1767    cSUBST_INFO = rb_define_class_under(cCB_SUBST, "Info", rb_cObject);
1768    rb_define_singleton_method(cSUBST_INFO, "inspect", substinfo_inspect, 0);
1769
1770    ID_SUBST_INFO = rb_intern("SUBST_INFO");
1771    rb_define_singleton_method(cCB_SUBST, "ret_val", cbsubst_ret_val, 1);
1772    rb_define_singleton_method(cCB_SUBST, "scan_args", cbsubst_scan_args, 2);
1773    rb_define_singleton_method(cCB_SUBST, "_sym2subst",
1774			       cbsubst_sym_to_subst, 1);
1775    rb_define_singleton_method(cCB_SUBST, "subst_arg",
1776                               cbsubst_get_subst_arg, -1);
1777    rb_define_singleton_method(cCB_SUBST, "_get_subst_key",
1778                               cbsubst_get_subst_key,  1);
1779    rb_define_singleton_method(cCB_SUBST, "_get_all_subst_keys",
1780                               cbsubst_get_all_subst_keys,  0);
1781    rb_define_singleton_method(cCB_SUBST, "_setup_subst_table",
1782                               cbsubst_table_setup, -1);
1783    rb_define_singleton_method(cCB_SUBST, "_get_extra_args_tbl",
1784                               cbsubst_get_extra_args_tbl,  0);
1785    rb_define_singleton_method(cCB_SUBST, "_define_attribute_aliases",
1786                               cbsubst_def_attr_aliases,  1);
1787
1788    rb_define_method(cCB_SUBST, "initialize", cbsubst_initialize, -1);
1789
1790    cbsubst_init();
1791
1792    /* --------------------- */
1793    rb_global_variable(&cTkCallbackEntry);
1794    cTkCallbackEntry = rb_define_class("TkCallbackEntry", cTK);
1795    rb_define_singleton_method(cTkCallbackEntry, "inspect", tk_cbe_inspect, 0);
1796
1797    /* --------------------- */
1798    rb_global_variable(&cTkObject);
1799    cTkObject = rb_define_class("TkObject", cTK);
1800    rb_define_method(cTkObject, "path", tkobj_path, 0);
1801
1802    /* --------------------- */
1803    rb_require("tcltklib");
1804    rb_global_variable(&cTclTkLib);
1805    cTclTkLib = rb_const_get(rb_cObject, rb_intern("TclTkLib"));
1806    ID_split_tklist = rb_intern("_split_tklist");
1807    ID_toUTF8 = rb_intern("_toUTF8");
1808    ID_fromUTF8 = rb_intern("_fromUTF8");
1809
1810    /* --------------------- */
1811    rb_define_singleton_method(cTK, "new", tk_s_new, -1);
1812
1813    /* --------------------- */
1814    rb_global_variable(&TK_None);
1815    TK_None = rb_obj_alloc(rb_cObject);
1816    rb_define_const(mTK, "None", TK_None);
1817    rb_define_singleton_method(TK_None, "to_s", tkNone_to_s, 0);
1818    rb_define_singleton_method(TK_None, "inspect", tkNone_inspect, 0);
1819    OBJ_FREEZE(TK_None);
1820
1821    /* --------------------- */
1822    rb_global_variable(&CALLBACK_TABLE);
1823    CALLBACK_TABLE = rb_hash_new();
1824
1825    /* --------------------- */
1826    rb_define_singleton_method(mTK, "untrust", tk_obj_untrust, 1);
1827
1828    rb_define_singleton_method(mTK, "eval_cmd", tk_eval_cmd, -1);
1829    rb_define_singleton_method(mTK, "callback", tk_do_callback, -1);
1830    rb_define_singleton_method(mTK, "install_cmd", tk_install_cmd, -1);
1831    rb_define_singleton_method(mTK, "uninstall_cmd", tk_uninstall_cmd, 1);
1832    rb_define_singleton_method(mTK, "_symbolkey2str", tk_symbolkey2str, 1);
1833    rb_define_singleton_method(mTK, "hash_kv", tk_hash_kv, -1);
1834    rb_define_singleton_method(mTK, "_get_eval_string",
1835                               tk_get_eval_string, -1);
1836    rb_define_singleton_method(mTK, "_get_eval_enc_str",
1837                               tk_get_eval_enc_str, 1);
1838    rb_define_singleton_method(mTK, "_conv_args", tk_conv_args, -1);
1839
1840    rb_define_singleton_method(mTK, "bool", tcl2rb_bool, 1);
1841    rb_define_singleton_method(mTK, "number", tcl2rb_number, 1);
1842    rb_define_singleton_method(mTK, "string", tcl2rb_string, 1);
1843    rb_define_singleton_method(mTK, "num_or_str", tcl2rb_num_or_str, 1);
1844    rb_define_singleton_method(mTK, "num_or_nil", tcl2rb_num_or_nil, 1);
1845
1846    rb_define_method(mTK, "_toUTF8", tk_toUTF8, -1);
1847    rb_define_method(mTK, "_fromUTF8", tk_fromUTF8, -1);
1848    rb_define_method(mTK, "_symbolkey2str", tk_symbolkey2str, 1);
1849    rb_define_method(mTK, "hash_kv", tk_hash_kv, -1);
1850    rb_define_method(mTK, "_get_eval_string", tk_get_eval_string, -1);
1851    rb_define_method(mTK, "_get_eval_enc_str", tk_get_eval_enc_str, 1);
1852    rb_define_method(mTK, "_conv_args", tk_conv_args, -1);
1853
1854    rb_define_method(mTK, "bool", tcl2rb_bool, 1);
1855    rb_define_method(mTK, "number", tcl2rb_number, 1);
1856    rb_define_method(mTK, "string", tcl2rb_string, 1);
1857    rb_define_method(mTK, "num_or_str", tcl2rb_num_or_str, 1);
1858    rb_define_method(mTK, "num_or_nil", tcl2rb_num_or_nil, 1);
1859
1860    /* --------------------- */
1861    rb_global_variable(&ENCODING_NAME_UTF8);
1862    ENCODING_NAME_UTF8 = rb_obj_freeze(rb_str_new2("utf-8"));
1863
1864    /* --------------------- */
1865}
1866