terminal.c revision 1574
1/*-
2 * Copyright (c) 1992, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Christos Zoulas of Cornell University.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#if !defined(lint) && !defined(SCCSID)
38static char sccsid[] = "@(#)term.c	8.1 (Berkeley) 6/4/93";
39#endif /* not lint && not SCCSID */
40
41/*
42 * term.c: Editor/termcap-curses interface
43 *	   We have to declare a static variable here, since the
44 *	   termcap putchar routine does not take an argument!
45 */
46#include "sys.h"
47#include <stdio.h>
48#include <signal.h>
49#include <string.h>
50#include <stdlib.h>
51#include <unistd.h>
52#include "termcap.h"	/* XXX: should be <termcap.h> */
53#include <sys/types.h>
54
55#include "el.h"
56
57/*
58 * IMPORTANT NOTE: these routines are allowed to look at the current screen
59 * and the current possition assuming that it is correct.  If this is not
60 * true, then the update will be WRONG!  This is (should be) a valid
61 * assumption...
62 */
63
64#define TC_BUFSIZE 2048
65
66#define GoodStr(a) (el->el_term.t_str[a] != NULL && \
67		    el->el_term.t_str[a][0] != '\0')
68#define Str(a) el->el_term.t_str[a]
69#define Val(a) el->el_term.t_val[a]
70
71private struct {
72    char   *b_name;
73    int     b_rate;
74} baud_rate[] = {
75#ifdef B0
76    { "0", B0 },
77#endif
78#ifdef B50
79    { "50", B50 },
80#endif
81#ifdef B75
82    { "75", B75 },
83#endif
84#ifdef B110
85    { "110", B110 },
86#endif
87#ifdef B134
88    { "134", B134 },
89#endif
90#ifdef B150
91    { "150", B150 },
92#endif
93#ifdef B200
94    { "200", B200 },
95#endif
96#ifdef B300
97    { "300", B300 },
98#endif
99#ifdef B600
100    { "600", B600 },
101#endif
102#ifdef B900
103    { "900", B900 },
104#endif
105#ifdef B1200
106    { "1200", B1200 },
107#endif
108#ifdef B1800
109    { "1800", B1800 },
110#endif
111#ifdef B2400
112    { "2400", B2400 },
113#endif
114#ifdef B3600
115    { "3600", B3600 },
116#endif
117#ifdef B4800
118    { "4800", B4800 },
119#endif
120#ifdef B7200
121    { "7200", B7200 },
122#endif
123#ifdef B9600
124    { "9600", B9600 },
125#endif
126#ifdef EXTA
127    { "19200", EXTA },
128#endif
129#ifdef B19200
130    { "19200", B19200 },
131#endif
132#ifdef EXTB
133    { "38400", EXTB },
134#endif
135#ifdef B38400
136    { "38400", B38400 },
137#endif
138    { NULL, 0 }
139};
140
141private struct termcapstr {
142    char   *name;
143    char   *long_name;
144} tstr[] = {
145
146#define T_al	0
147    {	"al",	"add new blank line"		},
148#define T_bl	1
149    {	"bl",	"audible bell"			},
150#define T_cd	2
151    {	"cd",	"clear to bottom"		},
152#define T_ce	3
153    {	"ce",	"clear to end of line"		},
154#define T_ch	4
155    {	"ch",	"cursor to horiz pos"		},
156#define T_cl	5
157    {	"cl",	"clear screen"			},
158#define	T_dc	6
159    {	"dc",	"delete a character"		},
160#define	T_dl	7
161    {	"dl",	"delete a line"		 	},
162#define	T_dm	8
163    {	"dm",	"start delete mode"		},
164#define	T_ed	9
165    {	"ed",	"end delete mode"		},
166#define	T_ei	10
167    {	"ei",	"end insert mode"		},
168#define	T_fs	11
169    {	"fs",	"cursor from status line"	},
170#define	T_ho	12
171    {	"ho",	"home cursor"			},
172#define	T_ic	13
173    {	"ic",	"insert character"		},
174#define	T_im	14
175    {	"im",	"start insert mode"		},
176#define	T_ip	15
177    {	"ip",	"insert padding"		},
178#define	T_kd	16
179    {	"kd",	"sends cursor down"		},
180#define	T_kl	17
181    {	"kl",	"sends cursor left"		},
182#define T_kr	18
183    {	"kr",	"sends cursor right"		},
184#define T_ku	19
185    {	"ku",	"sends cursor up"		},
186#define T_md	20
187    {	"md",	"begin bold"			},
188#define T_me	21
189    {	"me",	"end attributes"		},
190#define T_nd	22
191    {	"nd",	"non destructive space"	 	},
192#define T_se	23
193    {	"se",	"end standout"			},
194#define T_so	24
195    {	"so",	"begin standout"		},
196#define T_ts	25
197    {	"ts",	"cursor to status line"	 	},
198#define T_up	26
199    {	"up",	"cursor up one"		 	},
200#define T_us	27
201    {	"us",	"begin underline"		},
202#define T_ue	28
203    {	"ue",	"end underline"		 	},
204#define T_vb	29
205    {	"vb",	"visible bell"			},
206#define T_DC	30
207    {	"DC",	"delete multiple chars"	 	},
208#define T_DO	31
209    {	"DO",	"cursor down multiple"		},
210#define T_IC	32
211    {	"IC",	"insert multiple chars"	 	},
212#define T_LE	33
213    {	"LE",	"cursor left multiple"		},
214#define T_RI	34
215    {	"RI",	"cursor right multiple"	 	},
216#define T_UP	35
217    {	"UP",	"cursor up multiple"		},
218#define T_str	36
219    {	NULL,   NULL			 	}
220};
221
222private struct termcapval {
223    char   *name;
224    char   *long_name;
225} tval[] = {
226#define T_pt	0
227    {	"pt",	"has physical tabs"	},
228#define T_li	1
229    {	"li",	"Number of lines"	},
230#define T_co	2
231    {	"co",	"Number of columns"	},
232#define T_km	3
233    {	"km",	"Has meta key"		},
234#define T_xt	4
235    {	"xt",	"Tab chars destructive" },
236#define T_MT	5
237    {	"MT",	"Has meta key"		},	/* XXX? */
238#define T_val	6
239    {	NULL, 	NULL,			}
240};
241
242/* do two or more of the attributes use me */
243
244private	void	term_rebuffer_display	__P((EditLine *));
245private	void	term_free_display	__P((EditLine *));
246private	void	term_alloc_display	__P((EditLine *));
247private	void	term_alloc		__P((EditLine *,
248					     struct termcapstr *, char *));
249private void	term_init_arrow		__P((EditLine *));
250private void	term_reset_arrow	__P((EditLine *));
251
252
253private FILE *term_outfile = NULL;	/* XXX: How do we fix that? */
254
255
256/* term_setflags():
257 *	Set the terminal capability flags
258 */
259private void
260term_setflags(el)
261    EditLine *el;
262{
263    EL_FLAGS = 0;
264    if (el->el_tty.t_tabs)
265	EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0;
266
267    EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0;
268    EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0;
269    EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0;
270    EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ?
271		 TERM_CAN_INSERT : 0;
272    EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP))  ? TERM_CAN_UP : 0;
273
274    if (GoodStr(T_me) && GoodStr(T_ue))
275	EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ? TERM_CAN_ME : 0;
276    else
277	EL_FLAGS &= ~TERM_CAN_ME;
278    if (GoodStr(T_me) && GoodStr(T_se))
279	EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ? TERM_CAN_ME : 0;
280
281
282#ifdef DEBUG_SCREEN
283    if (!EL_CAN_UP) {
284	(void) fprintf(el->el_errfile, "WARNING: Your terminal cannot move up.\n");
285	(void) fprintf(el->el_errfile, "Editing may be odd for long lines.\n");
286    }
287    if (!EL_CAN_CEOL)
288	(void) fprintf(el->el_errfile, "no clear EOL capability.\n");
289    if (!EL_CAN_DELETE)
290	(void) fprintf(el->el_errfile, "no delete char capability.\n");
291    if (!EL_CAN_INSERT)
292	(void) fprintf(el->el_errfile, "no insert char capability.\n");
293#endif /* DEBUG_SCREEN */
294}
295
296
297/* term_init():
298 *	Initialize the terminal stuff
299 */
300protected int
301term_init(el)
302    EditLine *el;
303{
304    el->el_term.t_buf = (char *)  el_malloc(TC_BUFSIZE);
305    el->el_term.t_cap = (char *)  el_malloc(TC_BUFSIZE);
306    el->el_term.t_fkey = (fkey_t *) el_malloc(4 * sizeof(fkey_t));
307    el->el_term.t_loc = 0;
308    el->el_term.t_str = (char **) el_malloc(T_str * sizeof(char*));
309    (void) memset(el->el_term.t_str, 0, T_str * sizeof(char*));
310    el->el_term.t_val = (int *)   el_malloc(T_val * sizeof(int));
311    (void) memset(el->el_term.t_val, 0, T_val * sizeof(char*));
312    term_outfile = el->el_outfile;
313    (void) term_set(el, NULL);
314    term_init_arrow(el);
315    return 0;
316}
317
318/* term_end():
319 *	Clean up the terminal stuff
320 */
321protected void
322term_end(el)
323    EditLine *el;
324{
325    el_free((ptr_t) el->el_term.t_buf);
326    el->el_term.t_buf = NULL;
327    el_free((ptr_t) el->el_term.t_cap);
328    el->el_term.t_cap = NULL;
329    el->el_term.t_loc = 0;
330    el_free((ptr_t) el->el_term.t_str);
331    el->el_term.t_str = NULL;
332    el_free((ptr_t) el->el_term.t_val);
333    el->el_term.t_val = NULL;
334    term_free_display(el);
335}
336
337
338/* term_alloc():
339 *	Maintain a string pool for termcap strings
340 */
341private void
342term_alloc(el, t, cap)
343    EditLine *el;
344    struct termcapstr *t;
345    char   *cap;
346{
347    char    termbuf[TC_BUFSIZE];
348    int     tlen, clen;
349    char    **tlist = el->el_term.t_str;
350    char    **tmp, **str = &tlist[t - tstr];
351
352    if (cap == NULL || *cap == '\0') {
353	*str = NULL;
354	return;
355    }
356    else
357	clen = strlen(cap);
358
359    tlen  = *str == NULL ? 0 : strlen(*str);
360
361    /*
362     * New string is shorter; no need to allocate space
363     */
364    if (clen <= tlen) {
365	(void) strcpy(*str, cap);
366	return;
367    }
368
369    /*
370     * New string is longer; see if we have enough space to append
371     */
372    if (el->el_term.t_loc + 3 < TC_BUFSIZE) {
373	(void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap);
374	el->el_term.t_loc += clen + 1;	/* one for \0 */
375	return;
376    }
377
378    /*
379     * Compact our buffer; no need to check compaction, cause we know it
380     * fits...
381     */
382    tlen = 0;
383    for (tmp = tlist; tmp < &tlist[T_str]; tmp++)
384	if (*tmp != NULL && *tmp != '\0' && *tmp != *str) {
385	    char   *ptr;
386
387	    for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++)
388		continue;
389	    termbuf[tlen++] = '\0';
390	}
391    memcpy(el->el_term.t_buf, termbuf, TC_BUFSIZE);
392    el->el_term.t_loc = tlen;
393    if (el->el_term.t_loc + 3 >= TC_BUFSIZE) {
394	(void) fprintf(el->el_errfile, "Out of termcap string space.\n");
395	return;
396    }
397    (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap);
398    el->el_term.t_loc += clen + 1;		/* one for \0 */
399    return;
400} /* end term_alloc */
401
402
403/* term_rebuffer_display():
404 *	Rebuffer the display after the screen changed size
405 */
406private void
407term_rebuffer_display(el)
408    EditLine *el;
409{
410    coord_t *c = &el->el_term.t_size;
411
412    term_free_display(el);
413
414    /* make this public, -1 to avoid wraps */
415    c->h = Val(T_co) - 1;
416    c->v = (EL_BUFSIZ * 4) / c->h + 1;
417
418    term_alloc_display(el);
419} /* end term_rebuffer_display */
420
421
422/* term_alloc_display():
423 *	Allocate a new display.
424 */
425private void
426term_alloc_display(el)
427    EditLine *el;
428{
429    int i;
430    char  **b;
431    coord_t *c = &el->el_term.t_size;
432
433    b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
434    for (i = 0; i < c->v; i++)
435	b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
436    b[c->v] = NULL;
437    el->el_display = b;
438
439    b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
440    for (i = 0; i < c->v; i++)
441	b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
442    b[c->v] = NULL;
443    el->el_vdisplay = b;
444
445} /* end term_alloc_display */
446
447
448/* term_free_display():
449 *	Free the display buffers
450 */
451private void
452term_free_display(el)
453    EditLine *el;
454{
455    char  **b;
456    char  **bufp;
457
458    b = el->el_display;
459    el->el_display = NULL;
460    if (b != NULL) {
461	for (bufp = b; *bufp != NULL; bufp++)
462	    el_free((ptr_t) *bufp);
463	el_free((ptr_t) b);
464    }
465    b = el->el_vdisplay;
466    el->el_vdisplay = NULL;
467    if (b != NULL) {
468	for (bufp = b; *bufp != NULL; bufp++)
469	    el_free((ptr_t) * bufp);
470	el_free((ptr_t) b);
471    }
472} /* end term_free_display */
473
474
475/* term_move_to_line():
476 *	move to line <where> (first line == 0)
477 * 	as efficiently as possible
478 */
479protected void
480term_move_to_line(el, where)
481    EditLine *el;
482    int     where;
483{
484    int     del, i;
485
486    if (where == el->el_cursor.v)
487	return;
488
489    if (where > el->el_term.t_size.v) {
490#ifdef DEBUG_SCREEN
491	(void) fprintf(el->el_errfile,
492		"term_move_to_line: where is ridiculous: %d\r\n", where);
493#endif /* DEBUG_SCREEN */
494	return;
495    }
496
497    if ((del = where - el->el_cursor.v) > 0) {
498	if ((del > 1) && GoodStr(T_DO))
499	    (void) tputs(tgoto(Str(T_DO), del, del), del, term__putc);
500	else {
501	    for (i = 0; i < del; i++)
502		term__putc('\n');
503	    el->el_cursor.h = 0;	/* because the \n will become \r\n */
504	}
505    }
506    else {			/* del < 0 */
507	if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up)))
508	    (void) tputs(tgoto(Str(T_UP), -del, -del), -del, term__putc);
509	else {
510	    if (GoodStr(T_up))
511		for (i = 0; i < -del; i++)
512		    (void) tputs(Str(T_up), 1, term__putc);
513	}
514    }
515    el->el_cursor.v = where;		/* now where is here */
516} /* end term_move_to_line */
517
518
519/* term_move_to_char():
520 *	Move to the character position specified
521 */
522protected void
523term_move_to_char(el, where)
524    EditLine *el;
525    int     where;
526{
527    int     del, i;
528
529mc_again:
530    if (where == el->el_cursor.h)
531	return;
532
533    if (where > (el->el_term.t_size.h + 1)) {
534#ifdef DEBUG_SCREEN
535	(void) fprintf(el->el_errfile,
536		"term_move_to_char: where is riduculous: %d\r\n", where);
537#endif /* DEBUG_SCREEN */
538	return;
539    }
540
541    if (!where) {		/* if where is first column */
542	term__putc('\r');	/* do a CR */
543	el->el_cursor.h = 0;
544	return;
545    }
546
547    del = where - el->el_cursor.h;
548
549    if ((del < -4 || del > 4) && GoodStr(T_ch))
550	/* go there directly */
551	(void) tputs(tgoto(Str(T_ch), where, where), where, term__putc);
552    else {
553	if (del > 0) {		/* moving forward */
554	    if ((del > 4) && GoodStr(T_RI))
555		(void) tputs(tgoto(Str(T_RI), del, del), del, term__putc);
556	    else {
557		if (EL_CAN_TAB) {	/* if I can do tabs, use them */
558		    if ((el->el_cursor.h & 0370) != (where & 0370)) {
559			/* if not within tab stop */
560			for (i = (el->el_cursor.h & 0370);
561			     i < (where & 0370); i += 8)
562			    term__putc('\t');	/* then tab over */
563			el->el_cursor.h = where & 0370;
564		    }
565		}
566		/* it's usually cheaper to just write the chars, so we do. */
567
568		/* NOTE THAT term_overwrite() WILL CHANGE el->el_cursor.h!!! */
569		term_overwrite(el,
570			&el->el_display[el->el_cursor.v][el->el_cursor.h],
571		        where - el->el_cursor.h);
572
573	    }
574	}
575	else {			/* del < 0 := moving backward */
576	    if ((-del > 4) && GoodStr(T_LE))
577		(void) tputs(tgoto(Str(T_LE), -del, -del), -del, term__putc);
578	    else {		/* can't go directly there */
579		/* if the "cost" is greater than the "cost" from col 0 */
580		if (EL_CAN_TAB ? (-del > ((where >> 3) + (where & 07)))
581		    : (-del > where)) {
582		    term__putc('\r');	/* do a CR */
583		    el->el_cursor.h = 0;
584		    goto mc_again;	/* and try again */
585		}
586		for (i = 0; i < -del; i++)
587		    term__putc('\b');
588	    }
589	}
590    }
591    el->el_cursor.h = where;		/* now where is here */
592} /* end term_move_to_char */
593
594
595/* term_overwrite():
596 *	Overstrike num characters
597 */
598protected void
599term_overwrite(el, cp, n)
600    EditLine *el;
601    char *cp;
602    int n;
603{
604    if (n <= 0)
605	return;			/* catch bugs */
606
607    if (n > (el->el_term.t_size.h + 1)) {
608#ifdef DEBUG_SCREEN
609	(void) fprintf(el->el_errfile, "term_overwrite: n is riduculous: %d\r\n", n);
610#endif /* DEBUG_SCREEN */
611	return;
612    }
613
614    do {
615	term__putc(*cp++);
616	el->el_cursor.h++;
617    } while (--n);
618} /* end term_overwrite */
619
620
621/* term_deletechars():
622 *	Delete num characters
623 */
624protected void
625term_deletechars(el, num)
626    EditLine *el;
627    int     num;
628{
629    if (num <= 0)
630	return;
631
632    if (!EL_CAN_DELETE) {
633#ifdef DEBUG_EDIT
634	(void) fprintf(el->el_errfile, "   ERROR: cannot delete   \n");
635#endif /* DEBUG_EDIT */
636	return;
637    }
638
639    if (num > el->el_term.t_size.h) {
640#ifdef DEBUG_SCREEN
641	(void) fprintf(el->el_errfile,
642		"term_deletechars: num is riduculous: %d\r\n", num);
643#endif /* DEBUG_SCREEN */
644	return;
645    }
646
647    if (GoodStr(T_DC))		/* if I have multiple delete */
648	if ((num > 1) || !GoodStr(T_dc)) {	/* if dc would be more expen. */
649	    (void) tputs(tgoto(Str(T_DC), num, num), num, term__putc);
650	    return;
651	}
652
653    if (GoodStr(T_dm))		/* if I have delete mode */
654	(void) tputs(Str(T_dm), 1, term__putc);
655
656    if (GoodStr(T_dc))		/* else do one at a time */
657	while (num--)
658	    (void) tputs(Str(T_dc), 1, term__putc);
659
660    if (GoodStr(T_ed))		/* if I have delete mode */
661	(void) tputs(Str(T_ed), 1, term__putc);
662} /* end term_deletechars */
663
664
665/* term_insertwrite():
666 *	Puts terminal in insert character mode or inserts num
667 *	characters in the line
668 */
669protected void
670term_insertwrite(el, cp, num)
671    EditLine *el;
672    char *cp;
673    int num;
674{
675    if (num <= 0)
676	return;
677    if (!EL_CAN_INSERT) {
678#ifdef DEBUG_EDIT
679	(void) fprintf(el->el_errfile, "   ERROR: cannot insert   \n");
680#endif /* DEBUG_EDIT */
681	return;
682    }
683
684    if (num > el->el_term.t_size.h) {
685#ifdef DEBUG_SCREEN
686	(void) fprintf(el->el_errfile, "StartInsert: num is riduculous: %d\r\n", num);
687#endif /* DEBUG_SCREEN */
688	return;
689    }
690
691    if (GoodStr(T_IC))		/* if I have multiple insert */
692	if ((num > 1) || !GoodStr(T_ic)) {	/* if ic would be more expen. */
693	    (void) tputs(tgoto(Str(T_IC), num, num), num, term__putc);
694	    term_overwrite(el, cp, num);	/* this updates el_cursor.h */
695	    return;
696	}
697
698    if (GoodStr(T_im) && GoodStr(T_ei)) { /* if I have insert mode */
699	(void) tputs(Str(T_im), 1, term__putc);
700
701	el->el_cursor.h += num;
702	do
703	    term__putc(*cp++);
704	while (--num);
705
706	if (GoodStr(T_ip))	/* have to make num chars insert */
707	    (void) tputs(Str(T_ip), 1, term__putc);
708
709	(void) tputs(Str(T_ei), 1, term__putc);
710	return;
711    }
712
713    do {
714	if (GoodStr(T_ic))	/* have to make num chars insert */
715	    (void) tputs(Str(T_ic), 1, term__putc);	/* insert a char */
716
717	term__putc(*cp++);
718
719	el->el_cursor.h++;
720
721	if (GoodStr(T_ip))	/* have to make num chars insert */
722	    (void) tputs(Str(T_ip), 1, term__putc);/* pad the inserted char */
723
724    } while (--num);
725} /* end term_insertwrite */
726
727
728/* term_clear_EOL():
729 *	clear to end of line.  There are num characters to clear
730 */
731protected void
732term_clear_EOL(el, num)
733    EditLine *el;
734    int     num;
735{
736    int i;
737
738    if (EL_CAN_CEOL && GoodStr(T_ce))
739	(void) tputs(Str(T_ce), 1, term__putc);
740    else {
741	for (i = 0; i < num; i++)
742	    term__putc(' ');
743	el->el_cursor.h += num;		/* have written num spaces */
744    }
745} /* end term_clear_EOL */
746
747
748/* term_clear_screen():
749 *	Clear the screen
750 */
751protected void
752term_clear_screen(el)
753    EditLine *el;
754{				/* clear the whole screen and home */
755    if (GoodStr(T_cl))
756	/* send the clear screen code */
757	(void) tputs(Str(T_cl), Val(T_li), term__putc);
758    else if (GoodStr(T_ho) && GoodStr(T_cd)) {
759	(void) tputs(Str(T_ho), Val(T_li), term__putc);	/* home */
760	/* clear to bottom of screen */
761	(void) tputs(Str(T_cd), Val(T_li), term__putc);
762    }
763    else {
764	term__putc('\r');
765	term__putc('\n');
766    }
767} /* end term_clear_screen */
768
769
770/* term_beep():
771 *	Beep the way the terminal wants us
772 */
773protected void
774term_beep(el)
775    EditLine *el;
776{
777    if (GoodStr(T_vb))
778	(void) tputs(Str(T_vb), 1, term__putc);	/* visible bell */
779    else if (GoodStr(T_bl))
780	/* what termcap says we should use */
781	(void) tputs(Str(T_bl), 1, term__putc);
782    else
783	term__putc('\007');	/* an ASCII bell; ^G */
784} /* end term_beep */
785
786
787#ifdef notdef
788/* term_clear_to_bottom():
789 *	Clear to the bottom of the screen
790 */
791protected void
792term_clear_to_bottom(el)
793    EditLine *el;
794{
795    if (GoodStr(T_cd))
796	(void) tputs(Str(T_cd), Val(T_li), term__putc);
797    else if (GoodStr(T_ce))
798	(void) tputs(Str(T_ce), Val(T_li), term__putc);
799} /* end term_clear_to_bottom */
800#endif
801
802
803/* term_set():
804 *	Read in the terminal capabilities from the requested terminal
805 */
806protected int
807term_set(el, term)
808    EditLine *el;
809    char *term;
810{
811    int i;
812    char    buf[TC_BUFSIZE];
813    char   *area;
814    struct termcapstr *t;
815    sigset_t oset, nset;
816    int     lins, cols;
817
818    (void) sigemptyset(&nset);
819    (void) sigaddset(&nset, SIGWINCH);
820    (void) sigprocmask(SIG_BLOCK, &nset, &oset);
821
822    area = buf;
823
824
825    if (term == NULL)
826	term = getenv("TERM");
827
828    if (!term || !term[0])
829	term = "dumb";
830
831    memset(el->el_term.t_cap, 0, TC_BUFSIZE);
832
833    i = tgetent(el->el_term.t_cap, term);
834
835    if (i <= 0) {
836	if (i == -1)
837	    (void) fprintf(el->el_errfile, "Cannot open /etc/termcap.\n");
838	else if (i == 0)
839	    (void) fprintf(el->el_errfile,
840			   "No entry for terminal type \"%s\"\n", term);
841	(void) fprintf(el->el_errfile, "using dumb terminal settings.\n");
842	Val(T_co) = 80;		/* do a dumb terminal */
843	Val(T_pt) = Val(T_km) = Val(T_li) = 0;
844	Val(T_xt) = Val(T_MT);
845	for (t = tstr; t->name != NULL; t++)
846	    term_alloc(el, t, NULL);
847    }
848    else {
849	/* Can we tab */
850	Val(T_pt) = tgetflag("pt");
851	Val(T_xt) = tgetflag("xt");
852	/* do we have a meta? */
853	Val(T_km) = tgetflag("km");
854	Val(T_MT) = tgetflag("MT");
855	/* Get the size */
856	Val(T_co) = tgetnum("co");
857	Val(T_li) = tgetnum("li");
858	for (t = tstr; t->name != NULL; t++)
859	    term_alloc(el, t, tgetstr(t->name, &area));
860    }
861
862    if (Val(T_co) < 2)
863	Val(T_co) = 80;		/* just in case */
864    if (Val(T_li) < 1)
865	Val(T_li) = 24;
866
867    el->el_term.t_size.v = Val(T_co);
868    el->el_term.t_size.h = Val(T_li);
869
870    term_setflags(el);
871
872    (void) term_get_size(el, &lins, &cols);/* get the correct window size */
873    term_change_size(el, lins, cols);
874    (void) sigprocmask(SIG_SETMASK, &oset, NULL);
875    term_bind_arrow(el);
876    return 0;
877} /* end term_set */
878
879
880/* term_get_size():
881 *	Return the new window size in lines and cols, and
882 *	true if the size was changed.
883 */
884protected int
885term_get_size(el, lins, cols)
886    EditLine *el;
887    int    *lins, *cols;
888{
889
890    *cols = Val(T_co);
891    *lins = Val(T_li);
892
893#ifdef TIOCGWINSZ
894    {
895	struct winsize ws;
896	if (ioctl(el->el_infd, TIOCGWINSZ, (ioctl_t) &ws) != -1) {
897	    if (ws.ws_col)
898		*cols = ws.ws_col;
899	    if (ws.ws_row)
900		*lins = ws.ws_row;
901	}
902    }
903#endif
904#ifdef TIOCGSIZE
905    {
906	struct ttysize ts;
907	if (ioctl(el->el_infd, TIOCGSIZE, (ioctl_t) &ts) != -1) {
908	    if (ts.ts_cols)
909		*cols = ts.ts_cols;
910	    if (ts.ts_lines)
911		*lins = ts.ts_lines;
912	}
913    }
914#endif
915    return (Val(T_co) != *cols || Val(T_li) != *lins);
916} /* end term_get_size */
917
918
919/* term_change_size():
920 *	Change the size of the terminal
921 */
922protected void
923term_change_size(el, lins, cols)
924    EditLine *el;
925    int     lins, cols;
926{
927    /*
928     * Just in case
929     */
930    Val(T_co) = (cols < 2) ? 80 : cols;
931    Val(T_li) = (lins < 1) ? 24 : lins;
932
933    term_rebuffer_display(el);		/* re-make display buffers */
934    re_clear_display(el);
935} /* end term_change_size */
936
937
938/* term_init_arrow():
939 *	Initialize the arrow key bindings from termcap
940 */
941private void
942term_init_arrow(el)
943    EditLine *el;
944{
945    fkey_t *arrow = el->el_term.t_fkey;
946
947    arrow[A_K_DN].name    = "down";
948    arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY;
949    arrow[A_K_DN].type    = XK_CMD;
950
951    arrow[A_K_UP].name    = "up";
952    arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY;
953    arrow[A_K_UP].type    = XK_CMD;
954
955    arrow[A_K_LT].name    = "left";
956    arrow[A_K_LT].fun.cmd = ED_PREV_CHAR;
957    arrow[A_K_LT].type    = XK_CMD;
958
959    arrow[A_K_RT].name    = "right";
960    arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR;
961    arrow[A_K_RT].type    = XK_CMD;
962
963}
964
965
966/* term_reset_arrow():
967 *	Reset arrow key bindings
968 */
969private void
970term_reset_arrow(el)
971    EditLine *el;
972{
973    fkey_t *arrow = el->el_term.t_fkey;
974    static char strA[] = {033, '[', 'A', '\0'};
975    static char strB[] = {033, '[', 'B', '\0'};
976    static char strC[] = {033, '[', 'C', '\0'};
977    static char strD[] = {033, '[', 'D', '\0'};
978    static char stOA[] = {033, 'O', 'A', '\0'};
979    static char stOB[] = {033, 'O', 'B', '\0'};
980    static char stOC[] = {033, 'O', 'C', '\0'};
981    static char stOD[] = {033, 'O', 'D', '\0'};
982
983    key_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
984    key_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
985    key_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
986    key_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
987    key_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
988    key_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
989    key_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
990    key_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
991
992    if (el->el_map.type == MAP_VI) {
993	key_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
994	key_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
995	key_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
996	key_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
997	key_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
998	key_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
999	key_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1000	key_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1001    }
1002}
1003
1004
1005/* term_set_arrow():
1006 *	Set an arrow key binding
1007 */
1008protected int
1009term_set_arrow(el, name, fun, type)
1010    EditLine *el;
1011    char *name;
1012    key_value_t *fun;
1013    int type;
1014{
1015    fkey_t *arrow = el->el_term.t_fkey;
1016    int i;
1017
1018    for (i = 0; i < A_K_NKEYS; i++)
1019	if (strcmp(name, arrow[i].name) == 0) {
1020	    arrow[i].fun  = *fun;
1021	    arrow[i].type = type;
1022	    return 0;
1023	}
1024    return -1;
1025}
1026
1027
1028/* term_clear_arrow():
1029 *	Clear an arrow key binding
1030 */
1031protected int
1032term_clear_arrow(el, name)
1033    EditLine *el;
1034    char *name;
1035{
1036    fkey_t *arrow = el->el_term.t_fkey;
1037    int i;
1038
1039    for (i = 0; i < A_K_NKEYS; i++)
1040	if (strcmp(name, arrow[i].name) == 0) {
1041	    arrow[i].type = XK_NOD;
1042	    return 0;
1043	}
1044    return -1;
1045}
1046
1047
1048/* term_print_arrow():
1049 *	Print the arrow key bindings
1050 */
1051protected void
1052term_print_arrow(el, name)
1053    EditLine *el;
1054    char *name;
1055{
1056    int i;
1057    fkey_t *arrow = el->el_term.t_fkey;
1058
1059    for (i = 0; i < A_K_NKEYS; i++)
1060	if (*name == '\0' || strcmp(name, arrow[i].name) == 0)
1061	    if (arrow[i].type != XK_NOD)
1062		key_kprint(el, arrow[i].name, &arrow[i].fun, arrow[i].type);
1063}
1064
1065
1066/* term_bind_arrow():
1067 *	Bind the arrow keys
1068 */
1069protected void
1070term_bind_arrow(el)
1071    EditLine *el;
1072{
1073    el_action_t *map, *dmap;
1074    int     i, j;
1075    char   *p;
1076    fkey_t *arrow = el->el_term.t_fkey;
1077
1078    /* Check if the components needed are initialized */
1079    if (el->el_term.t_buf == NULL || el->el_map.key == NULL)
1080	return;
1081
1082    map  = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key;
1083    dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs;
1084
1085    term_reset_arrow(el);
1086
1087    for (i = 0; i < 4; i++) {
1088	p = el->el_term.t_str[arrow[i].key];
1089	if (p && *p) {
1090	    j = (unsigned char) *p;
1091	    /*
1092	     * Assign the arrow keys only if:
1093	     *
1094	     * 1. They are multi-character arrow keys and the user
1095	     *    has not re-assigned the leading character, or
1096	     *    has re-assigned the leading character to be
1097	     *	  ED_SEQUENCE_LEAD_IN
1098	     * 2. They are single arrow keys pointing to an unassigned key.
1099	     */
1100	    if (arrow[i].type == XK_NOD)
1101		key_clear(el, map, p);
1102	    else {
1103		if (p[1] && (dmap[j] == map[j] ||
1104			     map[j] == ED_SEQUENCE_LEAD_IN)) {
1105		    key_add(el, p, &arrow[i].fun, arrow[i].type);
1106		    map[j] = ED_SEQUENCE_LEAD_IN;
1107		}
1108		else if (map[j] == ED_UNASSIGNED) {
1109		    key_clear(el, map, p);
1110		    if (arrow[i].type == XK_CMD)
1111			map[j] = arrow[i].fun.cmd;
1112		    else
1113			key_add(el, p, &arrow[i].fun, arrow[i].type);
1114		}
1115	    }
1116	}
1117    }
1118}
1119
1120
1121/* term__putc():
1122 *	Add a character
1123 */
1124protected void
1125term__putc(c)
1126    int c;
1127{
1128    (void) fputc(c, term_outfile);
1129} /* end term__putc */
1130
1131
1132/* term__flush():
1133 *	Flush output
1134 */
1135protected void
1136term__flush()
1137{
1138    (void) fflush(term_outfile);
1139} /* end term__flush */
1140
1141
1142/* term_telltc():
1143 *	Print the current termcap characteristics
1144 */
1145protected int
1146/*ARGSUSED*/
1147term_telltc(el, argc, argv)
1148    EditLine *el;
1149    int argc;
1150    char **argv;
1151{
1152    struct termcapstr *t;
1153    char **ts;
1154    char upbuf[EL_BUFSIZ];
1155
1156    (void) fprintf(el->el_outfile, "\n\tYour terminal has the\n");
1157    (void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n");
1158    (void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n",
1159	    Val(T_co), Val(T_li));
1160    (void) fprintf(el->el_outfile,
1161		   "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no");
1162    (void) fprintf(el->el_outfile,
1163		   "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not ");
1164#ifdef notyet
1165    (void) fprintf(el->el_outfile, "\tIt %s automatic margins\n",
1166		   (T_Margin&MARGIN_AUTO)? "has": "does not have");
1167    if (T_Margin & MARGIN_AUTO)
1168	(void) fprintf(el->el_outfile, "\tIt %s magic margins\n",
1169			(T_Margin&MARGIN_MAGIC)?"has":"does not have");
1170#endif
1171
1172    for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++)
1173	(void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n", t->long_name,
1174		       t->name, *ts && **ts ?
1175			key__decode_str(*ts, upbuf, "") : "(empty)");
1176    (void) fputc('\n', el->el_outfile);
1177    return 0;
1178}
1179
1180
1181/* term_settc():
1182 *	Change the current terminal characteristics
1183 */
1184protected int
1185/*ARGSUSED*/
1186term_settc(el, argc, argv)
1187    EditLine *el;
1188    int argc;
1189    char **argv;
1190{
1191    struct termcapstr *ts;
1192    struct termcapval *tv;
1193    char   *what, *how;
1194
1195    if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
1196	return -1;
1197
1198    what = argv[1];
1199    how = argv[2];
1200
1201    /*
1202     * Do the strings first
1203     */
1204    for (ts = tstr; ts->name != NULL; ts++)
1205	if (strcmp(ts->name, what) == 0)
1206	    break;
1207
1208    if (ts->name != NULL) {
1209	term_alloc(el, ts, how);
1210	term_setflags(el);
1211	return 0;
1212    }
1213
1214    /*
1215     * Do the numeric ones second
1216     */
1217    for (tv = tval; tv->name != NULL; tv++)
1218	if (strcmp(tv->name, what) == 0)
1219	    break;
1220
1221    if (tv->name != NULL) {
1222	if (tv == &tval[T_pt] || tv == &tval[T_km]
1223#ifdef notyet
1224	    || tv == &tval[T_am] || tv == &tval[T_xn]
1225#endif
1226	    ) {
1227	    if (strcmp(how, "yes") == 0)
1228		el->el_term.t_val[tv - tval] = 1;
1229	    else if (strcmp(how, "no") == 0)
1230		el->el_term.t_val[tv - tval] = 0;
1231	    else {
1232		(void) fprintf(el->el_errfile, "settc: Bad value `%s'.\n", how);
1233		return -1;
1234	    }
1235	    term_setflags(el);
1236	    term_change_size(el, Val(T_li), Val(T_co));
1237	    return 0;
1238	}
1239	else {
1240	    el->el_term.t_val[tv - tval] = atoi(how);
1241	    el->el_term.t_size.v = Val(T_co);
1242	    el->el_term.t_size.h = Val(T_li);
1243	    if (tv == &tval[T_co] || tv == &tval[T_li])
1244		term_change_size(el, Val(T_li), Val(T_co));
1245	    return 0;
1246	}
1247    }
1248    return -1;
1249}
1250
1251
1252/* term_echotc():
1253 *	Print the termcap string out with variable substitution
1254 */
1255protected int
1256/*ARGSUSED*/
1257term_echotc(el, argc, argv)
1258    EditLine *el;
1259    int argc;
1260    char **argv;
1261{
1262    char   *cap, *scap;
1263    int     arg_need, arg_cols, arg_rows;
1264    int     verbose = 0, silent = 0;
1265    char   *area;
1266    static char *fmts = "%s\n", *fmtd = "%d\n";
1267    struct termcapstr *t;
1268    char    buf[TC_BUFSIZE];
1269
1270    area = buf;
1271
1272    if (argv == NULL || argv[1] == NULL)
1273	return -1;
1274    argv++;
1275
1276    if (argv[0][0] == '-') {
1277	switch (argv[0][1]) {
1278	case 'v':
1279	    verbose = 1;
1280	    break;
1281	case 's':
1282	    silent = 1;
1283	    break;
1284	default:
1285	    /* stderror(ERR_NAME | ERR_TCUSAGE); */
1286	    break;
1287	}
1288	argv++;
1289    }
1290    if (!*argv || *argv[0] == '\0')
1291	return 0;
1292    if (strcmp(*argv, "tabs") == 0) {
1293	(void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no");
1294	return 0;
1295    }
1296    else if (strcmp(*argv, "meta") == 0) {
1297	(void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no");
1298	return 0;
1299    }
1300#ifdef notyet
1301    else if (strcmp(*argv, "xn") == 0) {
1302	(void) fprintf(el->el_outfile, fmts, T_Margin & MARGIN_MAGIC ?
1303			"yes" : "no");
1304	return 0;
1305    }
1306    else if (strcmp(*argv, "am") == 0) {
1307	(void) fprintf(el->el_outfile, fmts, T_Margin & MARGIN_AUTO ?
1308			"yes" : "no");
1309	return 0;
1310    }
1311#endif
1312    else if (strcmp(*argv, "baud") == 0) {
1313	int     i;
1314
1315	for (i = 0; baud_rate[i].b_name != NULL; i++)
1316	    if (el->el_tty.t_speed == baud_rate[i].b_rate) {
1317		(void) fprintf(el->el_outfile, fmts, baud_rate[i].b_name);
1318		return 0;
1319	    }
1320	(void) fprintf(el->el_outfile, fmtd, 0);
1321	return 0;
1322    }
1323    else if (strcmp(*argv, "rows") == 0 || strcmp(*argv, "lines") == 0) {
1324	(void) fprintf(el->el_outfile, fmtd, Val(T_li));
1325	return 0;
1326    }
1327    else if (strcmp(*argv, "cols") == 0) {
1328	(void) fprintf(el->el_outfile, fmtd, Val(T_co));
1329	return 0;
1330    }
1331
1332    /*
1333     * Try to use our local definition first
1334     */
1335    scap = NULL;
1336    for (t = tstr; t->name != NULL; t++)
1337	if (strcmp(t->name, *argv) == 0) {
1338	    scap = el->el_term.t_str[t - tstr];
1339	    break;
1340	}
1341    if (t->name == NULL)
1342	scap = tgetstr(*argv, &area);
1343    if (!scap || scap[0] == '\0') {
1344	if (!silent)
1345	    (void) fprintf(el->el_errfile,
1346		"echotc: Termcap parameter `%s' not found.\n", *argv);
1347	return -1;
1348    }
1349
1350    /*
1351     * Count home many values we need for this capability.
1352     */
1353    for (cap = scap, arg_need = 0; *cap; cap++)
1354	if (*cap == '%')
1355	    switch (*++cap) {
1356	    case 'd':
1357	    case '2':
1358	    case '3':
1359	    case '.':
1360	    case '+':
1361		arg_need++;
1362		break;
1363	    case '%':
1364	    case '>':
1365	    case 'i':
1366	    case 'r':
1367	    case 'n':
1368	    case 'B':
1369	    case 'D':
1370		break;
1371	    default:
1372		/*
1373		 * hpux has lot's of them...
1374		 */
1375		if (verbose)
1376		    (void) fprintf(el->el_errfile,
1377			"echotc: Warning: unknown termcap %% `%c'.\n", *cap);
1378		/* This is bad, but I won't complain */
1379		break;
1380	    }
1381
1382    switch (arg_need) {
1383    case 0:
1384	argv++;
1385	if (*argv && *argv[0]) {
1386	    if (!silent)
1387		(void) fprintf(el->el_errfile,
1388		    "echotc: Warning: Extra argument `%s'.\n", *argv);
1389	    return -1;
1390	}
1391	(void) tputs(scap, 1, term__putc);
1392	break;
1393    case 1:
1394	argv++;
1395	if (!*argv || *argv[0] == '\0') {
1396	    if (!silent)
1397		(void) fprintf(el->el_errfile,
1398		    "echotc: Warning: Missing argument.\n");
1399	    return -1;
1400	}
1401	arg_cols = 0;
1402	arg_rows = atoi(*argv);
1403	argv++;
1404	if (*argv && *argv[0]) {
1405	    if (!silent)
1406		(void) fprintf(el->el_errfile,
1407		    "echotc: Warning: Extra argument `%s'.\n", *argv);
1408	    return -1;
1409	}
1410	(void) tputs(tgoto(scap, arg_cols, arg_rows), 1, term__putc);
1411	break;
1412    default:
1413	/* This is wrong, but I will ignore it... */
1414	if (verbose)
1415	    (void) fprintf(el->el_errfile,
1416		"echotc: Warning: Too many required arguments (%d).\n",
1417		arg_need);
1418	/*FALLTHROUGH*/
1419    case 2:
1420	argv++;
1421	if (!*argv || *argv[0] == '\0') {
1422	    if (!silent)
1423		(void) fprintf(el->el_errfile,
1424		    "echotc: Warning: Missing argument.\n");
1425	    return -1;
1426	}
1427	arg_cols = atoi(*argv);
1428	argv++;
1429	if (!*argv || *argv[0] == '\0') {
1430	    if (!silent)
1431		(void) fprintf(el->el_errfile,
1432		    "echotc: Warning: Missing argument.\n");
1433	    return -1;
1434	}
1435	arg_rows = atoi(*argv);
1436	argv++;
1437	if (*argv && *argv[0]) {
1438	    if (!silent)
1439		(void) fprintf(el->el_errfile,
1440		    "echotc: Warning: Extra argument `%s'.\n", *argv);
1441	    return -1;
1442	}
1443	(void) tputs(tgoto(scap, arg_cols, arg_rows), arg_rows, term__putc);
1444	break;
1445    }
1446    return 0;
1447}
1448