Loading ...
Sorry, an error occurred while loading the content.

Patch 5.6.079

Expand Messages
  • 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
    Message 1 of 1 , Jun 4, 2000
    • 0 Attachment
      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 <Ingo.Wilken@...-oldenburg.de>

      ==============================================================================
      --- 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 <Ingo.Wilken@...-oldenburg.de>

      ==============================================================================
      ***************
      *** 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 <Ingo.Wilken@...-oldenburg.de>
      ! * Last modification: Fri Mar 27 22:41:25 CET 1998
      * Requires Tcl 8.0 or higher.
      *
      * Variables:
      --- 8,14 ----

      /*
      * Tcl extensions by Ingo Wilken <Ingo.Wilken@...-oldenburg.de>
      ! * Last modification: Wed May 10 21:28:44 CEST 2000
      * Requires Tcl 8.0 or higher.
      *
      * Variables:
      ***************
      *** 70,84 ****
      #include <errno.h>
      #include <string.h>

      ! 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 <errno.h>
      #include <string.h>

      ! 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@...", 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@...");
      ! 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@...");
      ! 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@... --- http://www.moolenaar.net --\-\
      \-\-- Vim: http://www.vim.org ---- ICCF Holland: http://www.vim.org/iccf --/-/
    Your message has been successfully submitted and would be delivered to recipients shortly.