From: Takahiro Maebashi Date: 1998-04-27T14:27:44+09:00 Subject: [ruby-dev:2519] tcltklib ip_invoke 前橋です。 現在 tcltklib では Tcl_Eval を使って tcl のコマンドを実行していますが、 tcl インタープリタで文字列の解釈/展開が行われるため、例えば巨大な文字列 をテキストウィジェットに挿入したいといった場合に、その文字列に tcl のメ タキャラクタが含まれていると誤動作します(tk.rb ではメタキャラクタを回避 するようにバックスラッシュ等を挿入してから渡していますが、不完全です)。 それで、そもそも Tcl が文字列を展開するからいけないのだと思い、 Tcl_Eval を介さずに、直接 Tcl/Tk のコマンドに文字列引数を渡す仕組みをつ くってみました。元は Tcl/Tk 入門という本にのっていたやり方です。 以下のパッチを tcltklib.c にあてると、TclTkIp#_invoke というメソッドが追 加されます。引数は String を並べたもので、例えば tk.rb で使うとすれば ... INTERP = TclTkIp.new ... text = TkText.new INTERP._invoke(text.path, 'insert', 'insert', str) のような感じになります。str は、tcl で一切展開されず直接コマンドに渡りま す。その分処理も高速です。 ただ、tcl のコマンドが存在していなかった場合、パッチの中で /* invalid command name "av[0]" */ という部分で例外を発生させなければならないのですが、やり方がわからなかっ たので何もしていません。どうするのが正しいでしょうか? --- tcltklib.c.org Mon Apr 27 11:35:53 1998 +++ tcltklib.c Mon Apr 27 13:46:27 1998 @@ -205,6 +205,39 @@ return(str_new2(ptr->ip->result)); } +static VALUE +ip_invoke(int argc, VALUE *argv, VALUE obj) +{ + struct tcltkip *ptr; /* tcltkip data struct */ + int i; + Tcl_CmdInfo info; + char **av; + + /* get the data struct */ + Data_Get_Struct(obj, struct tcltkip, ptr); + + av = (char **)ALLOCA_N(char **, argc+1); + for (i = 0; i < argc; ++i) { + Check_Type(argv[i], T_STRING); + av[i] = ALLOCA_N(char, RSTRING(argv[i])->len+1); + strcpy(av[i], RSTRING(argv[i])->ptr); + } + av[argc] = NULL; + + if (!Tcl_GetCommandInfo(ptr->ip, av[0], &info)) { + /* invalid command name "av[0]" */ + } + + ptr->return_value = (*info.proc)(info.clientData, + ptr->ip, argc, av); + if (ptr->return_value == TCL_ERROR) { + Fail(ptr->ip->result); + } + + /* pass back the result (as string) */ + return(str_new2(ptr->ip->result)); +} + /* get return code from Tcl_Eval() */ static VALUE ip_retval(VALUE self) @@ -229,6 +262,7 @@ rb_define_singleton_method(ip, "new", ip_new, 0); rb_define_method(ip, "_eval", ip_eval, 1); + rb_define_method(ip, "_invoke", ip_invoke, -1); rb_define_method(ip, "_return_value", ip_retval, 0); rb_define_method(ip, "mainloop", lib_mainloop, 0);