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