11573Srgrimes/*-
21573Srgrimes * Copyright (c) 1992, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * This code is derived from software contributed to Berkeley by
61573Srgrimes * Christos Zoulas of Cornell University.
71573Srgrimes *
81573Srgrimes * Redistribution and use in source and binary forms, with or without
91573Srgrimes * modification, are permitted provided that the following conditions
101573Srgrimes * are met:
111573Srgrimes * 1. Redistributions of source code must retain the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer.
131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141573Srgrimes *    notice, this list of conditions and the following disclaimer in the
151573Srgrimes *    documentation and/or other materials provided with the distribution.
16148834Sstefanf * 3. Neither the name of the University nor the names of its contributors
171573Srgrimes *    may be used to endorse or promote products derived from this software
181573Srgrimes *    without specific prior written permission.
191573Srgrimes *
201573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301573Srgrimes * SUCH DAMAGE.
3184260Sobrien *
32237448Spfg *	$NetBSD: term.c,v 1.56 2009/12/28 21:54:21 christos Exp $
331573Srgrimes */
341573Srgrimes
351573Srgrimes#if !defined(lint) && !defined(SCCSID)
3611678Sbdestatic char sccsid[] = "@(#)term.c	8.2 (Berkeley) 4/30/95";
371573Srgrimes#endif /* not lint && not SCCSID */
3884260Sobrien#include <sys/cdefs.h>
3984260Sobrien__FBSDID("$FreeBSD$");
401573Srgrimes
411573Srgrimes/*
421573Srgrimes * term.c: Editor/termcap-curses interface
431573Srgrimes *	   We have to declare a static variable here, since the
441573Srgrimes *	   termcap putchar routine does not take an argument!
451573Srgrimes */
461573Srgrimes#include <stdio.h>
471573Srgrimes#include <signal.h>
481573Srgrimes#include <string.h>
491573Srgrimes#include <stdlib.h>
501573Srgrimes#include <unistd.h>
519898Sache#include <termcap.h>
52148834Sstefanf#include <curses.h>
53148834Sstefanf#include <ncurses.h>
54148834Sstefanf#include <term.h>
551573Srgrimes#include <sys/types.h>
5617141Sjkh#include <sys/ioctl.h>
571573Srgrimes
581573Srgrimes#include "el.h"
591573Srgrimes
601573Srgrimes/*
611573Srgrimes * IMPORTANT NOTE: these routines are allowed to look at the current screen
621573Srgrimes * and the current possition assuming that it is correct.  If this is not
631573Srgrimes * true, then the update will be WRONG!  This is (should be) a valid
641573Srgrimes * assumption...
651573Srgrimes */
661573Srgrimes
6784260Sobrien#define	TC_BUFSIZE	2048
681573Srgrimes
6984260Sobrien#define	GoodStr(a)	(el->el_term.t_str[a] != NULL && \
7084260Sobrien			    el->el_term.t_str[a][0] != '\0')
7184260Sobrien#define	Str(a)		el->el_term.t_str[a]
7284260Sobrien#define	Val(a)		el->el_term.t_val[a]
731573Srgrimes
7484260Sobrien#ifdef notdef
7584260Sobrienprivate const struct {
7684260Sobrien	const char *b_name;
7784260Sobrien	int b_rate;
7884260Sobrien} baud_rate[] = {
7984260Sobrien#ifdef B0
8084260Sobrien	{ "0", B0 },
8184260Sobrien#endif
8284260Sobrien#ifdef B50
8384260Sobrien	{ "50", B50 },
8484260Sobrien#endif
8584260Sobrien#ifdef B75
8684260Sobrien	{ "75", B75 },
8784260Sobrien#endif
8884260Sobrien#ifdef B110
8984260Sobrien	{ "110", B110 },
9084260Sobrien#endif
9184260Sobrien#ifdef B134
9284260Sobrien	{ "134", B134 },
9384260Sobrien#endif
9484260Sobrien#ifdef B150
9584260Sobrien	{ "150", B150 },
9684260Sobrien#endif
9784260Sobrien#ifdef B200
9884260Sobrien	{ "200", B200 },
9984260Sobrien#endif
10084260Sobrien#ifdef B300
10184260Sobrien	{ "300", B300 },
10284260Sobrien#endif
10384260Sobrien#ifdef B600
10484260Sobrien	{ "600", B600 },
10584260Sobrien#endif
10684260Sobrien#ifdef B900
10784260Sobrien	{ "900", B900 },
10884260Sobrien#endif
10984260Sobrien#ifdef B1200
11084260Sobrien	{ "1200", B1200 },
11184260Sobrien#endif
11284260Sobrien#ifdef B1800
11384260Sobrien	{ "1800", B1800 },
11484260Sobrien#endif
11584260Sobrien#ifdef B2400
11684260Sobrien	{ "2400", B2400 },
11784260Sobrien#endif
11884260Sobrien#ifdef B3600
11984260Sobrien	{ "3600", B3600 },
12084260Sobrien#endif
12184260Sobrien#ifdef B4800
12284260Sobrien	{ "4800", B4800 },
12384260Sobrien#endif
12484260Sobrien#ifdef B7200
12584260Sobrien	{ "7200", B7200 },
12684260Sobrien#endif
12784260Sobrien#ifdef B9600
12884260Sobrien	{ "9600", B9600 },
12984260Sobrien#endif
13084260Sobrien#ifdef EXTA
13184260Sobrien	{ "19200", EXTA },
13284260Sobrien#endif
13384260Sobrien#ifdef B19200
13484260Sobrien	{ "19200", B19200 },
13584260Sobrien#endif
13684260Sobrien#ifdef EXTB
13784260Sobrien	{ "38400", EXTB },
13884260Sobrien#endif
13984260Sobrien#ifdef B38400
14084260Sobrien	{ "38400", B38400 },
14184260Sobrien#endif
14284260Sobrien	{ NULL, 0 }
14384260Sobrien};
14484260Sobrien#endif
14584260Sobrien
14684260Sobrienprivate const struct termcapstr {
14784260Sobrien	const char *name;
14884260Sobrien	const char *long_name;
1491573Srgrimes} tstr[] = {
15084260Sobrien#define	T_al	0
15184260Sobrien	{ "al", "add new blank line" },
15284260Sobrien#define	T_bl	1
15384260Sobrien	{ "bl", "audible bell" },
15484260Sobrien#define	T_cd	2
15584260Sobrien	{ "cd", "clear to bottom" },
15684260Sobrien#define	T_ce	3
15784260Sobrien	{ "ce", "clear to end of line" },
15884260Sobrien#define	T_ch	4
15984260Sobrien	{ "ch", "cursor to horiz pos" },
16084260Sobrien#define	T_cl	5
16184260Sobrien	{ "cl", "clear screen" },
1621573Srgrimes#define	T_dc	6
16384260Sobrien	{ "dc", "delete a character" },
1641573Srgrimes#define	T_dl	7
16584260Sobrien	{ "dl", "delete a line" },
1661573Srgrimes#define	T_dm	8
16784260Sobrien	{ "dm", "start delete mode" },
1681573Srgrimes#define	T_ed	9
16984260Sobrien	{ "ed", "end delete mode" },
1701573Srgrimes#define	T_ei	10
17184260Sobrien	{ "ei", "end insert mode" },
1721573Srgrimes#define	T_fs	11
17384260Sobrien	{ "fs", "cursor from status line" },
1741573Srgrimes#define	T_ho	12
17584260Sobrien	{ "ho", "home cursor" },
1761573Srgrimes#define	T_ic	13
17784260Sobrien	{ "ic", "insert character" },
1788870Srgrimes#define	T_im	14
17984260Sobrien	{ "im", "start insert mode" },
1801573Srgrimes#define	T_ip	15
18184260Sobrien	{ "ip", "insert padding" },
1821573Srgrimes#define	T_kd	16
18384260Sobrien	{ "kd", "sends cursor down" },
1841573Srgrimes#define	T_kl	17
18584260Sobrien	{ "kl", "sends cursor left" },
18684260Sobrien#define	T_kr	18
18784260Sobrien	{ "kr", "sends cursor right" },
18884260Sobrien#define	T_ku	19
18984260Sobrien	{ "ku", "sends cursor up" },
19084260Sobrien#define	T_md	20
19184260Sobrien	{ "md", "begin bold" },
19284260Sobrien#define	T_me	21
19384260Sobrien	{ "me", "end attributes" },
19484260Sobrien#define	T_nd	22
19584260Sobrien	{ "nd", "non destructive space" },
19684260Sobrien#define	T_se	23
19784260Sobrien	{ "se", "end standout" },
19884260Sobrien#define	T_so	24
19984260Sobrien	{ "so", "begin standout" },
20084260Sobrien#define	T_ts	25
20184260Sobrien	{ "ts", "cursor to status line" },
20284260Sobrien#define	T_up	26
20384260Sobrien	{ "up", "cursor up one" },
20484260Sobrien#define	T_us	27
20584260Sobrien	{ "us", "begin underline" },
20684260Sobrien#define	T_ue	28
20784260Sobrien	{ "ue", "end underline" },
20884260Sobrien#define	T_vb	29
20984260Sobrien	{ "vb", "visible bell" },
21084260Sobrien#define	T_DC	30
21184260Sobrien	{ "DC", "delete multiple chars" },
21284260Sobrien#define	T_DO	31
21384260Sobrien	{ "DO", "cursor down multiple" },
21484260Sobrien#define	T_IC	32
21584260Sobrien	{ "IC", "insert multiple chars" },
21684260Sobrien#define	T_LE	33
21784260Sobrien	{ "LE", "cursor left multiple" },
21884260Sobrien#define	T_RI	34
21984260Sobrien	{ "RI", "cursor right multiple" },
22084260Sobrien#define	T_UP	35
22184260Sobrien	{ "UP", "cursor up multiple" },
22284260Sobrien#define	T_kh	36
22384260Sobrien	{ "kh", "send cursor home" },
22484260Sobrien#define	T_at7	37
22584260Sobrien	{ "@7", "send cursor end" },
226212235Sjilles#define	T_kD	38
227212235Sjilles	{ "kD", "send cursor delete" },
228212235Sjilles#define	T_str	39
22984260Sobrien	{ NULL, NULL }
2301573Srgrimes};
2311573Srgrimes
23284260Sobrienprivate const struct termcapval {
23384260Sobrien	const char *name;
23484260Sobrien	const char *long_name;
2351573Srgrimes} tval[] = {
23684260Sobrien#define	T_am	0
23784260Sobrien	{ "am", "has automatic margins" },
23884260Sobrien#define	T_pt	1
23984260Sobrien	{ "pt", "has physical tabs" },
24084260Sobrien#define	T_li	2
24184260Sobrien	{ "li", "Number of lines" },
24284260Sobrien#define	T_co	3
24384260Sobrien	{ "co", "Number of columns" },
24484260Sobrien#define	T_km	4
24584260Sobrien	{ "km", "Has meta key" },
24684260Sobrien#define	T_xt	5
24784260Sobrien	{ "xt", "Tab chars destructive" },
24884260Sobrien#define	T_xn	6
24984260Sobrien	{ "xn", "newline ignored at right margin" },
25084260Sobrien#define	T_MT	7
25184260Sobrien	{ "MT", "Has meta key" },			/* XXX? */
25284260Sobrien#define	T_val	8
25384260Sobrien	{ NULL, NULL, }
2541573Srgrimes};
2551573Srgrimes/* do two or more of the attributes use me */
2561573Srgrimes
25784260Sobrienprivate void	term_setflags(EditLine *);
25884260Sobrienprivate int	term_rebuffer_display(EditLine *);
25984260Sobrienprivate void	term_free_display(EditLine *);
26084260Sobrienprivate int	term_alloc_display(EditLine *);
261148834Sstefanfprivate void	term_alloc(EditLine *, const struct termcapstr *, const char *);
26284260Sobrienprivate void	term_init_arrow(EditLine *);
26384260Sobrienprivate void	term_reset_arrow(EditLine *);
264237448Spfgprivate int	term_putc(int);
265237448Spfgprivate void	term_tputs(EditLine *, const char *, int);
2661573Srgrimes
267237448Spfg#ifdef _REENTRANT
268237448Spfgprivate pthread_mutex_t term_mutex = PTHREAD_MUTEX_INITIALIZER;
269237448Spfg#endif
270237448Spfgprivate FILE *term_outfile = NULL;
2711573Srgrimes
2721573Srgrimes
2731573Srgrimes/* term_setflags():
2741573Srgrimes *	Set the terminal capability flags
2751573Srgrimes */
2761573Srgrimesprivate void
27784260Sobrienterm_setflags(EditLine *el)
2781573Srgrimes{
27984260Sobrien	EL_FLAGS = 0;
28084260Sobrien	if (el->el_tty.t_tabs)
28184260Sobrien		EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0;
2821573Srgrimes
28384260Sobrien	EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0;
28484260Sobrien	EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0;
28584260Sobrien	EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0;
28684260Sobrien	EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ?
28784260Sobrien	    TERM_CAN_INSERT : 0;
28884260Sobrien	EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0;
28984260Sobrien	EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0;
29084260Sobrien	EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0;
2911573Srgrimes
29284260Sobrien	if (GoodStr(T_me) && GoodStr(T_ue))
29384260Sobrien		EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ?
29484260Sobrien		    TERM_CAN_ME : 0;
29584260Sobrien	else
29684260Sobrien		EL_FLAGS &= ~TERM_CAN_ME;
29784260Sobrien	if (GoodStr(T_me) && GoodStr(T_se))
29884260Sobrien		EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ?
29984260Sobrien		    TERM_CAN_ME : 0;
3001573Srgrimes
3011573Srgrimes
3021573Srgrimes#ifdef DEBUG_SCREEN
30384260Sobrien	if (!EL_CAN_UP) {
30484260Sobrien		(void) fprintf(el->el_errfile,
30584260Sobrien		    "WARNING: Your terminal cannot move up.\n");
30684260Sobrien		(void) fprintf(el->el_errfile,
30784260Sobrien		    "Editing may be odd for long lines.\n");
30884260Sobrien	}
30984260Sobrien	if (!EL_CAN_CEOL)
31084260Sobrien		(void) fprintf(el->el_errfile, "no clear EOL capability.\n");
31184260Sobrien	if (!EL_CAN_DELETE)
31284260Sobrien		(void) fprintf(el->el_errfile, "no delete char capability.\n");
31384260Sobrien	if (!EL_CAN_INSERT)
31484260Sobrien		(void) fprintf(el->el_errfile, "no insert char capability.\n");
3151573Srgrimes#endif /* DEBUG_SCREEN */
3161573Srgrimes}
3171573Srgrimes
3181573Srgrimes/* term_init():
3191573Srgrimes *	Initialize the terminal stuff
3201573Srgrimes */
3211573Srgrimesprotected int
32284260Sobrienterm_init(EditLine *el)
3231573Srgrimes{
32484260Sobrien
32584260Sobrien	el->el_term.t_buf = (char *) el_malloc(TC_BUFSIZE);
32684260Sobrien	if (el->el_term.t_buf == NULL)
32784260Sobrien		return (-1);
32884260Sobrien	el->el_term.t_cap = (char *) el_malloc(TC_BUFSIZE);
32984260Sobrien	if (el->el_term.t_cap == NULL)
33084260Sobrien		return (-1);
33184260Sobrien	el->el_term.t_fkey = (fkey_t *) el_malloc(A_K_NKEYS * sizeof(fkey_t));
33284260Sobrien	if (el->el_term.t_fkey == NULL)
33384260Sobrien		return (-1);
33484260Sobrien	el->el_term.t_loc = 0;
33584260Sobrien	el->el_term.t_str = (char **) el_malloc(T_str * sizeof(char *));
33684260Sobrien	if (el->el_term.t_str == NULL)
33784260Sobrien		return (-1);
33884260Sobrien	(void) memset(el->el_term.t_str, 0, T_str * sizeof(char *));
33984260Sobrien	el->el_term.t_val = (int *) el_malloc(T_val * sizeof(int));
34084260Sobrien	if (el->el_term.t_val == NULL)
34184260Sobrien		return (-1);
34284260Sobrien	(void) memset(el->el_term.t_val, 0, T_val * sizeof(int));
343238810Spfg	(void) term_set(el, NULL);
34489735Sobrien	term_init_arrow(el);
34584260Sobrien	return (0);
3461573Srgrimes}
347148834Sstefanf
3481573Srgrimes/* term_end():
3491573Srgrimes *	Clean up the terminal stuff
3501573Srgrimes */
3511573Srgrimesprotected void
35284260Sobrienterm_end(EditLine *el)
3531573Srgrimes{
35484260Sobrien
35584260Sobrien	el_free((ptr_t) el->el_term.t_buf);
35684260Sobrien	el->el_term.t_buf = NULL;
35784260Sobrien	el_free((ptr_t) el->el_term.t_cap);
35884260Sobrien	el->el_term.t_cap = NULL;
35984260Sobrien	el->el_term.t_loc = 0;
36084260Sobrien	el_free((ptr_t) el->el_term.t_str);
36184260Sobrien	el->el_term.t_str = NULL;
36284260Sobrien	el_free((ptr_t) el->el_term.t_val);
36384260Sobrien	el->el_term.t_val = NULL;
364148834Sstefanf	el_free((ptr_t) el->el_term.t_fkey);
365148834Sstefanf	el->el_term.t_fkey = NULL;
36684260Sobrien	term_free_display(el);
3671573Srgrimes}
3681573Srgrimes
3691573Srgrimes
3701573Srgrimes/* term_alloc():
3711573Srgrimes *	Maintain a string pool for termcap strings
3721573Srgrimes */
3731573Srgrimesprivate void
374148834Sstefanfterm_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
3751573Srgrimes{
37684260Sobrien	char termbuf[TC_BUFSIZE];
377237448Spfg	size_t tlen, clen;
37884260Sobrien	char **tlist = el->el_term.t_str;
37984260Sobrien	char **tmp, **str = &tlist[t - tstr];
3801573Srgrimes
38184260Sobrien	if (cap == NULL || *cap == '\0') {
38284260Sobrien		*str = NULL;
38384260Sobrien		return;
38484260Sobrien	} else
38584260Sobrien		clen = strlen(cap);
3861573Srgrimes
38784260Sobrien	tlen = *str == NULL ? 0 : strlen(*str);
3881573Srgrimes
38984260Sobrien	/*
39084260Sobrien         * New string is shorter; no need to allocate space
39184260Sobrien         */
39284260Sobrien	if (clen <= tlen) {
393167457Sstefanf		if (*str)
394167457Sstefanf			(void) strcpy(*str, cap);	/* XXX strcpy is safe */
39584260Sobrien		return;
39684260Sobrien	}
39784260Sobrien	/*
39884260Sobrien         * New string is longer; see if we have enough space to append
39984260Sobrien         */
40084260Sobrien	if (el->el_term.t_loc + 3 < TC_BUFSIZE) {
40184260Sobrien						/* XXX strcpy is safe */
40284260Sobrien		(void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc],
40384260Sobrien		    cap);
404237448Spfg		el->el_term.t_loc += (int)clen + 1;	/* one for \0 */
40584260Sobrien		return;
40684260Sobrien	}
40784260Sobrien	/*
40884260Sobrien         * Compact our buffer; no need to check compaction, cause we know it
40984260Sobrien         * fits...
41084260Sobrien         */
41184260Sobrien	tlen = 0;
41284260Sobrien	for (tmp = tlist; tmp < &tlist[T_str]; tmp++)
41384260Sobrien		if (*tmp != NULL && *tmp != '\0' && *tmp != *str) {
41484260Sobrien			char *ptr;
4151573Srgrimes
41684260Sobrien			for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++)
41784260Sobrien				continue;
41884260Sobrien			termbuf[tlen++] = '\0';
41984260Sobrien		}
42084260Sobrien	memcpy(el->el_term.t_buf, termbuf, TC_BUFSIZE);
421237448Spfg	el->el_term.t_loc = (int)tlen;
42284260Sobrien	if (el->el_term.t_loc + 3 >= TC_BUFSIZE) {
42384260Sobrien		(void) fprintf(el->el_errfile,
42484260Sobrien		    "Out of termcap string space.\n");
42584260Sobrien		return;
42684260Sobrien	}
42784260Sobrien					/* XXX strcpy is safe */
42884260Sobrien	(void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap);
429237448Spfg	el->el_term.t_loc += (int)clen + 1;	/* one for \0 */
4301573Srgrimes	return;
43184260Sobrien}
4321573Srgrimes
4331573Srgrimes
4341573Srgrimes/* term_rebuffer_display():
4351573Srgrimes *	Rebuffer the display after the screen changed size
4361573Srgrimes */
43784260Sobrienprivate int
43884260Sobrienterm_rebuffer_display(EditLine *el)
4391573Srgrimes{
44084260Sobrien	coord_t *c = &el->el_term.t_size;
4411573Srgrimes
44284260Sobrien	term_free_display(el);
4431573Srgrimes
44484260Sobrien	c->h = Val(T_co);
44584260Sobrien	c->v = Val(T_li);
4461573Srgrimes
44784260Sobrien	if (term_alloc_display(el) == -1)
44884260Sobrien		return (-1);
44984260Sobrien	return (0);
45084260Sobrien}
4511573Srgrimes
4521573Srgrimes
4531573Srgrimes/* term_alloc_display():
4541573Srgrimes *	Allocate a new display.
4551573Srgrimes */
45684260Sobrienprivate int
45784260Sobrienterm_alloc_display(EditLine *el)
4581573Srgrimes{
45984260Sobrien	int i;
46084260Sobrien	char **b;
46184260Sobrien	coord_t *c = &el->el_term.t_size;
4621573Srgrimes
46384260Sobrien	b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
46484260Sobrien	if (b == NULL)
46584260Sobrien		return (-1);
46684260Sobrien	for (i = 0; i < c->v; i++) {
46784260Sobrien		b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
468167457Sstefanf		if (b[i] == NULL) {
469167457Sstefanf			while (--i >= 0)
470167457Sstefanf				el_free((ptr_t) b[i]);
471167457Sstefanf			el_free((ptr_t) b);
47284260Sobrien			return (-1);
473167457Sstefanf		}
47484260Sobrien	}
47584260Sobrien	b[c->v] = NULL;
47684260Sobrien	el->el_display = b;
4771573Srgrimes
47884260Sobrien	b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
47984260Sobrien	if (b == NULL)
48084260Sobrien		return (-1);
48184260Sobrien	for (i = 0; i < c->v; i++) {
48284260Sobrien		b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
483167457Sstefanf		if (b[i] == NULL) {
484167457Sstefanf			while (--i >= 0)
485167457Sstefanf				el_free((ptr_t) b[i]);
486167457Sstefanf			el_free((ptr_t) b);
48784260Sobrien			return (-1);
488167457Sstefanf		}
48984260Sobrien	}
49084260Sobrien	b[c->v] = NULL;
49184260Sobrien	el->el_vdisplay = b;
49284260Sobrien	return (0);
49384260Sobrien}
4941573Srgrimes
4951573Srgrimes
4961573Srgrimes/* term_free_display():
4971573Srgrimes *	Free the display buffers
4981573Srgrimes */
4991573Srgrimesprivate void
50084260Sobrienterm_free_display(EditLine *el)
5011573Srgrimes{
50284260Sobrien	char **b;
50384260Sobrien	char **bufp;
5041573Srgrimes
50584260Sobrien	b = el->el_display;
50684260Sobrien	el->el_display = NULL;
50784260Sobrien	if (b != NULL) {
50884260Sobrien		for (bufp = b; *bufp != NULL; bufp++)
50984260Sobrien			el_free((ptr_t) * bufp);
51084260Sobrien		el_free((ptr_t) b);
51184260Sobrien	}
51284260Sobrien	b = el->el_vdisplay;
51384260Sobrien	el->el_vdisplay = NULL;
51484260Sobrien	if (b != NULL) {
51584260Sobrien		for (bufp = b; *bufp != NULL; bufp++)
51684260Sobrien			el_free((ptr_t) * bufp);
51784260Sobrien		el_free((ptr_t) b);
51884260Sobrien	}
51984260Sobrien}
5201573Srgrimes
5211573Srgrimes
5221573Srgrimes/* term_move_to_line():
5231573Srgrimes *	move to line <where> (first line == 0)
5241573Srgrimes * 	as efficiently as possible
5251573Srgrimes */
5261573Srgrimesprotected void
52784260Sobrienterm_move_to_line(EditLine *el, int where)
5281573Srgrimes{
52984260Sobrien	int del;
5301573Srgrimes
53184260Sobrien	if (where == el->el_cursor.v)
53284260Sobrien		return;
5331573Srgrimes
53484260Sobrien	if (where > el->el_term.t_size.v) {
5351573Srgrimes#ifdef DEBUG_SCREEN
53684260Sobrien		(void) fprintf(el->el_errfile,
53784260Sobrien		    "term_move_to_line: where is ridiculous: %d\r\n", where);
5381573Srgrimes#endif /* DEBUG_SCREEN */
53984260Sobrien		return;
5401573Srgrimes	}
54184260Sobrien	if ((del = where - el->el_cursor.v) > 0) {
54284260Sobrien		while (del > 0) {
54384260Sobrien			if (EL_HAS_AUTO_MARGINS &&
54484260Sobrien			    el->el_display[el->el_cursor.v][0] != '\0') {
54584260Sobrien				/* move without newline */
54684260Sobrien				term_move_to_char(el, el->el_term.t_size.h - 1);
547237448Spfg				term_overwrite(el, &el->el_display
548237448Spfg				    [el->el_cursor.v][el->el_cursor.h],
549237448Spfg				    (size_t)(el->el_term.t_size.h -
550237448Spfg				    el->el_cursor.h));
55184260Sobrien				/* updates Cursor */
55284260Sobrien				del--;
55384260Sobrien			} else {
55484260Sobrien				if ((del > 1) && GoodStr(T_DO)) {
555237448Spfg					term_tputs(el, tgoto(Str(T_DO), del,
556237448Spfg					    del), del);
55784260Sobrien					del = 0;
55884260Sobrien				} else {
55984260Sobrien					for (; del > 0; del--)
560237448Spfg						term__putc(el, '\n');
56184260Sobrien					/* because the \n will become \r\n */
56284260Sobrien					el->el_cursor.h = 0;
56384260Sobrien				}
56484260Sobrien			}
56584260Sobrien		}
56684260Sobrien	} else {		/* del < 0 */
56784260Sobrien		if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up)))
568237448Spfg			term_tputs(el, tgoto(Str(T_UP), -del, -del), -del);
56984260Sobrien		else {
57084260Sobrien			if (GoodStr(T_up))
57184260Sobrien				for (; del < 0; del++)
572237448Spfg					term_tputs(el, Str(T_up), 1);
57384260Sobrien		}
5741573Srgrimes	}
57584260Sobrien	el->el_cursor.v = where;/* now where is here */
57684260Sobrien}
5771573Srgrimes
5781573Srgrimes
5791573Srgrimes/* term_move_to_char():
5801573Srgrimes *	Move to the character position specified
5811573Srgrimes */
5821573Srgrimesprotected void
58384260Sobrienterm_move_to_char(EditLine *el, int where)
5841573Srgrimes{
58584260Sobrien	int del, i;
5861573Srgrimes
5871573Srgrimesmc_again:
58884260Sobrien	if (where == el->el_cursor.h)
58984260Sobrien		return;
5901573Srgrimes
59184260Sobrien	if (where > el->el_term.t_size.h) {
5921573Srgrimes#ifdef DEBUG_SCREEN
59384260Sobrien		(void) fprintf(el->el_errfile,
59484260Sobrien		    "term_move_to_char: where is riduculous: %d\r\n", where);
5951573Srgrimes#endif /* DEBUG_SCREEN */
59684260Sobrien		return;
59784260Sobrien	}
59884260Sobrien	if (!where) {		/* if where is first column */
599237448Spfg		term__putc(el, '\r');	/* do a CR */
60084260Sobrien		el->el_cursor.h = 0;
60184260Sobrien		return;
60284260Sobrien	}
60384260Sobrien	del = where - el->el_cursor.h;
6041573Srgrimes
60584260Sobrien	if ((del < -4 || del > 4) && GoodStr(T_ch))
60684260Sobrien		/* go there directly */
607237448Spfg		term_tputs(el, tgoto(Str(T_ch), where, where), where);
60884260Sobrien	else {
60984260Sobrien		if (del > 0) {	/* moving forward */
61084260Sobrien			if ((del > 4) && GoodStr(T_RI))
611237448Spfg				term_tputs(el, tgoto(Str(T_RI), del, del), del);
61284260Sobrien			else {
61384260Sobrien					/* if I can do tabs, use them */
61484260Sobrien				if (EL_CAN_TAB) {
61584260Sobrien					if ((el->el_cursor.h & 0370) !=
616237448Spfg					    (where & ~0x7)) {
61784260Sobrien						/* if not within tab stop */
61884260Sobrien						for (i =
61984260Sobrien						    (el->el_cursor.h & 0370);
620237448Spfg						    i < (where & ~0x7);
62184260Sobrien						    i += 8)
622237448Spfg							term__putc(el, '\t');
62384260Sobrien							/* then tab over */
624237448Spfg						el->el_cursor.h = where & ~0x7;
62584260Sobrien					}
62684260Sobrien				}
62784260Sobrien				/*
62884260Sobrien				 * it's usually cheaper to just write the
62984260Sobrien				 * chars, so we do.
63084260Sobrien				 */
63184260Sobrien				/*
63284260Sobrien				 * NOTE THAT term_overwrite() WILL CHANGE
63384260Sobrien				 * el->el_cursor.h!!!
63484260Sobrien				 */
635237448Spfg				term_overwrite(el, &el->el_display[
636237448Spfg				    el->el_cursor.v][el->el_cursor.h],
637237448Spfg				    (size_t)(where - el->el_cursor.h));
6381573Srgrimes
63984260Sobrien			}
64084260Sobrien		} else {	/* del < 0 := moving backward */
64184260Sobrien			if ((-del > 4) && GoodStr(T_LE))
642237448Spfg				term_tputs(el, tgoto(Str(T_LE), -del, -del),
643237448Spfg				    -del);
64484260Sobrien			else {	/* can't go directly there */
64584260Sobrien				/*
64684260Sobrien				 * if the "cost" is greater than the "cost"
64784260Sobrien				 * from col 0
64884260Sobrien				 */
64984260Sobrien				if (EL_CAN_TAB ?
650148834Sstefanf				    ((unsigned int)-del >
651148834Sstefanf				    (((unsigned int) where >> 3) +
65284260Sobrien				     (where & 07)))
65384260Sobrien				    : (-del > where)) {
654237448Spfg					term__putc(el, '\r');	/* do a CR */
65584260Sobrien					el->el_cursor.h = 0;
65684260Sobrien					goto mc_again;	/* and try again */
65784260Sobrien				}
65884260Sobrien				for (i = 0; i < -del; i++)
659237448Spfg					term__putc(el, '\b');
66084260Sobrien			}
6611573Srgrimes		}
6621573Srgrimes	}
66384260Sobrien	el->el_cursor.h = where;		/* now where is here */
66484260Sobrien}
6651573Srgrimes
6661573Srgrimes
6671573Srgrimes/* term_overwrite():
6681573Srgrimes *	Overstrike num characters
6691573Srgrimes */
6701573Srgrimesprotected void
671237448Spfgterm_overwrite(EditLine *el, const char *cp, size_t n)
6721573Srgrimes{
673237448Spfg	if (n == 0)
674237448Spfg		return;
6751573Srgrimes
676237448Spfg	if (n > (size_t)el->el_term.t_size.h) {
6771573Srgrimes#ifdef DEBUG_SCREEN
67884260Sobrien		(void) fprintf(el->el_errfile,
67984260Sobrien		    "term_overwrite: n is riduculous: %d\r\n", n);
6801573Srgrimes#endif /* DEBUG_SCREEN */
68184260Sobrien		return;
68284260Sobrien	}
683237448Spfg
68484260Sobrien	do {
685237448Spfg		term__putc(el, *cp++);
68684260Sobrien		el->el_cursor.h++;
68784260Sobrien	} while (--n);
6881573Srgrimes
68984260Sobrien	if (el->el_cursor.h >= el->el_term.t_size.h) {	/* wrap? */
69084260Sobrien		if (EL_HAS_AUTO_MARGINS) {	/* yes */
69184260Sobrien			el->el_cursor.h = 0;
69284260Sobrien			el->el_cursor.v++;
69384260Sobrien			if (EL_HAS_MAGIC_MARGINS) {
69484260Sobrien				/* force the wrap to avoid the "magic"
69584260Sobrien				 * situation */
69684260Sobrien				char c;
69784260Sobrien				if ((c = el->el_display[el->el_cursor.v][el->el_cursor.h])
69884260Sobrien				    != '\0')
69984260Sobrien					term_overwrite(el, &c, 1);
700237448Spfg				else {
701237448Spfg					term__putc(el, ' ');
702237448Spfg					el->el_cursor.h = 1;
703237448Spfg				}
70484260Sobrien			}
70584260Sobrien		} else		/* no wrap, but cursor stays on screen */
706237448Spfg			el->el_cursor.h = el->el_term.t_size.h - 1;
70784260Sobrien	}
70884260Sobrien}
7091573Srgrimes
7101573Srgrimes
7111573Srgrimes/* term_deletechars():
7121573Srgrimes *	Delete num characters
7131573Srgrimes */
7141573Srgrimesprotected void
71584260Sobrienterm_deletechars(EditLine *el, int num)
7161573Srgrimes{
71784260Sobrien	if (num <= 0)
71884260Sobrien		return;
7191573Srgrimes
72084260Sobrien	if (!EL_CAN_DELETE) {
7211573Srgrimes#ifdef DEBUG_EDIT
72284260Sobrien		(void) fprintf(el->el_errfile, "   ERROR: cannot delete   \n");
7231573Srgrimes#endif /* DEBUG_EDIT */
72484260Sobrien		return;
72584260Sobrien	}
72684260Sobrien	if (num > el->el_term.t_size.h) {
7271573Srgrimes#ifdef DEBUG_SCREEN
72884260Sobrien		(void) fprintf(el->el_errfile,
72984260Sobrien		    "term_deletechars: num is riduculous: %d\r\n", num);
7301573Srgrimes#endif /* DEBUG_SCREEN */
73184260Sobrien		return;
7321573Srgrimes	}
73384260Sobrien	if (GoodStr(T_DC))	/* if I have multiple delete */
73484260Sobrien		if ((num > 1) || !GoodStr(T_dc)) {	/* if dc would be more
73584260Sobrien							 * expen. */
736237448Spfg			term_tputs(el, tgoto(Str(T_DC), num, num), num);
73784260Sobrien			return;
73884260Sobrien		}
73984260Sobrien	if (GoodStr(T_dm))	/* if I have delete mode */
740237448Spfg		term_tputs(el, Str(T_dm), 1);
7411573Srgrimes
74284260Sobrien	if (GoodStr(T_dc))	/* else do one at a time */
74384260Sobrien		while (num--)
744237448Spfg			term_tputs(el, Str(T_dc), 1);
7451573Srgrimes
74684260Sobrien	if (GoodStr(T_ed))	/* if I have delete mode */
747237448Spfg		term_tputs(el, Str(T_ed), 1);
74884260Sobrien}
7491573Srgrimes
7501573Srgrimes
7511573Srgrimes/* term_insertwrite():
7528870Srgrimes *	Puts terminal in insert character mode or inserts num
7538870Srgrimes *	characters in the line
7541573Srgrimes */
7551573Srgrimesprotected void
75684260Sobrienterm_insertwrite(EditLine *el, char *cp, int num)
7571573Srgrimes{
75884260Sobrien	if (num <= 0)
75984260Sobrien		return;
76084260Sobrien	if (!EL_CAN_INSERT) {
7611573Srgrimes#ifdef DEBUG_EDIT
76284260Sobrien		(void) fprintf(el->el_errfile, "   ERROR: cannot insert   \n");
7631573Srgrimes#endif /* DEBUG_EDIT */
76484260Sobrien		return;
76584260Sobrien	}
76684260Sobrien	if (num > el->el_term.t_size.h) {
7671573Srgrimes#ifdef DEBUG_SCREEN
76884260Sobrien		(void) fprintf(el->el_errfile,
76984260Sobrien		    "StartInsert: num is riduculous: %d\r\n", num);
7701573Srgrimes#endif /* DEBUG_SCREEN */
77184260Sobrien		return;
7721573Srgrimes	}
77384260Sobrien	if (GoodStr(T_IC))	/* if I have multiple insert */
77484260Sobrien		if ((num > 1) || !GoodStr(T_ic)) {
77584260Sobrien				/* if ic would be more expensive */
776237448Spfg			term_tputs(el, tgoto(Str(T_IC), num, num), num);
777237448Spfg			term_overwrite(el, cp, (size_t)num);
77884260Sobrien				/* this updates el_cursor.h */
77984260Sobrien			return;
78084260Sobrien		}
78184260Sobrien	if (GoodStr(T_im) && GoodStr(T_ei)) {	/* if I have insert mode */
782237448Spfg		term_tputs(el, Str(T_im), 1);
7831573Srgrimes
78484260Sobrien		el->el_cursor.h += num;
78584260Sobrien		do
786237448Spfg			term__putc(el, *cp++);
78784260Sobrien		while (--num);
7881573Srgrimes
78984260Sobrien		if (GoodStr(T_ip))	/* have to make num chars insert */
790237448Spfg			term_tputs(el, Str(T_ip), 1);
7911573Srgrimes
792237448Spfg		term_tputs(el, Str(T_ei), 1);
79384260Sobrien		return;
79484260Sobrien	}
79584260Sobrien	do {
79684260Sobrien		if (GoodStr(T_ic))	/* have to make num chars insert */
797237448Spfg			term_tputs(el, Str(T_ic), 1);
7981573Srgrimes
799237448Spfg		term__putc(el, *cp++);
8001573Srgrimes
80184260Sobrien		el->el_cursor.h++;
8021573Srgrimes
80384260Sobrien		if (GoodStr(T_ip))	/* have to make num chars insert */
804237448Spfg			term_tputs(el, Str(T_ip), 1);
80584260Sobrien					/* pad the inserted char */
8061573Srgrimes
80784260Sobrien	} while (--num);
80884260Sobrien}
8091573Srgrimes
8101573Srgrimes
8111573Srgrimes/* term_clear_EOL():
8128870Srgrimes *	clear to end of line.  There are num characters to clear
8131573Srgrimes */
8141573Srgrimesprotected void
81584260Sobrienterm_clear_EOL(EditLine *el, int num)
8161573Srgrimes{
81784260Sobrien	int i;
8181573Srgrimes
81984260Sobrien	if (EL_CAN_CEOL && GoodStr(T_ce))
820237448Spfg		term_tputs(el, Str(T_ce), 1);
82184260Sobrien	else {
82284260Sobrien		for (i = 0; i < num; i++)
823237448Spfg			term__putc(el, ' ');
82484260Sobrien		el->el_cursor.h += num;	/* have written num spaces */
82584260Sobrien	}
82684260Sobrien}
8271573Srgrimes
8281573Srgrimes
8291573Srgrimes/* term_clear_screen():
8308870Srgrimes *	Clear the screen
8311573Srgrimes */
8321573Srgrimesprotected void
83384260Sobrienterm_clear_screen(EditLine *el)
8341573Srgrimes{				/* clear the whole screen and home */
8351573Srgrimes
83684260Sobrien	if (GoodStr(T_cl))
83784260Sobrien		/* send the clear screen code */
838237448Spfg		term_tputs(el, Str(T_cl), Val(T_li));
83984260Sobrien	else if (GoodStr(T_ho) && GoodStr(T_cd)) {
840237448Spfg		term_tputs(el, Str(T_ho), Val(T_li));	/* home */
84184260Sobrien		/* clear to bottom of screen */
842237448Spfg		term_tputs(el, Str(T_cd), Val(T_li));
84384260Sobrien	} else {
844237448Spfg		term__putc(el, '\r');
845237448Spfg		term__putc(el, '\n');
84684260Sobrien	}
84784260Sobrien}
8481573Srgrimes
84984260Sobrien
8501573Srgrimes/* term_beep():
8511573Srgrimes *	Beep the way the terminal wants us
8521573Srgrimes */
8531573Srgrimesprotected void
85484260Sobrienterm_beep(EditLine *el)
8551573Srgrimes{
85684260Sobrien	if (GoodStr(T_bl))
85784260Sobrien		/* what termcap says we should use */
858237448Spfg		term_tputs(el, Str(T_bl), 1);
85984260Sobrien	else
860237448Spfg		term__putc(el, '\007');	/* an ASCII bell; ^G */
86184260Sobrien}
8621573Srgrimes
8631573Srgrimes
8641573Srgrimes#ifdef notdef
8651573Srgrimes/* term_clear_to_bottom():
8661573Srgrimes *	Clear to the bottom of the screen
8671573Srgrimes */
8681573Srgrimesprotected void
86984260Sobrienterm_clear_to_bottom(EditLine *el)
8701573Srgrimes{
87184260Sobrien	if (GoodStr(T_cd))
872237448Spfg		term_tputs(el, Str(T_cd), Val(T_li));
87384260Sobrien	else if (GoodStr(T_ce))
874237448Spfg		term_tputs(el, Str(T_ce), Val(T_li));
87584260Sobrien}
8761573Srgrimes#endif
8771573Srgrimes
878148834Sstefanfprotected void
879148834Sstefanfterm_get(EditLine *el, const char **term)
880148834Sstefanf{
881148834Sstefanf	*term = el->el_term.t_name;
882148834Sstefanf}
8831573Srgrimes
884148834Sstefanf
8851573Srgrimes/* term_set():
8861573Srgrimes *	Read in the terminal capabilities from the requested terminal
8871573Srgrimes */
8881573Srgrimesprotected int
889148834Sstefanfterm_set(EditLine *el, const char *term)
8901573Srgrimes{
89184260Sobrien	int i;
89284260Sobrien	char buf[TC_BUFSIZE];
89384260Sobrien	char *area;
89484260Sobrien	const struct termcapstr *t;
89584260Sobrien	sigset_t oset, nset;
89684260Sobrien	int lins, cols;
8971573Srgrimes
89884260Sobrien	(void) sigemptyset(&nset);
89984260Sobrien	(void) sigaddset(&nset, SIGWINCH);
90084260Sobrien	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
9011573Srgrimes
90284260Sobrien	area = buf;
9031573Srgrimes
9041573Srgrimes
90584260Sobrien	if (term == NULL)
90684260Sobrien		term = getenv("TERM");
9071573Srgrimes
90884260Sobrien	if (!term || !term[0])
90984260Sobrien		term = "dumb";
9101573Srgrimes
91184260Sobrien	if (strcmp(term, "emacs") == 0)
91284260Sobrien		el->el_flags |= EDIT_DISABLED;
9131573Srgrimes
91484260Sobrien	memset(el->el_term.t_cap, 0, TC_BUFSIZE);
9151573Srgrimes
91684260Sobrien	i = tgetent(el->el_term.t_cap, term);
9171573Srgrimes
91884260Sobrien	if (i <= 0) {
91984260Sobrien		if (i == -1)
92084260Sobrien			(void) fprintf(el->el_errfile,
92184260Sobrien			    "Cannot read termcap database;\n");
92284260Sobrien		else if (i == 0)
92384260Sobrien			(void) fprintf(el->el_errfile,
92484260Sobrien			    "No entry for terminal type \"%s\";\n", term);
92584260Sobrien		(void) fprintf(el->el_errfile,
92684260Sobrien		    "using dumb terminal settings.\n");
92784260Sobrien		Val(T_co) = 80;	/* do a dumb terminal */
92884260Sobrien		Val(T_pt) = Val(T_km) = Val(T_li) = 0;
92984260Sobrien		Val(T_xt) = Val(T_MT);
93084260Sobrien		for (t = tstr; t->name != NULL; t++)
93184260Sobrien			term_alloc(el, t, NULL);
93284260Sobrien	} else {
93384260Sobrien		/* auto/magic margins */
93484260Sobrien		Val(T_am) = tgetflag("am");
93584260Sobrien		Val(T_xn) = tgetflag("xn");
93684260Sobrien		/* Can we tab */
93784260Sobrien		Val(T_pt) = tgetflag("pt");
93884260Sobrien		Val(T_xt) = tgetflag("xt");
93984260Sobrien		/* do we have a meta? */
94084260Sobrien		Val(T_km) = tgetflag("km");
94184260Sobrien		Val(T_MT) = tgetflag("MT");
94284260Sobrien		/* Get the size */
94384260Sobrien		Val(T_co) = tgetnum("co");
94484260Sobrien		Val(T_li) = tgetnum("li");
945148834Sstefanf		for (t = tstr; t->name != NULL; t++) {
946148848Sstefanf			/* XXX: some systems' tgetstr needs non const */
947148834Sstefanf			term_alloc(el, t, tgetstr(strchr(t->name, *t->name),
948148834Sstefanf			    &area));
949148834Sstefanf		}
95084260Sobrien	}
9511573Srgrimes
95284260Sobrien	if (Val(T_co) < 2)
95384260Sobrien		Val(T_co) = 80;	/* just in case */
95484260Sobrien	if (Val(T_li) < 1)
95584260Sobrien		Val(T_li) = 24;
9561573Srgrimes
95784260Sobrien	el->el_term.t_size.v = Val(T_co);
95884260Sobrien	el->el_term.t_size.h = Val(T_li);
9591573Srgrimes
96084260Sobrien	term_setflags(el);
9611573Srgrimes
96284260Sobrien				/* get the correct window size */
96384260Sobrien	(void) term_get_size(el, &lins, &cols);
96484260Sobrien	if (term_change_size(el, lins, cols) == -1)
96584260Sobrien		return (-1);
96684260Sobrien	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
96784260Sobrien	term_bind_arrow(el);
968148834Sstefanf	el->el_term.t_name = term;
96984260Sobrien	return (i <= 0 ? -1 : 0);
97084260Sobrien}
9711573Srgrimes
97284260Sobrien
9731573Srgrimes/* term_get_size():
9741573Srgrimes *	Return the new window size in lines and cols, and
9758870Srgrimes *	true if the size was changed.
9761573Srgrimes */
9771573Srgrimesprotected int
97884260Sobrienterm_get_size(EditLine *el, int *lins, int *cols)
9791573Srgrimes{
9801573Srgrimes
98184260Sobrien	*cols = Val(T_co);
98284260Sobrien	*lins = Val(T_li);
9831573Srgrimes
9841573Srgrimes#ifdef TIOCGWINSZ
98584260Sobrien	{
98684260Sobrien		struct winsize ws;
98784260Sobrien		if (ioctl(el->el_infd, TIOCGWINSZ, (ioctl_t) & ws) != -1) {
98884260Sobrien			if (ws.ws_col)
98984260Sobrien				*cols = ws.ws_col;
99084260Sobrien			if (ws.ws_row)
99184260Sobrien				*lins = ws.ws_row;
99284260Sobrien		}
9931573Srgrimes	}
9941573Srgrimes#endif
9951573Srgrimes#ifdef TIOCGSIZE
99684260Sobrien	{
99784260Sobrien		struct ttysize ts;
99884260Sobrien		if (ioctl(el->el_infd, TIOCGSIZE, (ioctl_t) & ts) != -1) {
99984260Sobrien			if (ts.ts_cols)
100084260Sobrien				*cols = ts.ts_cols;
100184260Sobrien			if (ts.ts_lines)
100284260Sobrien				*lins = ts.ts_lines;
100384260Sobrien		}
10041573Srgrimes	}
10051573Srgrimes#endif
100684260Sobrien	return (Val(T_co) != *cols || Val(T_li) != *lins);
100784260Sobrien}
10081573Srgrimes
10091573Srgrimes
10101573Srgrimes/* term_change_size():
10111573Srgrimes *	Change the size of the terminal
10121573Srgrimes */
101384260Sobrienprotected int
101484260Sobrienterm_change_size(EditLine *el, int lins, int cols)
10151573Srgrimes{
101684260Sobrien	/*
101784260Sobrien         * Just in case
101884260Sobrien         */
101984260Sobrien	Val(T_co) = (cols < 2) ? 80 : cols;
102084260Sobrien	Val(T_li) = (lins < 1) ? 24 : lins;
10211573Srgrimes
102284260Sobrien	/* re-make display buffers */
102384260Sobrien	if (term_rebuffer_display(el) == -1)
102484260Sobrien		return (-1);
102584260Sobrien	re_clear_display(el);
102684260Sobrien	return (0);
102784260Sobrien}
10281573Srgrimes
10291573Srgrimes
10301573Srgrimes/* term_init_arrow():
10311573Srgrimes *	Initialize the arrow key bindings from termcap
10321573Srgrimes */
10331573Srgrimesprivate void
103484260Sobrienterm_init_arrow(EditLine *el)
10351573Srgrimes{
103684260Sobrien	fkey_t *arrow = el->el_term.t_fkey;
10371573Srgrimes
103884260Sobrien	arrow[A_K_DN].name = "down";
103984260Sobrien	arrow[A_K_DN].key = T_kd;
104084260Sobrien	arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY;
104184260Sobrien	arrow[A_K_DN].type = XK_CMD;
10421573Srgrimes
104384260Sobrien	arrow[A_K_UP].name = "up";
104484260Sobrien	arrow[A_K_UP].key = T_ku;
104584260Sobrien	arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY;
104684260Sobrien	arrow[A_K_UP].type = XK_CMD;
10471573Srgrimes
104884260Sobrien	arrow[A_K_LT].name = "left";
104984260Sobrien	arrow[A_K_LT].key = T_kl;
105084260Sobrien	arrow[A_K_LT].fun.cmd = ED_PREV_CHAR;
105184260Sobrien	arrow[A_K_LT].type = XK_CMD;
10521573Srgrimes
105384260Sobrien	arrow[A_K_RT].name = "right";
105484260Sobrien	arrow[A_K_RT].key = T_kr;
105584260Sobrien	arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR;
105684260Sobrien	arrow[A_K_RT].type = XK_CMD;
10571573Srgrimes
105884260Sobrien	arrow[A_K_HO].name = "home";
105984260Sobrien	arrow[A_K_HO].key = T_kh;
106084260Sobrien	arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG;
106184260Sobrien	arrow[A_K_HO].type = XK_CMD;
106263948Sache
106384260Sobrien	arrow[A_K_EN].name = "end";
106484260Sobrien	arrow[A_K_EN].key = T_at7;
106584260Sobrien	arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END;
106684260Sobrien	arrow[A_K_EN].type = XK_CMD;
1067212235Sjilles
1068212235Sjilles	arrow[A_K_DE].name = "delete";
1069212235Sjilles	arrow[A_K_DE].key = T_kD;
1070212235Sjilles	arrow[A_K_DE].fun.cmd = ED_DELETE_NEXT_CHAR;
1071212235Sjilles	arrow[A_K_DE].type = XK_CMD;
10721573Srgrimes}
10731573Srgrimes
10741573Srgrimes
10751573Srgrimes/* term_reset_arrow():
10761573Srgrimes *	Reset arrow key bindings
10771573Srgrimes */
10781573Srgrimesprivate void
107984260Sobrienterm_reset_arrow(EditLine *el)
10801573Srgrimes{
108184260Sobrien	fkey_t *arrow = el->el_term.t_fkey;
108284260Sobrien	static const char strA[] = {033, '[', 'A', '\0'};
108384260Sobrien	static const char strB[] = {033, '[', 'B', '\0'};
108484260Sobrien	static const char strC[] = {033, '[', 'C', '\0'};
108584260Sobrien	static const char strD[] = {033, '[', 'D', '\0'};
108684260Sobrien	static const char strH[] = {033, '[', 'H', '\0'};
108784260Sobrien	static const char strF[] = {033, '[', 'F', '\0'};
108884260Sobrien	static const char str1[] = {033, '[', '1', '~', '\0'};
108984260Sobrien	static const char str4[] = {033, '[', '4', '~', '\0'};
109084260Sobrien	static const char stOA[] = {033, 'O', 'A', '\0'};
109184260Sobrien	static const char stOB[] = {033, 'O', 'B', '\0'};
109284260Sobrien	static const char stOC[] = {033, 'O', 'C', '\0'};
109384260Sobrien	static const char stOD[] = {033, 'O', 'D', '\0'};
109484260Sobrien	static const char stOH[] = {033, 'O', 'H', '\0'};
109584260Sobrien	static const char stOF[] = {033, 'O', 'F', '\0'};
10961573Srgrimes
109784260Sobrien	key_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
109884260Sobrien	key_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
109984260Sobrien	key_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
110084260Sobrien	key_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
110184260Sobrien	key_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
110284260Sobrien	key_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
110384260Sobrien	key_add(el, str1, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
110484260Sobrien	key_add(el, str4, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
110584260Sobrien	key_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
110684260Sobrien	key_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
110784260Sobrien	key_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
110884260Sobrien	key_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
110984260Sobrien	key_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
111084260Sobrien	key_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
11111573Srgrimes
111284260Sobrien	if (el->el_map.type == MAP_VI) {
111384260Sobrien		key_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
111484260Sobrien		key_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
111584260Sobrien		key_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
111684260Sobrien		key_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
111784260Sobrien		key_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
111884260Sobrien		key_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
111984260Sobrien		key_add(el, &str1[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
112084260Sobrien		key_add(el, &str4[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
112184260Sobrien		key_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
112284260Sobrien		key_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
112384260Sobrien		key_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
112484260Sobrien		key_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
112584260Sobrien		key_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
112684260Sobrien		key_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
112784260Sobrien	}
11281573Srgrimes}
11291573Srgrimes
11301573Srgrimes
11311573Srgrimes/* term_set_arrow():
11321573Srgrimes *	Set an arrow key binding
11331573Srgrimes */
11341573Srgrimesprotected int
1135148834Sstefanfterm_set_arrow(EditLine *el, const char *name, key_value_t *fun, int type)
11361573Srgrimes{
113784260Sobrien	fkey_t *arrow = el->el_term.t_fkey;
113884260Sobrien	int i;
11391573Srgrimes
114084260Sobrien	for (i = 0; i < A_K_NKEYS; i++)
114184260Sobrien		if (strcmp(name, arrow[i].name) == 0) {
114284260Sobrien			arrow[i].fun = *fun;
114384260Sobrien			arrow[i].type = type;
114484260Sobrien			return (0);
114584260Sobrien		}
114684260Sobrien	return (-1);
11471573Srgrimes}
11481573Srgrimes
11491573Srgrimes
11501573Srgrimes/* term_clear_arrow():
11511573Srgrimes *	Clear an arrow key binding
11521573Srgrimes */
11531573Srgrimesprotected int
1154148834Sstefanfterm_clear_arrow(EditLine *el, const char *name)
11551573Srgrimes{
115684260Sobrien	fkey_t *arrow = el->el_term.t_fkey;
115784260Sobrien	int i;
11581573Srgrimes
115984260Sobrien	for (i = 0; i < A_K_NKEYS; i++)
116084260Sobrien		if (strcmp(name, arrow[i].name) == 0) {
116184260Sobrien			arrow[i].type = XK_NOD;
116284260Sobrien			return (0);
116384260Sobrien		}
116484260Sobrien	return (-1);
11651573Srgrimes}
11661573Srgrimes
11671573Srgrimes
11681573Srgrimes/* term_print_arrow():
11691573Srgrimes *	Print the arrow key bindings
11701573Srgrimes */
11711573Srgrimesprotected void
1172148834Sstefanfterm_print_arrow(EditLine *el, const char *name)
11731573Srgrimes{
117484260Sobrien	int i;
117584260Sobrien	fkey_t *arrow = el->el_term.t_fkey;
11761573Srgrimes
117784260Sobrien	for (i = 0; i < A_K_NKEYS; i++)
117884260Sobrien		if (*name == '\0' || strcmp(name, arrow[i].name) == 0)
117984260Sobrien			if (arrow[i].type != XK_NOD)
118084260Sobrien				key_kprint(el, arrow[i].name, &arrow[i].fun,
118184260Sobrien				    arrow[i].type);
11821573Srgrimes}
11831573Srgrimes
11841573Srgrimes
11851573Srgrimes/* term_bind_arrow():
11861573Srgrimes *	Bind the arrow keys
11871573Srgrimes */
11881573Srgrimesprotected void
118984260Sobrienterm_bind_arrow(EditLine *el)
11901573Srgrimes{
119184260Sobrien	el_action_t *map;
119284260Sobrien	const el_action_t *dmap;
119384260Sobrien	int i, j;
119484260Sobrien	char *p;
119584260Sobrien	fkey_t *arrow = el->el_term.t_fkey;
11961573Srgrimes
119784260Sobrien	/* Check if the components needed are initialized */
119884260Sobrien	if (el->el_term.t_buf == NULL || el->el_map.key == NULL)
119984260Sobrien		return;
12001573Srgrimes
120184260Sobrien	map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key;
120284260Sobrien	dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs;
12031573Srgrimes
120484260Sobrien	term_reset_arrow(el);
12051573Srgrimes
120684260Sobrien	for (i = 0; i < A_K_NKEYS; i++) {
120784260Sobrien		p = el->el_term.t_str[arrow[i].key];
120884260Sobrien		if (p && *p) {
120984260Sobrien			j = (unsigned char) *p;
121084260Sobrien			/*
121184260Sobrien		         * Assign the arrow keys only if:
121284260Sobrien		         *
121384260Sobrien		         * 1. They are multi-character arrow keys and the user
121484260Sobrien		         *    has not re-assigned the leading character, or
121584260Sobrien		         *    has re-assigned the leading character to be
121684260Sobrien		         *	  ED_SEQUENCE_LEAD_IN
121784260Sobrien		         * 2. They are single arrow keys pointing to an
121884260Sobrien			 *    unassigned key.
121984260Sobrien		         */
122084260Sobrien			if (arrow[i].type == XK_NOD)
122184260Sobrien				key_clear(el, map, p);
122284260Sobrien			else {
122384260Sobrien				if (p[1] && (dmap[j] == map[j] ||
122484260Sobrien					map[j] == ED_SEQUENCE_LEAD_IN)) {
122584260Sobrien					key_add(el, p, &arrow[i].fun,
122684260Sobrien					    arrow[i].type);
122784260Sobrien					map[j] = ED_SEQUENCE_LEAD_IN;
122884260Sobrien				} else if (map[j] == ED_UNASSIGNED) {
122984260Sobrien					key_clear(el, map, p);
123084260Sobrien					if (arrow[i].type == XK_CMD)
123184260Sobrien						map[j] = arrow[i].fun.cmd;
123284260Sobrien					else
123384260Sobrien						key_add(el, p, &arrow[i].fun,
123484260Sobrien						    arrow[i].type);
123584260Sobrien				}
123684260Sobrien			}
12371573Srgrimes		}
12381573Srgrimes	}
12391573Srgrimes}
12401573Srgrimes
1241237448Spfg/* term_putc():
1242237448Spfg *	Add a character
1243237448Spfg */
1244237448Spfgprivate int
1245237448Spfgterm_putc(int c)
1246237448Spfg{
12471573Srgrimes
1248237448Spfg	if (term_outfile == NULL)
1249237448Spfg		return -1;
1250237448Spfg	return fputc(c, term_outfile);
1251237448Spfg}
1252237448Spfg
1253237448Spfgprivate void
1254237448Spfgterm_tputs(EditLine *el, const char *cap, int affcnt)
1255237448Spfg{
1256237448Spfg#ifdef _REENTRANT
1257237448Spfg	pthread_mutex_lock(&term_mutex);
1258237448Spfg#endif
1259237448Spfg	term_outfile = el->el_outfile;
1260237448Spfg	(void)tputs(cap, affcnt, term_putc);
1261237448Spfg#ifdef _REENTRANT
1262237448Spfg	pthread_mutex_unlock(&term_mutex);
1263237448Spfg#endif
1264237448Spfg}
1265237448Spfg
12661573Srgrimes/* term__putc():
12671573Srgrimes *	Add a character
12681573Srgrimes */
12699898Sacheprotected int
1270237448Spfgterm__putc(EditLine *el, int c)
12711573Srgrimes{
12721573Srgrimes
1273237448Spfg	return fputc(c, el->el_outfile);
127484260Sobrien}
12751573Srgrimes
12761573Srgrimes/* term__flush():
12771573Srgrimes *	Flush output
12781573Srgrimes */
12791573Srgrimesprotected void
1280237448Spfgterm__flush(EditLine *el)
12811573Srgrimes{
12821573Srgrimes
1283237448Spfg	(void) fflush(el->el_outfile);
128484260Sobrien}
12851573Srgrimes
1286167457Sstefanf/* term_writec():
1287167457Sstefanf *	Write the given character out, in a human readable form
1288167457Sstefanf */
1289167457Sstefanfprotected void
1290167457Sstefanfterm_writec(EditLine *el, int c)
1291167457Sstefanf{
1292167457Sstefanf	char buf[8];
1293237448Spfg	size_t cnt = key__decode_char(buf, sizeof(buf), 0, c);
1294167457Sstefanf	buf[cnt] = '\0';
1295237448Spfg	term_overwrite(el, buf, (size_t)cnt);
1296237448Spfg	term__flush(el);
1297167457Sstefanf}
129884260Sobrien
1299167457Sstefanf
13001573Srgrimes/* term_telltc():
13011573Srgrimes *	Print the current termcap characteristics
13021573Srgrimes */
13031573Srgrimesprotected int
13041573Srgrimes/*ARGSUSED*/
1305148834Sstefanfterm_telltc(EditLine *el, int argc __unused,
1306148834Sstefanf    const char **argv __unused)
13071573Srgrimes{
130884260Sobrien	const struct termcapstr *t;
130984260Sobrien	char **ts;
131084260Sobrien	char upbuf[EL_BUFSIZ];
13111573Srgrimes
131284260Sobrien	(void) fprintf(el->el_outfile, "\n\tYour terminal has the\n");
131384260Sobrien	(void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n");
131484260Sobrien	(void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n",
13151573Srgrimes	    Val(T_co), Val(T_li));
131684260Sobrien	(void) fprintf(el->el_outfile,
131784260Sobrien	    "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no");
131884260Sobrien	(void) fprintf(el->el_outfile,
131984260Sobrien	    "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not ");
132084260Sobrien	(void) fprintf(el->el_outfile, "\tIt %s automatic margins\n",
132184260Sobrien	    EL_HAS_AUTO_MARGINS ? "has" : "does not have");
132284260Sobrien	if (EL_HAS_AUTO_MARGINS)
132384260Sobrien		(void) fprintf(el->el_outfile, "\tIt %s magic margins\n",
132484260Sobrien		    EL_HAS_MAGIC_MARGINS ? "has" : "does not have");
13251573Srgrimes
1326167457Sstefanf	for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++) {
1327167457Sstefanf		const char *ub;
1328167457Sstefanf		if (*ts && **ts) {
1329167457Sstefanf		    (void) key__decode_str(*ts, upbuf, sizeof(upbuf), "");
1330167457Sstefanf		    ub = upbuf;
1331167457Sstefanf		} else {
1332167457Sstefanf		    ub = "(empty)";
1333167457Sstefanf		}
133484260Sobrien		(void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n",
1335167457Sstefanf		    t->long_name, t->name, ub);
1336167457Sstefanf	}
133784260Sobrien	(void) fputc('\n', el->el_outfile);
133884260Sobrien	return (0);
13391573Srgrimes}
13401573Srgrimes
13411573Srgrimes
13421573Srgrimes/* term_settc():
13431573Srgrimes *	Change the current terminal characteristics
13441573Srgrimes */
13451573Srgrimesprotected int
13461573Srgrimes/*ARGSUSED*/
1347148834Sstefanfterm_settc(EditLine *el, int argc __unused,
1348148834Sstefanf    const char **argv)
13491573Srgrimes{
135084260Sobrien	const struct termcapstr *ts;
135184260Sobrien	const struct termcapval *tv;
1352148834Sstefanf	const char *what, *how;
13531573Srgrimes
135484260Sobrien	if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
1355170511Sstefanf		return -1;
13561573Srgrimes
135784260Sobrien	what = argv[1];
135884260Sobrien	how = argv[2];
13591573Srgrimes
136084260Sobrien	/*
136184260Sobrien         * Do the strings first
136284260Sobrien         */
136384260Sobrien	for (ts = tstr; ts->name != NULL; ts++)
136484260Sobrien		if (strcmp(ts->name, what) == 0)
136584260Sobrien			break;
13661573Srgrimes
136784260Sobrien	if (ts->name != NULL) {
136884260Sobrien		term_alloc(el, ts, how);
136984260Sobrien		term_setflags(el);
1370170511Sstefanf		return 0;
137184260Sobrien	}
137284260Sobrien	/*
137384260Sobrien         * Do the numeric ones second
137484260Sobrien         */
137584260Sobrien	for (tv = tval; tv->name != NULL; tv++)
137684260Sobrien		if (strcmp(tv->name, what) == 0)
137784260Sobrien			break;
13781573Srgrimes
1379170511Sstefanf	if (tv->name != NULL)
1380170511Sstefanf		return -1;
13811573Srgrimes
1382170511Sstefanf	if (tv == &tval[T_pt] || tv == &tval[T_km] ||
1383170511Sstefanf	    tv == &tval[T_am] || tv == &tval[T_xn]) {
1384170511Sstefanf		if (strcmp(how, "yes") == 0)
1385170511Sstefanf			el->el_term.t_val[tv - tval] = 1;
1386170511Sstefanf		else if (strcmp(how, "no") == 0)
1387170511Sstefanf			el->el_term.t_val[tv - tval] = 0;
1388170511Sstefanf		else {
1389170511Sstefanf			(void) fprintf(el->el_errfile,
1390170511Sstefanf			    "%s: Bad value `%s'.\n", argv[0], how);
1391170511Sstefanf			return -1;
139284260Sobrien		}
1393170511Sstefanf		term_setflags(el);
1394170511Sstefanf		if (term_change_size(el, Val(T_li), Val(T_co)) == -1)
1395170511Sstefanf			return -1;
1396170511Sstefanf		return 0;
1397170511Sstefanf	} else {
1398170511Sstefanf		long i;
1399170511Sstefanf		char *ep;
1400170511Sstefanf
1401170511Sstefanf		i = strtol(how, &ep, 10);
1402170511Sstefanf		if (*ep != '\0') {
1403170511Sstefanf			(void) fprintf(el->el_errfile,
1404170511Sstefanf			    "%s: Bad value `%s'.\n", argv[0], how);
1405170511Sstefanf			return -1;
1406170511Sstefanf		}
1407170511Sstefanf		el->el_term.t_val[tv - tval] = (int) i;
1408170511Sstefanf		el->el_term.t_size.v = Val(T_co);
1409170511Sstefanf		el->el_term.t_size.h = Val(T_li);
1410170511Sstefanf		if (tv == &tval[T_co] || tv == &tval[T_li])
1411170511Sstefanf			if (term_change_size(el, Val(T_li), Val(T_co))
1412170511Sstefanf			    == -1)
1413170511Sstefanf				return -1;
1414170511Sstefanf		return 0;
14151573Srgrimes	}
14161573Srgrimes}
14171573Srgrimes
14181573Srgrimes
1419170511Sstefanf/* term_gettc():
1420170511Sstefanf *	Get the current terminal characteristics
1421170511Sstefanf */
1422170511Sstefanfprotected int
1423170511Sstefanf/*ARGSUSED*/
1424170511Sstefanfterm_gettc(EditLine *el, int argc __unused, char **argv)
1425170511Sstefanf{
1426170511Sstefanf	const struct termcapstr *ts;
1427170511Sstefanf	const struct termcapval *tv;
1428170511Sstefanf	char *what;
1429170511Sstefanf	void *how;
1430170511Sstefanf
1431170511Sstefanf	if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
1432170511Sstefanf		return (-1);
1433170511Sstefanf
1434170511Sstefanf	what = argv[1];
1435170511Sstefanf	how = argv[2];
1436170511Sstefanf
1437170511Sstefanf	/*
1438170511Sstefanf         * Do the strings first
1439170511Sstefanf         */
1440170511Sstefanf	for (ts = tstr; ts->name != NULL; ts++)
1441170511Sstefanf		if (strcmp(ts->name, what) == 0)
1442170511Sstefanf			break;
1443170511Sstefanf
1444170511Sstefanf	if (ts->name != NULL) {
1445170511Sstefanf		*(char **)how = el->el_term.t_str[ts - tstr];
1446170511Sstefanf		return 0;
1447170511Sstefanf	}
1448170511Sstefanf	/*
1449170511Sstefanf         * Do the numeric ones second
1450170511Sstefanf         */
1451170511Sstefanf	for (tv = tval; tv->name != NULL; tv++)
1452170511Sstefanf		if (strcmp(tv->name, what) == 0)
1453170511Sstefanf			break;
1454170511Sstefanf
1455170511Sstefanf	if (tv->name == NULL)
1456170511Sstefanf		return -1;
1457170511Sstefanf
1458170511Sstefanf	if (tv == &tval[T_pt] || tv == &tval[T_km] ||
1459170511Sstefanf	    tv == &tval[T_am] || tv == &tval[T_xn]) {
1460170511Sstefanf		static char yes[] = "yes";
1461170511Sstefanf		static char no[] = "no";
1462170511Sstefanf		if (el->el_term.t_val[tv - tval])
1463170511Sstefanf			*(char **)how = yes;
1464170511Sstefanf		else
1465170511Sstefanf			*(char **)how = no;
1466170511Sstefanf		return 0;
1467170511Sstefanf	} else {
1468170511Sstefanf		*(int *)how = el->el_term.t_val[tv - tval];
1469170511Sstefanf		return 0;
1470170511Sstefanf	}
1471170511Sstefanf}
1472170511Sstefanf
14731573Srgrimes/* term_echotc():
14741573Srgrimes *	Print the termcap string out with variable substitution
14751573Srgrimes */
14761573Srgrimesprotected int
14771573Srgrimes/*ARGSUSED*/
1478148834Sstefanfterm_echotc(EditLine *el, int argc __unused,
1479148834Sstefanf    const char **argv)
14801573Srgrimes{
148184260Sobrien	char *cap, *scap, *ep;
148284260Sobrien	int arg_need, arg_cols, arg_rows;
148384260Sobrien	int verbose = 0, silent = 0;
148484260Sobrien	char *area;
148584260Sobrien	static const char fmts[] = "%s\n", fmtd[] = "%d\n";
148684260Sobrien	const struct termcapstr *t;
148784260Sobrien	char buf[TC_BUFSIZE];
148884260Sobrien	long i;
14891573Srgrimes
149084260Sobrien	area = buf;
14911573Srgrimes
149284260Sobrien	if (argv == NULL || argv[1] == NULL)
149384260Sobrien		return (-1);
149484260Sobrien	argv++;
14951573Srgrimes
149684260Sobrien	if (argv[0][0] == '-') {
149784260Sobrien		switch (argv[0][1]) {
149884260Sobrien		case 'v':
149984260Sobrien			verbose = 1;
150084260Sobrien			break;
150184260Sobrien		case 's':
150284260Sobrien			silent = 1;
150384260Sobrien			break;
150484260Sobrien		default:
150584260Sobrien			/* stderror(ERR_NAME | ERR_TCUSAGE); */
150684260Sobrien			break;
150784260Sobrien		}
150884260Sobrien		argv++;
15091573Srgrimes	}
151084260Sobrien	if (!*argv || *argv[0] == '\0')
151184260Sobrien		return (0);
151284260Sobrien	if (strcmp(*argv, "tabs") == 0) {
151384260Sobrien		(void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no");
151484260Sobrien		return (0);
151584260Sobrien	} else if (strcmp(*argv, "meta") == 0) {
151684260Sobrien		(void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no");
151784260Sobrien		return (0);
151884260Sobrien	} else if (strcmp(*argv, "xn") == 0) {
151984260Sobrien		(void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ?
152084260Sobrien		    "yes" : "no");
152184260Sobrien		return (0);
152284260Sobrien	} else if (strcmp(*argv, "am") == 0) {
152384260Sobrien		(void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ?
152484260Sobrien		    "yes" : "no");
152584260Sobrien		return (0);
152684260Sobrien	} else if (strcmp(*argv, "baud") == 0) {
152784260Sobrien#ifdef notdef
152884260Sobrien		int i;
152984260Sobrien
153084260Sobrien		for (i = 0; baud_rate[i].b_name != NULL; i++)
153184260Sobrien			if (el->el_tty.t_speed == baud_rate[i].b_rate) {
153284260Sobrien				(void) fprintf(el->el_outfile, fmts,
153384260Sobrien				    baud_rate[i].b_name);
153484260Sobrien				return (0);
153584260Sobrien			}
153684260Sobrien		(void) fprintf(el->el_outfile, fmtd, 0);
153784260Sobrien#else
1538148834Sstefanf		(void) fprintf(el->el_outfile, fmtd, (int)el->el_tty.t_speed);
15391573Srgrimes#endif
154084260Sobrien		return (0);
154184260Sobrien	} else if (strcmp(*argv, "rows") == 0 || strcmp(*argv, "lines") == 0) {
154284260Sobrien		(void) fprintf(el->el_outfile, fmtd, Val(T_li));
154384260Sobrien		return (0);
154484260Sobrien	} else if (strcmp(*argv, "cols") == 0) {
154584260Sobrien		(void) fprintf(el->el_outfile, fmtd, Val(T_co));
154684260Sobrien		return (0);
15471573Srgrimes	}
154884260Sobrien	/*
154984260Sobrien         * Try to use our local definition first
155084260Sobrien         */
155184260Sobrien	scap = NULL;
155284260Sobrien	for (t = tstr; t->name != NULL; t++)
155384260Sobrien		if (strcmp(t->name, *argv) == 0) {
155484260Sobrien			scap = el->el_term.t_str[t - tstr];
155584260Sobrien			break;
155684260Sobrien		}
1557148834Sstefanf	if (t->name == NULL) {
1558148848Sstefanf		/* XXX: some systems' tgetstr needs non const */
1559148834Sstefanf		scap = tgetstr(strchr(*argv, **argv), &area);
1560148834Sstefanf	}
156184260Sobrien	if (!scap || scap[0] == '\0') {
156284260Sobrien		if (!silent)
156384260Sobrien			(void) fprintf(el->el_errfile,
156484260Sobrien			    "echotc: Termcap parameter `%s' not found.\n",
156584260Sobrien			    *argv);
156684260Sobrien		return (-1);
156784260Sobrien	}
156884260Sobrien	/*
156984260Sobrien         * Count home many values we need for this capability.
157084260Sobrien         */
157184260Sobrien	for (cap = scap, arg_need = 0; *cap; cap++)
157284260Sobrien		if (*cap == '%')
157384260Sobrien			switch (*++cap) {
157484260Sobrien			case 'd':
157584260Sobrien			case '2':
157684260Sobrien			case '3':
157784260Sobrien			case '.':
157884260Sobrien			case '+':
157984260Sobrien				arg_need++;
158084260Sobrien				break;
158184260Sobrien			case '%':
158284260Sobrien			case '>':
158384260Sobrien			case 'i':
158484260Sobrien			case 'r':
158584260Sobrien			case 'n':
158684260Sobrien			case 'B':
158784260Sobrien			case 'D':
158884260Sobrien				break;
158984260Sobrien			default:
159084260Sobrien				/*
159184260Sobrien				 * hpux has lot's of them...
159284260Sobrien				 */
159384260Sobrien				if (verbose)
159484260Sobrien					(void) fprintf(el->el_errfile,
159584260Sobrien				"echotc: Warning: unknown termcap %% `%c'.\n",
159684260Sobrien					    *cap);
159784260Sobrien				/* This is bad, but I won't complain */
159884260Sobrien				break;
159984260Sobrien			}
16001573Srgrimes
160184260Sobrien	switch (arg_need) {
160284260Sobrien	case 0:
160384260Sobrien		argv++;
160484260Sobrien		if (*argv && *argv[0]) {
160584260Sobrien			if (!silent)
160684260Sobrien				(void) fprintf(el->el_errfile,
160784260Sobrien				    "echotc: Warning: Extra argument `%s'.\n",
160884260Sobrien				    *argv);
160984260Sobrien			return (-1);
161084260Sobrien		}
1611237448Spfg		term_tputs(el, scap, 1);
16121573Srgrimes		break;
161384260Sobrien	case 1:
161484260Sobrien		argv++;
161584260Sobrien		if (!*argv || *argv[0] == '\0') {
161684260Sobrien			if (!silent)
161784260Sobrien				(void) fprintf(el->el_errfile,
161884260Sobrien				    "echotc: Warning: Missing argument.\n");
161984260Sobrien			return (-1);
162084260Sobrien		}
162184260Sobrien		arg_cols = 0;
162284260Sobrien		i = strtol(*argv, &ep, 10);
162384260Sobrien		if (*ep != '\0' || i < 0) {
162484260Sobrien			if (!silent)
162584260Sobrien				(void) fprintf(el->el_errfile,
162684260Sobrien				    "echotc: Bad value `%s' for rows.\n",
162784260Sobrien				    *argv);
162884260Sobrien			return (-1);
162984260Sobrien		}
163084260Sobrien		arg_rows = (int) i;
163184260Sobrien		argv++;
163284260Sobrien		if (*argv && *argv[0]) {
163384260Sobrien			if (!silent)
163484260Sobrien				(void) fprintf(el->el_errfile,
163584260Sobrien				    "echotc: Warning: Extra argument `%s'.\n",
163684260Sobrien				    *argv);
163784260Sobrien			return (-1);
163884260Sobrien		}
1639237448Spfg		term_tputs(el, tgoto(scap, arg_cols, arg_rows), 1);
16401573Srgrimes		break;
164184260Sobrien	default:
164284260Sobrien		/* This is wrong, but I will ignore it... */
16431573Srgrimes		if (verbose)
164484260Sobrien			(void) fprintf(el->el_errfile,
164584260Sobrien			 "echotc: Warning: Too many required arguments (%d).\n",
164684260Sobrien			    arg_need);
164784260Sobrien		/* FALLTHROUGH */
164884260Sobrien	case 2:
164984260Sobrien		argv++;
165084260Sobrien		if (!*argv || *argv[0] == '\0') {
165184260Sobrien			if (!silent)
165284260Sobrien				(void) fprintf(el->el_errfile,
165384260Sobrien				    "echotc: Warning: Missing argument.\n");
165484260Sobrien			return (-1);
165584260Sobrien		}
165684260Sobrien		i = strtol(*argv, &ep, 10);
165784260Sobrien		if (*ep != '\0' || i < 0) {
165884260Sobrien			if (!silent)
165984260Sobrien				(void) fprintf(el->el_errfile,
166084260Sobrien				    "echotc: Bad value `%s' for cols.\n",
166184260Sobrien				    *argv);
166284260Sobrien			return (-1);
166384260Sobrien		}
166484260Sobrien		arg_cols = (int) i;
166584260Sobrien		argv++;
166684260Sobrien		if (!*argv || *argv[0] == '\0') {
166784260Sobrien			if (!silent)
166884260Sobrien				(void) fprintf(el->el_errfile,
166984260Sobrien				    "echotc: Warning: Missing argument.\n");
167084260Sobrien			return (-1);
167184260Sobrien		}
167284260Sobrien		i = strtol(*argv, &ep, 10);
167384260Sobrien		if (*ep != '\0' || i < 0) {
167484260Sobrien			if (!silent)
167584260Sobrien				(void) fprintf(el->el_errfile,
167684260Sobrien				    "echotc: Bad value `%s' for rows.\n",
167784260Sobrien				    *argv);
167884260Sobrien			return (-1);
167984260Sobrien		}
168084260Sobrien		arg_rows = (int) i;
168184260Sobrien		if (*ep != '\0') {
168284260Sobrien			if (!silent)
168384260Sobrien				(void) fprintf(el->el_errfile,
168484260Sobrien				    "echotc: Bad value `%s'.\n", *argv);
168584260Sobrien			return (-1);
168684260Sobrien		}
168784260Sobrien		argv++;
168884260Sobrien		if (*argv && *argv[0]) {
168984260Sobrien			if (!silent)
169084260Sobrien				(void) fprintf(el->el_errfile,
169184260Sobrien				    "echotc: Warning: Extra argument `%s'.\n",
169284260Sobrien				    *argv);
169384260Sobrien			return (-1);
169484260Sobrien		}
1695237448Spfg		term_tputs(el, tgoto(scap, arg_cols, arg_rows), arg_rows);
16961573Srgrimes		break;
16971573Srgrimes	}
169884260Sobrien	return (0);
16991573Srgrimes}
1700