terminal.c revision 148900
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. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 *	$NetBSD: term.c,v 1.41 2005/08/08 14:05:37 christos Exp $
33 */
34
35#if !defined(lint) && !defined(SCCSID)
36static char sccsid[] = "@(#)term.c	8.2 (Berkeley) 4/30/95";
37#endif /* not lint && not SCCSID */
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD: head/lib/libedit/term.c 148900 2005-08-09 13:37:59Z stefanf $");
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 <stdio.h>
47#include <signal.h>
48#include <string.h>
49#include <stdlib.h>
50#include <unistd.h>
51#include <termcap.h>
52#include <curses.h>
53#include <ncurses.h>
54#include <term.h>
55#include <sys/types.h>
56#include <sys/ioctl.h>
57
58#include "el.h"
59
60/*
61 * IMPORTANT NOTE: these routines are allowed to look at the current screen
62 * and the current possition assuming that it is correct.  If this is not
63 * true, then the update will be WRONG!  This is (should be) a valid
64 * assumption...
65 */
66
67#define	TC_BUFSIZE	2048
68
69#define	GoodStr(a)	(el->el_term.t_str[a] != NULL && \
70			    el->el_term.t_str[a][0] != '\0')
71#define	Str(a)		el->el_term.t_str[a]
72#define	Val(a)		el->el_term.t_val[a]
73
74#ifdef notdef
75private const struct {
76	const char *b_name;
77	int b_rate;
78} baud_rate[] = {
79#ifdef B0
80	{ "0", B0 },
81#endif
82#ifdef B50
83	{ "50", B50 },
84#endif
85#ifdef B75
86	{ "75", B75 },
87#endif
88#ifdef B110
89	{ "110", B110 },
90#endif
91#ifdef B134
92	{ "134", B134 },
93#endif
94#ifdef B150
95	{ "150", B150 },
96#endif
97#ifdef B200
98	{ "200", B200 },
99#endif
100#ifdef B300
101	{ "300", B300 },
102#endif
103#ifdef B600
104	{ "600", B600 },
105#endif
106#ifdef B900
107	{ "900", B900 },
108#endif
109#ifdef B1200
110	{ "1200", B1200 },
111#endif
112#ifdef B1800
113	{ "1800", B1800 },
114#endif
115#ifdef B2400
116	{ "2400", B2400 },
117#endif
118#ifdef B3600
119	{ "3600", B3600 },
120#endif
121#ifdef B4800
122	{ "4800", B4800 },
123#endif
124#ifdef B7200
125	{ "7200", B7200 },
126#endif
127#ifdef B9600
128	{ "9600", B9600 },
129#endif
130#ifdef EXTA
131	{ "19200", EXTA },
132#endif
133#ifdef B19200
134	{ "19200", B19200 },
135#endif
136#ifdef EXTB
137	{ "38400", EXTB },
138#endif
139#ifdef B38400
140	{ "38400", B38400 },
141#endif
142	{ NULL, 0 }
143};
144#endif
145
146private const struct termcapstr {
147	const char *name;
148	const char *long_name;
149} tstr[] = {
150#define	T_al	0
151	{ "al", "add new blank line" },
152#define	T_bl	1
153	{ "bl", "audible bell" },
154#define	T_cd	2
155	{ "cd", "clear to bottom" },
156#define	T_ce	3
157	{ "ce", "clear to end of line" },
158#define	T_ch	4
159	{ "ch", "cursor to horiz pos" },
160#define	T_cl	5
161	{ "cl", "clear screen" },
162#define	T_dc	6
163	{ "dc", "delete a character" },
164#define	T_dl	7
165	{ "dl", "delete a line" },
166#define	T_dm	8
167	{ "dm", "start delete mode" },
168#define	T_ed	9
169	{ "ed", "end delete mode" },
170#define	T_ei	10
171	{ "ei", "end insert mode" },
172#define	T_fs	11
173	{ "fs", "cursor from status line" },
174#define	T_ho	12
175	{ "ho", "home cursor" },
176#define	T_ic	13
177	{ "ic", "insert character" },
178#define	T_im	14
179	{ "im", "start insert mode" },
180#define	T_ip	15
181	{ "ip", "insert padding" },
182#define	T_kd	16
183	{ "kd", "sends cursor down" },
184#define	T_kl	17
185	{ "kl", "sends cursor left" },
186#define	T_kr	18
187	{ "kr", "sends cursor right" },
188#define	T_ku	19
189	{ "ku", "sends cursor up" },
190#define	T_md	20
191	{ "md", "begin bold" },
192#define	T_me	21
193	{ "me", "end attributes" },
194#define	T_nd	22
195	{ "nd", "non destructive space" },
196#define	T_se	23
197	{ "se", "end standout" },
198#define	T_so	24
199	{ "so", "begin standout" },
200#define	T_ts	25
201	{ "ts", "cursor to status line" },
202#define	T_up	26
203	{ "up", "cursor up one" },
204#define	T_us	27
205	{ "us", "begin underline" },
206#define	T_ue	28
207	{ "ue", "end underline" },
208#define	T_vb	29
209	{ "vb", "visible bell" },
210#define	T_DC	30
211	{ "DC", "delete multiple chars" },
212#define	T_DO	31
213	{ "DO", "cursor down multiple" },
214#define	T_IC	32
215	{ "IC", "insert multiple chars" },
216#define	T_LE	33
217	{ "LE", "cursor left multiple" },
218#define	T_RI	34
219	{ "RI", "cursor right multiple" },
220#define	T_UP	35
221	{ "UP", "cursor up multiple" },
222#define	T_kh	36
223	{ "kh", "send cursor home" },
224#define	T_at7	37
225	{ "@7", "send cursor end" },
226#define	T_str	38
227	{ NULL, NULL }
228};
229
230private const struct termcapval {
231	const char *name;
232	const char *long_name;
233} tval[] = {
234#define	T_am	0
235	{ "am", "has automatic margins" },
236#define	T_pt	1
237	{ "pt", "has physical tabs" },
238#define	T_li	2
239	{ "li", "Number of lines" },
240#define	T_co	3
241	{ "co", "Number of columns" },
242#define	T_km	4
243	{ "km", "Has meta key" },
244#define	T_xt	5
245	{ "xt", "Tab chars destructive" },
246#define	T_xn	6
247	{ "xn", "newline ignored at right margin" },
248#define	T_MT	7
249	{ "MT", "Has meta key" },			/* XXX? */
250#define	T_val	8
251	{ NULL, NULL, }
252};
253/* do two or more of the attributes use me */
254
255private void	term_setflags(EditLine *);
256private int	term_rebuffer_display(EditLine *);
257private void	term_free_display(EditLine *);
258private int	term_alloc_display(EditLine *);
259private void	term_alloc(EditLine *, const struct termcapstr *, const char *);
260private void	term_init_arrow(EditLine *);
261private void	term_reset_arrow(EditLine *);
262
263
264private FILE *term_outfile = NULL;	/* XXX: How do we fix that? */
265
266
267/* term_setflags():
268 *	Set the terminal capability flags
269 */
270private void
271term_setflags(EditLine *el)
272{
273	EL_FLAGS = 0;
274	if (el->el_tty.t_tabs)
275		EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0;
276
277	EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0;
278	EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0;
279	EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0;
280	EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ?
281	    TERM_CAN_INSERT : 0;
282	EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0;
283	EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0;
284	EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0;
285
286	if (GoodStr(T_me) && GoodStr(T_ue))
287		EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ?
288		    TERM_CAN_ME : 0;
289	else
290		EL_FLAGS &= ~TERM_CAN_ME;
291	if (GoodStr(T_me) && GoodStr(T_se))
292		EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ?
293		    TERM_CAN_ME : 0;
294
295
296#ifdef DEBUG_SCREEN
297	if (!EL_CAN_UP) {
298		(void) fprintf(el->el_errfile,
299		    "WARNING: Your terminal cannot move up.\n");
300		(void) fprintf(el->el_errfile,
301		    "Editing may be odd for long lines.\n");
302	}
303	if (!EL_CAN_CEOL)
304		(void) fprintf(el->el_errfile, "no clear EOL capability.\n");
305	if (!EL_CAN_DELETE)
306		(void) fprintf(el->el_errfile, "no delete char capability.\n");
307	if (!EL_CAN_INSERT)
308		(void) fprintf(el->el_errfile, "no insert char capability.\n");
309#endif /* DEBUG_SCREEN */
310}
311
312
313/* term_init():
314 *	Initialize the terminal stuff
315 */
316protected int
317term_init(EditLine *el)
318{
319
320	el->el_term.t_buf = (char *) el_malloc(TC_BUFSIZE);
321	if (el->el_term.t_buf == NULL)
322		return (-1);
323	el->el_term.t_cap = (char *) el_malloc(TC_BUFSIZE);
324	if (el->el_term.t_cap == NULL)
325		return (-1);
326	el->el_term.t_fkey = (fkey_t *) el_malloc(A_K_NKEYS * sizeof(fkey_t));
327	if (el->el_term.t_fkey == NULL)
328		return (-1);
329	el->el_term.t_loc = 0;
330	el->el_term.t_str = (char **) el_malloc(T_str * sizeof(char *));
331	if (el->el_term.t_str == NULL)
332		return (-1);
333	(void) memset(el->el_term.t_str, 0, T_str * sizeof(char *));
334	el->el_term.t_val = (int *) el_malloc(T_val * sizeof(int));
335	if (el->el_term.t_val == NULL)
336		return (-1);
337	(void) memset(el->el_term.t_val, 0, T_val * sizeof(int));
338	term_outfile = el->el_outfile;
339	term_init_arrow(el);
340	(void) term_set(el, NULL);
341	return (0);
342}
343
344/* term_end():
345 *	Clean up the terminal stuff
346 */
347protected void
348term_end(EditLine *el)
349{
350
351	el_free((ptr_t) el->el_term.t_buf);
352	el->el_term.t_buf = NULL;
353	el_free((ptr_t) el->el_term.t_cap);
354	el->el_term.t_cap = NULL;
355	el->el_term.t_loc = 0;
356	el_free((ptr_t) el->el_term.t_str);
357	el->el_term.t_str = NULL;
358	el_free((ptr_t) el->el_term.t_val);
359	el->el_term.t_val = NULL;
360	el_free((ptr_t) el->el_term.t_fkey);
361	el->el_term.t_fkey = 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, const 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				    ((unsigned int)-del >
639				    (((unsigned int) where >> 3) +
640				     (where & 07)))
641				    : (-del > where)) {
642					term__putc('\r');	/* do a CR */
643					el->el_cursor.h = 0;
644					goto mc_again;	/* and try again */
645				}
646				for (i = 0; i < -del; i++)
647					term__putc('\b');
648			}
649		}
650	}
651	el->el_cursor.h = where;		/* now where is here */
652}
653
654
655/* term_overwrite():
656 *	Overstrike num characters
657 */
658protected void
659term_overwrite(EditLine *el, const char *cp, int n)
660{
661	if (n <= 0)
662		return;		/* catch bugs */
663
664	if (n > el->el_term.t_size.h) {
665#ifdef DEBUG_SCREEN
666		(void) fprintf(el->el_errfile,
667		    "term_overwrite: n is riduculous: %d\r\n", n);
668#endif /* DEBUG_SCREEN */
669		return;
670	}
671	do {
672		term__putc(*cp++);
673		el->el_cursor.h++;
674	} while (--n);
675
676	if (el->el_cursor.h >= el->el_term.t_size.h) {	/* wrap? */
677		if (EL_HAS_AUTO_MARGINS) {	/* yes */
678			el->el_cursor.h = 0;
679			el->el_cursor.v++;
680			if (EL_HAS_MAGIC_MARGINS) {
681				/* force the wrap to avoid the "magic"
682				 * situation */
683				char c;
684				if ((c = el->el_display[el->el_cursor.v][el->el_cursor.h])
685				    != '\0')
686					term_overwrite(el, &c, 1);
687				else
688					term__putc(' ');
689				el->el_cursor.h = 1;
690			}
691		} else		/* no wrap, but cursor stays on screen */
692			el->el_cursor.h = el->el_term.t_size.h;
693	}
694}
695
696
697/* term_deletechars():
698 *	Delete num characters
699 */
700protected void
701term_deletechars(EditLine *el, int num)
702{
703	if (num <= 0)
704		return;
705
706	if (!EL_CAN_DELETE) {
707#ifdef DEBUG_EDIT
708		(void) fprintf(el->el_errfile, "   ERROR: cannot delete   \n");
709#endif /* DEBUG_EDIT */
710		return;
711	}
712	if (num > el->el_term.t_size.h) {
713#ifdef DEBUG_SCREEN
714		(void) fprintf(el->el_errfile,
715		    "term_deletechars: num is riduculous: %d\r\n", num);
716#endif /* DEBUG_SCREEN */
717		return;
718	}
719	if (GoodStr(T_DC))	/* if I have multiple delete */
720		if ((num > 1) || !GoodStr(T_dc)) {	/* if dc would be more
721							 * expen. */
722			(void) tputs(tgoto(Str(T_DC), num, num),
723			    num, term__putc);
724			return;
725		}
726	if (GoodStr(T_dm))	/* if I have delete mode */
727		(void) tputs(Str(T_dm), 1, term__putc);
728
729	if (GoodStr(T_dc))	/* else do one at a time */
730		while (num--)
731			(void) tputs(Str(T_dc), 1, term__putc);
732
733	if (GoodStr(T_ed))	/* if I have delete mode */
734		(void) tputs(Str(T_ed), 1, term__putc);
735}
736
737
738/* term_insertwrite():
739 *	Puts terminal in insert character mode or inserts num
740 *	characters in the line
741 */
742protected void
743term_insertwrite(EditLine *el, char *cp, int num)
744{
745	if (num <= 0)
746		return;
747	if (!EL_CAN_INSERT) {
748#ifdef DEBUG_EDIT
749		(void) fprintf(el->el_errfile, "   ERROR: cannot insert   \n");
750#endif /* DEBUG_EDIT */
751		return;
752	}
753	if (num > el->el_term.t_size.h) {
754#ifdef DEBUG_SCREEN
755		(void) fprintf(el->el_errfile,
756		    "StartInsert: num is riduculous: %d\r\n", num);
757#endif /* DEBUG_SCREEN */
758		return;
759	}
760	if (GoodStr(T_IC))	/* if I have multiple insert */
761		if ((num > 1) || !GoodStr(T_ic)) {
762				/* if ic would be more expensive */
763			(void) tputs(tgoto(Str(T_IC), num, num),
764			    num, term__putc);
765			term_overwrite(el, cp, num);
766				/* this updates el_cursor.h */
767			return;
768		}
769	if (GoodStr(T_im) && GoodStr(T_ei)) {	/* if I have insert mode */
770		(void) tputs(Str(T_im), 1, term__putc);
771
772		el->el_cursor.h += num;
773		do
774			term__putc(*cp++);
775		while (--num);
776
777		if (GoodStr(T_ip))	/* have to make num chars insert */
778			(void) tputs(Str(T_ip), 1, term__putc);
779
780		(void) tputs(Str(T_ei), 1, term__putc);
781		return;
782	}
783	do {
784		if (GoodStr(T_ic))	/* have to make num chars insert */
785			(void) tputs(Str(T_ic), 1, term__putc);
786					/* insert a char */
787
788		term__putc(*cp++);
789
790		el->el_cursor.h++;
791
792		if (GoodStr(T_ip))	/* have to make num chars insert */
793			(void) tputs(Str(T_ip), 1, term__putc);
794					/* pad the inserted char */
795
796	} while (--num);
797}
798
799
800/* term_clear_EOL():
801 *	clear to end of line.  There are num characters to clear
802 */
803protected void
804term_clear_EOL(EditLine *el, int num)
805{
806	int i;
807
808	if (EL_CAN_CEOL && GoodStr(T_ce))
809		(void) tputs(Str(T_ce), 1, term__putc);
810	else {
811		for (i = 0; i < num; i++)
812			term__putc(' ');
813		el->el_cursor.h += num;	/* have written num spaces */
814	}
815}
816
817
818/* term_clear_screen():
819 *	Clear the screen
820 */
821protected void
822term_clear_screen(EditLine *el)
823{				/* clear the whole screen and home */
824
825	if (GoodStr(T_cl))
826		/* send the clear screen code */
827		(void) tputs(Str(T_cl), Val(T_li), term__putc);
828	else if (GoodStr(T_ho) && GoodStr(T_cd)) {
829		(void) tputs(Str(T_ho), Val(T_li), term__putc);	/* home */
830		/* clear to bottom of screen */
831		(void) tputs(Str(T_cd), Val(T_li), term__putc);
832	} else {
833		term__putc('\r');
834		term__putc('\n');
835	}
836}
837
838
839/* term_beep():
840 *	Beep the way the terminal wants us
841 */
842protected void
843term_beep(EditLine *el)
844{
845	if (GoodStr(T_bl))
846		/* what termcap says we should use */
847		(void) tputs(Str(T_bl), 1, term__putc);
848	else
849		term__putc('\007');	/* an ASCII bell; ^G */
850}
851
852
853#ifdef notdef
854/* term_clear_to_bottom():
855 *	Clear to the bottom of the screen
856 */
857protected void
858term_clear_to_bottom(EditLine *el)
859{
860	if (GoodStr(T_cd))
861		(void) tputs(Str(T_cd), Val(T_li), term__putc);
862	else if (GoodStr(T_ce))
863		(void) tputs(Str(T_ce), Val(T_li), term__putc);
864}
865#endif
866
867protected void
868term_get(EditLine *el, const char **term)
869{
870	*term = el->el_term.t_name;
871}
872
873
874/* term_set():
875 *	Read in the terminal capabilities from the requested terminal
876 */
877protected int
878term_set(EditLine *el, const char *term)
879{
880	int i;
881	char buf[TC_BUFSIZE];
882	char *area;
883	const struct termcapstr *t;
884	sigset_t oset, nset;
885	int lins, cols;
886
887	(void) sigemptyset(&nset);
888	(void) sigaddset(&nset, SIGWINCH);
889	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
890
891	area = buf;
892
893
894	if (term == NULL)
895		term = getenv("TERM");
896
897	if (!term || !term[0])
898		term = "dumb";
899
900	if (strcmp(term, "emacs") == 0)
901		el->el_flags |= EDIT_DISABLED;
902
903	memset(el->el_term.t_cap, 0, TC_BUFSIZE);
904
905	i = tgetent(el->el_term.t_cap, term);
906
907	if (i <= 0) {
908		if (i == -1)
909			(void) fprintf(el->el_errfile,
910			    "Cannot read termcap database;\n");
911		else if (i == 0)
912			(void) fprintf(el->el_errfile,
913			    "No entry for terminal type \"%s\";\n", term);
914		(void) fprintf(el->el_errfile,
915		    "using dumb terminal settings.\n");
916		Val(T_co) = 80;	/* do a dumb terminal */
917		Val(T_pt) = Val(T_km) = Val(T_li) = 0;
918		Val(T_xt) = Val(T_MT);
919		for (t = tstr; t->name != NULL; t++)
920			term_alloc(el, t, NULL);
921	} else {
922		/* auto/magic margins */
923		Val(T_am) = tgetflag("am");
924		Val(T_xn) = tgetflag("xn");
925		/* Can we tab */
926		Val(T_pt) = tgetflag("pt");
927		Val(T_xt) = tgetflag("xt");
928		/* do we have a meta? */
929		Val(T_km) = tgetflag("km");
930		Val(T_MT) = tgetflag("MT");
931		/* Get the size */
932		Val(T_co) = tgetnum("co");
933		Val(T_li) = tgetnum("li");
934		for (t = tstr; t->name != NULL; t++) {
935			/* XXX: some systems' tgetstr needs non const */
936			term_alloc(el, t, tgetstr(strchr(t->name, *t->name),
937			    &area));
938		}
939	}
940
941	if (Val(T_co) < 2)
942		Val(T_co) = 80;	/* just in case */
943	if (Val(T_li) < 1)
944		Val(T_li) = 24;
945
946	el->el_term.t_size.v = Val(T_co);
947	el->el_term.t_size.h = Val(T_li);
948
949	term_setflags(el);
950
951				/* get the correct window size */
952	(void) term_get_size(el, &lins, &cols);
953	if (term_change_size(el, lins, cols) == -1)
954		return (-1);
955	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
956	term_bind_arrow(el);
957	el->el_term.t_name = term;
958	return (i <= 0 ? -1 : 0);
959}
960
961
962/* term_get_size():
963 *	Return the new window size in lines and cols, and
964 *	true if the size was changed.
965 */
966protected int
967term_get_size(EditLine *el, int *lins, int *cols)
968{
969
970	*cols = Val(T_co);
971	*lins = Val(T_li);
972
973#ifdef TIOCGWINSZ
974	{
975		struct winsize ws;
976		if (ioctl(el->el_infd, TIOCGWINSZ, (ioctl_t) & ws) != -1) {
977			if (ws.ws_col)
978				*cols = ws.ws_col;
979			if (ws.ws_row)
980				*lins = ws.ws_row;
981		}
982	}
983#endif
984#ifdef TIOCGSIZE
985	{
986		struct ttysize ts;
987		if (ioctl(el->el_infd, TIOCGSIZE, (ioctl_t) & ts) != -1) {
988			if (ts.ts_cols)
989				*cols = ts.ts_cols;
990			if (ts.ts_lines)
991				*lins = ts.ts_lines;
992		}
993	}
994#endif
995	return (Val(T_co) != *cols || Val(T_li) != *lins);
996}
997
998
999/* term_change_size():
1000 *	Change the size of the terminal
1001 */
1002protected int
1003term_change_size(EditLine *el, int lins, int cols)
1004{
1005	/*
1006         * Just in case
1007         */
1008	Val(T_co) = (cols < 2) ? 80 : cols;
1009	Val(T_li) = (lins < 1) ? 24 : lins;
1010
1011	/* re-make display buffers */
1012	if (term_rebuffer_display(el) == -1)
1013		return (-1);
1014	re_clear_display(el);
1015	return (0);
1016}
1017
1018
1019/* term_init_arrow():
1020 *	Initialize the arrow key bindings from termcap
1021 */
1022private void
1023term_init_arrow(EditLine *el)
1024{
1025	fkey_t *arrow = el->el_term.t_fkey;
1026
1027	arrow[A_K_DN].name = "down";
1028	arrow[A_K_DN].key = T_kd;
1029	arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY;
1030	arrow[A_K_DN].type = XK_CMD;
1031
1032	arrow[A_K_UP].name = "up";
1033	arrow[A_K_UP].key = T_ku;
1034	arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY;
1035	arrow[A_K_UP].type = XK_CMD;
1036
1037	arrow[A_K_LT].name = "left";
1038	arrow[A_K_LT].key = T_kl;
1039	arrow[A_K_LT].fun.cmd = ED_PREV_CHAR;
1040	arrow[A_K_LT].type = XK_CMD;
1041
1042	arrow[A_K_RT].name = "right";
1043	arrow[A_K_RT].key = T_kr;
1044	arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR;
1045	arrow[A_K_RT].type = XK_CMD;
1046
1047	arrow[A_K_HO].name = "home";
1048	arrow[A_K_HO].key = T_kh;
1049	arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG;
1050	arrow[A_K_HO].type = XK_CMD;
1051
1052	arrow[A_K_EN].name = "end";
1053	arrow[A_K_EN].key = T_at7;
1054	arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END;
1055	arrow[A_K_EN].type = XK_CMD;
1056}
1057
1058
1059/* term_reset_arrow():
1060 *	Reset arrow key bindings
1061 */
1062private void
1063term_reset_arrow(EditLine *el)
1064{
1065	fkey_t *arrow = el->el_term.t_fkey;
1066	static const char strA[] = {033, '[', 'A', '\0'};
1067	static const char strB[] = {033, '[', 'B', '\0'};
1068	static const char strC[] = {033, '[', 'C', '\0'};
1069	static const char strD[] = {033, '[', 'D', '\0'};
1070	static const char strH[] = {033, '[', 'H', '\0'};
1071	static const char strF[] = {033, '[', 'F', '\0'};
1072	static const char str1[] = {033, '[', '1', '~', '\0'};
1073	static const char str4[] = {033, '[', '4', '~', '\0'};
1074	static const char stOA[] = {033, 'O', 'A', '\0'};
1075	static const char stOB[] = {033, 'O', 'B', '\0'};
1076	static const char stOC[] = {033, 'O', 'C', '\0'};
1077	static const char stOD[] = {033, 'O', 'D', '\0'};
1078	static const char stOH[] = {033, 'O', 'H', '\0'};
1079	static const char stOF[] = {033, 'O', 'F', '\0'};
1080
1081	key_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1082	key_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1083	key_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1084	key_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1085	key_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1086	key_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1087	key_add(el, str1, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1088	key_add(el, str4, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1089	key_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1090	key_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1091	key_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1092	key_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1093	key_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1094	key_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1095
1096	if (el->el_map.type == MAP_VI) {
1097		key_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1098		key_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1099		key_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1100		key_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1101		key_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1102		key_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1103		key_add(el, &str1[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1104		key_add(el, &str4[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1105		key_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1106		key_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1107		key_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1108		key_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1109		key_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1110		key_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1111	}
1112}
1113
1114
1115/* term_set_arrow():
1116 *	Set an arrow key binding
1117 */
1118protected int
1119term_set_arrow(EditLine *el, const char *name, key_value_t *fun, int type)
1120{
1121	fkey_t *arrow = el->el_term.t_fkey;
1122	int i;
1123
1124	for (i = 0; i < A_K_NKEYS; i++)
1125		if (strcmp(name, arrow[i].name) == 0) {
1126			arrow[i].fun = *fun;
1127			arrow[i].type = type;
1128			return (0);
1129		}
1130	return (-1);
1131}
1132
1133
1134/* term_clear_arrow():
1135 *	Clear an arrow key binding
1136 */
1137protected int
1138term_clear_arrow(EditLine *el, const char *name)
1139{
1140	fkey_t *arrow = el->el_term.t_fkey;
1141	int i;
1142
1143	for (i = 0; i < A_K_NKEYS; i++)
1144		if (strcmp(name, arrow[i].name) == 0) {
1145			arrow[i].type = XK_NOD;
1146			return (0);
1147		}
1148	return (-1);
1149}
1150
1151
1152/* term_print_arrow():
1153 *	Print the arrow key bindings
1154 */
1155protected void
1156term_print_arrow(EditLine *el, const char *name)
1157{
1158	int i;
1159	fkey_t *arrow = el->el_term.t_fkey;
1160
1161	for (i = 0; i < A_K_NKEYS; i++)
1162		if (*name == '\0' || strcmp(name, arrow[i].name) == 0)
1163			if (arrow[i].type != XK_NOD)
1164				key_kprint(el, arrow[i].name, &arrow[i].fun,
1165				    arrow[i].type);
1166}
1167
1168
1169/* term_bind_arrow():
1170 *	Bind the arrow keys
1171 */
1172protected void
1173term_bind_arrow(EditLine *el)
1174{
1175	el_action_t *map;
1176	const el_action_t *dmap;
1177	int i, j;
1178	char *p;
1179	fkey_t *arrow = el->el_term.t_fkey;
1180
1181	/* Check if the components needed are initialized */
1182	if (el->el_term.t_buf == NULL || el->el_map.key == NULL)
1183		return;
1184
1185	map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key;
1186	dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs;
1187
1188	term_reset_arrow(el);
1189
1190	for (i = 0; i < A_K_NKEYS; i++) {
1191		p = el->el_term.t_str[arrow[i].key];
1192		if (p && *p) {
1193			j = (unsigned char) *p;
1194			/*
1195		         * Assign the arrow keys only if:
1196		         *
1197		         * 1. They are multi-character arrow keys and the user
1198		         *    has not re-assigned the leading character, or
1199		         *    has re-assigned the leading character to be
1200		         *	  ED_SEQUENCE_LEAD_IN
1201		         * 2. They are single arrow keys pointing to an
1202			 *    unassigned key.
1203		         */
1204			if (arrow[i].type == XK_NOD)
1205				key_clear(el, map, p);
1206			else {
1207				if (p[1] && (dmap[j] == map[j] ||
1208					map[j] == ED_SEQUENCE_LEAD_IN)) {
1209					key_add(el, p, &arrow[i].fun,
1210					    arrow[i].type);
1211					map[j] = ED_SEQUENCE_LEAD_IN;
1212				} else if (map[j] == ED_UNASSIGNED) {
1213					key_clear(el, map, p);
1214					if (arrow[i].type == XK_CMD)
1215						map[j] = arrow[i].fun.cmd;
1216					else
1217						key_add(el, p, &arrow[i].fun,
1218						    arrow[i].type);
1219				}
1220			}
1221		}
1222	}
1223}
1224
1225
1226/* term__putc():
1227 *	Add a character
1228 */
1229protected int
1230term__putc(int c)
1231{
1232
1233	return (fputc(c, term_outfile));
1234}
1235
1236
1237/* term__flush():
1238 *	Flush output
1239 */
1240protected void
1241term__flush(void)
1242{
1243
1244	(void) fflush(term_outfile);
1245}
1246
1247
1248/* term_telltc():
1249 *	Print the current termcap characteristics
1250 */
1251protected int
1252/*ARGSUSED*/
1253term_telltc(EditLine *el, int argc __unused,
1254    const char **argv __unused)
1255{
1256	const struct termcapstr *t;
1257	char **ts;
1258	char upbuf[EL_BUFSIZ];
1259
1260	(void) fprintf(el->el_outfile, "\n\tYour terminal has the\n");
1261	(void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n");
1262	(void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n",
1263	    Val(T_co), Val(T_li));
1264	(void) fprintf(el->el_outfile,
1265	    "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no");
1266	(void) fprintf(el->el_outfile,
1267	    "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not ");
1268	(void) fprintf(el->el_outfile, "\tIt %s automatic margins\n",
1269	    EL_HAS_AUTO_MARGINS ? "has" : "does not have");
1270	if (EL_HAS_AUTO_MARGINS)
1271		(void) fprintf(el->el_outfile, "\tIt %s magic margins\n",
1272		    EL_HAS_MAGIC_MARGINS ? "has" : "does not have");
1273
1274	for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++)
1275		(void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n",
1276		    t->long_name,
1277		    t->name, *ts && **ts ?
1278		    key__decode_str(*ts, upbuf, "") : "(empty)");
1279	(void) fputc('\n', el->el_outfile);
1280	return (0);
1281}
1282
1283
1284/* term_settc():
1285 *	Change the current terminal characteristics
1286 */
1287protected int
1288/*ARGSUSED*/
1289term_settc(EditLine *el, int argc __unused,
1290    const char **argv)
1291{
1292	const struct termcapstr *ts;
1293	const struct termcapval *tv;
1294	const char *what, *how;
1295
1296	if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
1297		return (-1);
1298
1299	what = argv[1];
1300	how = argv[2];
1301
1302	/*
1303         * Do the strings first
1304         */
1305	for (ts = tstr; ts->name != NULL; ts++)
1306		if (strcmp(ts->name, what) == 0)
1307			break;
1308
1309	if (ts->name != NULL) {
1310		term_alloc(el, ts, how);
1311		term_setflags(el);
1312		return (0);
1313	}
1314	/*
1315         * Do the numeric ones second
1316         */
1317	for (tv = tval; tv->name != NULL; tv++)
1318		if (strcmp(tv->name, what) == 0)
1319			break;
1320
1321	if (tv->name != NULL) {
1322		if (tv == &tval[T_pt] || tv == &tval[T_km] ||
1323		    tv == &tval[T_am] || tv == &tval[T_xn]) {
1324			if (strcmp(how, "yes") == 0)
1325				el->el_term.t_val[tv - tval] = 1;
1326			else if (strcmp(how, "no") == 0)
1327				el->el_term.t_val[tv - tval] = 0;
1328			else {
1329				(void) fprintf(el->el_errfile,
1330				    "settc: Bad value `%s'.\n", how);
1331				return (-1);
1332			}
1333			term_setflags(el);
1334			if (term_change_size(el, Val(T_li), Val(T_co)) == -1)
1335				return (-1);
1336			return (0);
1337		} else {
1338			long i;
1339			char *ep;
1340
1341			i = strtol(how, &ep, 10);
1342			if (*ep != '\0') {
1343				(void) fprintf(el->el_errfile,
1344				    "settc: Bad value `%s'.\n", how);
1345				return (-1);
1346			}
1347			el->el_term.t_val[tv - tval] = (int) i;
1348			el->el_term.t_size.v = Val(T_co);
1349			el->el_term.t_size.h = Val(T_li);
1350			if (tv == &tval[T_co] || tv == &tval[T_li])
1351				if (term_change_size(el, Val(T_li), Val(T_co))
1352				    == -1)
1353					return (-1);
1354			return (0);
1355		}
1356	}
1357	return (-1);
1358}
1359
1360
1361/* term_echotc():
1362 *	Print the termcap string out with variable substitution
1363 */
1364protected int
1365/*ARGSUSED*/
1366term_echotc(EditLine *el, int argc __unused,
1367    const char **argv)
1368{
1369	char *cap, *scap, *ep;
1370	int arg_need, arg_cols, arg_rows;
1371	int verbose = 0, silent = 0;
1372	char *area;
1373	static const char fmts[] = "%s\n", fmtd[] = "%d\n";
1374	const struct termcapstr *t;
1375	char buf[TC_BUFSIZE];
1376	long i;
1377
1378	area = buf;
1379
1380	if (argv == NULL || argv[1] == NULL)
1381		return (-1);
1382	argv++;
1383
1384	if (argv[0][0] == '-') {
1385		switch (argv[0][1]) {
1386		case 'v':
1387			verbose = 1;
1388			break;
1389		case 's':
1390			silent = 1;
1391			break;
1392		default:
1393			/* stderror(ERR_NAME | ERR_TCUSAGE); */
1394			break;
1395		}
1396		argv++;
1397	}
1398	if (!*argv || *argv[0] == '\0')
1399		return (0);
1400	if (strcmp(*argv, "tabs") == 0) {
1401		(void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no");
1402		return (0);
1403	} else if (strcmp(*argv, "meta") == 0) {
1404		(void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no");
1405		return (0);
1406	} else if (strcmp(*argv, "xn") == 0) {
1407		(void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ?
1408		    "yes" : "no");
1409		return (0);
1410	} else if (strcmp(*argv, "am") == 0) {
1411		(void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ?
1412		    "yes" : "no");
1413		return (0);
1414	} else if (strcmp(*argv, "baud") == 0) {
1415#ifdef notdef
1416		int i;
1417
1418		for (i = 0; baud_rate[i].b_name != NULL; i++)
1419			if (el->el_tty.t_speed == baud_rate[i].b_rate) {
1420				(void) fprintf(el->el_outfile, fmts,
1421				    baud_rate[i].b_name);
1422				return (0);
1423			}
1424		(void) fprintf(el->el_outfile, fmtd, 0);
1425#else
1426		(void) fprintf(el->el_outfile, fmtd, (int)el->el_tty.t_speed);
1427#endif
1428		return (0);
1429	} else if (strcmp(*argv, "rows") == 0 || strcmp(*argv, "lines") == 0) {
1430		(void) fprintf(el->el_outfile, fmtd, Val(T_li));
1431		return (0);
1432	} else if (strcmp(*argv, "cols") == 0) {
1433		(void) fprintf(el->el_outfile, fmtd, Val(T_co));
1434		return (0);
1435	}
1436	/*
1437         * Try to use our local definition first
1438         */
1439	scap = NULL;
1440	for (t = tstr; t->name != NULL; t++)
1441		if (strcmp(t->name, *argv) == 0) {
1442			scap = el->el_term.t_str[t - tstr];
1443			break;
1444		}
1445	if (t->name == NULL) {
1446		/* XXX: some systems' tgetstr needs non const */
1447		scap = tgetstr(strchr(*argv, **argv), &area);
1448	}
1449	if (!scap || scap[0] == '\0') {
1450		if (!silent)
1451			(void) fprintf(el->el_errfile,
1452			    "echotc: Termcap parameter `%s' not found.\n",
1453			    *argv);
1454		return (-1);
1455	}
1456	/*
1457         * Count home many values we need for this capability.
1458         */
1459	for (cap = scap, arg_need = 0; *cap; cap++)
1460		if (*cap == '%')
1461			switch (*++cap) {
1462			case 'd':
1463			case '2':
1464			case '3':
1465			case '.':
1466			case '+':
1467				arg_need++;
1468				break;
1469			case '%':
1470			case '>':
1471			case 'i':
1472			case 'r':
1473			case 'n':
1474			case 'B':
1475			case 'D':
1476				break;
1477			default:
1478				/*
1479				 * hpux has lot's of them...
1480				 */
1481				if (verbose)
1482					(void) fprintf(el->el_errfile,
1483				"echotc: Warning: unknown termcap %% `%c'.\n",
1484					    *cap);
1485				/* This is bad, but I won't complain */
1486				break;
1487			}
1488
1489	switch (arg_need) {
1490	case 0:
1491		argv++;
1492		if (*argv && *argv[0]) {
1493			if (!silent)
1494				(void) fprintf(el->el_errfile,
1495				    "echotc: Warning: Extra argument `%s'.\n",
1496				    *argv);
1497			return (-1);
1498		}
1499		(void) tputs(scap, 1, term__putc);
1500		break;
1501	case 1:
1502		argv++;
1503		if (!*argv || *argv[0] == '\0') {
1504			if (!silent)
1505				(void) fprintf(el->el_errfile,
1506				    "echotc: Warning: Missing argument.\n");
1507			return (-1);
1508		}
1509		arg_cols = 0;
1510		i = strtol(*argv, &ep, 10);
1511		if (*ep != '\0' || i < 0) {
1512			if (!silent)
1513				(void) fprintf(el->el_errfile,
1514				    "echotc: Bad value `%s' for rows.\n",
1515				    *argv);
1516			return (-1);
1517		}
1518		arg_rows = (int) i;
1519		argv++;
1520		if (*argv && *argv[0]) {
1521			if (!silent)
1522				(void) fprintf(el->el_errfile,
1523				    "echotc: Warning: Extra argument `%s'.\n",
1524				    *argv);
1525			return (-1);
1526		}
1527		(void) tputs(tgoto(scap, arg_cols, arg_rows), 1, term__putc);
1528		break;
1529	default:
1530		/* This is wrong, but I will ignore it... */
1531		if (verbose)
1532			(void) fprintf(el->el_errfile,
1533			 "echotc: Warning: Too many required arguments (%d).\n",
1534			    arg_need);
1535		/* FALLTHROUGH */
1536	case 2:
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 cols.\n",
1549				    *argv);
1550			return (-1);
1551		}
1552		arg_cols = (int) i;
1553		argv++;
1554		if (!*argv || *argv[0] == '\0') {
1555			if (!silent)
1556				(void) fprintf(el->el_errfile,
1557				    "echotc: Warning: Missing argument.\n");
1558			return (-1);
1559		}
1560		i = strtol(*argv, &ep, 10);
1561		if (*ep != '\0' || i < 0) {
1562			if (!silent)
1563				(void) fprintf(el->el_errfile,
1564				    "echotc: Bad value `%s' for rows.\n",
1565				    *argv);
1566			return (-1);
1567		}
1568		arg_rows = (int) i;
1569		if (*ep != '\0') {
1570			if (!silent)
1571				(void) fprintf(el->el_errfile,
1572				    "echotc: Bad value `%s'.\n", *argv);
1573			return (-1);
1574		}
1575		argv++;
1576		if (*argv && *argv[0]) {
1577			if (!silent)
1578				(void) fprintf(el->el_errfile,
1579				    "echotc: Warning: Extra argument `%s'.\n",
1580				    *argv);
1581			return (-1);
1582		}
1583		(void) tputs(tgoto(scap, arg_cols, arg_rows), arg_rows,
1584		    term__putc);
1585		break;
1586	}
1587	return (0);
1588}
1589