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