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[] = "@(#)tcl.c	8.16 (Berkeley) 10/16/96";
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 "tcl_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							\
47 	scr_msg = __global_list->scr_msg;				\
48 	__global_list->scr_msg = msghandler;
49 #define	ENDMESSAGE							\
50 	__global_list->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
tcl_fscreen(clientData,interp,argc,argv)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(Tcl_GetStringResult(interp), "%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
tcl_aline(clientData,interp,argc,argv)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;
108 	rval = api_aline(sp, (recno_t)lno, argv[3], strlen(argv[3]));
109 	ENDMESSAGE;
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
tcl_dline(clientData,interp,argc,argv)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;
142 	rval = api_dline(sp, (recno_t)lno);
143 	ENDMESSAGE;
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
tcl_gline(clientData,interp,argc,argv)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;
177 	rval = api_gline(sp, (recno_t)lno, &p, &len);
178 	ENDMESSAGE;
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
tcl_iline(clientData,interp,argc,argv)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;
219 	rval = api_iline(sp, (recno_t)lno, argv[3], strlen(argv[3]));
220 	ENDMESSAGE;
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
tcl_lline(clientData,interp,argc,argv)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 	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;
252 	rval = api_lline(sp, &last);
253 	ENDMESSAGE;
254 	if (rval)
255 		return (TCL_ERROR);
256 
257 	(void)sprintf(Tcl_GetStringResult(interp), "%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
tcl_sline(clientData,interp,argc,argv)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;
289 	rval = api_sline(sp, (recno_t)lno, argv[3], strlen(argv[3]));
290 	ENDMESSAGE;
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
tcl_getmark(clientData,interp,argc,argv)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;
325 	rval = api_getmark(sp, (int)argv[2][0], &cursor);
326 	ENDMESSAGE;
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
tcl_setmark(clientData,interp,argc,argv)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;
372 	rval = api_setmark(sp, (int)argv[2][0], &cursor);
373 	ENDMESSAGE;
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
tcl_getcursor(clientData,interp,argc,argv)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;
408 	rval = api_getcursor(sp, &cursor);
409 	ENDMESSAGE;
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
tcl_setcursor(clientData,interp,argc,argv)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;
455 	rval = api_setcursor(sp, &cursor);
456 	ENDMESSAGE;
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
tcl_msg(clientData,interp,argc,argv)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
tcl_iscreen(clientData,interp,argc,argv)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;
517 	rval = api_edit(sp, argv[2], &nsp, 1);
518 	ENDMESSAGE;
519 
520 	if (rval)
521 		return (TCL_ERROR);
522 
523 	(void)sprintf(Tcl_GetStringResult(interp), "%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
tcl_escreen(clientData,interp,argc,argv)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;
554 	rval = api_escreen(sp);
555 	ENDMESSAGE;
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
tcl_swscreen(clientData,interp,argc,argv)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;
590 	rval = api_swscreen(sp, new);
591 	ENDMESSAGE;
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
tcl_map(clientData,interp,argc,argv)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;
624 	(void)snprintf(command, sizeof(command), ":tcl %s\n", argv[3]);
625 	rval = api_map(sp, argv[2], command, strlen(command));
626 	ENDMESSAGE;
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
tcl_unmap(clientData,interp,argc,argv)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;
658 	rval = api_unmap(sp, argv[2]);
659 	ENDMESSAGE;
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
tcl_opts_set(clientData,interp,argc,argv)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;
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;
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
tcl_opts_get(clientData,interp,argc,argv)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;
731 	rval = api_opts_get(sp, argv[2], &value, NULL);
732 	ENDMESSAGE;
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
tcl_init(gp)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
getscreenid(interp,spp,id,name)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 recno_t and size_t are larger than ints.
816  */
817 static int
getint(interp,msg,s,intp)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
msghandler(sp,mtype,msg,len)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