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