Mercurial > hg > RemoteEditor > vim7
comparison src/if_tcl.c @ 48:67300faee616 v7-3-618
v7-3-618
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Wed, 01 Aug 2012 18:08:28 +0900 |
parents | c16898406ff2 |
children |
comparison
equal
deleted
inserted
replaced
47:6c0584ec21b1 | 48:67300faee616 |
---|---|
72 #ifdef DYNAMIC_TCL | 72 #ifdef DYNAMIC_TCL |
73 # define USE_TCL_STUBS /* use tcl's stubs mechanism */ | 73 # define USE_TCL_STUBS /* use tcl's stubs mechanism */ |
74 #endif | 74 #endif |
75 | 75 |
76 #include <tcl.h> | 76 #include <tcl.h> |
77 #include <errno.h> | |
78 #include <string.h> | 77 #include <string.h> |
79 | 78 |
80 typedef struct | 79 typedef struct |
81 { | 80 { |
82 Tcl_Interp *interp; | 81 Tcl_Interp *interp; |
82 int exitvalue; | |
83 int range_start, range_end; | 83 int range_start, range_end; |
84 int lbase; | 84 int lbase; |
85 char *curbuf, *curwin; | 85 char *curbuf, *curwin; |
86 } tcl_info; | 86 } tcl_info; |
87 | 87 |
88 static tcl_info tclinfo = { NULL, 0, 0, 0, NULL, NULL }; | 88 static tcl_info tclinfo = { NULL, 0, 0, 0, 0, NULL, NULL }; |
89 | 89 |
90 #define VAR_RANGE1 "::vim::range(start)" | 90 #define VAR_RANGE1 "::vim::range(start)" |
91 #define VAR_RANGE2 "::vim::range(begin)" | 91 #define VAR_RANGE2 "::vim::range(begin)" |
92 #define VAR_RANGE3 "::vim::range(end)" | 92 #define VAR_RANGE3 "::vim::range(end)" |
93 #define VAR_CURBUF "::vim::current(buffer)" | 93 #define VAR_CURBUF "::vim::current(buffer)" |
278 /**************************************************************************** | 278 /**************************************************************************** |
279 Tcl commands | 279 Tcl commands |
280 ****************************************************************************/ | 280 ****************************************************************************/ |
281 | 281 |
282 /* | 282 /* |
283 * Replace standard "exit" and "catch" commands. | 283 * Replace standard "exit" command. |
284 * | 284 * |
285 * This is a design flaw in Tcl - the standard "exit" command just calls | 285 * Delete the Tcl interpreter; a new one will be created with the next |
286 * exit() and kills the application. It should return TCL_EXIT to the | 286 * :tcl command). The exit code is saved (and retrieved in tclexit()). |
287 * app, which then decides if it wants to terminate or not. In our case, | 287 * Since Tcl's exit is never expected to return and this replacement |
288 * we just delete the Tcl interpreter (and create a new one with the next | 288 * does, then (except for a trivial case) additional Tcl commands will |
289 * :tcl command). | 289 * be run. Since the interpreter is now marked as deleted, an error |
290 */ | 290 * will be returned -- typically "attempt to call eval in deleted |
291 #define TCL_EXIT 5 | 291 * interpreter". Hopefully, at this point, checks for TCL_ERROR take |
292 | 292 * place and control percolates back up to Vim -- but with this new error |
293 * string in the interpreter's result value. Therefore it would be | |
294 * useless for this routine to return the exit code via Tcl_SetResult(). | |
295 */ | |
293 static int | 296 static int |
294 exitcmd(dummy, interp, objc, objv) | 297 exitcmd(dummy, interp, objc, objv) |
295 ClientData dummy UNUSED; | 298 ClientData dummy UNUSED; |
296 Tcl_Interp *interp; | 299 Tcl_Interp *interp; |
297 int objc; | 300 int objc; |
304 case 2: | 307 case 2: |
305 if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) | 308 if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) |
306 break; | 309 break; |
307 /* FALLTHROUGH */ | 310 /* FALLTHROUGH */ |
308 case 1: | 311 case 1: |
309 Tcl_SetObjResult(interp, Tcl_NewIntObj(value)); | 312 tclinfo.exitvalue = value; |
310 return TCL_EXIT; | 313 |
314 Tcl_DeleteInterp(interp); | |
315 break; | |
311 default: | 316 default: |
312 Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?"); | 317 Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?"); |
313 } | |
314 return TCL_ERROR; | |
315 } | |
316 | |
317 static int | |
318 catchcmd(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 } | 318 } |
351 return TCL_ERROR; | 319 return TCL_ERROR; |
352 } | 320 } |
353 | 321 |
354 /* | 322 /* |
371 } | 339 } |
372 | 340 |
373 /* | 341 /* |
374 * "::vim::buffer list" - create a list of buffer commands. | 342 * "::vim::buffer list" - create a list of buffer commands. |
375 * "::vim::buffer {N}" - create buffer command for buffer N. | 343 * "::vim::buffer {N}" - create buffer command for buffer N. |
344 * "::vim::buffer exists {N}" - test if buffer N exists. | |
376 * "::vim::buffer new" - create a new buffer (not implemented) | 345 * "::vim::buffer new" - create a new buffer (not implemented) |
377 */ | 346 */ |
378 static int | 347 static int |
379 buffercmd(dummy, interp, objc, objv) | 348 buffercmd(dummy, interp, objc, objv) |
380 ClientData dummy UNUSED; | 349 ClientData dummy UNUSED; |
1662 | 1631 |
1663 | 1632 |
1664 static Tcl_ChannelType channel_type = | 1633 static Tcl_ChannelType channel_type = |
1665 { | 1634 { |
1666 "vimmessage", /* typeName */ | 1635 "vimmessage", /* typeName */ |
1667 NULL, /* version */ | 1636 TCL_CHANNEL_VERSION_2, /* version */ |
1668 channel_close, /* closeProc */ | 1637 channel_close, /* closeProc */ |
1669 channel_input, /* inputProc */ | 1638 channel_input, /* inputProc */ |
1670 channel_output, /* outputProc */ | 1639 channel_output, /* outputProc */ |
1671 NULL, /* seekProc */ | 1640 NULL, /* seekProc */ |
1672 NULL, /* setOptionProc */ | 1641 NULL, /* setOptionProc */ |
1677 NULL, /* blockModeProc */ | 1646 NULL, /* blockModeProc */ |
1678 #ifdef TCL_CHANNEL_VERSION_2 | 1647 #ifdef TCL_CHANNEL_VERSION_2 |
1679 NULL, /* flushProc */ | 1648 NULL, /* flushProc */ |
1680 NULL, /* handlerProc */ | 1649 NULL, /* handlerProc */ |
1681 #endif | 1650 #endif |
1651 /* The following should not be necessary since TCL_CHANNEL_VERSION_2 was | |
1652 * set above */ | |
1682 #ifdef TCL_CHANNEL_VERSION_3 | 1653 #ifdef TCL_CHANNEL_VERSION_3 |
1683 NULL, /* wideSeekProc */ | 1654 NULL, /* wideSeekProc */ |
1684 #endif | 1655 #endif |
1685 #ifdef TCL_CHANNEL_VERSION_4 | 1656 #ifdef TCL_CHANNEL_VERSION_4 |
1686 NULL, /* threadActionProc */ | 1657 NULL, /* threadActionProc */ |
1740 if (!tclinfo.interp) | 1711 if (!tclinfo.interp) |
1741 { | 1712 { |
1742 Tcl_Interp *interp; | 1713 Tcl_Interp *interp; |
1743 static Tcl_Channel ch1, ch2; | 1714 static Tcl_Channel ch1, ch2; |
1744 | 1715 |
1745 /* replace stdout and stderr */ | 1716 /* Create replacement channels for stdout and stderr; this has to be |
1717 * done each time an interpreter is created since the channels are closed | |
1718 * when the interpreter is deleted */ | |
1746 ch1 = Tcl_CreateChannel(&channel_type, "vimout", VIMOUT, TCL_WRITABLE); | 1719 ch1 = Tcl_CreateChannel(&channel_type, "vimout", VIMOUT, TCL_WRITABLE); |
1747 ch2 = Tcl_CreateChannel(&channel_type, "vimerr", VIMERR, TCL_WRITABLE); | 1720 ch2 = Tcl_CreateChannel(&channel_type, "vimerr", VIMERR, TCL_WRITABLE); |
1748 Tcl_SetStdChannel(ch1, TCL_STDOUT); | 1721 Tcl_SetStdChannel(ch1, TCL_STDOUT); |
1749 Tcl_SetStdChannel(ch2, TCL_STDERR); | 1722 Tcl_SetStdChannel(ch2, TCL_STDERR); |
1750 | 1723 |
1760 /* VIM sure is interactive */ | 1733 /* VIM sure is interactive */ |
1761 Tcl_SetVar(interp, "tcl_interactive", "1", TCL_GLOBAL_ONLY); | 1734 Tcl_SetVar(interp, "tcl_interactive", "1", TCL_GLOBAL_ONLY); |
1762 #endif | 1735 #endif |
1763 | 1736 |
1764 Tcl_SetChannelOption(interp, ch1, "-buffering", "line"); | 1737 Tcl_SetChannelOption(interp, ch1, "-buffering", "line"); |
1738 #ifdef WIN3264 | |
1739 Tcl_SetChannelOption(interp, ch1, "-translation", "lf"); | |
1740 #endif | |
1765 Tcl_SetChannelOption(interp, ch2, "-buffering", "line"); | 1741 Tcl_SetChannelOption(interp, ch2, "-buffering", "line"); |
1766 | 1742 #ifdef WIN3264 |
1767 /* replace some standard Tcl commands */ | 1743 Tcl_SetChannelOption(interp, ch2, "-translation", "lf"); |
1744 #endif | |
1745 | |
1746 /* replace standard Tcl exit command */ | |
1768 Tcl_DeleteCommand(interp, "exit"); | 1747 Tcl_DeleteCommand(interp, "exit"); |
1769 Tcl_CreateObjCommand(interp, "exit", exitcmd, | 1748 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); | 1749 (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); |
1774 | 1750 |
1775 /* new commands, in ::vim namespace */ | 1751 /* new commands, in ::vim namespace */ |
1776 Tcl_CreateObjCommand(interp, "::vim::buffer", buffercmd, | 1752 Tcl_CreateObjCommand(interp, "::vim::buffer", buffercmd, |
1777 (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); | 1753 (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); |
1820 /* Interpreter already exists, just update variables */ | 1796 /* Interpreter already exists, just update variables */ |
1821 tclinfo.range_start = row2tcl(eap->line1); | 1797 tclinfo.range_start = row2tcl(eap->line1); |
1822 tclinfo.range_end = row2tcl(eap->line2); | 1798 tclinfo.range_end = row2tcl(eap->line2); |
1823 tclupdatevars(); | 1799 tclupdatevars(); |
1824 } | 1800 } |
1801 | |
1802 tclinfo.exitvalue = 0; | |
1825 return OK; | 1803 return OK; |
1826 } | 1804 } |
1827 | 1805 |
1828 static void | 1806 static void |
1829 tclerrmsg(text) | 1807 tclerrmsg(text) |
1883 tclexit(error) | 1861 tclexit(error) |
1884 int error; | 1862 int error; |
1885 { | 1863 { |
1886 int newerr = OK; | 1864 int newerr = OK; |
1887 | 1865 |
1888 if (error == TCL_EXIT ) | 1866 if (Tcl_InterpDeleted(tclinfo.interp) /* True if we intercepted Tcl's exit command */ |
1889 { | 1867 #if (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 5) || TCL_MAJOR_VERSION > 8 |
1890 int retval; | 1868 || Tcl_LimitExceeded(tclinfo.interp) /* True if the interpreter cannot continue */ |
1869 #endif | |
1870 ) | |
1871 { | |
1891 char buf[50]; | 1872 char buf[50]; |
1892 Tcl_Obj *robj; | 1873 |
1893 | 1874 sprintf(buf, _("E572: exit code %d"), tclinfo.exitvalue); |
1894 robj = Tcl_GetObjResult(tclinfo.interp); | 1875 tclerrmsg(buf); |
1895 if( Tcl_GetIntFromObj(tclinfo.interp, robj, &retval) != TCL_OK ) | 1876 if (tclinfo.exitvalue == 0) |
1896 { | 1877 { |
1897 EMSG(_("E281: TCL ERROR: exit code is not int!? Please report this to vim-dev@vim.org")); | 1878 did_emsg = 0; |
1898 newerr = FAIL; | 1879 newerr = OK; |
1899 } | 1880 } |
1900 else | 1881 else |
1901 { | 1882 newerr = FAIL; |
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 | 1883 |
1913 tcldelthisinterp(); | 1884 tcldelthisinterp(); |
1914 } | 1885 } |
1915 else | 1886 else |
1916 { | 1887 { |
2020 break; | 1991 break; |
2021 } | 1992 } |
2022 Tcl_SetVar(tclinfo.interp, var_line, line, 0); | 1993 Tcl_SetVar(tclinfo.interp, var_line, line, 0); |
2023 Tcl_AllowExceptions(tclinfo.interp); | 1994 Tcl_AllowExceptions(tclinfo.interp); |
2024 err = Tcl_Eval(tclinfo.interp, script); | 1995 err = Tcl_Eval(tclinfo.interp, script); |
2025 if (err != TCL_OK) | 1996 if (err != TCL_OK |
1997 || Tcl_InterpDeleted(tclinfo.interp) | |
1998 #if (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 5) || TCL_MAJOR_VERSION > 8 | |
1999 || Tcl_LimitExceeded(tclinfo.interp) | |
2000 #endif | |
2001 ) | |
2026 break; | 2002 break; |
2027 line = (char *)Tcl_GetVar(tclinfo.interp, var_line, 0); | 2003 line = (char *)Tcl_GetVar(tclinfo.interp, var_line, 0); |
2028 if (line) | 2004 if (line) |
2029 { | 2005 { |
2030 if (ml_replace((linenr_T)rs, (char_u *)line, TRUE) != OK) | 2006 if (ml_replace((linenr_T)rs, (char_u *)line, TRUE) != OK) |