To: vim-dev@vim.org Subject: Patch 5.6.079 Fcc: outbox From: Bram Moolenaar ------------ Patch 5.6.079 Problem: Vim could crash when several Tcl interpreters are created and destroyed. Solution: handle the "exit" command and nested ":tcl" commands better. (Ingo Wilken) Files: runtime/doc/if_tcl.txt, src/if_tcl.c *** ../vim-5.6.78/runtime/doc/if_tcl.txt Sun Jan 16 14:12:55 2000 --- runtime/doc/if_tcl.txt Sun Jun 4 17:37:22 2000 *************** *** 1,4 **** ! *if_tcl.txt* For Vim version 5.6. Last change: 1999 Sep 16 VIM REFERENCE MANUAL by Ingo Wilken --- 1,4 ---- ! *if_tcl.txt* For Vim version 5.6. Last change: 2000 Jun 04 VIM REFERENCE MANUAL by Ingo Wilken *************** *** 11,17 **** 3. Tcl variables |tcl-variables| 4. Tcl window commands |tcl-window-cmds| 5. Tcl buffer commands |tcl-buffer-cmds| ! 6. Output from Tcl |tcl-output| 7. Known bugs & problems |tcl-bugs| 8. Examples |tcl-examples| --- 11,17 ---- 3. Tcl variables |tcl-variables| 4. Tcl window commands |tcl-window-cmds| 5. Tcl buffer commands |tcl-buffer-cmds| ! 6. Miscellaneous; Output from Tcl |tcl-misc| |tcl-output| 7. Known bugs & problems |tcl-bugs| 8. Examples |tcl-examples| *************** *** 19,26 **** The Tcl interface only works when Vim was compiled with the |+tcl| feature. ! WARNING: This is a BETA release! Things might be different in the final ! version. There are probably still some bugs. Please send bug reports, comments, ideas etc to ============================================================================== --- 19,25 ---- The Tcl interface only works when Vim was compiled with the |+tcl| feature. ! WARNING: There are probably still some bugs. Please send bug reports, comments, ideas etc to ============================================================================== *************** *** 40,46 **** See |tcl-var-line| and |tcl-var-lnum|. {not in Vi} *:tclfile* *:tclf* ! :tclf[ile] {file} Execute the Tcl script in {file}. {not in Vi} Note that Tcl objects (like variables) persist from one command to the next, --- 39,47 ---- See |tcl-var-line| and |tcl-var-lnum|. {not in Vi} *:tclfile* *:tclf* ! :tclf[ile] {file} Execute the Tcl script in {file}. This is the same as ! ":tcl source {file}", but allows file name completion. ! {not in Vi} Note that Tcl objects (like variables) persist from one command to the next, *************** *** 391,397 **** > if { [$buf option modified] } { $buf command "w" } ============================================================================== ! 6. Output from Tcl *tcl-output* Two new I/O streams are available in Tcl, "vimout" and "vimerr". All output directed to them is displayed in the vim message area, as information messages --- 392,405 ---- > if { [$buf option modified] } { $buf command "w" } ============================================================================== ! 6. Miscellaneous; Output from Tcl *tcl-misc* *tcl-output* ! ! The standard Tcl commands "exit" and "catch" are replaced by custom versions. ! "exit" terminates the current Tcl script and returns to vim, which deletes the ! Tcl interpreter. Another call to ":tcl" then creates a new Tcl interpreter. ! "exit" does NOT terminate vim! "catch" works as before, except that it does ! not prevent script termination from "exit". An exit code != 0 causes the ex ! command that invoked the Tcl script to return an error. Two new I/O streams are available in Tcl, "vimout" and "vimerr". All output directed to them is displayed in the vim message area, as information messages *************** *** 402,421 **** ============================================================================== 7. Known bugs & problems *tcl-bugs* ! The standard "exit" command is replaced by a version that tells vim to delete ! the Tcl interpreter when the current command or script is completed, and ! returns an error. Another call to ":tcl", ":tcldo" or ":tclfile" then creates ! a new Tcl interpreter. Unfortunately, there's a side effect: "exit" no ! longer is a sure way to terminate the script. The error from "exit" can be ! caught (using "catch"), and script processing continues from this point. This ! is a design flaw in Tcl, and cannot be fixed without massive hacking. ! ! Calling ":tcl", ":tcldo" or ":tclfile" from inside Tcl (via "::vim::command") ! may have unexpected side effects. The command is executed in the master ! interpreter - making "::vim::command" available in a safe child interpreter ! therefore makes the child unsafe. Global variables "line" or "lnum" are ! destroyed when ":tcldo" is called. (The final version should probably catch ! attemts to execute ":tcl" etc from within Tcl, and just return an error.) Input from stdin is currently not supported. --- 410,425 ---- ============================================================================== 7. Known bugs & problems *tcl-bugs* ! Calling one of the Tcl ex commands from inside Tcl (via "::vim::command") may ! have unexpected side effects. The command creates a new interpreter, which ! has the same abilities as the standard interpreter - making "::vim::command" ! available in a safe child interpreter therefore makes the child unsafe. (It ! would be trivial to block nested :tcl* calls or ensure that such calls from a ! safe interpreter create only new safe interpreters, but quite pointless - ! depending on vim's configuration, "::vim::command" may execute arbitrary code ! in any number of other scripting languages.) A call to "exit" within this new ! interpreter does not affect the old interpreter; it only terminates the new ! interpreter, then script processing continues normally in the old interpreter. Input from stdin is currently not supported. *** ../vim-5.6.78/src/if_tcl.c Sun Sep 5 20:17:21 1999 --- src/if_tcl.c Sun Jun 4 18:10:43 2000 *************** *** 8,14 **** /* * Tcl extensions by Ingo Wilken ! * Last modification: Fri Mar 27 22:41:25 CET 1998 * Requires Tcl 8.0 or higher. * * Variables: --- 8,14 ---- /* * Tcl extensions by Ingo Wilken ! * Last modification: Wed May 10 21:28:44 CEST 2000 * Requires Tcl 8.0 or higher. * * Variables: *************** *** 70,84 **** #include #include ! static struct ! { Tcl_Interp *interp; ! unsigned int refcount; ! unsigned do_exit:1; ! unsigned valid:1; ! int range_start, range_end, lbase; char *curbuf, *curwin; ! } tcl = {(Tcl_Interp *)NULL, 0, 0, 0}; #define VAR_RANGE1 "::vim::range(start)" #define VAR_RANGE2 "::vim::range(begin)" --- 70,83 ---- #include #include ! typedef struct { Tcl_Interp *interp; ! int range_start, range_end; ! int lbase; char *curbuf, *curwin; ! } tcl_info; ! ! static tcl_info tclinfo = { NULL, 0, 0, 0, NULL, NULL }; #define VAR_RANGE1 "::vim::range(start)" #define VAR_RANGE2 "::vim::range(begin)" *************** *** 90,99 **** #define VAR_CURLNUM "lnum" #define VARNAME_SIZE 64 ! #define row2tcl(x) ((x) - (tcl.lbase==0)) ! #define row2vim(x) ((x) + (tcl.lbase==0)) ! #define col2tcl(x) ((x) + (tcl.lbase!=0)) ! #define col2vim(x) ((x) - (tcl.lbase!=0)) #define VIMOUT ((ClientData)1) --- 89,98 ---- #define VAR_CURLNUM "lnum" #define VARNAME_SIZE 64 ! #define row2tcl(x) ((x) - (tclinfo.lbase==0)) ! #define row2vim(x) ((x) + (tclinfo.lbase==0)) ! #define col2tcl(x) ((x) + (tclinfo.lbase!=0)) ! #define col2vim(x) ((x) - (tclinfo.lbase!=0)) #define VIMOUT ((ClientData)1) *************** *** 122,127 **** --- 121,127 ---- static int tcldoexcommand _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn)); static int tclsetoption _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn)); static int tclvimexpr _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn)); + static void tcldelthisinterp _ANSI_ARGS_ ((void)); static int vimerror _ANSI_ARGS_((Tcl_Interp *interp)); static void tclmsg _ANSI_ARGS_((char *text)); *************** *** 135,144 **** ****************************************************************************/ /* ! * "exit" - replaces Tcl's standard "exit" command. ! * Unfortunately, this is one of Tcl's design flaws - "exit" should have ! * been implemented as returning TCL_EXIT to the application. */ /* ARGSUSED */ static int exitcmd(dummy, interp, objc, objv) --- 135,150 ---- ****************************************************************************/ /* ! * Replace standard "exit" and "catch" commands. ! * ! * This is a design flaw in Tcl - the standard "exit" command just calls ! * exit() and kills the application. It should return TCL_EXIT to the ! * app, which then decides if it wants to terminate or not. In our case, ! * we just delete the Tcl interpreter (and create a new one with the next ! * :tcl command). */ + #define TCL_EXIT 5 + /* ARGSUSED */ static int exitcmd(dummy, interp, objc, objv) *************** *** 148,171 **** Tcl_Obj *CONST objv[]; { int value = 0; - char buf[32]; switch (objc) { case 2: if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) break; case 1: ! sprintf(buf, "exit code %d", value); ! Tcl_SetResult(interp, buf, TCL_VOLATILE); ! tcl.do_exit = 1; ! break; default: Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?"); } return TCL_ERROR; } /* * "::vim::beep" - what Vi[m] does best :-) */ --- 154,212 ---- Tcl_Obj *CONST objv[]; { int value = 0; switch (objc) { case 2: if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) break; + /* FALLTHROUGH */ case 1: ! Tcl_SetObjResult(interp, Tcl_NewIntObj(value)); ! return TCL_EXIT; default: Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?"); } return TCL_ERROR; } + static int + catchcmd(dummy, interp, objc, objv) + ClientData dummy; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST objv[]; + { + char *varname = NULL; + int result; + + switch (objc) + { + case 3: + varname = Tcl_GetStringFromObj(objv[2], NULL); + /* fallthrough */ + case 2: + Tcl_ResetResult(interp); + Tcl_AllowExceptions(interp); + result = Tcl_EvalObj(interp, objv[1]); + if (result == TCL_EXIT) + return result; + if (varname) + { + if (Tcl_SetVar(interp, varname, Tcl_GetStringResult(interp), 0) == NULL) + { + Tcl_SetResult(interp, "couldn't save command result in variable", TCL_STATIC); + return TCL_ERROR; + } + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(result)); + return TCL_OK; + default: + Tcl_WrongNumArgs(interp, 1, objv, "command ?varName?"); + } + return TCL_ERROR; + } + /* * "::vim::beep" - what Vi[m] does best :-) */ *************** *** 1069,1075 **** Tcl_Obj *CONST objv[]; int objn; { ! int save1, save2, save3; int err, flag, nobjs; char *arg; --- 1110,1116 ---- Tcl_Obj *CONST objv[]; int objn; { ! tcl_info saveinfo; int err, flag, nobjs; char *arg; *************** *** 1095,1103 **** ++objn; } ! save1 = tcl.range_start; ! save2 = tcl.range_end; ! save3 = tcl.lbase; arg = Tcl_GetStringFromObj(objv[objn], NULL); if (flag) --- 1136,1145 ---- ++objn; } ! memcpy(&saveinfo, &tclinfo, sizeof(tcl_info)); ! tclinfo.interp = NULL; ! tclinfo.curwin = NULL; ! tclinfo.curbuf = NULL; arg = Tcl_GetStringFromObj(objv[objn], NULL); if (flag) *************** *** 1107,1115 **** --emsg_off; err = vimerror(interp); ! tcl.range_start = save1; ! tcl.range_end = save2; ! tcl.lbase = save3; tclupdatevars(); return err; --- 1149,1158 ---- --emsg_off; err = vimerror(interp); ! /* If the ex command created a new Tcl interpreter, remove it */ ! if (tclinfo.interp) ! tcldelthisinterp(); ! memcpy(&tclinfo, &saveinfo, sizeof(tcl_info)); tclupdatevars(); return err; *************** *** 1319,1326 **** return NULL; } #endif } ! sprintf(name, "::vim::%s_%lx", prefix, (long)vimobj); cmd = Tcl_CreateObjCommand(interp, name, proc, (ClientData)ref, (Tcl_CmdDeleteProc *)delref); if (!cmd) --- 1362,1374 ---- return NULL; } #endif + ref->interp = NULL; + ref->next = (struct ref *)(*refstartP); + (*refstartP) = (void *)ref; } ! ! /* This might break on some exotic systems... */ ! sprintf(name, "::vim::%s_%lx", prefix, (unsigned long)vimobj); cmd = Tcl_CreateObjCommand(interp, name, proc, (ClientData)ref, (Tcl_CmdDeleteProc *)delref); if (!cmd) *************** *** 1330,1337 **** ref->cmd = cmd; ref->delcmd = NULL; ref->vimobj = vimobj; - ref->next = (struct ref *)(*refstartP); - (*refstartP) = (void *)ref; } return name; } --- 1378,1383 ---- *************** *** 1378,1384 **** reflist = reflist->next; } /* This should never happen. Famous last word? */ ! Tcl_SetResult(interp, "TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim.org", TCL_STATIC); return TCL_ERROR; } --- 1424,1431 ---- reflist = reflist->next; } /* This should never happen. Famous last word? */ ! EMSG("TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim.org"); ! Tcl_SetResult(interp, "cannot register callback command: buffer/window reference not found", TCL_STATIC); return TCL_ERROR; } *************** *** 1508,1628 **** char *name; strcpy(varname, VAR_RANGE1); ! Tcl_UpdateLinkedVar(tcl.interp, varname); strcpy(varname, VAR_RANGE2); ! Tcl_UpdateLinkedVar(tcl.interp, varname); strcpy(varname, VAR_RANGE3); ! Tcl_UpdateLinkedVar(tcl.interp, varname); strcpy(varname, VAR_LBASE); ! Tcl_UpdateLinkedVar(tcl.interp, varname); ! name = tclgetbuffer(tcl.interp, curbuf); ! strcpy(tcl.curbuf, name); strcpy(varname, VAR_CURBUF); ! Tcl_UpdateLinkedVar(tcl.interp, varname); ! name = tclgetwindow(tcl.interp, curwin); ! strcpy(tcl.curwin, name); strcpy(varname, VAR_CURWIN); ! Tcl_UpdateLinkedVar(tcl.interp, varname); } ! static void tclinit(eap) EXARG *eap; { ! char varname[VARNAME_SIZE]; /* must be writeable memory */ char *name; ! if (!tcl.valid) { ! static Tcl_ChannelType ct1, ct2; ! Tcl_Channel ch1, ch2; ! tcl.interp = Tcl_CreateInterp(); ! if (Tcl_Init(tcl.interp) == TCL_ERROR) ! return; ! tcl.do_exit = 0; #if 0 /* VIM sure is interactive */ ! Tcl_SetVar(tcl.interp, "tcl_interactive", "1", TCL_GLOBAL_ONLY); #endif ! /* replace stdout and stderr */ ! ct1 = channel_type; ! ch1 = Tcl_CreateChannel(&ct1, "vimout", VIMOUT, TCL_WRITABLE); ! Tcl_RegisterChannel(tcl.interp, ch1); ! Tcl_SetStdChannel(ch1, TCL_STDOUT); ! Tcl_SetChannelOption(tcl.interp, ch1, "-buffering", "line"); ! ct2 = channel_type; ! ch2 = Tcl_CreateChannel(&ct2, "vimerr", VIMERR, TCL_WRITABLE); ! Tcl_RegisterChannel(tcl.interp, ch2); ! Tcl_SetStdChannel(ch2, TCL_STDERR); ! Tcl_SetChannelOption(tcl.interp, ch2, "-buffering", "line"); /* replace some standard Tcl commands */ ! Tcl_DeleteCommand(tcl.interp, "exit"); ! Tcl_CreateObjCommand(tcl.interp, "exit", exitcmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); /* new commands, in ::vim namespace */ ! Tcl_CreateObjCommand(tcl.interp, "::vim::buffer", buffercmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); ! Tcl_CreateObjCommand(tcl.interp, "::vim::window", windowcmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); ! Tcl_CreateObjCommand(tcl.interp, "::vim::command", commandcmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); ! Tcl_CreateObjCommand(tcl.interp, "::vim::beep", beepcmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); ! Tcl_CreateObjCommand(tcl.interp, "::vim::option", optioncmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); ! Tcl_CreateObjCommand(tcl.interp, "::vim::expr", exprcmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); /* "lbase" variable */ ! tcl.lbase = 1; strcpy(varname, VAR_LBASE); ! Tcl_LinkVar(tcl.interp, varname, (char *)&tcl.lbase, TCL_LINK_INT); /* "range" variable */ ! tcl.range_start = eap->line1; strcpy(varname, VAR_RANGE1); ! Tcl_LinkVar(tcl.interp, varname, (char *)&tcl.range_start, TCL_LINK_INT|TCL_LINK_READ_ONLY); strcpy(varname, VAR_RANGE2); ! Tcl_LinkVar(tcl.interp, varname, (char *)&tcl.range_start, TCL_LINK_INT|TCL_LINK_READ_ONLY); ! tcl.range_end = eap->line2; strcpy(varname, VAR_RANGE3); ! Tcl_LinkVar(tcl.interp, varname, (char *)&tcl.range_end, TCL_LINK_INT|TCL_LINK_READ_ONLY); /* "current" variable */ ! tcl.curbuf = Tcl_Alloc(VARNAME_SIZE); ! tcl.curwin = Tcl_Alloc(VARNAME_SIZE); ! name = tclgetbuffer(tcl.interp, curbuf); ! strcpy(tcl.curbuf, name); strcpy(varname, VAR_CURBUF); ! Tcl_LinkVar(tcl.interp, varname, (char *)&tcl.curbuf, TCL_LINK_STRING|TCL_LINK_READ_ONLY); ! name = tclgetwindow(tcl.interp, curwin); ! strcpy(tcl.curwin, name); strcpy(varname, VAR_CURWIN); ! Tcl_LinkVar(tcl.interp, varname, (char *)&tcl.curwin, TCL_LINK_STRING|TCL_LINK_READ_ONLY); ! tcl.valid = 1; } else { /* Interpreter already exists, just update variables */ ! tcl.range_start = row2tcl(eap->line1); ! tcl.range_end = row2tcl(eap->line2); tclupdatevars(); } ! ! Tcl_Preserve(tcl.interp); /* protect interpreter from deletion */ ! ++tcl.refcount; } ! static void tclerrmsg(text) char *text; { --- 1555,1677 ---- char *name; strcpy(varname, VAR_RANGE1); ! Tcl_UpdateLinkedVar(tclinfo.interp, varname); strcpy(varname, VAR_RANGE2); ! Tcl_UpdateLinkedVar(tclinfo.interp, varname); strcpy(varname, VAR_RANGE3); ! Tcl_UpdateLinkedVar(tclinfo.interp, varname); strcpy(varname, VAR_LBASE); ! Tcl_UpdateLinkedVar(tclinfo.interp, varname); ! name = tclgetbuffer(tclinfo.interp, curbuf); ! strcpy(tclinfo.curbuf, name); strcpy(varname, VAR_CURBUF); ! Tcl_UpdateLinkedVar(tclinfo.interp, varname); ! name = tclgetwindow(tclinfo.interp, curwin); ! strcpy(tclinfo.curwin, name); strcpy(varname, VAR_CURWIN); ! Tcl_UpdateLinkedVar(tclinfo.interp, varname); } ! static int tclinit(eap) EXARG *eap; { ! char varname[VARNAME_SIZE]; /* Tcl_LinkVar requires writeable varname */ char *name; ! if (!tclinfo.interp) { ! Tcl_Interp *interp; ! static Tcl_Channel ch1, ch2; ! /* replace stdout and stderr */ ! ch1 = Tcl_CreateChannel(&channel_type, "vimout", VIMOUT, TCL_WRITABLE); ! ch2 = Tcl_CreateChannel(&channel_type, "vimerr", VIMERR, TCL_WRITABLE); ! Tcl_SetStdChannel(ch1, TCL_STDOUT); ! Tcl_SetStdChannel(ch2, TCL_STDERR); ! ! interp = Tcl_CreateInterp(); ! Tcl_Preserve(interp); ! if (Tcl_Init(interp) == TCL_ERROR) ! { ! Tcl_Release(interp); ! Tcl_DeleteInterp(interp); ! return FAIL; ! } #if 0 /* VIM sure is interactive */ ! Tcl_SetVar(interp, "tcl_interactive", "1", TCL_GLOBAL_ONLY); #endif ! Tcl_SetChannelOption(interp, ch1, "-buffering", "line"); ! Tcl_SetChannelOption(interp, ch2, "-buffering", "line"); /* replace some standard Tcl commands */ ! Tcl_DeleteCommand(interp, "exit"); ! Tcl_CreateObjCommand(interp, "exit", exitcmd, ! (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); ! Tcl_DeleteCommand(interp, "catch"); ! Tcl_CreateObjCommand(interp, "catch", catchcmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); /* new commands, in ::vim namespace */ ! Tcl_CreateObjCommand(interp, "::vim::buffer", buffercmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); ! Tcl_CreateObjCommand(interp, "::vim::window", windowcmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); ! Tcl_CreateObjCommand(interp, "::vim::command", commandcmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); ! Tcl_CreateObjCommand(interp, "::vim::beep", beepcmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); ! Tcl_CreateObjCommand(interp, "::vim::option", optioncmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); ! Tcl_CreateObjCommand(interp, "::vim::expr", exprcmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); /* "lbase" variable */ ! tclinfo.lbase = 1; strcpy(varname, VAR_LBASE); ! Tcl_LinkVar(interp, varname, (char *)&tclinfo.lbase, TCL_LINK_INT); /* "range" variable */ ! tclinfo.range_start = eap->line1; strcpy(varname, VAR_RANGE1); ! Tcl_LinkVar(interp, varname, (char *)&tclinfo.range_start, TCL_LINK_INT|TCL_LINK_READ_ONLY); strcpy(varname, VAR_RANGE2); ! Tcl_LinkVar(interp, varname, (char *)&tclinfo.range_start, TCL_LINK_INT|TCL_LINK_READ_ONLY); ! tclinfo.range_end = eap->line2; strcpy(varname, VAR_RANGE3); ! Tcl_LinkVar(interp, varname, (char *)&tclinfo.range_end, TCL_LINK_INT|TCL_LINK_READ_ONLY); /* "current" variable */ ! tclinfo.curbuf = Tcl_Alloc(VARNAME_SIZE); ! tclinfo.curwin = Tcl_Alloc(VARNAME_SIZE); ! name = tclgetbuffer(interp, curbuf); ! strcpy(tclinfo.curbuf, name); strcpy(varname, VAR_CURBUF); ! Tcl_LinkVar(interp, varname, (char *)&tclinfo.curbuf, TCL_LINK_STRING|TCL_LINK_READ_ONLY); ! name = tclgetwindow(interp, curwin); ! strcpy(tclinfo.curwin, name); strcpy(varname, VAR_CURWIN); ! Tcl_LinkVar(interp, varname, (char *)&tclinfo.curwin, TCL_LINK_STRING|TCL_LINK_READ_ONLY); ! tclinfo.interp = interp; } else { /* Interpreter already exists, just update variables */ ! tclinfo.range_start = row2tcl(eap->line1); ! tclinfo.range_end = row2tcl(eap->line2); tclupdatevars(); } ! return OK; } ! static void tclerrmsg(text) char *text; { *************** *** 1654,1687 **** MSG(text); } static int tclexit(error) int error; { ! int newerr; ! char *result; ! result = Tcl_GetStringResult(tcl.interp); ! if (error == TCL_OK) { ! tclmsg(result); ! newerr = OK; } else { ! tclerrmsg(result); ! newerr = FAIL; ! } ! if (--tcl.refcount == 0 && tcl.do_exit) ! { ! if (!Tcl_InterpDeleted(tcl.interp)) ! Tcl_DeleteInterp(tcl.interp); ! Tcl_Free(tcl.curbuf); ! Tcl_Free(tcl.curwin); ! tcl.valid = 0; ! /* TODO: should call Tcl_UnlinkVar */ } - Tcl_Release(tcl.interp); return newerr; } --- 1703,1780 ---- MSG(text); } + + static void + tcldelthisinterp() + { + if (!Tcl_InterpDeleted(tclinfo.interp)) + Tcl_DeleteInterp(tclinfo.interp); + Tcl_Release(tclinfo.interp); + /* The interpreter is now gets deleted. All registered commands (esp. + * window and buffer commands) are deleted, triggering their deletion + * callback, which deletes all refs pointing to this interpreter. + * We could garbage-collect the unused ref structs in all windows and + * buffers, but unless the user creates hundreds of sub-interpreters + * all refering to lots of windows and buffers, this is hardly worth + * the effort. Unused refs are recycled by other interpreters, and + * all refs are free'd when the window/buffer gets closed by vim. + */ + + tclinfo.interp = NULL; + Tcl_Free(tclinfo.curbuf); + Tcl_Free(tclinfo.curwin); + tclinfo.curbuf = tclinfo.curwin = NULL; + } + static int tclexit(error) int error; { ! int newerr = OK; ! if (error == TCL_EXIT ) { ! int retval; ! char buf[32]; ! Tcl_Obj *robj; ! ! robj = Tcl_GetObjResult(tclinfo.interp); ! if( Tcl_GetIntFromObj(tclinfo.interp, robj, &retval) != TCL_OK ) ! { ! EMSG("TCL ERROR: exit code is not int!? Please report this to vim-dev@vim.org"); ! newerr = FAIL; ! } ! else ! { ! sprintf(buf, "exit code %d", retval); ! tclerrmsg(buf); ! if (retval == 0 ) ! { ! did_emsg = 0; ! newerr = OK; ! } ! else ! newerr = FAIL; ! } ! ! tcldelthisinterp(); } else { ! char *result; ! ! result = Tcl_GetStringResult(tclinfo.interp); ! if (error == TCL_OK) ! { ! tclmsg(result); ! newerr = OK; ! } ! else ! { ! tclerrmsg(result); ! newerr = FAIL; ! } } return newerr; } *************** *** 1693,1701 **** char *script = (char *)eap->arg; int err; ! tclinit(eap); ! err = Tcl_Eval(tcl.interp, script); ! return tclexit(err); } int --- 1786,1799 ---- char *script = (char *)eap->arg; int err; ! err = tclinit(eap); ! if (err == OK) ! { ! Tcl_AllowExceptions(tclinfo.interp); ! err = Tcl_Eval(tclinfo.interp, script); ! err = tclexit(err); ! } ! return err; } int *************** *** 1705,1713 **** char *file = (char *)eap->arg; int err; ! tclinit(eap); ! err = Tcl_EvalFile(tcl.interp, file); ! return tclexit(err); } int --- 1803,1816 ---- char *file = (char *)eap->arg; int err; ! err = tclinit(eap); ! if (err == OK) ! { ! Tcl_AllowExceptions(tclinfo.interp); ! err = Tcl_EvalFile(tclinfo.interp, file); ! err = tclexit(err); ! } ! return err; } int *************** *** 1725,1738 **** strcpy(var_lnum, VAR_CURLNUM); strcpy(var_line, VAR_CURLINE); ! tclinit(eap); lnum = row2tcl(rs); ! Tcl_LinkVar(tcl.interp, var_lnum, (char *)&lnum, TCL_LINK_INT|TCL_LINK_READ_ONLY); err = TCL_OK; if (u_save((linenr_t)(rs-1), (linenr_t)(re+1)) != OK) { ! Tcl_SetResult(tcl.interp, "cannot save undo information", TCL_STATIC); err = TCL_ERROR; } while (err == TCL_OK && rs <= re) --- 1828,1843 ---- strcpy(var_lnum, VAR_CURLNUM); strcpy(var_line, VAR_CURLINE); ! err = tclinit(eap); ! if (err != OK) ! return err; lnum = row2tcl(rs); ! Tcl_LinkVar(tclinfo.interp, var_lnum, (char *)&lnum, TCL_LINK_INT|TCL_LINK_READ_ONLY); err = TCL_OK; if (u_save((linenr_t)(rs-1), (linenr_t)(re+1)) != OK) { ! Tcl_SetResult(tclinfo.interp, "cannot save undo information", TCL_STATIC); err = TCL_ERROR; } while (err == TCL_OK && rs <= re) *************** *** 1740,1759 **** line = (char *)ml_get_buf(curbuf, (linenr_t)rs, FALSE); if (!line) { ! Tcl_SetResult(tcl.interp, "cannot get line", TCL_STATIC); err = TCL_ERROR; break; } ! Tcl_SetVar(tcl.interp, var_line, line, 0); ! err = Tcl_Eval(tcl.interp, script); if (err != TCL_OK) break; ! line = Tcl_GetVar(tcl.interp, var_line, 0); if (line) { if (ml_replace((linenr_t)rs, (char_u *)line, TRUE) != OK) { ! Tcl_SetResult(tcl.interp, "cannot replace line", TCL_STATIC); err = TCL_ERROR; break; } --- 1845,1865 ---- line = (char *)ml_get_buf(curbuf, (linenr_t)rs, FALSE); if (!line) { ! Tcl_SetResult(tclinfo.interp, "cannot get line", TCL_STATIC); err = TCL_ERROR; break; } ! Tcl_SetVar(tclinfo.interp, var_line, line, 0); ! Tcl_AllowExceptions(tclinfo.interp); ! err = Tcl_Eval(tclinfo.interp, script); if (err != TCL_OK) break; ! line = Tcl_GetVar(tclinfo.interp, var_line, 0); if (line) { if (ml_replace((linenr_t)rs, (char_u *)line, TRUE) != OK) { ! Tcl_SetResult(tclinfo.interp, "cannot replace line", TCL_STATIC); err = TCL_ERROR; break; } *************** *** 1764,1780 **** } ++rs; ++lnum; ! Tcl_UpdateLinkedVar(tcl.interp, var_lnum); } update_curbuf(NOT_VALID); ! Tcl_UnsetVar(tcl.interp, var_line, 0); ! Tcl_UnlinkVar(tcl.interp, var_lnum); if (err == TCL_OK) ! Tcl_ResetResult(tcl.interp); return tclexit(err); } static void tcldelallrefs(ref) --- 1870,1887 ---- } ++rs; ++lnum; ! Tcl_UpdateLinkedVar(tclinfo.interp, var_lnum); } update_curbuf(NOT_VALID); ! Tcl_UnsetVar(tclinfo.interp, var_line, 0); ! Tcl_UnlinkVar(tclinfo.interp, var_lnum); if (err == TCL_OK) ! Tcl_ResetResult(tclinfo.interp); return tclexit(err); } + static void tcldelallrefs(ref) *** ../vim-5.6.78/src/version.c Sat Jun 3 20:47:43 2000 --- src/version.c Sun Jun 4 19:43:16 2000 *************** *** 420,421 **** --- 420,423 ---- { /* Add new patch number below this line */ + /**/ + 79, /**/ -- ARTHUR: It is I, Arthur, son of Uther Pendragon, from the castle of Camelot. King of all Britons, defeator of the Saxons, sovereign of all England! [Pause] SOLDIER: Get away! "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /-/-- Bram Moolenaar --- Bram@moolenaar.net --- http://www.moolenaar.net --\-\ \-\-- Vim: http://www.vim.org ---- ICCF Holland: http://www.vim.org/iccf --/-/