xref: /vim-8.2.3635/src/if_tcl.c (revision 12ee7ff0)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * Tcl extensions by Ingo Wilken <[email protected]>
12  * Last modification: Wed May 10 21:28:44 CEST 2000
13  * Requires Tcl 8.0 or higher.
14  *
15  *  Variables:
16  *  ::vim::current(buffer)	# Name of buffer command for current buffer.
17  *  ::vim::current(window)	# Name of window command for current window.
18  *  ::vim::range(start)		# Start of current range (line number).
19  *  ::vim::range(end)		# End of current range (line number).
20  *  ::vim::lbase		# Start of line/column numbers (1 or 0).
21  *
22  *  Commands:
23  *  ::vim::command {cmd}	# Execute ex command {cmd}.
24  *  ::vim::option {opt} [val]	# Get/Set option {opt}.
25  *  ::vim::expr {expr}		# Evaluate {expr} using vim's evaluator.
26  *  ::vim::beep			# Guess.
27  *
28  *  set buf [::vim::buffer {n}]	# Create Tcl command for buffer N.
29  *  set bl [::vim::buffer list] # Get list of Tcl commands of all buffers.
30  *  ::vim::buffer exists {n}	# True if buffer {n} exists.
31  *
32  *  set wl [::vim::window list] # Get list of Tcl commands of all windows.
33  *
34  *  set n [$win height]		# Report window height.
35  *  $win height {n}		# Set window height to {n}.
36  *  array set pos [$win cursor] # Get cursor position.
37  *  $win cursor {row} {col}	# Set cursor position.
38  *  $win cursor pos		# Set cursor position from array var "pos"
39  *  $win delcmd {cmd}		# Register callback command for closed window.
40  *  $win option {opt} [val]	# Get/Set vim option in context of $win.
41  *  $win command {cmd}		# Execute ex command in context of $win.
42  *  $win expr {expr}		# Evaluate vim expression in context of $win.
43  *  set buf [$win buffer]	# Create Tcl command for window's buffer.
44  *
45  *  $buf name			# Reports file name in buffer.
46  *  $buf number			# Reports buffer number.
47  *  set l [$buf get {n}]	# Get buffer line {n} as a string.
48  *  set L [$buf get {n} {m}]	# Get lines {n} through {m} as a list.
49  *  $buf count			# Reports number of lines in buffer.
50  *  $buf last			# Reports number of last line in buffer.
51  *  $buf delete {n}		# Delete line {n}.
52  *  $buf delete {n} {m}		# Delete lines {n} through {m}.
53  *  $buf set {n} {l}		# Set line {n} to string {l}.
54  *  $buf set {n} {m} {L}	# Set lines {n} through {m} from list {L}.
55  *				# Delete/inserts lines as appropriate.
56  *  $buf option {opt} [val]	# Get/Set vim option in context of $buf.
57  *  $buf command {cmd}		# Execute ex command in context of $buf
58  *  $buf expr {cmd}		# Evaluate vim expression in context of $buf.
59  *  array set pos [$buf mark {m}]   # Get position of mark.
60  *  $buf append {n} {str}	# Append string {str} to buffer,after line {n}.
61  *  $buf insert {n} {str}	# Insert string {str} in buffer as line {n}.
62  *  $buf delcmd {cmd}		# Register callback command for deleted buffer.
63  *  set wl [$buf windows]	# Get list of Tcl commands for all windows of
64  *				# this buffer.
65 TODO:
66  *  ::vim::buffer new		#   create new buffer + Tcl command
67  */
68 
69 #include "vim.h"
70 #undef EXTERN			/* tcl.h defines it too */
71 
72 #ifdef DYNAMIC_TCL
73 # define USE_TCL_STUBS /* use tcl's stubs mechanism */
74 #endif
75 
76 #include <tcl.h>
77 #include <string.h>
78 
79 typedef struct
80 {
81     Tcl_Interp *interp;
82     int exitvalue;
83     int range_start, range_end;
84     int lbase;
85     char *curbuf, *curwin;
86 } tcl_info;
87 
88 static tcl_info tclinfo = { NULL, 0, 0, 0, 0, NULL, NULL };
89 
90 #define VAR_RANGE1	"::vim::range(start)"
91 #define VAR_RANGE2	"::vim::range(begin)"
92 #define VAR_RANGE3	"::vim::range(end)"
93 #define VAR_CURBUF	"::vim::current(buffer)"
94 #define VAR_CURWIN	"::vim::current(window)"
95 #define VAR_LBASE	"::vim::lbase"
96 #define VAR_CURLINE	"line"
97 #define VAR_CURLNUM	"lnum"
98 #define VARNAME_SIZE	64
99 
100 #define row2tcl(x)  ((x) - (tclinfo.lbase==0))
101 #define row2vim(x)  ((x) + (tclinfo.lbase==0))
102 #define col2tcl(x)  ((x) + (tclinfo.lbase!=0))
103 #define col2vim(x)  ((x) - (tclinfo.lbase!=0))
104 
105 
106 #define VIMOUT	((ClientData)1)
107 #define VIMERR	((ClientData)2)
108 
109 /* This appears to be new in Tcl 8.4. */
110 #ifndef CONST84
111 # define CONST84
112 #endif
113 
114 /*
115  *  List of Tcl interpreters who reference a vim window or buffer.
116  *  Each buffer and window has its own list in the w_tcl_ref or b_tcl_ref
117  *  struct member.  We need this because Tcl can create sub-interpreters with
118  *  the "interp" command, and each interpreter can reference all windows and
119  *  buffers.
120  */
121 struct ref
122 {
123     struct ref	*next;
124 
125     Tcl_Interp	*interp;
126     Tcl_Command cmd;	    /* Tcl command that represents this object */
127     Tcl_Obj	*delcmd;    /* Tcl command to call when object is being del. */
128     void	*vimobj;    /* Vim window or buffer (win_T* or buf_T*) */
129 };
130 static char * tclgetbuffer _ANSI_ARGS_((Tcl_Interp *interp, buf_T *buf));
131 static char * tclgetwindow _ANSI_ARGS_((Tcl_Interp *interp, win_T *win));
132 static int tclsetdelcmd _ANSI_ARGS_((Tcl_Interp *interp, struct ref *reflist, void *vimobj, Tcl_Obj *delcmd));
133 static int tclgetlinenum _ANSI_ARGS_ ((Tcl_Interp *interp, Tcl_Obj *obj, int *valueP, buf_T *buf));
134 static win_T *tclfindwin _ANSI_ARGS_ ((buf_T *buf));
135 static int tcldoexcommand _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn));
136 static int tclsetoption _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn));
137 static int tclvimexpr _ANSI_ARGS_ ((Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int objn));
138 static void tcldelthisinterp _ANSI_ARGS_ ((void));
139 
140 static int vimerror _ANSI_ARGS_((Tcl_Interp *interp));
141 static void tclmsg _ANSI_ARGS_((char *text));
142 static void tclerrmsg _ANSI_ARGS_((char *text));
143 static void tclupdatevars _ANSI_ARGS_((void));
144 
145 static struct ref refsdeleted;	/* dummy object for deleted ref list */
146 
147 /*****************************************************************************
148  * TCL interface manager
149  ****************************************************************************/
150 
151 #if defined(DYNAMIC_TCL) || defined(PROTO)
152 # ifndef DYNAMIC_TCL_DLL
153 #  define DYNAMIC_TCL_DLL "tcl83.dll"
154 # endif
155 # ifndef DYNAMIC_TCL_VER
156 #  define DYNAMIC_TCL_VER "8.3"
157 # endif
158 
159 # ifndef  DYNAMIC_TCL /* Just generating prototypes */
160 typedef int HANDLE;
161 # endif
162 
163 # ifndef MSWIN
164 #  include <dlfcn.h>
165 #  define HANDLE void*
166 #  define TCL_PROC void*
167 #  define load_dll(n) dlopen((n), RTLD_LAZY|RTLD_GLOBAL)
168 #  define symbol_from_dll dlsym
169 #  define close_dll dlclose
170 # else
171 #  define TCL_PROC FARPROC
172 #  define load_dll vimLoadLib
173 #  define symbol_from_dll GetProcAddress
174 #  define close_dll FreeLibrary
175 # endif
176 
177 /*
178  * Declare HANDLE for tcl.dll and function pointers.
179  */
180 static HANDLE hTclLib = NULL;
181 Tcl_Interp* (*dll_Tcl_CreateInterp)();
182 void (*dll_Tcl_FindExecutable)(const void *);
183 
184 /*
185  * Table of name to function pointer of tcl.
186  */
187 static struct {
188     char* name;
189     TCL_PROC* ptr;
190 } tcl_funcname_table[] = {
191     {"Tcl_CreateInterp", (TCL_PROC*)&dll_Tcl_CreateInterp},
192     {"Tcl_FindExecutable", (TCL_PROC*)&dll_Tcl_FindExecutable},
193     {NULL, NULL},
194 };
195 
196 /*
197  * Make all runtime-links of tcl.
198  *
199  * 1. Get module handle using LoadLibraryEx.
200  * 2. Get pointer to tcl function by GetProcAddress.
201  * 3. Repeat 2, until get all functions will be used.
202  *
203  * Parameter 'libname' provides name of DLL.
204  * Return OK or FAIL.
205  */
206     static int
207 tcl_runtime_link_init(char *libname, int verbose)
208 {
209     int i;
210 
211     if (hTclLib)
212 	return OK;
213     if (!(hTclLib = load_dll(libname)))
214     {
215 	if (verbose)
216 	    semsg(_(e_loadlib), libname);
217 	return FAIL;
218     }
219     for (i = 0; tcl_funcname_table[i].ptr; ++i)
220     {
221 	if (!(*tcl_funcname_table[i].ptr = symbol_from_dll(hTclLib,
222 			tcl_funcname_table[i].name)))
223 	{
224 	    close_dll(hTclLib);
225 	    hTclLib = NULL;
226 	    if (verbose)
227 		semsg(_(e_loadfunc), tcl_funcname_table[i].name);
228 	    return FAIL;
229 	}
230     }
231     return OK;
232 }
233 #endif /* defined(DYNAMIC_TCL) || defined(PROTO) */
234 
235 #ifdef DYNAMIC_TCL
236 static char *find_executable_arg = NULL;
237 #endif
238 
239     void
240 vim_tcl_init(char *arg)
241 {
242 #ifndef DYNAMIC_TCL
243     Tcl_FindExecutable(arg);
244 #else
245     find_executable_arg = arg;
246 #endif
247 }
248 
249 #if defined(DYNAMIC_TCL) || defined(PROTO)
250 
251 static int stubs_initialized = FALSE;
252 
253 /*
254  * Return TRUE if the TCL interface can be used.
255  */
256     int
257 tcl_enabled(int verbose)
258 {
259     if (!stubs_initialized && find_executable_arg != NULL
260 	    && tcl_runtime_link_init((char *)p_tcldll, verbose) == OK)
261     {
262 	Tcl_Interp *interp;
263 
264 	dll_Tcl_FindExecutable(find_executable_arg);
265 
266 	if ((interp = dll_Tcl_CreateInterp()) != NULL)
267 	{
268 	    if (Tcl_InitStubs(interp, DYNAMIC_TCL_VER, 0))
269 	    {
270 		Tcl_DeleteInterp(interp);
271 		stubs_initialized = TRUE;
272 	    }
273 	    /* FIXME: When Tcl_InitStubs() was failed, how delete interp? */
274 	}
275     }
276     return stubs_initialized;
277 }
278 #endif
279 
280     void
281 tcl_end(void)
282 {
283 #ifdef DYNAMIC_TCL
284     if (hTclLib)
285     {
286 	close_dll(hTclLib);
287 	hTclLib = NULL;
288     }
289 #endif
290 }
291 
292 /****************************************************************************
293   Tcl commands
294  ****************************************************************************/
295 
296 /*
297  * Replace standard "exit" command.
298  *
299  * Delete the Tcl interpreter; a new one will be created with the next
300  * :tcl command). The exit code is saved (and retrieved in tclexit()).
301  * Since Tcl's exit is never expected to return and this replacement
302  * does, then (except for a trivial case) additional Tcl commands will
303  * be run. Since the interpreter is now marked as deleted, an error
304  * will be returned -- typically "attempt to call eval in deleted
305  * interpreter". Hopefully, at this point, checks for TCL_ERROR take
306  * place and control percolates back up to Vim -- but with this new error
307  * string in the interpreter's result value. Therefore it would be
308  * useless for this routine to return the exit code via Tcl_SetResult().
309  */
310     static int
311 exitcmd(
312     ClientData dummy UNUSED,
313     Tcl_Interp *interp,
314     int objc,
315     Tcl_Obj *CONST objv[])
316 {
317     int value = 0;
318 
319     switch (objc)
320     {
321 	case 2:
322 	    if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK)
323 		break;
324 	    /* FALLTHROUGH */
325 	case 1:
326 	    tclinfo.exitvalue = value;
327 
328 	    Tcl_DeleteInterp(interp);
329 	    break;
330 	default:
331 	    Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?");
332     }
333     return TCL_ERROR;
334 }
335 
336 /*
337  *  "::vim::beep" - what Vi[m] does best :-)
338  */
339     static int
340 beepcmd(
341     ClientData dummy UNUSED,
342     Tcl_Interp *interp,
343     int objc,
344     Tcl_Obj *CONST objv[])
345 {
346     if (objc != 1)
347     {
348 	Tcl_WrongNumArgs(interp, 1, objv, NULL);
349 	return TCL_ERROR;
350     }
351     vim_beep(BO_LANG);
352     return TCL_OK;
353 }
354 
355 /*
356  *  "::vim::buffer list" - create a list of buffer commands.
357  *  "::vim::buffer {N}" - create buffer command for buffer N.
358  *  "::vim::buffer exists {N}" - test if buffer N exists.
359  *  "::vim::buffer new" - create a new buffer (not implemented)
360  */
361     static int
362 buffercmd(
363     ClientData dummy UNUSED,
364     Tcl_Interp *interp,
365     int objc,
366     Tcl_Obj *CONST objv[])
367 {
368     char	*name;
369     buf_T	*buf;
370     Tcl_Obj	*resobj;
371     int		err, n, idx;
372     enum {BCMD_EXISTS, BCMD_LIST};
373     static CONST84 char *bcmdoptions[] =
374     {
375 	"exists", "list", (char *)0
376     };
377 
378     if (objc < 2)
379     {
380 	Tcl_WrongNumArgs(interp, 1, objv, "option");
381 	return TCL_ERROR;
382     }
383     err = Tcl_GetIntFromObj(interp, objv[1], &n);
384     if (err == TCL_OK)
385     {
386 	if (objc != 2)
387 	{
388 	    Tcl_WrongNumArgs(interp, 1, objv, "bufNumber");
389 	    return TCL_ERROR;
390 	}
391 	FOR_ALL_BUFFERS(buf)
392 	{
393 	    if (buf->b_fnum == n)
394 	    {
395 		name = tclgetbuffer(interp, buf);
396 		if (name == NULL)
397 		    return TCL_ERROR;
398 		Tcl_SetResult(interp, name, TCL_VOLATILE);
399 		return TCL_OK;
400 	    }
401 	}
402 	Tcl_SetResult(interp, _("invalid buffer number"), TCL_STATIC);
403 	return TCL_ERROR;
404     }
405     Tcl_ResetResult(interp); /* clear error from Tcl_GetIntFromObj */
406 
407     err = Tcl_GetIndexFromObj(interp, objv[1], bcmdoptions, "option", 0, &idx);
408     if (err != TCL_OK)
409 	return err;
410     switch (idx)
411     {
412 	case BCMD_LIST:
413 	    if (objc != 2)
414 	    {
415 		Tcl_WrongNumArgs(interp, 2, objv, "");
416 		err = TCL_ERROR;
417 		break;
418 	    }
419 	    FOR_ALL_BUFFERS(buf)
420 	    {
421 		name = tclgetbuffer(interp, buf);
422 		if (name == NULL)
423 		{
424 		    err = TCL_ERROR;
425 		    break;
426 		}
427 		Tcl_AppendElement(interp, name);
428 	    }
429 	    break;
430 
431 	case BCMD_EXISTS:
432 	    if (objc != 3)
433 	    {
434 		Tcl_WrongNumArgs(interp, 2, objv, "bufNumber");
435 		err = TCL_ERROR;
436 		break;
437 	    }
438 	    err = Tcl_GetIntFromObj(interp, objv[2], &n);
439 	    if (err == TCL_OK)
440 	    {
441 		buf = buflist_findnr(n);
442 		resobj = Tcl_NewIntObj(buf != NULL);
443 		Tcl_SetObjResult(interp, resobj);
444 	    }
445 	    break;
446 
447 	default:
448 	    Tcl_SetResult(interp, _("not implemented yet"), TCL_STATIC);
449 	    err = TCL_ERROR;
450     }
451     return err;
452 }
453 
454 /*
455  * "::vim::window list" - create list of window commands.
456  */
457     static int
458 windowcmd(
459     ClientData	dummy UNUSED,
460     Tcl_Interp	*interp,
461     int		objc,
462     Tcl_Obj	*CONST objv[])
463 {
464     char	*what, *string;
465     win_T	*win;
466 
467     if (objc != 2)
468     {
469 	Tcl_WrongNumArgs(interp, 1, objv, "option");
470 	return TCL_ERROR;
471     }
472     what = Tcl_GetStringFromObj(objv[1], NULL);
473     if (strcmp(what, "list") == 0)
474     {
475 	FOR_ALL_WINDOWS(win)
476 	{
477 	    string = tclgetwindow(interp, win);
478 	    if (string == NULL)
479 		return TCL_ERROR;
480 	    Tcl_AppendElement(interp, string);
481 	}
482 	return TCL_OK;
483     }
484     Tcl_SetResult(interp, _("unknown option"), TCL_STATIC);
485     return TCL_ERROR;
486 }
487 
488 /*
489  * flags for bufselfcmd and winselfcmd to indicate outstanding actions.
490  */
491 #define FL_UPDATE_SCREEN	(1<<0)
492 #define FL_UPDATE_CURBUF	(1<<1)
493 #define FL_ADJUST_CURSOR	(1<<2)
494 
495 /*
496  * This function implements the buffer commands.
497  */
498     static int
499 bufselfcmd(
500     ClientData	ref,
501     Tcl_Interp	*interp,
502     int		objc,
503     Tcl_Obj	*CONST objv[])
504 {
505     int		opt, err, idx, flags;
506     int		val1, val2, n, i;
507     buf_T	*buf, *savebuf;
508     win_T	*win, *savewin;
509     Tcl_Obj	*resobj;
510     pos_T	*pos;
511     char	*line;
512 
513     enum
514     {
515 	BUF_APPEND, BUF_COMMAND, BUF_COUNT, BUF_DELCMD, BUF_DELETE, BUF_EXPR,
516 	BUF_GET, BUF_INSERT, BUF_LAST, BUF_MARK, BUF_NAME, BUF_NUMBER,
517 	BUF_OPTION, BUF_SET, BUF_WINDOWS
518     };
519     static CONST84 char *bufoptions[] =
520     {
521 	"append", "command", "count", "delcmd", "delete", "expr",
522 	"get", "insert", "last", "mark", "name", "number",
523 	"option", "set", "windows", (char *)0
524     };
525 
526     if (objc < 2)
527     {
528 	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
529 	return TCL_ERROR;
530     }
531 
532     err = Tcl_GetIndexFromObj(interp, objv[1], bufoptions, "option", 0, &idx);
533     if (err != TCL_OK)
534 	return err;
535 
536     buf = (buf_T *)((struct ref *)ref)->vimobj;
537     savebuf = curbuf;  curbuf = buf;
538     savewin = curwin;  curwin = tclfindwin(buf);
539     flags = 0;
540     opt = 0;
541 
542     switch (idx)
543     {
544 	case BUF_COMMAND:
545 	    err = tcldoexcommand(interp, objc, objv, 2);
546 	    flags |= FL_UPDATE_SCREEN;
547 	    break;
548 
549 	case BUF_OPTION:
550 	    err = tclsetoption(interp, objc, objv, 2);
551 	    flags |= FL_UPDATE_SCREEN;
552 	    break;
553 
554 	case BUF_EXPR:
555 	    err = tclvimexpr(interp, objc, objv, 2);
556 	    break;
557 
558 	case BUF_NAME:
559 	    /*
560 	     *	Get filename of buffer.
561 	     */
562 	    if (objc != 2)
563 	    {
564 		Tcl_WrongNumArgs(interp, 2, objv, NULL);
565 		err = TCL_ERROR;
566 		break;
567 	    }
568 	    if (buf->b_ffname)
569 		Tcl_SetResult(interp, (char *)buf->b_ffname, TCL_VOLATILE);
570 	    else
571 		Tcl_SetResult(interp, "", TCL_STATIC);
572 	    break;
573 
574 	case BUF_LAST:
575 	    /*
576 	     * Get line number of last line.
577 	     */
578 	    opt = 1;
579 	    /* fallthrough */
580 	case BUF_COUNT:
581 	    /*
582 	     * Get number of lines in buffer.
583 	     */
584 	    if (objc != 2)
585 	    {
586 		Tcl_WrongNumArgs(interp, 2, objv, NULL);
587 		err = TCL_ERROR;
588 		break;
589 	    }
590 	    val1 = (int)buf->b_ml.ml_line_count;
591 	    if (opt)
592 		val1 = row2tcl(val1);
593 
594 	    resobj = Tcl_NewIntObj(val1);
595 	    Tcl_SetObjResult(interp, resobj);
596 	    break;
597 
598 	case BUF_NUMBER:
599 	    /*
600 	     * Get buffer's number.
601 	     */
602 	    if (objc != 2)
603 	    {
604 		Tcl_WrongNumArgs(interp, 2, objv, NULL);
605 		err = TCL_ERROR;
606 		break;
607 	    }
608 	    resobj = Tcl_NewIntObj((int)buf->b_fnum);
609 	    Tcl_SetObjResult(interp, resobj);
610 	    break;
611 
612 	case BUF_GET:
613 	    if (objc != 3 && objc != 4)
614 	    {
615 		Tcl_WrongNumArgs(interp, 2, objv, "lineNumber ?lineNumber?");
616 		err = TCL_ERROR;
617 		break;
618 	    }
619 	    err = tclgetlinenum(interp, objv[2], &val1, buf);
620 	    if (err != TCL_OK)
621 		break;
622 	    if (objc == 4)
623 	    {
624 		err = tclgetlinenum(interp, objv[3], &val2, buf);
625 		if (err != TCL_OK)
626 		    break;
627 		if (val1 > val2)
628 		{
629 		    n = val1; val1 = val2; val2 = n;
630 		}
631 		Tcl_ResetResult(interp);
632 
633 		for (n = val1; n <= val2 && err == TCL_OK; n++)
634 		{
635 		    line = (char *)ml_get_buf(buf, (linenr_T)n, FALSE);
636 		    if (line)
637 			Tcl_AppendElement(interp, line);
638 		    else
639 			err = TCL_ERROR;
640 		}
641 	    }
642 	    else {  /* objc == 3 */
643 		line = (char *)ml_get_buf(buf, (linenr_T)val1, FALSE);
644 		Tcl_SetResult(interp, line, TCL_VOLATILE);
645 	    }
646 	    break;
647 
648 	case BUF_SET:
649 	    if (objc != 4 && objc != 5)
650 	    {
651 		Tcl_WrongNumArgs(interp, 3, objv, "lineNumber ?lineNumber? stringOrList");
652 		err = TCL_ERROR;
653 		break;
654 	    }
655 	    err = tclgetlinenum(interp, objv[2], &val1, buf);
656 	    if (err != TCL_OK)
657 		return TCL_ERROR;
658 	    if (objc == 4)
659 	    {
660 		/*
661 		 *  Replace one line with a string.
662 		 *	$buf set {n} {string}
663 		 */
664 		line = Tcl_GetStringFromObj(objv[3], NULL);
665 		if (u_savesub((linenr_T)val1) != OK)
666 		{
667 		    Tcl_SetResult(interp, _("cannot save undo information"), TCL_STATIC);
668 		    err = TCL_ERROR;
669 		}
670 		else
671 		if (ml_replace((linenr_T)val1, (char_u *)line, TRUE) != OK)
672 		{
673 		    Tcl_SetResult(interp, _("cannot replace line"), TCL_STATIC);
674 		    err = TCL_ERROR;
675 		}
676 		else
677 		{
678 		    changed_bytes((linenr_T)val1, 0);
679 		    flags |= FL_UPDATE_CURBUF;
680 		}
681 		break;
682 	    }
683 	    else
684 	    {
685 		/*
686 		 * Replace several lines with the elements of a Tcl list.
687 		 *	$buf set {n} {m} {list}
688 		 * If the list contains more than {m}-{n}+1 elements, they
689 		 * are * inserted after line {m}.  If the list contains fewer
690 		 * elements, * the lines from {n}+length({list}) through {m}
691 		 * are deleted.
692 		 */
693 		int	    lc;
694 		Tcl_Obj	    **lv;
695 
696 		err = tclgetlinenum(interp, objv[3], &val2, buf);
697 		if (err != TCL_OK)
698 		    break;
699 		err = Tcl_ListObjGetElements(interp, objv[4], &lc, &lv);
700 		if (err != TCL_OK)
701 		    break;
702 		if (val1 > val2)
703 		{
704 		    n = val1;
705 		    val1 = val2;
706 		    val2 = n;
707 		}
708 
709 		n = val1;
710 		if (u_save((linenr_T)(val1 - 1), (linenr_T)(val2 + 1)) != OK)
711 		{
712 		    Tcl_SetResult(interp, _("cannot save undo information"),
713 								  TCL_STATIC);
714 		    err = TCL_ERROR;
715 		    break;
716 		}
717 		flags |= FL_UPDATE_CURBUF;
718 
719 		for (i = 0; i < lc && n <= val2; i++)
720 		{
721 		    line = Tcl_GetStringFromObj(lv[i], NULL);
722 		    if (ml_replace((linenr_T)n, (char_u *)line, TRUE) != OK)
723 			goto setListError;
724 		    ++n;
725 		}
726 		if (i < lc)
727 		{
728 		    /* append lines */
729 		    do
730 		    {
731 			line = Tcl_GetStringFromObj(lv[i], NULL);
732 			if (ml_append((linenr_T)(n - 1),
733 					      (char_u *)line, 0, FALSE) != OK)
734 			    goto setListError;
735 			++n;
736 			++i;
737 		    } while (i < lc);
738 		}
739 		else if (n <= val2)
740 		{
741 		    /* did not replace all lines, delete */
742 		    i = n;
743 		    do
744 		    {
745 			if (ml_delete((linenr_T)i, FALSE) != OK)
746 			    goto setListError;
747 			++n;
748 		    } while (n <= val2);
749 		}
750 		lc -= val2 - val1 + 1;	/* number of lines to be replaced */
751 		mark_adjust((linenr_T)val1, (linenr_T)val2, (long)MAXLNUM,
752 								    (long)lc);
753 		changed_lines((linenr_T)val1, 0, (linenr_T)val2 + 1, (long)lc);
754 		break;
755     setListError:
756 		u_undo(1);  /* ??? */
757 		Tcl_SetResult(interp, _("cannot set line(s)"), TCL_STATIC);
758 		err = TCL_ERROR;
759 	    }
760 	    break;
761 
762 	case BUF_DELETE:
763 	    if (objc != 3  &&  objc != 4)
764 	    {
765 		Tcl_WrongNumArgs(interp, 3, objv, "lineNumber ?lineNumber?");
766 		err = TCL_ERROR;
767 		break;
768 	    }
769 	    err = tclgetlinenum(interp, objv[2], &val1, buf);
770 	    if (err != TCL_OK)
771 		break;
772 	    val2 = val1;
773 	    if (objc == 4)
774 	    {
775 		err = tclgetlinenum(interp, objv[3], &val2, buf);
776 		if (err != TCL_OK)
777 		    return err;
778 		if (val1 > val2)
779 		{
780 		    i = val1; val1 = val2; val2 = i;
781 		}
782 	    }
783 	    n = val2 - val1 + 1;
784 	    if (u_savedel((linenr_T)val1, (long)n) != OK)
785 	    {
786 		Tcl_SetResult(interp, _("cannot save undo information"),
787 								  TCL_STATIC);
788 		err = TCL_ERROR;
789 		break;
790 	    }
791 	    for (i = 0; i < n; i++)
792 	    {
793 		ml_delete((linenr_T)val1, FALSE);
794 		err = vimerror(interp);
795 		if (err != TCL_OK)
796 		    break;
797 	    }
798 	    if (i > 0)
799 		deleted_lines_mark((linenr_T)val1, (long)i);
800 	    flags |= FL_ADJUST_CURSOR|FL_UPDATE_SCREEN;
801 	    break;
802 
803 	case BUF_MARK:
804 	    if (objc != 3)
805 	    {
806 		Tcl_WrongNumArgs(interp, 2, objv, "markName");
807 		err = TCL_ERROR;
808 		break;
809 	    }
810 	    line = Tcl_GetStringFromObj(objv[2], NULL);
811 
812 	    pos = NULL;
813 	    if (line[0] != '\0'  &&  line[1] == '\0')
814 		pos = getmark(line[0], FALSE);
815 	    if (pos == NULL)
816 	    {
817 		Tcl_SetResult(interp, _("invalid mark name"), TCL_STATIC);
818 		err = TCL_ERROR;
819 		break;
820 	    }
821 	    err = vimerror(interp);
822 	    if (err != TCL_OK)
823 		break;
824 	    if (pos->lnum <= 0)
825 	    {
826 		Tcl_SetResult(interp, _("mark not set"), TCL_STATIC);
827 		err = TCL_ERROR;
828 	    }
829 	    else
830 	    {
831 		char rbuf[64];
832 
833 		sprintf(rbuf, _("row %d column %d"),
834 			     (int)row2tcl(pos->lnum), (int)col2tcl(pos->col));
835 		Tcl_SetResult(interp, rbuf, TCL_VOLATILE);
836 	    }
837 	    break;
838 
839 	case BUF_INSERT:
840 	    opt = 1;
841 	    /* fallthrough */
842 	case BUF_APPEND:
843 	    if (objc != 4)
844 	    {
845 		Tcl_WrongNumArgs(interp, 2, objv, "lineNum text");
846 		err = TCL_ERROR;
847 		break;
848 	    }
849 	    err = tclgetlinenum(interp, objv[2], &val1, buf);
850 	    if (err != TCL_OK)
851 		break;
852 	    if (opt)
853 		--val1;
854 	    if (u_save((linenr_T)val1, (linenr_T)(val1+1)) != OK)
855 	    {
856 		Tcl_SetResult(interp, _("cannot save undo information"),
857 								  TCL_STATIC);
858 		err = TCL_ERROR;
859 		break;
860 	    }
861 
862 	    line = Tcl_GetStringFromObj(objv[3], NULL);
863 	    if (ml_append((linenr_T)val1, (char_u *)line, 0, FALSE) != OK)
864 	    {
865 		Tcl_SetResult(interp, _("cannot insert/append line"),
866 								  TCL_STATIC);
867 		err = TCL_ERROR;
868 		break;
869 	    }
870 	    appended_lines_mark((linenr_T)val1, 1L);
871 	    flags |= FL_UPDATE_SCREEN;
872 	    break;
873 
874 	case BUF_WINDOWS:
875 	    /*
876 	     * Return list of window commands.
877 	     */
878 	    if (objc != 2)
879 	    {
880 		Tcl_WrongNumArgs(interp, 2, objv, NULL);
881 		err = TCL_ERROR;
882 		break;
883 	    }
884 	    Tcl_ResetResult(interp);
885 	    FOR_ALL_WINDOWS(win)
886 	    {
887 		if (win->w_buffer == buf)
888 		{
889 		    line = tclgetwindow(interp, win);
890 		    if (line != NULL)
891 			Tcl_AppendElement(interp, line);
892 		    else
893 		    {
894 			err = TCL_ERROR;
895 			break;
896 		    }
897 		}
898 	    }
899 	    break;
900 
901 	case BUF_DELCMD:
902 	    /*
903 	     * Register deletion callback.
904 	     * TODO: Should be able to register multiple callbacks
905 	     */
906 	    if (objc != 3)
907 	    {
908 		Tcl_WrongNumArgs(interp, 2, objv, "command");
909 		err = TCL_ERROR;
910 		break;
911 	    }
912 	    err = tclsetdelcmd(interp, buf->b_tcl_ref, (void *)buf, objv[2]);
913 	    break;
914 
915 	default:
916 	    Tcl_SetResult(interp, _("not implemented yet"), TCL_STATIC);
917 	    err = TCL_ERROR;
918     }
919 
920     if (flags & FL_UPDATE_CURBUF)
921 	redraw_curbuf_later(NOT_VALID);
922     curbuf = savebuf;
923     curwin = savewin;
924     if (flags & FL_ADJUST_CURSOR)
925 	check_cursor();
926     if (flags & (FL_UPDATE_SCREEN | FL_UPDATE_CURBUF))
927 	update_screen(NOT_VALID);
928 
929     return err;
930 }
931 
932 /*
933  * This function implements the window commands.
934  */
935     static int
936 winselfcmd(
937     ClientData	ref,
938     Tcl_Interp	*interp,
939     int		objc,
940     Tcl_Obj	*CONST objv[])
941 {
942     int		err, idx, flags;
943     int		val1, val2;
944     Tcl_Obj	*resobj;
945     win_T	*savewin, *win;
946     buf_T	*savebuf;
947     char	*str;
948 
949     enum
950     {
951 	WIN_BUFFER, WIN_COMMAND, WIN_CURSOR, WIN_DELCMD, WIN_EXPR,
952 	WIN_HEIGHT, WIN_OPTION
953     };
954     static CONST84 char *winoptions[] =
955     {
956 	"buffer", "command", "cursor", "delcmd", "expr",
957 	"height", "option", (char *)0
958     };
959 
960     if (objc < 2)
961     {
962 	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
963 	return TCL_ERROR;
964     }
965 
966     err = Tcl_GetIndexFromObj(interp, objv[1], winoptions, "option", 0,  &idx);
967     if (err != TCL_OK)
968 	return TCL_ERROR;
969 
970     win = (win_T *)((struct ref *)ref)->vimobj;
971     savewin = curwin;  curwin = win;
972     savebuf = curbuf;  curbuf = win->w_buffer;
973     flags = 0;
974 
975     switch (idx)
976     {
977 	case WIN_OPTION:
978 	    err = tclsetoption(interp, objc, objv, 2);
979 	    flags |= FL_UPDATE_SCREEN;
980 	    break;
981 
982 	case WIN_COMMAND:
983 	    err = tcldoexcommand(interp, objc, objv, 2);
984 	    flags |= FL_UPDATE_SCREEN;
985 	    break;
986 
987 	case WIN_EXPR:
988 	    err = tclvimexpr(interp, objc, objv, 2);
989 	    break;
990 
991 	case WIN_HEIGHT:
992 	    if (objc == 3)
993 	    {
994 		err = Tcl_GetIntFromObj(interp, objv[2], &val1);
995 		if (err != TCL_OK)
996 		    break;
997 #ifdef FEAT_GUI
998 		need_mouse_correct = TRUE;
999 #endif
1000 		win_setheight(val1);
1001 		err = vimerror(interp);
1002 		if (err != TCL_OK)
1003 		    break;
1004 	    }
1005 	    else
1006 	    if (objc != 2)
1007 	    {
1008 		Tcl_WrongNumArgs(interp, 2, objv, "?value?");
1009 		err = TCL_ERROR;
1010 		break;
1011 	    }
1012 
1013 	    resobj = Tcl_NewIntObj((int)(win->w_height));
1014 	    Tcl_SetObjResult(interp, resobj);
1015 	    break;
1016 
1017 	case WIN_BUFFER:
1018 	    if (objc != 2)
1019 	    {
1020 		Tcl_WrongNumArgs(interp, 2, objv, NULL);
1021 		err = TCL_ERROR;
1022 		break;
1023 	    }
1024 	    str = tclgetbuffer(interp, win->w_buffer);
1025 	    if (str)
1026 		Tcl_SetResult(interp, str, TCL_VOLATILE);
1027 	    else
1028 		err = TCL_ERROR;
1029 	    break;
1030 
1031 	case WIN_DELCMD:
1032 	    if (objc != 3)
1033 	    {
1034 		Tcl_WrongNumArgs(interp, 2, objv, "command");
1035 		err = TCL_ERROR;
1036 		break;
1037 	    }
1038 	    err = tclsetdelcmd(interp, win->w_tcl_ref, (void *)win, objv[2]);
1039 	    break;
1040 
1041 	case WIN_CURSOR:
1042 	    if (objc > 4)
1043 	    {
1044 		Tcl_WrongNumArgs(interp, 2, objv, "?arg1 ?arg2??");
1045 		err = TCL_ERROR;
1046 		break;
1047 	    }
1048 	    if (objc == 2)
1049 	    {
1050 		char buf[64];
1051 
1052 		sprintf(buf, _("row %d column %d"), (int)row2tcl(win->w_cursor.lnum), (int)col2tcl(win->w_cursor.col));
1053 		Tcl_SetResult(interp, buf, TCL_VOLATILE);
1054 		break;
1055 	    }
1056 	    else if (objc == 3)
1057 	    {
1058 		Tcl_Obj *part, *var;
1059 
1060 		part = Tcl_NewStringObj("row", -1);
1061 		var = Tcl_ObjGetVar2(interp, objv[2], part, TCL_LEAVE_ERR_MSG);
1062 		if (var == NULL)
1063 		{
1064 		    err = TCL_ERROR;
1065 		    break;
1066 		}
1067 		err = tclgetlinenum(interp, var, &val1, win->w_buffer);
1068 		if (err != TCL_OK)
1069 		    break;
1070 		part = Tcl_NewStringObj("column", -1);
1071 		var = Tcl_ObjGetVar2(interp, objv[2], part, TCL_LEAVE_ERR_MSG);
1072 		if (var == NULL)
1073 		{
1074 		    err = TCL_ERROR;
1075 		    break;
1076 		}
1077 		err = Tcl_GetIntFromObj(interp, var, &val2);
1078 		if (err != TCL_OK)
1079 		    break;
1080 	    }
1081 	    else {  /* objc == 4 */
1082 		err = tclgetlinenum(interp, objv[2], &val1, win->w_buffer);
1083 		if (err != TCL_OK)
1084 		    break;
1085 		err = Tcl_GetIntFromObj(interp, objv[3], &val2);
1086 		if (err != TCL_OK)
1087 		    break;
1088 	    }
1089 	    /* TODO: should check column */
1090 	    win->w_cursor.lnum = val1;
1091 	    win->w_cursor.col = col2vim(val2);
1092 	    win->w_set_curswant = TRUE;
1093 	    flags |= FL_UPDATE_SCREEN;
1094 	    break;
1095 
1096 	default:
1097 	    Tcl_SetResult(interp, _("not implemented yet"), TCL_STATIC);
1098 	    break;
1099     }
1100 
1101     curwin = savewin;
1102     curbuf = savebuf;
1103     if (flags & FL_UPDATE_SCREEN)
1104 	update_screen(NOT_VALID);
1105 
1106     return err;
1107 }
1108 
1109 
1110     static int
1111 commandcmd(
1112     ClientData	dummy UNUSED,
1113     Tcl_Interp	*interp,
1114     int		objc,
1115     Tcl_Obj	*CONST objv[])
1116 {
1117     int		err;
1118 
1119     err = tcldoexcommand(interp, objc, objv, 1);
1120     update_screen(VALID);
1121     return err;
1122 }
1123 
1124     static int
1125 optioncmd(
1126     ClientData	dummy UNUSED,
1127     Tcl_Interp	*interp,
1128     int		objc,
1129     Tcl_Obj	*CONST objv[])
1130 {
1131     int		err;
1132 
1133     err = tclsetoption(interp, objc, objv, 1);
1134     update_screen(VALID);
1135     return err;
1136 }
1137 
1138     static int
1139 exprcmd(
1140     ClientData	dummy UNUSED,
1141     Tcl_Interp	*interp,
1142     int		objc,
1143     Tcl_Obj	*CONST objv[])
1144 {
1145     return tclvimexpr(interp, objc, objv, 1);
1146 }
1147 
1148 /****************************************************************************
1149   Support functions for Tcl commands
1150  ****************************************************************************/
1151 
1152 /*
1153  * Get a line number from 'obj' and convert it to vim's range.
1154  */
1155     static int
1156 tclgetlinenum(
1157     Tcl_Interp	*interp,
1158     Tcl_Obj	*obj,
1159     int		*valueP,
1160     buf_T	*buf)
1161 {
1162     int err, i;
1163 
1164     enum { LN_BEGIN, LN_BOTTOM, LN_END, LN_FIRST, LN_LAST, LN_START, LN_TOP };
1165 
1166     static CONST84 char *keyw[] =
1167     {
1168 	"begin", "bottom", "end", "first", "last", "start", "top", (char *)0
1169     };
1170 
1171     err = Tcl_GetIndexFromObj(interp, obj, keyw, "", 0, &i);
1172     if (err == TCL_OK)
1173     {
1174 	switch (i)
1175 	{
1176 	    case LN_BEGIN:
1177 	    case LN_FIRST:
1178 	    case LN_START:
1179 	    case LN_TOP:
1180 		*valueP = 1;
1181 		break;
1182 	    case LN_BOTTOM:
1183 	    case LN_END:
1184 	    case LN_LAST:
1185 		*valueP = buf->b_ml.ml_line_count;
1186 		break;
1187 	}
1188 	return TCL_OK;
1189     }
1190     Tcl_ResetResult(interp);
1191 
1192     err = Tcl_GetIntFromObj(interp, obj, &i);
1193     if (err != TCL_OK)
1194 	return err;
1195     i = row2vim(i);
1196     if (i < 1  ||  i > buf->b_ml.ml_line_count)
1197     {
1198 	Tcl_SetResult(interp, _("line number out of range"), TCL_STATIC);
1199 	return TCL_ERROR;
1200     }
1201     *valueP = i;
1202     return TCL_OK;
1203 }
1204 
1205 /*
1206  * Find the first window in the window list that displays the buffer.
1207  */
1208     static win_T *
1209 tclfindwin(buf_T *buf)
1210 {
1211     win_T *win;
1212 
1213     FOR_ALL_WINDOWS(win)
1214     {
1215 	if (win->w_buffer == buf)
1216 	    return win;
1217     }
1218     return curwin;  /* keep current window context */
1219 }
1220 
1221 /*
1222  * Do-it-all function for "::vim::command", "$buf command" and "$win command".
1223  */
1224     static int
1225 tcldoexcommand(
1226     Tcl_Interp	*interp,
1227     int		objc,
1228     Tcl_Obj	*CONST objv[],
1229     int		objn)
1230 {
1231     tcl_info	saveinfo;
1232     int		err, flag, nobjs;
1233     char	*arg;
1234 
1235     nobjs = objc - objn;
1236     if (nobjs < 1 || nobjs > 2)
1237     {
1238 	Tcl_WrongNumArgs(interp, objn, objv, "?-quiet? exCommand");
1239 	return TCL_ERROR;
1240     }
1241 
1242     flag = 0;
1243     if (nobjs == 2)
1244     {
1245 	arg = Tcl_GetStringFromObj(objv[objn], NULL);
1246 	if (strcmp(arg, "-quiet") == 0)
1247 	    flag = 1;
1248 	else
1249 	{
1250 	    Tcl_ResetResult(interp);
1251 	    Tcl_AppendResult(interp, _("unknown flag: "), arg, (char *)0);
1252 	    return TCL_ERROR;
1253 	}
1254 	++objn;
1255     }
1256 
1257     memcpy(&saveinfo, &tclinfo, sizeof(tcl_info));
1258     tclinfo.interp = NULL;
1259     tclinfo.curwin = NULL;
1260     tclinfo.curbuf = NULL;
1261 
1262     arg = Tcl_GetStringFromObj(objv[objn], NULL);
1263     if (flag)
1264 	++emsg_off;
1265     do_cmdline_cmd((char_u *)arg);
1266     if (flag)
1267 	--emsg_off;
1268     err = vimerror(interp);
1269 
1270     /* If the ex command created a new Tcl interpreter, remove it */
1271     if (tclinfo.interp)
1272 	tcldelthisinterp();
1273     memcpy(&tclinfo, &saveinfo, sizeof(tcl_info));
1274     tclupdatevars();
1275 
1276     return err;
1277 }
1278 
1279 /*
1280  * Do-it-all function for "::vim::option", "$buf option" and "$win option".
1281  */
1282     static int
1283 tclsetoption(
1284     Tcl_Interp	*interp,
1285     int		objc,
1286     Tcl_Obj	*CONST objv[],
1287     int		objn)
1288 {
1289     int		err, nobjs, idx;
1290     char_u	*option;
1291     int		isnum;
1292     long	lval;
1293     char_u	*sval;
1294     Tcl_Obj	*resobj;
1295 
1296     enum { OPT_OFF, OPT_ON, OPT_TOGGLE };
1297     static CONST84 char *optkw[] = { "off", "on", "toggle", (char *)0 };
1298 
1299     nobjs = objc - objn;
1300     if (nobjs != 1 && nobjs != 2)
1301     {
1302 	Tcl_WrongNumArgs(interp, objn, objv, "vimOption ?value?");
1303 	return TCL_ERROR;
1304     }
1305 
1306     option = (char_u *)Tcl_GetStringFromObj(objv[objn], NULL);
1307     ++objn;
1308     isnum = get_option_value(option, &lval, &sval, 0);
1309     err = TCL_OK;
1310     switch (isnum)
1311     {
1312 	case 0:
1313 	    Tcl_SetResult(interp, (char *)sval, TCL_VOLATILE);
1314 	    vim_free(sval);
1315 	    break;
1316 	case 1:
1317 	    resobj = Tcl_NewLongObj(lval);
1318 	    Tcl_SetObjResult(interp, resobj);
1319 	    break;
1320 	default:
1321 	    Tcl_SetResult(interp, _("unknown vimOption"), TCL_STATIC);
1322 	    return TCL_ERROR;
1323     }
1324     if (nobjs == 2)
1325     {
1326 	if (isnum)
1327 	{
1328 	    sval = NULL;    /* avoid compiler warning */
1329 	    err = Tcl_GetIndexFromObj(interp, objv[objn], optkw, "", 0, &idx);
1330 	    if (err != TCL_OK)
1331 	    {
1332 		Tcl_ResetResult(interp);
1333 		err = Tcl_GetLongFromObj(interp, objv[objn], &lval);
1334 	    }
1335 	    else
1336 	    switch (idx)
1337 	    {
1338 		case OPT_ON:
1339 		    lval = 1;
1340 		    break;
1341 		case OPT_OFF:
1342 		    lval = 0;
1343 		    break;
1344 		case OPT_TOGGLE:
1345 		    lval = !lval;
1346 		    break;
1347 	    }
1348 	}
1349 	else
1350 	    sval = (char_u *)Tcl_GetStringFromObj(objv[objn], NULL);
1351 	if (err == TCL_OK)
1352 	{
1353 	    set_option_value(option, lval, sval, OPT_LOCAL);
1354 	    err = vimerror(interp);
1355 	}
1356     }
1357     return err;
1358 }
1359 
1360 /*
1361  * Do-it-all function for "::vim::expr", "$buf expr" and "$win expr".
1362  */
1363     static int
1364 tclvimexpr(
1365     Tcl_Interp	*interp,
1366     int		objc,
1367     Tcl_Obj	*CONST objv[],
1368     int		objn)
1369 {
1370 #ifdef FEAT_EVAL
1371     char	*expr, *str;
1372 #endif
1373     int		err;
1374 
1375     if (objc - objn != 1)
1376     {
1377 	Tcl_WrongNumArgs(interp, objn, objv, "vimExpr");
1378 	return TCL_ERROR;
1379     }
1380 
1381 #ifdef FEAT_EVAL
1382     expr = Tcl_GetStringFromObj(objv[objn], NULL);
1383     str = (char *)eval_to_string((char_u *)expr, NULL, TRUE);
1384     if (str == NULL)
1385 	Tcl_SetResult(interp, _("invalid expression"), TCL_STATIC);
1386     else
1387     {
1388 	Tcl_SetResult(interp, str, TCL_VOLATILE);
1389 	vim_free(str);
1390     }
1391     err = vimerror(interp);
1392 #else
1393     Tcl_SetResult(interp, _("expressions disabled at compile time"), TCL_STATIC);
1394     err = TCL_ERROR;
1395 #endif
1396 
1397     return err;
1398 }
1399 
1400 /*
1401  * Check for internal vim errors.
1402  */
1403     static int
1404 vimerror(Tcl_Interp *interp)
1405 {
1406     if (got_int)
1407     {
1408 	Tcl_SetResult(interp, _("keyboard interrupt"), TCL_STATIC);
1409 	return TCL_ERROR;
1410     }
1411     else if (did_emsg)
1412     {
1413 	Tcl_SetResult(interp, _("vim error"), TCL_STATIC);
1414 	return TCL_ERROR;
1415     }
1416     return TCL_OK;
1417 }
1418 
1419 /*
1420  * Functions that handle the reference lists:
1421  *   delref() - callback for Tcl's DeleteCommand
1422  *   tclgetref() - find/create Tcl command for a win_T* or buf_T* object
1423  *   tclgetwindow() - window frontend for tclgetref()
1424  *   tclgetbuffer() - buffer frontend for tclgetref()
1425  *   tclsetdelcmd() - add Tcl callback command to a vim object
1426  */
1427     static void
1428 delref(ClientData cref)
1429 {
1430     struct ref *ref = (struct ref *)cref;
1431 
1432     if (ref->delcmd)
1433     {
1434 	Tcl_DecrRefCount(ref->delcmd);
1435 	ref->delcmd = NULL;
1436     }
1437     ref->interp = NULL;
1438 }
1439 
1440     static char *
1441 tclgetref(
1442     Tcl_Interp	*interp,
1443     void	**refstartP,	/* ptr to w_tcl_ref/b_tcl-ref member of
1444 				   win_T/buf_T struct */
1445     char	*prefix,	/* "win" or "buf" */
1446     void	*vimobj,	/* win_T* or buf_T* */
1447     Tcl_ObjCmdProc *proc)	/* winselfcmd or bufselfcmd */
1448 {
1449     struct ref *ref, *unused = NULL;
1450     static char name[VARNAME_SIZE];
1451     Tcl_Command cmd;
1452 
1453     ref = (struct ref *)(*refstartP);
1454     if (ref == &refsdeleted)
1455     {
1456 	Tcl_SetResult(interp, _("cannot create buffer/window command: object is being deleted"), TCL_STATIC);
1457 	return NULL;
1458     }
1459 
1460     while (ref != NULL)
1461     {
1462 	if (ref->interp == interp)
1463 	    break;
1464 	if (ref->interp == NULL)
1465 	    unused = ref;
1466 	ref = ref->next;
1467     }
1468 
1469     if (ref)
1470 	vim_snprintf(name, sizeof(name), "::vim::%s",
1471 					Tcl_GetCommandName(interp, ref->cmd));
1472     else
1473     {
1474 	if (unused)
1475 	    ref = unused;
1476 	else
1477 	{
1478 	    ref = (struct ref *)Tcl_Alloc(sizeof(struct ref));
1479 	    ref->interp = NULL;
1480 	    ref->next = (struct ref *)(*refstartP);
1481 	    (*refstartP) = (void *)ref;
1482 	}
1483 
1484 	/* This might break on some exotic systems... */
1485 	vim_snprintf(name, sizeof(name), "::vim::%s_%lx",
1486 					       prefix, (unsigned long)vimobj);
1487 	cmd = Tcl_CreateObjCommand(interp, name, proc,
1488 	    (ClientData)ref, (Tcl_CmdDeleteProc *)delref);
1489 	if (!cmd)
1490 	    return NULL;
1491 
1492 	ref->interp = interp;
1493 	ref->cmd = cmd;
1494 	ref->delcmd = NULL;
1495 	ref->vimobj = vimobj;
1496     }
1497     return name;
1498 }
1499 
1500     static char *
1501 tclgetwindow(Tcl_Interp *interp, win_T *win)
1502 {
1503     return tclgetref(interp, &(win->w_tcl_ref), "win", (void *)win, winselfcmd);
1504 }
1505 
1506     static char *
1507 tclgetbuffer(Tcl_Interp *interp, buf_T *buf)
1508 {
1509     return tclgetref(interp, &(buf->b_tcl_ref), "buf", (void *)buf, bufselfcmd);
1510 }
1511 
1512     static int
1513 tclsetdelcmd(
1514     Tcl_Interp	*interp,
1515     struct ref	*reflist,
1516     void	*vimobj,
1517     Tcl_Obj	*delcmd)
1518 {
1519     if (reflist == &refsdeleted)
1520     {
1521 	Tcl_SetResult(interp, _("cannot register callback command: buffer/window is already being deleted"), TCL_STATIC);
1522 	return TCL_ERROR;
1523     }
1524 
1525     while (reflist != NULL)
1526     {
1527 	if (reflist->interp == interp && reflist->vimobj == vimobj)
1528 	{
1529 	    if (reflist->delcmd)
1530 		Tcl_DecrRefCount(reflist->delcmd);
1531 	    Tcl_IncrRefCount(delcmd);
1532 	    reflist->delcmd = delcmd;
1533 	    return TCL_OK;
1534 	}
1535 	reflist = reflist->next;
1536     }
1537     /* This should never happen.  Famous last word? */
1538     emsg(_("E280: TCL FATAL ERROR: reflist corrupt!? Please report this to [email protected]"));
1539     Tcl_SetResult(interp, _("cannot register callback command: buffer/window reference not found"), TCL_STATIC);
1540     return TCL_ERROR;
1541 }
1542 
1543 
1544 /*******************************************
1545     I/O Channel
1546 ********************************************/
1547 
1548     static int
1549 tcl_channel_close(ClientData instance, Tcl_Interp *interp UNUSED)
1550 {
1551     int		err = 0;
1552 
1553     /* currently does nothing */
1554 
1555     if (instance != VIMOUT && instance != VIMERR)
1556     {
1557 	Tcl_SetErrno(EBADF);
1558 	err = EBADF;
1559     }
1560     return err;
1561 }
1562 
1563     static int
1564 tcl_channel_input(
1565     ClientData	instance UNUSED,
1566     char	*buf UNUSED,
1567     int		bufsiz UNUSED,
1568     int		*errptr)
1569 {
1570 
1571     /* input is currently not supported */
1572 
1573     Tcl_SetErrno(EINVAL);
1574     if (errptr)
1575 	*errptr = EINVAL;
1576     return -1;
1577 }
1578 
1579     static int
1580 tcl_channel_output(
1581     ClientData	instance,
1582     const char	*buf,
1583     int		bufsiz,
1584     int		*errptr)
1585 {
1586     char_u	*str;
1587     int		result;
1588 
1589     /* The buffer is not guaranteed to be 0-terminated, and we don't if
1590      * there is enough room to add a '\0'.  So we have to create a copy
1591      * of the buffer...
1592      */
1593     str = vim_strnsave((char_u *)buf, bufsiz);
1594     if (!str)
1595     {
1596 	Tcl_SetErrno(ENOMEM);
1597 	if (errptr)
1598 	    *errptr = ENOMEM;
1599 	return -1;
1600     }
1601 
1602     result = bufsiz;
1603     if (instance == VIMOUT)
1604 	tclmsg((char *)str);
1605     else
1606     if (instance == VIMERR)
1607 	tclerrmsg((char *)str);
1608     else
1609     {
1610 	Tcl_SetErrno(EBADF);
1611 	if (errptr)
1612 	    *errptr = EBADF;
1613 	result = -1;
1614     }
1615     vim_free(str);
1616     return result;
1617 }
1618 
1619     static void
1620 tcl_channel_watch(ClientData instance UNUSED, int mask UNUSED)
1621 {
1622     Tcl_SetErrno(EINVAL);
1623 }
1624 
1625     static int
1626 tcl_channel_gethandle(
1627     ClientData	instance UNUSED,
1628     int		direction UNUSED,
1629     ClientData	*handleptr UNUSED)
1630 {
1631     Tcl_SetErrno(EINVAL);
1632     return EINVAL;
1633 }
1634 
1635 
1636 static Tcl_ChannelType tcl_channel_type =
1637 {
1638     "vimmessage",	/* typeName */
1639     TCL_CHANNEL_VERSION_2, /* version */
1640     tcl_channel_close,	/* closeProc */
1641     tcl_channel_input,	/* inputProc */
1642     tcl_channel_output,	/* outputProc */
1643     NULL,		/* seekProc */
1644     NULL,		/* setOptionProc */
1645     NULL,		/* getOptionProc */
1646     tcl_channel_watch,	/* watchProc */
1647     tcl_channel_gethandle, /* getHandleProc */
1648     NULL,		/* close2Proc */
1649     NULL,		/* blockModeProc */
1650 #ifdef TCL_CHANNEL_VERSION_2
1651     NULL,		/* flushProc */
1652     NULL,		/* handlerProc */
1653 #endif
1654 /* The following should not be necessary since TCL_CHANNEL_VERSION_2 was
1655  * set above */
1656 #ifdef TCL_CHANNEL_VERSION_3
1657     NULL,		/* wideSeekProc */
1658 #endif
1659 #ifdef TCL_CHANNEL_VERSION_4
1660     NULL,		/* threadActionProc */
1661 #endif
1662 #ifdef TCL_CHANNEL_VERSION_5
1663     NULL		/* truncateProc */
1664 #endif
1665 };
1666 
1667 /**********************************
1668   Interface to vim
1669  **********************************/
1670 
1671     static void
1672 tclupdatevars(void)
1673 {
1674     char varname[VARNAME_SIZE];	/* must be writeable */
1675     char *name;
1676 
1677     strcpy(varname, VAR_RANGE1);
1678     Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1679     strcpy(varname, VAR_RANGE2);
1680     Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1681     strcpy(varname, VAR_RANGE3);
1682     Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1683 
1684     strcpy(varname, VAR_LBASE);
1685     Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1686 
1687     name = tclgetbuffer(tclinfo.interp, curbuf);
1688     strcpy(tclinfo.curbuf, name);
1689     strcpy(varname, VAR_CURBUF);
1690     Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1691 
1692     name = tclgetwindow(tclinfo.interp, curwin);
1693     strcpy(tclinfo.curwin, name);
1694     strcpy(varname, VAR_CURWIN);
1695     Tcl_UpdateLinkedVar(tclinfo.interp, varname);
1696 }
1697 
1698 
1699     static int
1700 tclinit(exarg_T *eap)
1701 {
1702     char varname[VARNAME_SIZE];	/* Tcl_LinkVar requires writeable varname */
1703     char *name;
1704 
1705 #ifdef DYNAMIC_TCL
1706     if (!tcl_enabled(TRUE))
1707     {
1708 	emsg(_("E571: Sorry, this command is disabled: the Tcl library could not be loaded."));
1709 	return FAIL;
1710     }
1711 #endif
1712 
1713     if (!tclinfo.interp)
1714     {
1715 	Tcl_Interp *interp;
1716 	static Tcl_Channel ch1, ch2;
1717 
1718 	/* Create replacement channels for stdout and stderr; this has to be
1719 	 * done each time an interpreter is created since the channels are closed
1720 	 * when the interpreter is deleted */
1721 	ch1 = Tcl_CreateChannel(&tcl_channel_type, "vimout", VIMOUT, TCL_WRITABLE);
1722 	ch2 = Tcl_CreateChannel(&tcl_channel_type, "vimerr", VIMERR, TCL_WRITABLE);
1723 	Tcl_SetStdChannel(ch1, TCL_STDOUT);
1724 	Tcl_SetStdChannel(ch2, TCL_STDERR);
1725 
1726 	interp = Tcl_CreateInterp();
1727 	Tcl_Preserve(interp);
1728 	if (Tcl_Init(interp) == TCL_ERROR)
1729 	{
1730 	    Tcl_Release(interp);
1731 	    Tcl_DeleteInterp(interp);
1732 	    return FAIL;
1733 	}
1734 #if 0
1735 	/* VIM sure is interactive */
1736 	Tcl_SetVar(interp, "tcl_interactive", "1", TCL_GLOBAL_ONLY);
1737 #endif
1738 
1739 	Tcl_SetChannelOption(interp, ch1, "-buffering", "line");
1740 #ifdef MSWIN
1741 	Tcl_SetChannelOption(interp, ch1, "-translation", "lf");
1742 #endif
1743 	Tcl_SetChannelOption(interp, ch2, "-buffering", "line");
1744 #ifdef MSWIN
1745 	Tcl_SetChannelOption(interp, ch2, "-translation", "lf");
1746 #endif
1747 
1748 	/* replace standard Tcl exit command */
1749 	Tcl_DeleteCommand(interp, "exit");
1750 	Tcl_CreateObjCommand(interp, "exit", exitcmd,
1751 	    (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1752 
1753 	/* new commands, in ::vim namespace */
1754 	Tcl_CreateObjCommand(interp, "::vim::buffer", buffercmd,
1755 	    (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1756 	Tcl_CreateObjCommand(interp, "::vim::window", windowcmd,
1757 	   (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1758 	Tcl_CreateObjCommand(interp, "::vim::command", commandcmd,
1759 	   (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1760 	Tcl_CreateObjCommand(interp, "::vim::beep", beepcmd,
1761 	   (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1762 	Tcl_CreateObjCommand(interp, "::vim::option", optioncmd,
1763 	   (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1764 	Tcl_CreateObjCommand(interp, "::vim::expr", exprcmd,
1765 	   (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
1766 
1767 	/* "lbase" variable */
1768 	tclinfo.lbase = 1;
1769 	strcpy(varname, VAR_LBASE);
1770 	Tcl_LinkVar(interp, varname, (char *)&tclinfo.lbase, TCL_LINK_INT);
1771 
1772 	/* "range" variable */
1773 	tclinfo.range_start = eap->line1;
1774 	strcpy(varname, VAR_RANGE1);
1775 	Tcl_LinkVar(interp, varname, (char *)&tclinfo.range_start, TCL_LINK_INT|TCL_LINK_READ_ONLY);
1776 	strcpy(varname, VAR_RANGE2);
1777 	Tcl_LinkVar(interp, varname, (char *)&tclinfo.range_start, TCL_LINK_INT|TCL_LINK_READ_ONLY);
1778 	tclinfo.range_end   = eap->line2;
1779 	strcpy(varname, VAR_RANGE3);
1780 	Tcl_LinkVar(interp, varname, (char *)&tclinfo.range_end, TCL_LINK_INT|TCL_LINK_READ_ONLY);
1781 
1782 	/* "current" variable */
1783 	tclinfo.curbuf = Tcl_Alloc(VARNAME_SIZE);
1784 	tclinfo.curwin = Tcl_Alloc(VARNAME_SIZE);
1785 	name = tclgetbuffer(interp, curbuf);
1786 	strcpy(tclinfo.curbuf, name);
1787 	strcpy(varname, VAR_CURBUF);
1788 	Tcl_LinkVar(interp, varname, (char *)&tclinfo.curbuf, TCL_LINK_STRING|TCL_LINK_READ_ONLY);
1789 	name = tclgetwindow(interp, curwin);
1790 	strcpy(tclinfo.curwin, name);
1791 	strcpy(varname, VAR_CURWIN);
1792 	Tcl_LinkVar(interp, varname, (char *)&tclinfo.curwin, TCL_LINK_STRING|TCL_LINK_READ_ONLY);
1793 
1794 	tclinfo.interp = interp;
1795     }
1796     else
1797     {
1798 	/* Interpreter already exists, just update variables */
1799 	tclinfo.range_start = row2tcl(eap->line1);
1800 	tclinfo.range_end = row2tcl(eap->line2);
1801 	tclupdatevars();
1802     }
1803 
1804     tclinfo.exitvalue = 0;
1805     return OK;
1806 }
1807 
1808     static void
1809 tclerrmsg(char *text)
1810 {
1811     char *next;
1812 
1813     while ((next=strchr(text, '\n')))
1814     {
1815 	*next++ = '\0';
1816 	emsg(text);
1817 	text = next;
1818     }
1819     if (*text)
1820 	emsg(text);
1821 }
1822 
1823     static void
1824 tclmsg(char *text)
1825 {
1826     char *next;
1827 
1828     while ((next=strchr(text, '\n')))
1829     {
1830 	*next++ = '\0';
1831 	msg(text);
1832 	text = next;
1833     }
1834     if (*text)
1835 	msg(text);
1836 }
1837 
1838     static void
1839 tcldelthisinterp(void)
1840 {
1841     if (!Tcl_InterpDeleted(tclinfo.interp))
1842 	Tcl_DeleteInterp(tclinfo.interp);
1843     Tcl_Release(tclinfo.interp);
1844     /* The interpreter is now gets deleted.  All registered commands (esp.
1845      * window and buffer commands) are deleted, triggering their deletion
1846      * callback, which deletes all refs pointing to this interpreter.
1847      * We could garbage-collect the unused ref structs in all windows and
1848      * buffers, but unless the user creates hundreds of sub-interpreters
1849      * all referring to lots of windows and buffers, this is hardly worth
1850      * the effort.  Unused refs are recycled by other interpreters, and
1851      * all refs are free'd when the window/buffer gets closed by vim.
1852      */
1853 
1854     tclinfo.interp = NULL;
1855     Tcl_Free(tclinfo.curbuf);
1856     Tcl_Free(tclinfo.curwin);
1857     tclinfo.curbuf = tclinfo.curwin = NULL;
1858 }
1859 
1860     static int
1861 tclexit(int error)
1862 {
1863     int newerr = OK;
1864 
1865     if (Tcl_InterpDeleted(tclinfo.interp)     /* True if we intercepted Tcl's exit command */
1866 #if (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 5) || TCL_MAJOR_VERSION > 8
1867 	|| Tcl_LimitExceeded(tclinfo.interp)  /* True if the interpreter cannot continue */
1868 #endif
1869 	)
1870     {
1871 	char buf[50];
1872 
1873 	sprintf(buf, _("E572: exit code %d"), tclinfo.exitvalue);
1874 	tclerrmsg(buf);
1875 	if (tclinfo.exitvalue == 0)
1876 	{
1877 	    did_emsg = 0;
1878 	    newerr = OK;
1879 	}
1880 	else
1881 	    newerr = FAIL;
1882 
1883 	tcldelthisinterp();
1884     }
1885     else
1886     {
1887 	char *result;
1888 
1889 	result = (char *)Tcl_GetStringResult(tclinfo.interp);
1890 	if (error == TCL_OK)
1891 	{
1892 	    tclmsg(result);
1893 	    newerr = OK;
1894 	}
1895 	else
1896 	{
1897 	    tclerrmsg(result);
1898 	    newerr = FAIL;
1899 	}
1900     }
1901 
1902     return newerr;
1903 }
1904 
1905 /*
1906  * ":tcl"
1907  */
1908     void
1909 ex_tcl(exarg_T *eap)
1910 {
1911     char_u	*script;
1912     int		err;
1913 
1914     script = script_get(eap, eap->arg);
1915     if (!eap->skip)
1916     {
1917 	err = tclinit(eap);
1918 	if (err == OK)
1919 	{
1920 	    Tcl_AllowExceptions(tclinfo.interp);
1921 	    if (script == NULL)
1922 		err = Tcl_Eval(tclinfo.interp, (char *)eap->arg);
1923 	    else
1924 		err = Tcl_Eval(tclinfo.interp, (char *)script);
1925 	    err = tclexit(err);
1926 	}
1927     }
1928     vim_free(script);
1929 }
1930 
1931 /*
1932  * ":tclfile"
1933  */
1934     void
1935 ex_tclfile(exarg_T *eap)
1936 {
1937     char *file = (char *)eap->arg;
1938     int err;
1939 
1940     err = tclinit(eap);
1941     if (err == OK)
1942     {
1943 	Tcl_AllowExceptions(tclinfo.interp);
1944 	err = Tcl_EvalFile(tclinfo.interp, file);
1945 	err = tclexit(err);
1946     }
1947 }
1948 
1949 /*
1950  * ":tcldo"
1951  */
1952     void
1953 ex_tcldo(exarg_T *eap)
1954 {
1955     char	*script, *line;
1956     int		err, rs, re, lnum;
1957     char	var_lnum[VARNAME_SIZE]; /* must be writeable memory */
1958     char	var_line[VARNAME_SIZE];
1959     linenr_T	first_line = 0;
1960     linenr_T	last_line = 0;
1961     buf_T	*was_curbuf = curbuf;
1962 
1963     rs = eap->line1;
1964     re = eap->line2;
1965     script = (char *)eap->arg;
1966     strcpy(var_lnum, VAR_CURLNUM);
1967     strcpy(var_line, VAR_CURLINE);
1968 
1969     err = tclinit(eap);
1970     if (err != OK)
1971 	return;
1972 
1973     lnum = row2tcl(rs);
1974     Tcl_LinkVar(tclinfo.interp, var_lnum, (char *)&lnum, TCL_LINK_INT|TCL_LINK_READ_ONLY);
1975     err = TCL_OK;
1976     if (u_save((linenr_T)(rs-1), (linenr_T)(re+1)) != OK)
1977     {
1978 	Tcl_SetResult(tclinfo.interp, _("cannot save undo information"), TCL_STATIC);
1979 	err = TCL_ERROR;
1980     }
1981     while (err == TCL_OK  &&  rs <= re)
1982     {
1983 	if ((linenr_T)rs > curbuf->b_ml.ml_line_count)
1984 	    break;
1985 	line = (char *)ml_get_buf(curbuf, (linenr_T)rs, FALSE);
1986 	if (!line)
1987 	{
1988 	    Tcl_SetResult(tclinfo.interp, _("cannot get line"), TCL_STATIC);
1989 	    err = TCL_ERROR;
1990 	    break;
1991 	}
1992 	Tcl_SetVar(tclinfo.interp, var_line, line, 0);
1993 	Tcl_AllowExceptions(tclinfo.interp);
1994 	err = Tcl_Eval(tclinfo.interp, script);
1995 	if (err != TCL_OK
1996 	    || Tcl_InterpDeleted(tclinfo.interp)
1997 #if (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 5) || TCL_MAJOR_VERSION > 8
1998 	    || Tcl_LimitExceeded(tclinfo.interp)
1999 #endif
2000 	    || curbuf != was_curbuf)
2001 	    break;
2002 	line = (char *)Tcl_GetVar(tclinfo.interp, var_line, 0);
2003 	if (line)
2004 	{
2005 	    if (ml_replace((linenr_T)rs, (char_u *)line, TRUE) != OK)
2006 	    {
2007 		Tcl_SetResult(tclinfo.interp, _("cannot replace line"), TCL_STATIC);
2008 		err = TCL_ERROR;
2009 		break;
2010 	    }
2011 	    if (first_line == 0)
2012 		first_line = rs;
2013 	    last_line = rs;
2014 	}
2015 	++rs;
2016 	++lnum;
2017 	Tcl_UpdateLinkedVar(tclinfo.interp, var_lnum);
2018     }
2019     if (first_line)
2020 	changed_lines(first_line, 0, last_line + 1, (long)0);
2021 
2022     Tcl_UnsetVar(tclinfo.interp, var_line, 0);
2023     Tcl_UnlinkVar(tclinfo.interp, var_lnum);
2024     if (err == TCL_OK)
2025 	Tcl_ResetResult(tclinfo.interp);
2026 
2027     (void)tclexit(err);
2028 }
2029 
2030     static void
2031 tcldelallrefs(struct ref *ref)
2032 {
2033     struct ref	*next;
2034     int		err;
2035     char	*result;
2036 
2037 #ifdef DYNAMIC_TCL
2038     /* TODO: this code currently crashes Vim on exit */
2039     if (exiting)
2040 	return;
2041 #endif
2042 
2043     while (ref != NULL)
2044     {
2045 	next = ref->next;
2046 	if (ref->interp)
2047 	{
2048 	    if (ref->delcmd)
2049 	    {
2050 		err = Tcl_GlobalEvalObj(ref->interp, ref->delcmd);
2051 		if (err != TCL_OK)
2052 		{
2053 		    result = (char *)Tcl_GetStringResult(ref->interp);
2054 		    if (result)
2055 			tclerrmsg(result);
2056 		}
2057 		Tcl_DecrRefCount(ref->delcmd);
2058 		ref->delcmd = NULL;
2059 	    }
2060 	    Tcl_DeleteCommandFromToken(ref->interp, ref->cmd);
2061 	}
2062 	Tcl_Free((char *)ref);
2063 	ref = next;
2064     }
2065 }
2066 
2067     void
2068 tcl_buffer_free(buf_T *buf)
2069 {
2070     struct ref *reflist;
2071 
2072 #ifdef DYNAMIC_TCL
2073     if (!stubs_initialized)	/* Not using Tcl, nothing to do. */
2074 	return;
2075 #endif
2076 
2077     reflist = (struct ref *)(buf->b_tcl_ref);
2078     if (reflist != &refsdeleted)
2079     {
2080 	buf->b_tcl_ref = (void *)&refsdeleted;
2081 	tcldelallrefs(reflist);
2082 	buf->b_tcl_ref = NULL;
2083     }
2084 }
2085 
2086     void
2087 tcl_window_free(win_T *win)
2088 {
2089     struct ref *reflist;
2090 
2091 #ifdef DYNAMIC_TCL
2092     if (!stubs_initialized)	/* Not using Tcl, nothing to do. */
2093 	return;
2094 #endif
2095 
2096     reflist = (struct ref*)(win->w_tcl_ref);
2097     if (reflist != &refsdeleted)
2098     {
2099 	win->w_tcl_ref = (void *)&refsdeleted;
2100 	tcldelallrefs(reflist);
2101 	win->w_tcl_ref = NULL;
2102     }
2103 }
2104 
2105 /* The End */
2106