xref: /netbsd-src/external/bsd/nvi/dist/tcl_api/tcl.c (revision 2f698edb5c1cb2dcd9e762b0abb50c41dde8b6b7)
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 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 
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
tcl_fscreen(clientData,interp,argc,argv)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
tcl_aline(clientData,interp,argc,argv)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
tcl_dline(clientData,interp,argc,argv)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
tcl_gline(clientData,interp,argc,argv)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
tcl_iline(clientData,interp,argc,argv)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
tcl_lline(clientData,interp,argc,argv)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
tcl_sline(clientData,interp,argc,argv)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
tcl_getmark(clientData,interp,argc,argv)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
tcl_setmark(clientData,interp,argc,argv)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
tcl_getcursor(clientData,interp,argc,argv)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
tcl_setcursor(clientData,interp,argc,argv)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
tcl_msg(clientData,interp,argc,argv)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
tcl_iscreen(clientData,interp,argc,argv)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
tcl_escreen(clientData,interp,argc,argv)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
tcl_swscreen(clientData,interp,argc,argv)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
tcl_map(clientData,interp,argc,argv)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
tcl_unmap(clientData,interp,argc,argv)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
tcl_opts_set(clientData,interp,argc,argv)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
tcl_opts_get(clientData,interp,argc,argv)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
tcl_init(gp)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
getscreenid(interp,spp,id,name)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
getint(interp,msg,s,intp)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
msghandler(sp,mtype,msg,len)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