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
17static 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 2014/01/26 21:43:45 christos 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
41static int  getint __P((Tcl_Interp *, char *, char *, int *));
42static int  getscreenid __P((Tcl_Interp *, SCR **, char *, char *));
43static void msghandler __P((SCR *, mtype_t, char *, size_t));
44
45extern 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 */
64static int
65tcl_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 */
92static int
93tcl_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 */
126static int
127tcl_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 */
160static int
161tcl_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 */
203static int
204tcl_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 */
237static int
238tcl_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 */
273static int
274tcl_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 */
308static int
309tcl_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 */
350static int
351tcl_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 */
391static int
392tcl_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 */
433static int
434tcl_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 */
473static int
474tcl_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 */
502static int
503tcl_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 */
539static int
540tcl_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 */
572static int
573tcl_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 */
608static int
609tcl_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 */
643static int
644tcl_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 */
676static int
677tcl_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 */
715static int
716tcl_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 */
751int
752tcl_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 */
795static int
796getscreenid(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 */
822static int
823getint(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 */
846static void
847msghandler(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