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