Home | History | Annotate | Line # | Download | only in tcl_api
tcl.c revision 1.3.4.2
      1 /*-
      2  * Copyright (c) 1992, 1993, 1994
      3  *	The Regents of the University of California.  All rights reserved.
      4  * Copyright (c) 1992, 1993, 1994, 1995
      5  *	Keith Bostic.  All rights reserved.
      6  * Copyright (c) 1995
      7  *	George V. Neville-Neil. All rights reserved.
      8  *
      9  * See the LICENSE file for redistribution information.
     10  */
     11 
     12 #include "config.h"
     13 
     14 #include <sys/cdefs.h>
     15 #if 0
     16 #ifndef lint
     17 static const char sccsid[] = "Id: tcl.c,v 8.19 2001/08/24 12:17:27 skimo Exp  (Berkeley) Date: 2001/08/24 12:17:27 ";
     18 #endif /* not lint */
     19 #else
     20 __RCSID("$NetBSD: tcl.c,v 1.3.4.2 2014/05/22 15:50:35 yamt Exp $");
     21 #endif
     22 
     23 #include <sys/types.h>
     24 #include <sys/queue.h>
     25 #include <sys/time.h>
     26 
     27 #include <bitstring.h>
     28 #include <errno.h>
     29 #include <limits.h>
     30 #include <signal.h>
     31 #include <stdio.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <tcl.h>
     35 #include <termios.h>
     36 #include <unistd.h>
     37 
     38 #include "../common/common.h"
     39 #include "tcl_api_extern.h"
     40 
     41 static int  getint __P((Tcl_Interp *, char *, char *, int *));
     42 static int  getscreenid __P((Tcl_Interp *, SCR **, char *, char *));
     43 static void msghandler __P((SCR *, mtype_t, char *, size_t));
     44 
     45 extern GS *__global_list;			/* XXX */
     46 
     47 /*
     48  * INITMESSAGE --
     49  *	Macros to point messages at the Tcl message handler.
     50  */
     51 #define	INITMESSAGE(sp)							\
     52 	scr_msg = sp->wp->scr_msg;					\
     53 	sp->wp->scr_msg = msghandler;
     54 #define	ENDMESSAGE(sp)							\
     55 	sp->wp->scr_msg = scr_msg;
     56 
     57 /*
     58  * tcl_fscreen --
     59  *	Return the screen id associated with file name.
     60  *
     61  * Tcl Command: viFindScreen
     62  * Usage: viFindScreen file
     63  */
     64 static int
     65 tcl_fscreen(clientData, interp, argc, argv)
     66 	ClientData clientData;
     67 	Tcl_Interp *interp;
     68 	int argc;
     69 	char **argv;
     70 {
     71 	SCR *sp;
     72 
     73 	if (argc != 2) {
     74 		Tcl_SetResult(interp, "Usage: viFindScreen file", TCL_STATIC);
     75 		return (TCL_ERROR);
     76 	}
     77 
     78 	if (getscreenid(interp, &sp, NULL, argv[1]))
     79 		return (TCL_ERROR);
     80 
     81 	(void)sprintf(interp->result, "%d", sp->id);
     82 	return (TCL_OK);
     83 }
     84 
     85 /*
     86  * tcl_aline --
     87  *	-- Append the string text after the line in lineNumber.
     88  *
     89  * Tcl Command: viAppendLine
     90  * Usage: viAppendLine screenId lineNumber text
     91  */
     92 static int
     93 tcl_aline(clientData, interp, argc, argv)
     94 	ClientData clientData;
     95 	Tcl_Interp *interp;
     96 	int argc;
     97 	char **argv;
     98 {
     99 	SCR *sp;
    100 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    101 	int lno, rval;
    102 
    103 	if (argc != 4) {
    104 		Tcl_SetResult(interp,
    105 		    "Usage: viAppendLine screenId lineNumber text", TCL_STATIC);
    106 		return (TCL_ERROR);
    107 	}
    108 
    109 	if (getscreenid(interp, &sp, argv[1], NULL) ||
    110 	    getint(interp, "line number", argv[2], &lno))
    111 		return (TCL_ERROR);
    112 	INITMESSAGE(sp);
    113 	rval = api_aline(sp, (db_recno_t)lno, argv[3], strlen(argv[3]));
    114 	ENDMESSAGE(sp);
    115 
    116 	return (rval ? TCL_ERROR : TCL_OK);
    117 }
    118 
    119 /*
    120  * tcl_dline --
    121  *	Delete lineNum.
    122  *
    123  * Tcl Command: viDelLine
    124  * Usage: viDelLine screenId lineNum
    125  */
    126 static int
    127 tcl_dline(clientData, interp, argc, argv)
    128 	ClientData clientData;
    129 	Tcl_Interp *interp;
    130 	int argc;
    131 	char **argv;
    132 {
    133 	SCR *sp;
    134 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    135 	int lno, rval;
    136 
    137 	if (argc != 3) {
    138 		Tcl_SetResult(interp,
    139 		    "Usage: viDelLine screenId lineNumber", TCL_STATIC);
    140 		return (TCL_ERROR);
    141 	}
    142 
    143 	if (getscreenid(interp, &sp, argv[1], NULL) ||
    144 	    getint(interp, "line number", argv[2], &lno))
    145 		return (TCL_ERROR);
    146 	INITMESSAGE(sp);
    147 	rval = api_dline(sp, (db_recno_t)lno);
    148 	ENDMESSAGE(sp);
    149 
    150 	return (rval ? TCL_ERROR : TCL_OK);
    151 }
    152 
    153 /*
    154  * tcl_gline --
    155  *	Return lineNumber.
    156  *
    157  * Tcl Command: viGetLine
    158  * Usage: viGetLine screenId lineNumber
    159  */
    160 static int
    161 tcl_gline(clientData, interp, argc, argv)
    162 	ClientData clientData;
    163 	Tcl_Interp *interp;
    164 	int argc;
    165 	char **argv;
    166 {
    167 	SCR *sp;
    168 	size_t len;
    169 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    170 	int lno, rval;
    171 	char *line, *p;
    172 
    173 	if (argc != 3) {
    174 		Tcl_SetResult(interp,
    175 		    "Usage: viGetLine screenId lineNumber", TCL_STATIC);
    176 		return (TCL_ERROR);
    177 	}
    178 	if (getscreenid(interp, &sp, argv[1], NULL) ||
    179 	    getint(interp, "line number", argv[2], &lno))
    180 		return (TCL_ERROR);
    181 	INITMESSAGE(sp);
    182 	rval = api_gline(sp, (db_recno_t)lno, &p, &len);
    183 	ENDMESSAGE(sp);
    184 
    185 	if (rval)
    186 		return (TCL_ERROR);
    187 
    188 	if ((line = malloc(len + 1)) == NULL)
    189 		exit(1);				/* XXX */
    190 	memmove(line, p, len);
    191 	line[len] = '\0';
    192 	Tcl_SetResult(interp, line, TCL_DYNAMIC);
    193 	return (TCL_OK);
    194 }
    195 
    196 /*
    197  * tcl_iline --
    198  *	Insert the string text after the line in lineNumber.
    199  *
    200  * Tcl Command: viInsertLine
    201  * Usage: viInsertLine screenId lineNumber text
    202  */
    203 static int
    204 tcl_iline(clientData, interp, argc, argv)
    205 	ClientData clientData;
    206 	Tcl_Interp *interp;
    207 	int argc;
    208 	char **argv;
    209 {
    210 	SCR *sp;
    211 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    212 	int lno, rval;
    213 
    214 	if (argc != 4) {
    215 		Tcl_SetResult(interp,
    216 		    "Usage: viInsertLine screenId lineNumber text", TCL_STATIC);
    217 		return (TCL_ERROR);
    218 	}
    219 
    220 	if (getscreenid(interp, &sp, argv[1], NULL) ||
    221 	    getint(interp, "line number", argv[2], &lno))
    222 		return (TCL_ERROR);
    223 	INITMESSAGE(sp);
    224 	rval = api_iline(sp, (db_recno_t)lno, argv[3], strlen(argv[3]));
    225 	ENDMESSAGE(sp);
    226 
    227 	return (rval ? TCL_ERROR : TCL_OK);
    228 }
    229 
    230 /*
    231  * tcl_lline --
    232  *	Return the last line in the screen.
    233  *
    234  * Tcl Command: viLastLine
    235  * Usage: viLastLine screenId
    236  */
    237 static int
    238 tcl_lline(clientData, interp, argc, argv)
    239 	ClientData clientData;
    240 	Tcl_Interp *interp;
    241 	int argc;
    242 	char **argv;
    243 {
    244 	SCR *sp;
    245 	db_recno_t last;
    246 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    247 	int rval;
    248 
    249 	if (argc != 2) {
    250 		Tcl_SetResult(interp, "Usage: viLastLine screenId", TCL_STATIC);
    251 		return (TCL_ERROR);
    252 	}
    253 
    254 	if (getscreenid(interp, &sp, argv[1], NULL))
    255 		return (TCL_ERROR);
    256 	INITMESSAGE(sp);
    257 	rval = api_lline(sp, &last);
    258 	ENDMESSAGE(sp);
    259 	if (rval)
    260 		return (TCL_ERROR);
    261 
    262 	(void)sprintf(interp->result, "%lu", (unsigned long)last);
    263 	return (TCL_OK);
    264 }
    265 
    266 /*
    267  * tcl_sline --
    268  *	Set lineNumber to the text supplied.
    269  *
    270  * Tcl Command: viSetLine
    271  * Usage: viSetLine screenId lineNumber text
    272  */
    273 static int
    274 tcl_sline(clientData, interp, argc, argv)
    275 	ClientData clientData;
    276 	Tcl_Interp *interp;
    277 	int argc;
    278 	char **argv;
    279 {
    280 	SCR *sp;
    281 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    282 	int lno, rval;
    283 
    284 	if (argc != 4) {
    285 		Tcl_SetResult(interp,
    286 		    "Usage: viSetLine screenId lineNumber text", TCL_STATIC);
    287 		return (TCL_ERROR);
    288 	}
    289 
    290 	if (getscreenid(interp, &sp, argv[1], NULL) ||
    291 	    getint(interp, "line number", argv[2], &lno))
    292 		return (TCL_ERROR);
    293 	INITMESSAGE(sp);
    294 	rval = api_sline(sp, (db_recno_t)lno, argv[3], strlen(argv[3]));
    295 	ENDMESSAGE(sp);
    296 
    297 	return (rval ? TCL_ERROR : TCL_OK);
    298 }
    299 
    300 /*
    301  * tcl_getmark --
    302  *	Return the mark's cursor position as a list with two elements.
    303  *	{line, column}.
    304  *
    305  * Tcl Command: viGetMark
    306  * Usage: viGetMark screenId mark
    307  */
    308 static int
    309 tcl_getmark(clientData, interp, argc, argv)
    310 	ClientData clientData;
    311 	Tcl_Interp *interp;
    312 	int argc;
    313 	char **argv;
    314 {
    315 	MARK cursor;
    316 	SCR *sp;
    317 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    318 	int rval;
    319 	char buf[20];
    320 
    321 	if (argc != 3) {
    322 		Tcl_SetResult(interp,
    323 		    "Usage: viGetMark screenId mark", TCL_STATIC);
    324 		return (TCL_ERROR);
    325 	}
    326 
    327 	if (getscreenid(interp, &sp, argv[1], NULL))
    328 		return (TCL_ERROR);
    329 	INITMESSAGE(sp);
    330 	rval = api_getmark(sp, (int)argv[2][0], &cursor);
    331 	ENDMESSAGE(sp);
    332 
    333 	if (rval)
    334 		return (TCL_ERROR);
    335 
    336 	(void)snprintf(buf, sizeof(buf), "%lu", (u_long)cursor.lno);
    337 	Tcl_AppendElement(interp, buf);
    338 	(void)snprintf(buf, sizeof(buf), "%lu", (u_long)cursor.cno);
    339 	Tcl_AppendElement(interp, buf);
    340 	return (TCL_OK);
    341 }
    342 
    343 /*
    344  * tcl_setmark --
    345  *	Set the mark to the line and column numbers supplied.
    346  *
    347  * Tcl Command: viSetMark
    348  * Usage: viSetMark screenId mark line column
    349  */
    350 static int
    351 tcl_setmark(clientData, interp, argc, argv)
    352 	ClientData clientData;
    353 	Tcl_Interp *interp;
    354 	int argc;
    355 	char **argv;
    356 {
    357 	MARK cursor;
    358 	SCR *sp;
    359 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    360 	int i, rval;
    361 
    362 	if (argc != 5) {
    363 		Tcl_SetResult(interp,
    364 		    "Usage: viSetMark screenId mark line column", TCL_STATIC);
    365 		return (TCL_ERROR);
    366 	}
    367 
    368 	if (getscreenid(interp, &sp, argv[1], NULL))
    369 		return (TCL_ERROR);
    370 	if (getint(interp, "line number", argv[3], &i))
    371 		return (TCL_ERROR);
    372 	cursor.lno = i;
    373 	if (getint(interp, "column number", argv[4], &i))
    374 		return (TCL_ERROR);
    375 	cursor.cno = i;
    376 	INITMESSAGE(sp);
    377 	rval = api_setmark(sp, (int)argv[2][0], &cursor);
    378 	ENDMESSAGE(sp);
    379 
    380 	return (rval ? TCL_ERROR : TCL_OK);
    381 }
    382 
    383 /*
    384  * tcl_getcursor --
    385  *	Return the current cursor position as a list with two elements.
    386  *	{line, column}.
    387  *
    388  * Tcl Command: viGetCursor
    389  * Usage: viGetCursor screenId
    390  */
    391 static int
    392 tcl_getcursor(clientData, interp, argc, argv)
    393 	ClientData clientData;
    394 	Tcl_Interp *interp;
    395 	int argc;
    396 	char **argv;
    397 {
    398 	MARK cursor;
    399 	SCR *sp;
    400 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    401 	int rval;
    402 	char buf[20];
    403 
    404 	if (argc != 2) {
    405 		Tcl_SetResult(interp,
    406 		    "Usage: viGetCursor screenId", TCL_STATIC);
    407 		return (TCL_ERROR);
    408 	}
    409 
    410 	if (getscreenid(interp, &sp, argv[1], NULL))
    411 		return (TCL_ERROR);
    412 	INITMESSAGE(sp);
    413 	rval = api_getcursor(sp, &cursor);
    414 	ENDMESSAGE(sp);
    415 
    416 	if (rval)
    417 		return (TCL_ERROR);
    418 
    419 	(void)snprintf(buf, sizeof(buf), "%lu", (u_long)cursor.lno);
    420 	Tcl_AppendElement(interp, buf);
    421 	(void)snprintf(buf, sizeof(buf), "%lu", (u_long)cursor.cno);
    422 	Tcl_AppendElement(interp, buf);
    423 	return (TCL_OK);
    424 }
    425 
    426 /*
    427  * tcl_setcursor --
    428  *	Set the cursor to the line and column numbers supplied.
    429  *
    430  * Tcl Command: viSetCursor
    431  * Usage: viSetCursor screenId line column
    432  */
    433 static int
    434 tcl_setcursor(clientData, interp, argc, argv)
    435 	ClientData clientData;
    436 	Tcl_Interp *interp;
    437 	int argc;
    438 	char **argv;
    439 {
    440 	MARK cursor;
    441 	SCR *sp;
    442 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    443 	int i, rval;
    444 
    445 	if (argc != 4) {
    446 		Tcl_SetResult(interp,
    447 		    "Usage: viSetCursor screenId line column", TCL_STATIC);
    448 		return (TCL_ERROR);
    449 	}
    450 
    451 	if (getscreenid(interp, &sp, argv[1], NULL))
    452 		return (TCL_ERROR);
    453 	if (getint(interp, "screen id", argv[2], &i))
    454 		return (TCL_ERROR);
    455 	cursor.lno = i;
    456 	if (getint(interp, "screen id", argv[3], &i))
    457 		return (TCL_ERROR);
    458 	cursor.cno = i;
    459 	INITMESSAGE(sp);
    460 	rval = api_setcursor(sp, &cursor);
    461 	ENDMESSAGE(sp);
    462 
    463 	return (rval ? TCL_ERROR : TCL_OK);
    464 }
    465 
    466 /*
    467  * tcl_msg --
    468  *	Set the message line to text.
    469  *
    470  * Tcl Command: viMsg
    471  * Usage: viMsg screenId text
    472  */
    473 static int
    474 tcl_msg(clientData, interp, argc, argv)
    475 	ClientData clientData;
    476 	Tcl_Interp *interp;
    477 	int argc;
    478 	char **argv;
    479 {
    480 	SCR *sp;
    481 
    482 	if (argc != 3) {
    483 		Tcl_SetResult(interp, "Usage: viMsg screenId text", TCL_STATIC);
    484 		return (TCL_ERROR);
    485 	}
    486 
    487 	if (getscreenid(interp, &sp, argv[1], NULL))
    488 		return (TCL_ERROR);
    489 	api_imessage(sp, argv[2]);
    490 
    491 	return (TCL_OK);
    492 }
    493 
    494 /*
    495  * tcl_iscreen --
    496  *	Create a new screen.  If a filename is specified then the screen
    497  *	is opened with that file.
    498  *
    499  * Tcl Command: viNewScreen
    500  * Usage: viNewScreen screenId [file]
    501  */
    502 static int
    503 tcl_iscreen(clientData, interp, argc, argv)
    504 	ClientData clientData;
    505 	Tcl_Interp *interp;
    506 	int argc;
    507 	char **argv;
    508 {
    509 	SCR *sp, *nsp;
    510 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    511 	int rval;
    512 
    513 	if (argc != 2 && argc != 3) {
    514 		Tcl_SetResult(interp,
    515 		    "Usage: viNewScreen screenId [file]", TCL_STATIC);
    516 		return (TCL_ERROR);
    517 	}
    518 
    519 	if (getscreenid(interp, &sp, argv[1], NULL))
    520 		return (TCL_ERROR);
    521 	INITMESSAGE(sp);
    522 	rval = api_edit(sp, argv[2], &nsp, 1);
    523 	ENDMESSAGE(sp);
    524 
    525 	if (rval)
    526 		return (TCL_ERROR);
    527 
    528 	(void)sprintf(interp->result, "%d", nsp->id);
    529 	return (TCL_OK);
    530 }
    531 
    532 /*
    533  * tcl_escreen --
    534  *	End a screen.
    535  *
    536  * Tcl Command: viEndScreen
    537  * Usage: viEndScreen screenId
    538  */
    539 static int
    540 tcl_escreen(clientData, interp, argc, argv)
    541 	ClientData clientData;
    542 	Tcl_Interp *interp;
    543 	int argc;
    544 	char **argv;
    545 {
    546 	SCR *sp;
    547 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    548 	int rval;
    549 
    550 	if (argc != 2) {
    551 		Tcl_SetResult(interp,
    552 		     "Usage: viEndScreen screenId", TCL_STATIC);
    553 		return (TCL_ERROR);
    554 	}
    555 
    556 	if (getscreenid(interp, &sp, argv[1], NULL))
    557 		return (TCL_ERROR);
    558 	INITMESSAGE(sp);
    559 	rval = api_escreen(sp);
    560 	ENDMESSAGE(sp);
    561 
    562 	return (rval ? TCL_ERROR : TCL_OK);
    563 }
    564 
    565 /*
    566  * tcl_swscreen --
    567  *	Change the current focus to screen.
    568  *
    569  * Tcl Command: viSwitchScreen
    570  * Usage: viSwitchScreen screenId screenId
    571  */
    572 static int
    573 tcl_swscreen(clientData, interp, argc, argv)
    574 	ClientData clientData;
    575 	Tcl_Interp *interp;
    576 	int argc;
    577 	char **argv;
    578 {
    579 	SCR *sp, *new;
    580 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    581 	int rval;
    582 
    583 	if (argc != 3) {
    584 		Tcl_SetResult(interp,
    585 		    "Usage: viSwitchScreen cur_screenId new_screenId",
    586 		    TCL_STATIC);
    587 		return (TCL_ERROR);
    588 	}
    589 
    590 	if (getscreenid(interp, &sp, argv[1], NULL))
    591 		return (TCL_ERROR);
    592 	if (getscreenid(interp, &new, argv[2], NULL))
    593 		return (TCL_ERROR);
    594 	INITMESSAGE(sp);
    595 	rval = api_swscreen(sp, new);
    596 	ENDMESSAGE(sp);
    597 
    598 	return (rval ? TCL_ERROR : TCL_OK);
    599 }
    600 
    601 /*
    602  * tcl_map --
    603  *	Associate a key with a tcl procedure.
    604  *
    605  * Tcl Command: viMapKey
    606  * Usage: viMapKey screenId key tclproc
    607  */
    608 static int
    609 tcl_map(clientData, interp, argc, argv)
    610 	ClientData clientData;
    611 	Tcl_Interp *interp;
    612 	int argc;
    613 	char **argv;
    614 {
    615 	SCR *sp;
    616 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    617 	int rval;
    618 	char command[256];
    619 
    620 	if (argc != 4) {
    621 		Tcl_SetResult(interp,
    622 		    "Usage: viMapKey screenId key tclproc", TCL_STATIC);
    623 		return (TCL_ERROR);
    624 	}
    625 
    626 	if (getscreenid(interp, &sp, argv[1], NULL))
    627 		return (TCL_ERROR);
    628 	INITMESSAGE(sp);
    629 	(void)snprintf(command, sizeof(command), ":tcl %s\n", argv[3]);
    630 	rval = api_map(sp, argv[2], command, strlen(command));
    631 	ENDMESSAGE(sp);
    632 
    633 	return (rval ? TCL_ERROR : TCL_OK);
    634 }
    635 
    636 /*
    637  * tcl_unmap --
    638  *	Unmap a key.
    639  *
    640  * Tcl Command: viUnmapKey
    641  * Usage: viUnmMapKey screenId key
    642  */
    643 static int
    644 tcl_unmap(clientData, interp, argc, argv)
    645 	ClientData clientData;
    646 	Tcl_Interp *interp;
    647 	int argc;
    648 	char **argv;
    649 {
    650 	SCR *sp;
    651 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    652 	int rval;
    653 
    654 	if (argc != 3) {
    655 		Tcl_SetResult(interp,
    656 		    "Usage: viUnmapKey screenId key", TCL_STATIC);
    657 		return (TCL_ERROR);
    658 	}
    659 
    660 	if (getscreenid(interp, &sp, argv[1], NULL))
    661 		return (TCL_ERROR);
    662 	INITMESSAGE(sp);
    663 	rval = api_unmap(sp, argv[2]);
    664 	ENDMESSAGE(sp);
    665 
    666 	return (rval ? TCL_ERROR : TCL_OK);
    667 }
    668 
    669 /*
    670  * tcl_opts_set --
    671  *	Set an option.
    672  *
    673  * Tcl Command: viSetOpt
    674  * Usage: viSetOpt screenId command
    675  */
    676 static int
    677 tcl_opts_set(clientData, interp, argc, argv)
    678 	ClientData clientData;
    679 	Tcl_Interp *interp;
    680 	int argc;
    681 	char **argv;
    682 {
    683 	SCR *sp;
    684 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    685 	int rval;
    686 	char *setting;
    687 
    688 	if (argc != 3) {
    689 		Tcl_SetResult(interp,
    690 		    "Usage: viSetOpt screenId command", TCL_STATIC);
    691 		return (TCL_ERROR);
    692 	}
    693 
    694 	if (getscreenid(interp, &sp, argv[1], NULL))
    695 		return (TCL_ERROR);
    696 	INITMESSAGE(sp);
    697 	/*rval = api_opts_set(sp, argv[2]);*/
    698 	MALLOC(sp, setting, char *, strlen(argv[2])+6);
    699 	strcpy(setting, ":set ");
    700 	strcpy(setting+5, argv[2]);
    701 	rval=api_run_str(sp, setting);
    702 	free(setting);
    703 	ENDMESSAGE(sp);
    704 
    705 	return (rval ? TCL_ERROR : TCL_OK);
    706 }
    707 
    708 /*
    709  * tcl_opts_get --
    710  	Return the value of an option.
    711  *
    712  * Tcl Command: viGetOpt
    713  * Usage: viGetOpt screenId option
    714  */
    715 static int
    716 tcl_opts_get(clientData, interp, argc, argv)
    717 	ClientData clientData;
    718 	Tcl_Interp *interp;
    719 	int argc;
    720 	char **argv;
    721 {
    722 	SCR *sp;
    723 	void (*scr_msg) __P((SCR *, mtype_t, char *, size_t));
    724 	int rval;
    725 	char *value;
    726 
    727 	if (argc != 3) {
    728 		Tcl_SetResult(interp,
    729 		    "Usage: viGetOpt screenId option", TCL_STATIC);
    730 		return (TCL_ERROR);
    731 	}
    732 
    733 	if (getscreenid(interp, &sp, argv[1], NULL))
    734 		return (TCL_ERROR);
    735 	INITMESSAGE(sp);
    736 	rval = api_opts_get(sp, argv[2], &value, NULL);
    737 	ENDMESSAGE(sp);
    738 	if (rval)
    739 		return (TCL_ERROR);
    740 
    741 	Tcl_SetResult(interp, value, TCL_DYNAMIC);
    742 	return (TCL_OK);
    743 }
    744 
    745 /*
    746  * tcl_init --
    747  *	Create the TCL commands used by nvi.
    748  *
    749  * PUBLIC: int tcl_init __P((GS *));
    750  */
    751 int
    752 tcl_init(gp)
    753 	GS *gp;
    754 {
    755 	gp->tcl_interp = Tcl_CreateInterp();
    756 	if (Tcl_Init(gp->tcl_interp) == TCL_ERROR)
    757 		return (1);
    758 
    759 #define	TCC(name, function) {						\
    760 	Tcl_CreateCommand(gp->tcl_interp, name, function,		\
    761 	    (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);		\
    762 }
    763 	TCC("viAppendLine", tcl_aline);
    764 	TCC("viDelLine", tcl_dline);
    765 	TCC("viEndScreen", tcl_escreen);
    766 	TCC("viFindScreen", tcl_fscreen);
    767 	TCC("viGetCursor", tcl_getcursor);
    768 	TCC("viGetLine", tcl_gline);
    769 	TCC("viGetMark", tcl_getmark);
    770 	TCC("viGetOpt", tcl_opts_get);
    771 	TCC("viInsertLine", tcl_iline);
    772 	TCC("viLastLine", tcl_lline);
    773 	TCC("viMapKey", tcl_map);
    774 	TCC("viMsg", tcl_msg);
    775 	TCC("viNewScreen", tcl_iscreen);
    776 	TCC("viSetCursor", tcl_setcursor);
    777 	TCC("viSetLine", tcl_sline);
    778 	TCC("viSetMark", tcl_setmark);
    779 	TCC("viSetOpt", tcl_opts_set);
    780 	TCC("viSwitchScreen", tcl_swscreen);
    781 	TCC("viUnmapKey", tcl_unmap);
    782 
    783 	return (0);
    784 }
    785 
    786 /*
    787  * getscreenid --
    788  *	Get the specified screen pointer.
    789  *
    790  * XXX
    791  * This is fatal.  We can't post a message into vi that we're unable to find
    792  * the screen without first finding the screen... So, this must be the first
    793  * thing a Tcl routine does, and, if it fails, the last as well.
    794  */
    795 static int
    796 getscreenid(interp, spp, id, name)
    797 	Tcl_Interp *interp;
    798 	SCR **spp;
    799 	char *id, *name;
    800 {
    801 	int scr_no;
    802 	char buf[64];
    803 
    804 	if (id != NULL && getint(interp, "screen id", id, &scr_no))
    805 		return (1);
    806 	if ((*spp = api_fscreen(scr_no, name)) == NULL) {
    807 		(void)snprintf(buf, sizeof(buf),
    808 		    "unknown screen id: %s", name == NULL ? id : name);
    809 		Tcl_SetResult(interp, buf, TCL_VOLATILE);
    810 		return (1);
    811 	}
    812 	return (0);
    813 }
    814 
    815 /*
    816  * getint --
    817  *	Get a Tcl integer.
    818  *
    819  * XXX
    820  * This code assumes that both db_recno_t and size_t are larger than ints.
    821  */
    822 static int
    823 getint(interp, msg, s, intp)
    824 	Tcl_Interp *interp;
    825 	char *msg, *s;
    826 	int *intp;
    827 {
    828 	char buf[64];
    829 
    830 	if (Tcl_GetInt(interp, s, intp) == TCL_ERROR)
    831 		return (1);
    832 	if (*intp < 0) {
    833 		(void)snprintf(buf, sizeof(buf),
    834 		    "illegal %s %s: may not be negative", msg, s);
    835 		Tcl_SetResult(interp, buf, TCL_VOLATILE);
    836 		return (1);
    837 	}
    838 	return (0);
    839 }
    840 
    841 /*
    842  * msghandler --
    843  *	Tcl message routine so that error messages are processed in
    844  *	Tcl, not in nvi.
    845  */
    846 static void
    847 msghandler(sp, mtype, msg, len)
    848 	SCR *sp;
    849 	mtype_t mtype;
    850 	char *msg;
    851 	size_t len;
    852 {
    853 	/* Replace the trailing <newline> with an EOS. */
    854 	msg[len - 1] = '\0';
    855 
    856 	Tcl_SetResult(sp->gp->tcl_interp, msg, TCL_VOLATILE);
    857 }
    858