xref: /openbsd-src/lib/libedit/terminal.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1*5b133f3fSguenther /*	$OpenBSD: terminal.c,v 1.20 2023/03/08 04:43:05 guenther Exp $	*/
2a32142afSschwarze /*	$NetBSD: terminal.c,v 1.17 2016/02/15 15:35:03 christos Exp $	*/
34664509cSschwarze 
44664509cSschwarze /*-
54664509cSschwarze  * Copyright (c) 1992, 1993
64664509cSschwarze  *	The Regents of the University of California.  All rights reserved.
74664509cSschwarze  *
84664509cSschwarze  * This code is derived from software contributed to Berkeley by
94664509cSschwarze  * Christos Zoulas of Cornell University.
104664509cSschwarze  *
114664509cSschwarze  * Redistribution and use in source and binary forms, with or without
124664509cSschwarze  * modification, are permitted provided that the following conditions
134664509cSschwarze  * are met:
144664509cSschwarze  * 1. Redistributions of source code must retain the above copyright
154664509cSschwarze  *    notice, this list of conditions and the following disclaimer.
164664509cSschwarze  * 2. Redistributions in binary form must reproduce the above copyright
174664509cSschwarze  *    notice, this list of conditions and the following disclaimer in the
184664509cSschwarze  *    documentation and/or other materials provided with the distribution.
194664509cSschwarze  * 3. Neither the name of the University nor the names of its contributors
204664509cSschwarze  *    may be used to endorse or promote products derived from this software
214664509cSschwarze  *    without specific prior written permission.
224664509cSschwarze  *
234664509cSschwarze  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
244664509cSschwarze  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
254664509cSschwarze  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
264664509cSschwarze  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
274664509cSschwarze  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
284664509cSschwarze  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
294664509cSschwarze  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
304664509cSschwarze  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
314664509cSschwarze  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
324664509cSschwarze  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
334664509cSschwarze  * SUCH DAMAGE.
344664509cSschwarze  */
354664509cSschwarze 
364664509cSschwarze #include "config.h"
374664509cSschwarze 
384664509cSschwarze /*
39fd40972aSschwarze  * terminal.c: Editor/termcap-curses interface
404664509cSschwarze  *	       We have to declare a static variable here, since the
414664509cSschwarze  *	       termcap putchar routine does not take an argument!
424664509cSschwarze  */
437ccfa089Sschwarze #include <sys/types.h>
447ccfa089Sschwarze #include <sys/ioctl.h>
454664509cSschwarze #include <limits.h>
467ccfa089Sschwarze #include <signal.h>
477ccfa089Sschwarze #include <stdio.h>
487ccfa089Sschwarze #include <stdlib.h>
497ccfa089Sschwarze #include <string.h>
507ccfa089Sschwarze #include <unistd.h>
514664509cSschwarze #ifdef HAVE_TERMCAP_H
524664509cSschwarze #include <termcap.h>
534664509cSschwarze #endif
544664509cSschwarze #ifdef HAVE_CURSES_H
554664509cSschwarze #include <curses.h>
564664509cSschwarze #elif HAVE_NCURSES_H
574664509cSschwarze #include <ncurses.h>
584664509cSschwarze #endif
594664509cSschwarze /* Solaris's term.h does horrid things. */
604664509cSschwarze 
614664509cSschwarze #if defined(HAVE_TERM_H) && !defined(__sun)
624664509cSschwarze #include <term.h>
634664509cSschwarze #endif
644664509cSschwarze 
654664509cSschwarze #ifdef _REENTRANT
664664509cSschwarze #include <pthread.h>
674664509cSschwarze #endif
684664509cSschwarze 
694664509cSschwarze #include "el.h"
7013e01c7aSschwarze #include "fcns.h"
714664509cSschwarze 
724664509cSschwarze /*
734664509cSschwarze  * IMPORTANT NOTE: these routines are allowed to look at the current screen
744664509cSschwarze  * and the current position assuming that it is correct.  If this is not
754664509cSschwarze  * true, then the update will be WRONG!  This is (should be) a valid
764664509cSschwarze  * assumption...
774664509cSschwarze  */
784664509cSschwarze 
794664509cSschwarze #define	TC_BUFSIZE	2048
804664509cSschwarze 
81fd40972aSschwarze #define	GoodStr(a)	(el->el_terminal.t_str[a] != NULL && \
82fd40972aSschwarze 			    el->el_terminal.t_str[a][0] != '\0')
83fd40972aSschwarze #define	Str(a)		el->el_terminal.t_str[a]
84fd40972aSschwarze #define	Val(a)		el->el_terminal.t_val[a]
854664509cSschwarze 
86ddc81437Sschwarze static const struct termcapstr {
874664509cSschwarze 	const char *name;
884664509cSschwarze 	const char *long_name;
894664509cSschwarze } tstr[] = {
904664509cSschwarze #define	T_al	0
914664509cSschwarze 	{ "al", "add new blank line" },
924664509cSschwarze #define	T_bl	1
934664509cSschwarze 	{ "bl", "audible bell" },
944664509cSschwarze #define	T_cd	2
954664509cSschwarze 	{ "cd", "clear to bottom" },
964664509cSschwarze #define	T_ce	3
974664509cSschwarze 	{ "ce", "clear to end of line" },
984664509cSschwarze #define	T_ch	4
994664509cSschwarze 	{ "ch", "cursor to horiz pos" },
1004664509cSschwarze #define	T_cl	5
1014664509cSschwarze 	{ "cl", "clear screen" },
1024664509cSschwarze #define	T_dc	6
1034664509cSschwarze 	{ "dc", "delete a character" },
1044664509cSschwarze #define	T_dl	7
1054664509cSschwarze 	{ "dl", "delete a line" },
1064664509cSschwarze #define	T_dm	8
1074664509cSschwarze 	{ "dm", "start delete mode" },
1084664509cSschwarze #define	T_ed	9
1094664509cSschwarze 	{ "ed", "end delete mode" },
1104664509cSschwarze #define	T_ei	10
1114664509cSschwarze 	{ "ei", "end insert mode" },
1124664509cSschwarze #define	T_fs	11
1134664509cSschwarze 	{ "fs", "cursor from status line" },
1144664509cSschwarze #define	T_ho	12
1154664509cSschwarze 	{ "ho", "home cursor" },
1164664509cSschwarze #define	T_ic	13
1174664509cSschwarze 	{ "ic", "insert character" },
1184664509cSschwarze #define	T_im	14
1194664509cSschwarze 	{ "im", "start insert mode" },
1204664509cSschwarze #define	T_ip	15
1214664509cSschwarze 	{ "ip", "insert padding" },
1224664509cSschwarze #define	T_kd	16
1234664509cSschwarze 	{ "kd", "sends cursor down" },
1244664509cSschwarze #define	T_kl	17
1254664509cSschwarze 	{ "kl", "sends cursor left" },
1264664509cSschwarze #define	T_kr	18
1274664509cSschwarze 	{ "kr", "sends cursor right" },
1284664509cSschwarze #define	T_ku	19
1294664509cSschwarze 	{ "ku", "sends cursor up" },
1304664509cSschwarze #define	T_md	20
1314664509cSschwarze 	{ "md", "begin bold" },
1324664509cSschwarze #define	T_me	21
1334664509cSschwarze 	{ "me", "end attributes" },
1344664509cSschwarze #define	T_nd	22
1354664509cSschwarze 	{ "nd", "non destructive space" },
1364664509cSschwarze #define	T_se	23
1374664509cSschwarze 	{ "se", "end standout" },
1384664509cSschwarze #define	T_so	24
1394664509cSschwarze 	{ "so", "begin standout" },
1404664509cSschwarze #define	T_ts	25
1414664509cSschwarze 	{ "ts", "cursor to status line" },
1424664509cSschwarze #define	T_up	26
1434664509cSschwarze 	{ "up", "cursor up one" },
1444664509cSschwarze #define	T_us	27
1454664509cSschwarze 	{ "us", "begin underline" },
1464664509cSschwarze #define	T_ue	28
1474664509cSschwarze 	{ "ue", "end underline" },
1484664509cSschwarze #define	T_vb	29
1494664509cSschwarze 	{ "vb", "visible bell" },
1504664509cSschwarze #define	T_DC	30
1514664509cSschwarze 	{ "DC", "delete multiple chars" },
1524664509cSschwarze #define	T_DO	31
1534664509cSschwarze 	{ "DO", "cursor down multiple" },
1544664509cSschwarze #define	T_IC	32
1554664509cSschwarze 	{ "IC", "insert multiple chars" },
1564664509cSschwarze #define	T_LE	33
1574664509cSschwarze 	{ "LE", "cursor left multiple" },
1584664509cSschwarze #define	T_RI	34
1594664509cSschwarze 	{ "RI", "cursor right multiple" },
1604664509cSschwarze #define	T_UP	35
1614664509cSschwarze 	{ "UP", "cursor up multiple" },
1624664509cSschwarze #define	T_kh	36
1634664509cSschwarze 	{ "kh", "send cursor home" },
1644664509cSschwarze #define	T_at7	37
1654664509cSschwarze 	{ "@7", "send cursor end" },
1664664509cSschwarze #define	T_str	38
1674664509cSschwarze 	{ NULL, NULL }
1684664509cSschwarze };
1694664509cSschwarze 
170ddc81437Sschwarze static const struct termcapval {
1714664509cSschwarze 	const char *name;
1724664509cSschwarze 	const char *long_name;
1734664509cSschwarze } tval[] = {
1744664509cSschwarze #define	T_am	0
1754664509cSschwarze 	{ "am", "has automatic margins" },
1764664509cSschwarze #define	T_pt	1
1774664509cSschwarze 	{ "pt", "has physical tabs" },
1784664509cSschwarze #define	T_li	2
1794664509cSschwarze 	{ "li", "Number of lines" },
1804664509cSschwarze #define	T_co	3
1814664509cSschwarze 	{ "co", "Number of columns" },
1824664509cSschwarze #define	T_km	4
1834664509cSschwarze 	{ "km", "Has meta key" },
1844664509cSschwarze #define	T_xt	5
1854664509cSschwarze 	{ "xt", "Tab chars destructive" },
1864664509cSschwarze #define	T_xn	6
1874664509cSschwarze 	{ "xn", "newline ignored at right margin" },
1884664509cSschwarze #define	T_MT	7
1894664509cSschwarze 	{ "MT", "Has meta key" },			/* XXX? */
1904664509cSschwarze #define	T_val	8
1914664509cSschwarze 	{ NULL, NULL, }
1924664509cSschwarze };
1934664509cSschwarze /* do two or more of the attributes use me */
1944664509cSschwarze 
195ddc81437Sschwarze static void	terminal_setflags(EditLine *);
196ddc81437Sschwarze static int	terminal_rebuffer_display(EditLine *);
197ddc81437Sschwarze static void	terminal_free_display(EditLine *);
198ddc81437Sschwarze static int	terminal_alloc_display(EditLine *);
199ddc81437Sschwarze static void	terminal_alloc(EditLine *, const struct termcapstr *,
200fd40972aSschwarze     const char *);
201ddc81437Sschwarze static void	terminal_init_arrow(EditLine *);
202ddc81437Sschwarze static void	terminal_reset_arrow(EditLine *);
203ddc81437Sschwarze static int	terminal_putc(int);
204ddc81437Sschwarze static void	terminal_tputs(EditLine *, const char *, int);
2054664509cSschwarze 
2064664509cSschwarze #ifdef _REENTRANT
207ddc81437Sschwarze static pthread_mutex_t terminal_mutex = PTHREAD_MUTEX_INITIALIZER;
2084664509cSschwarze #endif
209ddc81437Sschwarze static FILE *terminal_outfile = NULL;
2104664509cSschwarze 
2114664509cSschwarze 
212fd40972aSschwarze /* terminal_setflags():
2134664509cSschwarze  *	Set the terminal capability flags
2144664509cSschwarze  */
215ddc81437Sschwarze static void
terminal_setflags(EditLine * el)216fd40972aSschwarze terminal_setflags(EditLine *el)
2174664509cSschwarze {
2184664509cSschwarze 	EL_FLAGS = 0;
2194664509cSschwarze 	if (el->el_tty.t_tabs)
2204664509cSschwarze 		EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0;
2214664509cSschwarze 
2224664509cSschwarze 	EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0;
2234664509cSschwarze 	EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0;
2244664509cSschwarze 	EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0;
2254664509cSschwarze 	EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ?
2264664509cSschwarze 	    TERM_CAN_INSERT : 0;
2274664509cSschwarze 	EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0;
2284664509cSschwarze 	EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0;
2294664509cSschwarze 	EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0;
2304664509cSschwarze 
2314664509cSschwarze 	if (GoodStr(T_me) && GoodStr(T_ue))
2324664509cSschwarze 		EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ?
2334664509cSschwarze 		    TERM_CAN_ME : 0;
2344664509cSschwarze 	else
2354664509cSschwarze 		EL_FLAGS &= ~TERM_CAN_ME;
2364664509cSschwarze 	if (GoodStr(T_me) && GoodStr(T_se))
2374664509cSschwarze 		EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ?
2384664509cSschwarze 		    TERM_CAN_ME : 0;
2394664509cSschwarze 
2404664509cSschwarze 
2414664509cSschwarze #ifdef DEBUG_SCREEN
2424664509cSschwarze 	if (!EL_CAN_UP) {
2434664509cSschwarze 		(void) fprintf(el->el_errfile,
2444664509cSschwarze 		    "WARNING: Your terminal cannot move up.\n");
2454664509cSschwarze 		(void) fprintf(el->el_errfile,
2464664509cSschwarze 		    "Editing may be odd for long lines.\n");
2474664509cSschwarze 	}
2484664509cSschwarze 	if (!EL_CAN_CEOL)
2494664509cSschwarze 		(void) fprintf(el->el_errfile, "no clear EOL capability.\n");
2504664509cSschwarze 	if (!EL_CAN_DELETE)
2514664509cSschwarze 		(void) fprintf(el->el_errfile, "no delete char capability.\n");
2524664509cSschwarze 	if (!EL_CAN_INSERT)
2534664509cSschwarze 		(void) fprintf(el->el_errfile, "no insert char capability.\n");
2544664509cSschwarze #endif /* DEBUG_SCREEN */
2554664509cSschwarze }
2564664509cSschwarze 
257fd40972aSschwarze /* terminal_init():
2584664509cSschwarze  *	Initialize the terminal stuff
2594664509cSschwarze  */
2604664509cSschwarze protected int
terminal_init(EditLine * el)261fd40972aSschwarze terminal_init(EditLine *el)
2624664509cSschwarze {
2634664509cSschwarze 
264fd40972aSschwarze 	el->el_terminal.t_buf = (char *)malloc(TC_BUFSIZE);
265fd40972aSschwarze 	if (el->el_terminal.t_buf == NULL)
266a32142afSschwarze 		goto fail1;
267fd40972aSschwarze 	el->el_terminal.t_cap = (char *)malloc(TC_BUFSIZE);
268fd40972aSschwarze 	if (el->el_terminal.t_cap == NULL)
2694664509cSschwarze 		goto fail2;
270fd40972aSschwarze 	el->el_terminal.t_fkey = reallocarray(NULL, A_K_NKEYS,
271fd40972aSschwarze 	    sizeof(*el->el_terminal.t_fkey));
272fd40972aSschwarze 	if (el->el_terminal.t_fkey == NULL)
2734664509cSschwarze 		goto fail3;
274fd40972aSschwarze 	el->el_terminal.t_loc = 0;
2757b6df1e2Stb 	el->el_terminal.t_str = calloc(T_str, sizeof(char *));
276fd40972aSschwarze 	if (el->el_terminal.t_str == NULL)
2774664509cSschwarze 		goto fail4;
2787b6df1e2Stb 	el->el_terminal.t_val = calloc(T_val, sizeof(int));
279fd40972aSschwarze 	if (el->el_terminal.t_val == NULL)
2804664509cSschwarze 		goto fail5;
281fd40972aSschwarze 	(void) terminal_set(el, NULL);
282fd40972aSschwarze 	terminal_init_arrow(el);
28328d54ee8Sschwarze 	return 0;
2844664509cSschwarze fail5:
285fd40972aSschwarze 	free(el->el_terminal.t_str);
286fd40972aSschwarze 	el->el_terminal.t_str = NULL;
2874664509cSschwarze fail4:
288fd40972aSschwarze 	free(el->el_terminal.t_fkey);
289fd40972aSschwarze 	el->el_terminal.t_fkey = NULL;
2904664509cSschwarze fail3:
291fd40972aSschwarze 	free(el->el_terminal.t_cap);
292fd40972aSschwarze 	el->el_terminal.t_cap = NULL;
2934664509cSschwarze fail2:
294fd40972aSschwarze 	free(el->el_terminal.t_buf);
295fd40972aSschwarze 	el->el_terminal.t_buf = NULL;
296a32142afSschwarze fail1:
29728d54ee8Sschwarze 	return -1;
2984664509cSschwarze }
2994664509cSschwarze 
300fd40972aSschwarze /* terminal_end():
3014664509cSschwarze  *	Clean up the terminal stuff
3024664509cSschwarze  */
3034664509cSschwarze protected void
terminal_end(EditLine * el)304fd40972aSschwarze terminal_end(EditLine *el)
3054664509cSschwarze {
3064664509cSschwarze 
307fd40972aSschwarze 	free(el->el_terminal.t_buf);
308fd40972aSschwarze 	el->el_terminal.t_buf = NULL;
309fd40972aSschwarze 	free(el->el_terminal.t_cap);
310fd40972aSschwarze 	el->el_terminal.t_cap = NULL;
311fd40972aSschwarze 	el->el_terminal.t_loc = 0;
312fd40972aSschwarze 	free(el->el_terminal.t_str);
313fd40972aSschwarze 	el->el_terminal.t_str = NULL;
314fd40972aSschwarze 	free(el->el_terminal.t_val);
315fd40972aSschwarze 	el->el_terminal.t_val = NULL;
316fd40972aSschwarze 	free(el->el_terminal.t_fkey);
317fd40972aSschwarze 	el->el_terminal.t_fkey = NULL;
318fd40972aSschwarze 	terminal_free_display(el);
3194664509cSschwarze }
3204664509cSschwarze 
3214664509cSschwarze 
322fd40972aSschwarze /* terminal_alloc():
3234664509cSschwarze  *	Maintain a string pool for termcap strings
3244664509cSschwarze  */
325ddc81437Sschwarze static void
terminal_alloc(EditLine * el,const struct termcapstr * t,const char * cap)326fd40972aSschwarze terminal_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
3274664509cSschwarze {
3284664509cSschwarze 	char termbuf[TC_BUFSIZE];
3294664509cSschwarze 	size_t tlen, clen;
330fd40972aSschwarze 	char **tlist = el->el_terminal.t_str;
3314664509cSschwarze 	char **tmp, **str = &tlist[t - tstr];
3324664509cSschwarze 
3334664509cSschwarze 	if (cap == NULL || *cap == '\0') {
3344664509cSschwarze 		*str = NULL;
3354664509cSschwarze 		return;
3364664509cSschwarze 	} else
3374664509cSschwarze 		clen = strlen(cap);
3384664509cSschwarze 
3394664509cSschwarze 	tlen = *str == NULL ? 0 : strlen(*str);
3404664509cSschwarze 
3414664509cSschwarze 	/*
3424664509cSschwarze          * New string is shorter; no need to allocate space
3434664509cSschwarze          */
3444664509cSschwarze 	if (clen <= tlen) {
3454664509cSschwarze 		if (*str)
3464664509cSschwarze 			(void) strlcpy(*str, cap, tlen + 1);
3474664509cSschwarze 		return;
3484664509cSschwarze 	}
3494664509cSschwarze 	/*
3504664509cSschwarze          * New string is longer; see if we have enough space to append
3514664509cSschwarze          */
352fd40972aSschwarze 	if (el->el_terminal.t_loc + 3 < TC_BUFSIZE) {
353fd40972aSschwarze 		tlen = TC_BUFSIZE - el->el_terminal.t_loc;
354fd40972aSschwarze 		(void) strlcpy(*str = &el->el_terminal.t_buf[
355fd40972aSschwarze 		    el->el_terminal.t_loc], cap, tlen);
356fd40972aSschwarze 		el->el_terminal.t_loc += (int)clen + 1;	/* one for \0 */
3574664509cSschwarze 		return;
3584664509cSschwarze 	}
3594664509cSschwarze 	/*
3604664509cSschwarze          * Compact our buffer; no need to check compaction, cause we know it
3614664509cSschwarze          * fits...
3624664509cSschwarze          */
3634664509cSschwarze 	tlen = 0;
3644664509cSschwarze 	for (tmp = tlist; tmp < &tlist[T_str]; tmp++)
365fcbc4fc7Sschwarze 		if (*tmp != NULL && **tmp != '\0' && *tmp != *str) {
3664664509cSschwarze 			char *ptr;
3674664509cSschwarze 
3684664509cSschwarze 			for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++)
3694664509cSschwarze 				continue;
3704664509cSschwarze 			termbuf[tlen++] = '\0';
3714664509cSschwarze 		}
372fd40972aSschwarze 	memcpy(el->el_terminal.t_buf, termbuf, TC_BUFSIZE);
373fd40972aSschwarze 	el->el_terminal.t_loc = (int)tlen;
374fd40972aSschwarze 	if (el->el_terminal.t_loc + 3 >= TC_BUFSIZE) {
3754664509cSschwarze 		(void) fprintf(el->el_errfile,
3764664509cSschwarze 		    "Out of termcap string space.\n");
3774664509cSschwarze 		return;
3784664509cSschwarze 	}
379fd40972aSschwarze 	tlen = TC_BUFSIZE - el->el_terminal.t_loc;
380fd40972aSschwarze 	(void) strlcpy(*str = &el->el_terminal.t_buf[el->el_terminal.t_loc],
381fd40972aSschwarze 	    cap, tlen);
382fd40972aSschwarze 	el->el_terminal.t_loc += (int)clen + 1;	/* one for \0 */
3834664509cSschwarze 	return;
3844664509cSschwarze }
3854664509cSschwarze 
3864664509cSschwarze 
387fd40972aSschwarze /* terminal_rebuffer_display():
3884664509cSschwarze  *	Rebuffer the display after the screen changed size
3894664509cSschwarze  */
390ddc81437Sschwarze static int
terminal_rebuffer_display(EditLine * el)391fd40972aSschwarze terminal_rebuffer_display(EditLine *el)
3924664509cSschwarze {
393fd40972aSschwarze 	coord_t *c = &el->el_terminal.t_size;
3944664509cSschwarze 
395fd40972aSschwarze 	terminal_free_display(el);
3964664509cSschwarze 
3974664509cSschwarze 	c->h = Val(T_co);
3984664509cSschwarze 	c->v = Val(T_li);
3994664509cSschwarze 
400fd40972aSschwarze 	if (terminal_alloc_display(el) == -1)
40128d54ee8Sschwarze 		return -1;
40228d54ee8Sschwarze 	return 0;
4034664509cSschwarze }
4044664509cSschwarze 
4054664509cSschwarze 
406fd40972aSschwarze /* terminal_alloc_display():
4074664509cSschwarze  *	Allocate a new display.
4084664509cSschwarze  */
409ddc81437Sschwarze static int
terminal_alloc_display(EditLine * el)410fd40972aSschwarze terminal_alloc_display(EditLine *el)
4114664509cSschwarze {
412a32142afSschwarze 	int i;
413e3191321Sschwarze 	wchar_t **b;
414fd40972aSschwarze 	coord_t *c = &el->el_terminal.t_size;
4154664509cSschwarze 
4162853db30Syasuoka 	b = calloc(c->v + 1, sizeof(*b));
4174664509cSschwarze 	if (b == NULL)
4184664509cSschwarze 		goto done;
4194664509cSschwarze 	for (i = 0; i < c->v; i++) {
4202853db30Syasuoka 		b[i] = calloc(c->h + 1, sizeof(**b));
4214664509cSschwarze 		if (b[i] == NULL) {
4224664509cSschwarze 			while (--i >= 0)
4237b85e16bSschwarze 				free(b[i]);
4247b85e16bSschwarze 			free(b);
4254664509cSschwarze 			goto done;
4264664509cSschwarze 		}
4274664509cSschwarze 	}
4284664509cSschwarze 	b[c->v] = NULL;
4294664509cSschwarze 	el->el_display = b;
4304664509cSschwarze 
4312853db30Syasuoka 	b = calloc(c->v + 1, sizeof(*b));
4324664509cSschwarze 	if (b == NULL)
4334664509cSschwarze 		goto done;
4344664509cSschwarze 	for (i = 0; i < c->v; i++) {
4352853db30Syasuoka 		b[i] = calloc(c->h + 1, sizeof(**b));
4364664509cSschwarze 		if (b[i] == NULL) {
4374664509cSschwarze 			while (--i >= 0)
4387b85e16bSschwarze 				free(b[i]);
4397b85e16bSschwarze 			free(b);
4404664509cSschwarze 			goto done;
4414664509cSschwarze 		}
4424664509cSschwarze 	}
4434664509cSschwarze 	b[c->v] = NULL;
4444664509cSschwarze 	el->el_vdisplay = b;
445a32142afSschwarze 	return 0;
4464664509cSschwarze done:
447fd40972aSschwarze 	terminal_free_display(el);
448a32142afSschwarze 	return -1;
4494664509cSschwarze }
4504664509cSschwarze 
4514664509cSschwarze 
452fd40972aSschwarze /* terminal_free_display():
4534664509cSschwarze  *	Free the display buffers
4544664509cSschwarze  */
455ddc81437Sschwarze static void
terminal_free_display(EditLine * el)456fd40972aSschwarze terminal_free_display(EditLine *el)
4574664509cSschwarze {
458e3191321Sschwarze 	wchar_t **b;
459e3191321Sschwarze 	wchar_t **bufp;
4604664509cSschwarze 
4614664509cSschwarze 	b = el->el_display;
4624664509cSschwarze 	el->el_display = NULL;
4634664509cSschwarze 	if (b != NULL) {
4644664509cSschwarze 		for (bufp = b; *bufp != NULL; bufp++)
4657b85e16bSschwarze 			free(*bufp);
4667b85e16bSschwarze 		free(b);
4674664509cSschwarze 	}
4684664509cSschwarze 	b = el->el_vdisplay;
4694664509cSschwarze 	el->el_vdisplay = NULL;
4704664509cSschwarze 	if (b != NULL) {
4714664509cSschwarze 		for (bufp = b; *bufp != NULL; bufp++)
4727b85e16bSschwarze 			free(*bufp);
4737b85e16bSschwarze 		free(b);
4744664509cSschwarze 	}
4754664509cSschwarze }
4764664509cSschwarze 
4774664509cSschwarze 
478fd40972aSschwarze /* terminal_move_to_line():
4794664509cSschwarze  *	move to line <where> (first line == 0)
4804664509cSschwarze  *	as efficiently as possible
4814664509cSschwarze  */
4824664509cSschwarze protected void
terminal_move_to_line(EditLine * el,int where)483fd40972aSschwarze terminal_move_to_line(EditLine *el, int where)
4844664509cSschwarze {
4854664509cSschwarze 	int del;
4864664509cSschwarze 
4874664509cSschwarze 	if (where == el->el_cursor.v)
4884664509cSschwarze 		return;
4894664509cSschwarze 
490fd40972aSschwarze 	if (where > el->el_terminal.t_size.v) {
4914664509cSschwarze #ifdef DEBUG_SCREEN
4924664509cSschwarze 		(void) fprintf(el->el_errfile,
493fd40972aSschwarze 		    "terminal_move_to_line: where is ridiculous: %d\r\n",
494fd40972aSschwarze 		    where);
4954664509cSschwarze #endif /* DEBUG_SCREEN */
4964664509cSschwarze 		return;
4974664509cSschwarze 	}
4984664509cSschwarze 	if ((del = where - el->el_cursor.v) > 0) {
4994664509cSschwarze 		while (del > 0) {
5004664509cSschwarze 			if (EL_HAS_AUTO_MARGINS &&
5014664509cSschwarze 			    el->el_display[el->el_cursor.v][0] != '\0') {
502fd40972aSschwarze                                 size_t h = el->el_terminal.t_size.h - 1;
5034664509cSschwarze                                 for (; h > 0 &&
5044664509cSschwarze                                          el->el_display[el->el_cursor.v][h] ==
5054664509cSschwarze                                                  MB_FILL_CHAR;
5064664509cSschwarze                                          h--)
5074664509cSschwarze                                                 continue;
5084664509cSschwarze 				/* move without newline */
509fd40972aSschwarze 				terminal_move_to_char(el, (int)h);
510fd40972aSschwarze 				terminal_overwrite(el, &el->el_display
5114664509cSschwarze 				    [el->el_cursor.v][el->el_cursor.h],
512fd40972aSschwarze 				    (size_t)(el->el_terminal.t_size.h -
5134664509cSschwarze 				    el->el_cursor.h));
5144664509cSschwarze 				/* updates Cursor */
5154664509cSschwarze 				del--;
5164664509cSschwarze 			} else {
5174664509cSschwarze 				if ((del > 1) && GoodStr(T_DO)) {
518fd40972aSschwarze 					terminal_tputs(el, tgoto(Str(T_DO), del,
5194664509cSschwarze 					    del), del);
5204664509cSschwarze 					del = 0;
5214664509cSschwarze 				} else {
5224664509cSschwarze 					for (; del > 0; del--)
523fd40972aSschwarze 						terminal__putc(el, '\n');
5244664509cSschwarze 					/* because the \n will become \r\n */
5254664509cSschwarze 					el->el_cursor.h = 0;
5264664509cSschwarze 				}
5274664509cSschwarze 			}
5284664509cSschwarze 		}
5294664509cSschwarze 	} else {		/* del < 0 */
5304664509cSschwarze 		if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up)))
531fd40972aSschwarze 			terminal_tputs(el, tgoto(Str(T_UP), -del, -del), -del);
5324664509cSschwarze 		else {
5334664509cSschwarze 			if (GoodStr(T_up))
5344664509cSschwarze 				for (; del < 0; del++)
535fd40972aSschwarze 					terminal_tputs(el, Str(T_up), 1);
5364664509cSschwarze 		}
5374664509cSschwarze 	}
5384664509cSschwarze 	el->el_cursor.v = where;/* now where is here */
5394664509cSschwarze }
5404664509cSschwarze 
5414664509cSschwarze 
542fd40972aSschwarze /* terminal_move_to_char():
5434664509cSschwarze  *	Move to the character position specified
5444664509cSschwarze  */
5454664509cSschwarze protected void
terminal_move_to_char(EditLine * el,int where)546fd40972aSschwarze terminal_move_to_char(EditLine *el, int where)
5474664509cSschwarze {
5484664509cSschwarze 	int del, i;
5494664509cSschwarze 
5504664509cSschwarze mc_again:
5514664509cSschwarze 	if (where == el->el_cursor.h)
5524664509cSschwarze 		return;
5534664509cSschwarze 
554fd40972aSschwarze 	if (where > el->el_terminal.t_size.h) {
5554664509cSschwarze #ifdef DEBUG_SCREEN
5564664509cSschwarze 		(void) fprintf(el->el_errfile,
557fd40972aSschwarze 		    "terminal_move_to_char: where is riduculous: %d\r\n",
558fd40972aSschwarze 		    where);
5594664509cSschwarze #endif /* DEBUG_SCREEN */
5604664509cSschwarze 		return;
5614664509cSschwarze 	}
5624664509cSschwarze 	if (!where) {		/* if where is first column */
563fd40972aSschwarze 		terminal__putc(el, '\r');	/* do a CR */
5644664509cSschwarze 		el->el_cursor.h = 0;
5654664509cSschwarze 		return;
5664664509cSschwarze 	}
5674664509cSschwarze 	del = where - el->el_cursor.h;
5684664509cSschwarze 
5694664509cSschwarze 	if ((del < -4 || del > 4) && GoodStr(T_ch))
5704664509cSschwarze 		/* go there directly */
571fd40972aSschwarze 		terminal_tputs(el, tgoto(Str(T_ch), where, where), where);
5724664509cSschwarze 	else {
5734664509cSschwarze 		if (del > 0) {	/* moving forward */
5744664509cSschwarze 			if ((del > 4) && GoodStr(T_RI))
575fd40972aSschwarze 				terminal_tputs(el, tgoto(Str(T_RI), del, del),
576fd40972aSschwarze 				    del);
5774664509cSschwarze 			else {
5784664509cSschwarze 					/* if I can do tabs, use them */
5794664509cSschwarze 				if (EL_CAN_TAB) {
5804664509cSschwarze 					if ((el->el_cursor.h & 0370) !=
5814664509cSschwarze 					    (where & ~0x7)
5824664509cSschwarze 					    && (el->el_display[
5834664509cSschwarze 					    el->el_cursor.v][where & 0370] !=
5844664509cSschwarze 					    MB_FILL_CHAR)
5854664509cSschwarze 					    ) {
5864664509cSschwarze 						/* if not within tab stop */
5874664509cSschwarze 						for (i =
5884664509cSschwarze 						    (el->el_cursor.h & 0370);
5894664509cSschwarze 						    i < (where & ~0x7);
5904664509cSschwarze 						    i += 8)
591fd40972aSschwarze 							terminal__putc(el,
592fd40972aSschwarze 							    '\t');
5934664509cSschwarze 							/* then tab over */
5944664509cSschwarze 						el->el_cursor.h = where & ~0x7;
5954664509cSschwarze 					}
5964664509cSschwarze 				}
5974664509cSschwarze 				/*
5984664509cSschwarze 				 * it's usually cheaper to just write the
5994664509cSschwarze 				 * chars, so we do.
6004664509cSschwarze 				 */
6014664509cSschwarze 				/*
602fd40972aSschwarze 				 * NOTE THAT terminal_overwrite() WILL CHANGE
6034664509cSschwarze 				 * el->el_cursor.h!!!
6044664509cSschwarze 				 */
605fd40972aSschwarze 				terminal_overwrite(el, &el->el_display[
6064664509cSschwarze 				    el->el_cursor.v][el->el_cursor.h],
6074664509cSschwarze 				    (size_t)(where - el->el_cursor.h));
6084664509cSschwarze 
6094664509cSschwarze 			}
6104664509cSschwarze 		} else {	/* del < 0 := moving backward */
6114664509cSschwarze 			if ((-del > 4) && GoodStr(T_LE))
612fd40972aSschwarze 				terminal_tputs(el, tgoto(Str(T_LE), -del, -del),
6134664509cSschwarze 				    -del);
6144664509cSschwarze 			else {	/* can't go directly there */
6154664509cSschwarze 				/*
6164664509cSschwarze 				 * if the "cost" is greater than the "cost"
6174664509cSschwarze 				 * from col 0
6184664509cSschwarze 				 */
6194664509cSschwarze 				if (EL_CAN_TAB ?
6204664509cSschwarze 				    ((unsigned int)-del >
6214664509cSschwarze 				    (((unsigned int) where >> 3) +
6224664509cSschwarze 				     (where & 07)))
6234664509cSschwarze 				    : (-del > where)) {
624fd40972aSschwarze 					terminal__putc(el, '\r');/* do a CR */
6254664509cSschwarze 					el->el_cursor.h = 0;
6264664509cSschwarze 					goto mc_again;	/* and try again */
6274664509cSschwarze 				}
6284664509cSschwarze 				for (i = 0; i < -del; i++)
629fd40972aSschwarze 					terminal__putc(el, '\b');
6304664509cSschwarze 			}
6314664509cSschwarze 		}
6324664509cSschwarze 	}
6334664509cSschwarze 	el->el_cursor.h = where;		/* now where is here */
6344664509cSschwarze }
6354664509cSschwarze 
6364664509cSschwarze 
637fd40972aSschwarze /* terminal_overwrite():
6384664509cSschwarze  *	Overstrike num characters
6394664509cSschwarze  *	Assumes MB_FILL_CHARs are present to keep the column count correct
6404664509cSschwarze  */
6414664509cSschwarze protected void
terminal_overwrite(EditLine * el,const wchar_t * cp,size_t n)642e3191321Sschwarze terminal_overwrite(EditLine *el, const wchar_t *cp, size_t n)
6434664509cSschwarze {
6444664509cSschwarze 	if (n == 0)
6454664509cSschwarze 		return;
6464664509cSschwarze 
647fd40972aSschwarze 	if (n > (size_t)el->el_terminal.t_size.h) {
6484664509cSschwarze #ifdef DEBUG_SCREEN
6494664509cSschwarze 		(void) fprintf(el->el_errfile,
65030806f50Sschwarze 		    "terminal_overwrite: n is riduculous: %zu\r\n", n);
6514664509cSschwarze #endif /* DEBUG_SCREEN */
6524664509cSschwarze 		return;
6534664509cSschwarze 	}
6544664509cSschwarze 
6554664509cSschwarze         do {
656fd40972aSschwarze                 /* terminal__putc() ignores any MB_FILL_CHARs */
657fd40972aSschwarze                 terminal__putc(el, *cp++);
6584664509cSschwarze                 el->el_cursor.h++;
6594664509cSschwarze         } while (--n);
6604664509cSschwarze 
661fd40972aSschwarze 	if (el->el_cursor.h >= el->el_terminal.t_size.h) {	/* wrap? */
6624664509cSschwarze 		if (EL_HAS_AUTO_MARGINS) {	/* yes */
6634664509cSschwarze 			el->el_cursor.h = 0;
6644664509cSschwarze 			el->el_cursor.v++;
6654664509cSschwarze 			if (EL_HAS_MAGIC_MARGINS) {
6664664509cSschwarze 				/* force the wrap to avoid the "magic"
6674664509cSschwarze 				 * situation */
668e3191321Sschwarze 				wchar_t c;
6694664509cSschwarze 				if ((c = el->el_display[el->el_cursor.v]
6704664509cSschwarze 				    [el->el_cursor.h]) != '\0') {
671fd40972aSschwarze 					terminal_overwrite(el, &c, 1);
6724664509cSschwarze 					while (el->el_display[el->el_cursor.v]
6734664509cSschwarze 					    [el->el_cursor.h] == MB_FILL_CHAR)
6744664509cSschwarze 						el->el_cursor.h++;
6754664509cSschwarze 				} else {
676fd40972aSschwarze 					terminal__putc(el, ' ');
6774664509cSschwarze 					el->el_cursor.h = 1;
6784664509cSschwarze 				}
6794664509cSschwarze 			}
6804664509cSschwarze 		} else		/* no wrap, but cursor stays on screen */
681fd40972aSschwarze 			el->el_cursor.h = el->el_terminal.t_size.h - 1;
6824664509cSschwarze 	}
6834664509cSschwarze }
6844664509cSschwarze 
6854664509cSschwarze 
686fd40972aSschwarze /* terminal_deletechars():
6874664509cSschwarze  *	Delete num characters
6884664509cSschwarze  */
6894664509cSschwarze protected void
terminal_deletechars(EditLine * el,int num)690fd40972aSschwarze terminal_deletechars(EditLine *el, int num)
6914664509cSschwarze {
6924664509cSschwarze 	if (num <= 0)
6934664509cSschwarze 		return;
6944664509cSschwarze 
6954664509cSschwarze 	if (!EL_CAN_DELETE) {
6964664509cSschwarze #ifdef DEBUG_EDIT
6974664509cSschwarze 		(void) fprintf(el->el_errfile, "   ERROR: cannot delete   \n");
6984664509cSschwarze #endif /* DEBUG_EDIT */
6994664509cSschwarze 		return;
7004664509cSschwarze 	}
701fd40972aSschwarze 	if (num > el->el_terminal.t_size.h) {
7024664509cSschwarze #ifdef DEBUG_SCREEN
7034664509cSschwarze 		(void) fprintf(el->el_errfile,
704fd40972aSschwarze 		    "terminal_deletechars: num is riduculous: %d\r\n", num);
7054664509cSschwarze #endif /* DEBUG_SCREEN */
7064664509cSschwarze 		return;
7074664509cSschwarze 	}
7084664509cSschwarze 	if (GoodStr(T_DC))	/* if I have multiple delete */
7094664509cSschwarze 		if ((num > 1) || !GoodStr(T_dc)) {	/* if dc would be more
7104664509cSschwarze 							 * expen. */
711fd40972aSschwarze 			terminal_tputs(el, tgoto(Str(T_DC), num, num), num);
7124664509cSschwarze 			return;
7134664509cSschwarze 		}
7144664509cSschwarze 	if (GoodStr(T_dm))	/* if I have delete mode */
715fd40972aSschwarze 		terminal_tputs(el, Str(T_dm), 1);
7164664509cSschwarze 
7174664509cSschwarze 	if (GoodStr(T_dc))	/* else do one at a time */
7184664509cSschwarze 		while (num--)
719fd40972aSschwarze 			terminal_tputs(el, Str(T_dc), 1);
7204664509cSschwarze 
7214664509cSschwarze 	if (GoodStr(T_ed))	/* if I have delete mode */
722fd40972aSschwarze 		terminal_tputs(el, Str(T_ed), 1);
7234664509cSschwarze }
7244664509cSschwarze 
7254664509cSschwarze 
726fd40972aSschwarze /* terminal_insertwrite():
7274664509cSschwarze  *	Puts terminal in insert character mode or inserts num
7284664509cSschwarze  *	characters in the line
7294664509cSschwarze  *      Assumes MB_FILL_CHARs are present to keep column count correct
7304664509cSschwarze  */
7314664509cSschwarze protected void
terminal_insertwrite(EditLine * el,wchar_t * cp,int num)732e3191321Sschwarze terminal_insertwrite(EditLine *el, wchar_t *cp, int num)
7334664509cSschwarze {
7344664509cSschwarze 	if (num <= 0)
7354664509cSschwarze 		return;
7364664509cSschwarze 	if (!EL_CAN_INSERT) {
7374664509cSschwarze #ifdef DEBUG_EDIT
7384664509cSschwarze 		(void) fprintf(el->el_errfile, "   ERROR: cannot insert   \n");
7394664509cSschwarze #endif /* DEBUG_EDIT */
7404664509cSschwarze 		return;
7414664509cSschwarze 	}
742fd40972aSschwarze 	if (num > el->el_terminal.t_size.h) {
7434664509cSschwarze #ifdef DEBUG_SCREEN
7444664509cSschwarze 		(void) fprintf(el->el_errfile,
7454664509cSschwarze 		    "StartInsert: num is riduculous: %d\r\n", num);
7464664509cSschwarze #endif /* DEBUG_SCREEN */
7474664509cSschwarze 		return;
7484664509cSschwarze 	}
7494664509cSschwarze 	if (GoodStr(T_IC))	/* if I have multiple insert */
7504664509cSschwarze 		if ((num > 1) || !GoodStr(T_ic)) {
7514664509cSschwarze 				/* if ic would be more expensive */
752fd40972aSschwarze 			terminal_tputs(el, tgoto(Str(T_IC), num, num), num);
753fd40972aSschwarze 			terminal_overwrite(el, cp, (size_t)num);
7544664509cSschwarze 				/* this updates el_cursor.h */
7554664509cSschwarze 			return;
7564664509cSschwarze 		}
7574664509cSschwarze 	if (GoodStr(T_im) && GoodStr(T_ei)) {	/* if I have insert mode */
758fd40972aSschwarze 		terminal_tputs(el, Str(T_im), 1);
7594664509cSschwarze 
7604664509cSschwarze 		el->el_cursor.h += num;
7614664509cSschwarze 		do
762fd40972aSschwarze 			terminal__putc(el, *cp++);
7634664509cSschwarze 		while (--num);
7644664509cSschwarze 
7654664509cSschwarze 		if (GoodStr(T_ip))	/* have to make num chars insert */
766fd40972aSschwarze 			terminal_tputs(el, Str(T_ip), 1);
7674664509cSschwarze 
768fd40972aSschwarze 		terminal_tputs(el, Str(T_ei), 1);
7694664509cSschwarze 		return;
7704664509cSschwarze 	}
7714664509cSschwarze 	do {
7724664509cSschwarze 		if (GoodStr(T_ic))	/* have to make num chars insert */
773fd40972aSschwarze 			terminal_tputs(el, Str(T_ic), 1);
7744664509cSschwarze 
775fd40972aSschwarze 		terminal__putc(el, *cp++);
7764664509cSschwarze 
7774664509cSschwarze 		el->el_cursor.h++;
7784664509cSschwarze 
7794664509cSschwarze 		if (GoodStr(T_ip))	/* have to make num chars insert */
780fd40972aSschwarze 			terminal_tputs(el, Str(T_ip), 1);
7814664509cSschwarze 					/* pad the inserted char */
7824664509cSschwarze 
7834664509cSschwarze 	} while (--num);
7844664509cSschwarze }
7854664509cSschwarze 
7864664509cSschwarze 
787fd40972aSschwarze /* terminal_clear_EOL():
7884664509cSschwarze  *	clear to end of line.  There are num characters to clear
7894664509cSschwarze  */
7904664509cSschwarze protected void
terminal_clear_EOL(EditLine * el,int num)791fd40972aSschwarze terminal_clear_EOL(EditLine *el, int num)
7924664509cSschwarze {
7934664509cSschwarze 	int i;
7944664509cSschwarze 
7954664509cSschwarze 	if (EL_CAN_CEOL && GoodStr(T_ce))
796fd40972aSschwarze 		terminal_tputs(el, Str(T_ce), 1);
7974664509cSschwarze 	else {
7984664509cSschwarze 		for (i = 0; i < num; i++)
799fd40972aSschwarze 			terminal__putc(el, ' ');
8004664509cSschwarze 		el->el_cursor.h += num;	/* have written num spaces */
8014664509cSschwarze 	}
8024664509cSschwarze }
8034664509cSschwarze 
8044664509cSschwarze 
805fd40972aSschwarze /* terminal_clear_screen():
8064664509cSschwarze  *	Clear the screen
8074664509cSschwarze  */
8084664509cSschwarze protected void
terminal_clear_screen(EditLine * el)809fd40972aSschwarze terminal_clear_screen(EditLine *el)
8104664509cSschwarze {				/* clear the whole screen and home */
8114664509cSschwarze 
8124664509cSschwarze 	if (GoodStr(T_cl))
8134664509cSschwarze 		/* send the clear screen code */
814fd40972aSschwarze 		terminal_tputs(el, Str(T_cl), Val(T_li));
8154664509cSschwarze 	else if (GoodStr(T_ho) && GoodStr(T_cd)) {
816fd40972aSschwarze 		terminal_tputs(el, Str(T_ho), Val(T_li));	/* home */
8174664509cSschwarze 		/* clear to bottom of screen */
818fd40972aSschwarze 		terminal_tputs(el, Str(T_cd), Val(T_li));
8194664509cSschwarze 	} else {
820fd40972aSschwarze 		terminal__putc(el, '\r');
821fd40972aSschwarze 		terminal__putc(el, '\n');
8224664509cSschwarze 	}
8234664509cSschwarze }
8244664509cSschwarze 
8254664509cSschwarze 
826fd40972aSschwarze /* terminal_beep():
8274664509cSschwarze  *	Beep the way the terminal wants us
8284664509cSschwarze  */
8294664509cSschwarze protected void
terminal_beep(EditLine * el)830fd40972aSschwarze terminal_beep(EditLine *el)
8314664509cSschwarze {
8324664509cSschwarze 	if (GoodStr(T_bl))
8334664509cSschwarze 		/* what termcap says we should use */
834fd40972aSschwarze 		terminal_tputs(el, Str(T_bl), 1);
8354664509cSschwarze 	else
836fd40972aSschwarze 		terminal__putc(el, '\007');	/* an ASCII bell; ^G */
8374664509cSschwarze }
8384664509cSschwarze 
8394664509cSschwarze 
8404664509cSschwarze protected void
terminal_get(EditLine * el,const char ** term)841fd40972aSschwarze terminal_get(EditLine *el, const char **term)
8424664509cSschwarze {
843fd40972aSschwarze 	*term = el->el_terminal.t_name;
8444664509cSschwarze }
8454664509cSschwarze 
8464664509cSschwarze 
847fd40972aSschwarze /* terminal_set():
8484664509cSschwarze  *	Read in the terminal capabilities from the requested terminal
8494664509cSschwarze  */
8504664509cSschwarze protected int
terminal_set(EditLine * el,const char * term)851fd40972aSschwarze terminal_set(EditLine *el, const char *term)
8524664509cSschwarze {
8534664509cSschwarze 	int i;
8544664509cSschwarze 	char buf[TC_BUFSIZE];
8554664509cSschwarze 	char *area;
8564664509cSschwarze 	const struct termcapstr *t;
8574664509cSschwarze 	sigset_t oset, nset;
8584664509cSschwarze 	int lins, cols;
8594664509cSschwarze 
8604664509cSschwarze 	(void) sigemptyset(&nset);
8614664509cSschwarze 	(void) sigaddset(&nset, SIGWINCH);
8624664509cSschwarze 	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
8634664509cSschwarze 
8644664509cSschwarze 	area = buf;
8654664509cSschwarze 
8664664509cSschwarze 
8674664509cSschwarze 	if (term == NULL)
8684664509cSschwarze 		term = getenv("TERM");
8694664509cSschwarze 
8704664509cSschwarze 	if (!term || !term[0])
8714664509cSschwarze 		term = "dumb";
8724664509cSschwarze 
8734664509cSschwarze 	if (strcmp(term, "emacs") == 0)
8744664509cSschwarze 		el->el_flags |= EDIT_DISABLED;
8754664509cSschwarze 
876fd40972aSschwarze 	(void) memset(el->el_terminal.t_cap, 0, TC_BUFSIZE);
8774664509cSschwarze 
878fd40972aSschwarze 	i = tgetent(el->el_terminal.t_cap, term);
8794664509cSschwarze 
8804664509cSschwarze 	if (i <= 0) {
8814664509cSschwarze 		if (i == -1)
8824664509cSschwarze 			(void) fprintf(el->el_errfile,
8834664509cSschwarze 			    "Cannot read termcap database;\n");
8844664509cSschwarze 		else if (i == 0)
8854664509cSschwarze 			(void) fprintf(el->el_errfile,
8864664509cSschwarze 			    "No entry for terminal type \"%s\";\n", term);
8874664509cSschwarze 		(void) fprintf(el->el_errfile,
8884664509cSschwarze 		    "using dumb terminal settings.\n");
8894664509cSschwarze 		Val(T_co) = 80;	/* do a dumb terminal */
8904664509cSschwarze 		Val(T_pt) = Val(T_km) = Val(T_li) = 0;
8914664509cSschwarze 		Val(T_xt) = Val(T_MT);
8924664509cSschwarze 		for (t = tstr; t->name != NULL; t++)
893fd40972aSschwarze 			terminal_alloc(el, t, NULL);
8944664509cSschwarze 	} else {
8954664509cSschwarze 		/* auto/magic margins */
8964664509cSschwarze 		Val(T_am) = tgetflag("am");
8974664509cSschwarze 		Val(T_xn) = tgetflag("xn");
8984664509cSschwarze 		/* Can we tab */
8994664509cSschwarze 		Val(T_pt) = tgetflag("pt");
9004664509cSschwarze 		Val(T_xt) = tgetflag("xt");
9014664509cSschwarze 		/* do we have a meta? */
9024664509cSschwarze 		Val(T_km) = tgetflag("km");
9034664509cSschwarze 		Val(T_MT) = tgetflag("MT");
9044664509cSschwarze 		/* Get the size */
9054664509cSschwarze 		Val(T_co) = tgetnum("co");
9064664509cSschwarze 		Val(T_li) = tgetnum("li");
9074664509cSschwarze 		for (t = tstr; t->name != NULL; t++) {
9084664509cSschwarze 			/* XXX: some systems' tgetstr needs non const */
909fd40972aSschwarze 			terminal_alloc(el, t, tgetstr(strchr(t->name, *t->name),
9104664509cSschwarze 			    &area));
9114664509cSschwarze 		}
9124664509cSschwarze 	}
9134664509cSschwarze 
9144664509cSschwarze 	if (Val(T_co) < 2)
9154664509cSschwarze 		Val(T_co) = 80;	/* just in case */
9164664509cSschwarze 	if (Val(T_li) < 1)
9174664509cSschwarze 		Val(T_li) = 24;
9184664509cSschwarze 
919fd40972aSschwarze 	el->el_terminal.t_size.v = Val(T_co);
920fd40972aSschwarze 	el->el_terminal.t_size.h = Val(T_li);
9214664509cSschwarze 
922fd40972aSschwarze 	terminal_setflags(el);
9234664509cSschwarze 
9244664509cSschwarze 				/* get the correct window size */
925fd40972aSschwarze 	(void) terminal_get_size(el, &lins, &cols);
926fd40972aSschwarze 	if (terminal_change_size(el, lins, cols) == -1)
92728d54ee8Sschwarze 		return -1;
9284664509cSschwarze 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
929fd40972aSschwarze 	terminal_bind_arrow(el);
930fd40972aSschwarze 	el->el_terminal.t_name = term;
93128d54ee8Sschwarze 	return i <= 0 ? -1 : 0;
9324664509cSschwarze }
9334664509cSschwarze 
9344664509cSschwarze 
935fd40972aSschwarze /* terminal_get_size():
9364664509cSschwarze  *	Return the new window size in lines and cols, and
9374664509cSschwarze  *	true if the size was changed.
9384664509cSschwarze  */
9394664509cSschwarze protected int
terminal_get_size(EditLine * el,int * lins,int * cols)940fd40972aSschwarze terminal_get_size(EditLine *el, int *lins, int *cols)
9414664509cSschwarze {
9424664509cSschwarze 
9434664509cSschwarze 	*cols = Val(T_co);
9444664509cSschwarze 	*lins = Val(T_li);
9454664509cSschwarze 
9464664509cSschwarze #ifdef TIOCGWINSZ
9474664509cSschwarze 	{
9484664509cSschwarze 		struct winsize ws;
9497b85e16bSschwarze 		if (ioctl(el->el_infd, TIOCGWINSZ, &ws) != -1) {
9504664509cSschwarze 			if (ws.ws_col)
9514664509cSschwarze 				*cols = ws.ws_col;
9524664509cSschwarze 			if (ws.ws_row)
9534664509cSschwarze 				*lins = ws.ws_row;
9544664509cSschwarze 		}
9554664509cSschwarze 	}
9564664509cSschwarze #endif
9574664509cSschwarze #ifdef TIOCGSIZE
9584664509cSschwarze 	{
9594664509cSschwarze 		struct ttysize ts;
9607b85e16bSschwarze 		if (ioctl(el->el_infd, TIOCGSIZE, &ts) != -1) {
9614664509cSschwarze 			if (ts.ts_cols)
9624664509cSschwarze 				*cols = ts.ts_cols;
9634664509cSschwarze 			if (ts.ts_lines)
9644664509cSschwarze 				*lins = ts.ts_lines;
9654664509cSschwarze 		}
9664664509cSschwarze 	}
9674664509cSschwarze #endif
96828d54ee8Sschwarze 	return Val(T_co) != *cols || Val(T_li) != *lins;
9694664509cSschwarze }
9704664509cSschwarze 
9714664509cSschwarze 
972fd40972aSschwarze /* terminal_change_size():
9734664509cSschwarze  *	Change the size of the terminal
9744664509cSschwarze  */
9754664509cSschwarze protected int
terminal_change_size(EditLine * el,int lins,int cols)976fd40972aSschwarze terminal_change_size(EditLine *el, int lins, int cols)
9774664509cSschwarze {
9784664509cSschwarze 	/*
9794664509cSschwarze          * Just in case
9804664509cSschwarze          */
9814664509cSschwarze 	Val(T_co) = (cols < 2) ? 80 : cols;
9824664509cSschwarze 	Val(T_li) = (lins < 1) ? 24 : lins;
9834664509cSschwarze 
9844664509cSschwarze 	/* re-make display buffers */
985fd40972aSschwarze 	if (terminal_rebuffer_display(el) == -1)
98628d54ee8Sschwarze 		return -1;
9874664509cSschwarze 	re_clear_display(el);
98828d54ee8Sschwarze 	return 0;
9894664509cSschwarze }
9904664509cSschwarze 
9914664509cSschwarze 
992fd40972aSschwarze /* terminal_init_arrow():
9934664509cSschwarze  *	Initialize the arrow key bindings from termcap
9944664509cSschwarze  */
995ddc81437Sschwarze static void
terminal_init_arrow(EditLine * el)996fd40972aSschwarze terminal_init_arrow(EditLine *el)
9974664509cSschwarze {
998fd40972aSschwarze 	funckey_t *arrow = el->el_terminal.t_fkey;
9994664509cSschwarze 
10005c93237dSschwarze 	arrow[A_K_DN].name = L"down";
10014664509cSschwarze 	arrow[A_K_DN].key = T_kd;
10024664509cSschwarze 	arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY;
10034664509cSschwarze 	arrow[A_K_DN].type = XK_CMD;
10044664509cSschwarze 
10055c93237dSschwarze 	arrow[A_K_UP].name = L"up";
10064664509cSschwarze 	arrow[A_K_UP].key = T_ku;
10074664509cSschwarze 	arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY;
10084664509cSschwarze 	arrow[A_K_UP].type = XK_CMD;
10094664509cSschwarze 
10105c93237dSschwarze 	arrow[A_K_LT].name = L"left";
10114664509cSschwarze 	arrow[A_K_LT].key = T_kl;
10124664509cSschwarze 	arrow[A_K_LT].fun.cmd = ED_PREV_CHAR;
10134664509cSschwarze 	arrow[A_K_LT].type = XK_CMD;
10144664509cSschwarze 
10155c93237dSschwarze 	arrow[A_K_RT].name = L"right";
10164664509cSschwarze 	arrow[A_K_RT].key = T_kr;
10174664509cSschwarze 	arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR;
10184664509cSschwarze 	arrow[A_K_RT].type = XK_CMD;
10194664509cSschwarze 
10205c93237dSschwarze 	arrow[A_K_HO].name = L"home";
10214664509cSschwarze 	arrow[A_K_HO].key = T_kh;
10224664509cSschwarze 	arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG;
10234664509cSschwarze 	arrow[A_K_HO].type = XK_CMD;
10244664509cSschwarze 
10255c93237dSschwarze 	arrow[A_K_EN].name = L"end";
10264664509cSschwarze 	arrow[A_K_EN].key = T_at7;
10274664509cSschwarze 	arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END;
10284664509cSschwarze 	arrow[A_K_EN].type = XK_CMD;
10294664509cSschwarze }
10304664509cSschwarze 
10314664509cSschwarze 
1032fd40972aSschwarze /* terminal_reset_arrow():
10334664509cSschwarze  *	Reset arrow key bindings
10344664509cSschwarze  */
1035ddc81437Sschwarze static void
terminal_reset_arrow(EditLine * el)1036fd40972aSschwarze terminal_reset_arrow(EditLine *el)
10374664509cSschwarze {
1038fd40972aSschwarze 	funckey_t *arrow = el->el_terminal.t_fkey;
1039e3191321Sschwarze 	static const wchar_t strA[] = L"\033[A";
1040e3191321Sschwarze 	static const wchar_t strB[] = L"\033[B";
1041e3191321Sschwarze 	static const wchar_t strC[] = L"\033[C";
1042e3191321Sschwarze 	static const wchar_t strD[] = L"\033[D";
1043e3191321Sschwarze 	static const wchar_t strH[] = L"\033[H";
1044e3191321Sschwarze 	static const wchar_t strF[] = L"\033[F";
1045e3191321Sschwarze 	static const wchar_t stOA[] = L"\033OA";
1046e3191321Sschwarze 	static const wchar_t stOB[] = L"\033OB";
1047e3191321Sschwarze 	static const wchar_t stOC[] = L"\033OC";
1048e3191321Sschwarze 	static const wchar_t stOD[] = L"\033OD";
1049e3191321Sschwarze 	static const wchar_t stOH[] = L"\033OH";
1050e3191321Sschwarze 	static const wchar_t stOF[] = L"\033OF";
10514664509cSschwarze 
105236facb13Sschwarze 	keymacro_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
105336facb13Sschwarze 	keymacro_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
105436facb13Sschwarze 	keymacro_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
105536facb13Sschwarze 	keymacro_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
105636facb13Sschwarze 	keymacro_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
105736facb13Sschwarze 	keymacro_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
105836facb13Sschwarze 	keymacro_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
105936facb13Sschwarze 	keymacro_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
106036facb13Sschwarze 	keymacro_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
106136facb13Sschwarze 	keymacro_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
106236facb13Sschwarze 	keymacro_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
106336facb13Sschwarze 	keymacro_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
10644664509cSschwarze 
106536facb13Sschwarze 	if (el->el_map.type != MAP_VI)
106636facb13Sschwarze 		return;
106736facb13Sschwarze 	keymacro_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
106836facb13Sschwarze 	keymacro_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
106936facb13Sschwarze 	keymacro_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
107036facb13Sschwarze 	keymacro_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
107136facb13Sschwarze 	keymacro_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
107236facb13Sschwarze 	keymacro_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
107336facb13Sschwarze 	keymacro_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
107436facb13Sschwarze 	keymacro_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
107536facb13Sschwarze 	keymacro_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
107636facb13Sschwarze 	keymacro_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
107736facb13Sschwarze 	keymacro_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
107836facb13Sschwarze 	keymacro_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
10794664509cSschwarze }
10804664509cSschwarze 
10814664509cSschwarze 
1082fd40972aSschwarze /* terminal_set_arrow():
10834664509cSschwarze  *	Set an arrow key binding
10844664509cSschwarze  */
10854664509cSschwarze protected int
terminal_set_arrow(EditLine * el,const wchar_t * name,keymacro_value_t * fun,int type)1086e3191321Sschwarze terminal_set_arrow(EditLine *el, const wchar_t *name, keymacro_value_t *fun,
1087fd40972aSschwarze     int type)
10884664509cSschwarze {
1089fd40972aSschwarze 	funckey_t *arrow = el->el_terminal.t_fkey;
10904664509cSschwarze 	int i;
10914664509cSschwarze 
10924664509cSschwarze 	for (i = 0; i < A_K_NKEYS; i++)
10935c93237dSschwarze 		if (wcscmp(name, arrow[i].name) == 0) {
10944664509cSschwarze 			arrow[i].fun = *fun;
10954664509cSschwarze 			arrow[i].type = type;
109628d54ee8Sschwarze 			return 0;
10974664509cSschwarze 		}
109828d54ee8Sschwarze 	return -1;
10994664509cSschwarze }
11004664509cSschwarze 
11014664509cSschwarze 
1102fd40972aSschwarze /* terminal_clear_arrow():
11034664509cSschwarze  *	Clear an arrow key binding
11044664509cSschwarze  */
11054664509cSschwarze protected int
terminal_clear_arrow(EditLine * el,const wchar_t * name)1106e3191321Sschwarze terminal_clear_arrow(EditLine *el, const wchar_t *name)
11074664509cSschwarze {
1108fd40972aSschwarze 	funckey_t *arrow = el->el_terminal.t_fkey;
11094664509cSschwarze 	int i;
11104664509cSschwarze 
11114664509cSschwarze 	for (i = 0; i < A_K_NKEYS; i++)
11125c93237dSschwarze 		if (wcscmp(name, arrow[i].name) == 0) {
11134664509cSschwarze 			arrow[i].type = XK_NOD;
111428d54ee8Sschwarze 			return 0;
11154664509cSschwarze 		}
111628d54ee8Sschwarze 	return -1;
11174664509cSschwarze }
11184664509cSschwarze 
11194664509cSschwarze 
1120fd40972aSschwarze /* terminal_print_arrow():
11214664509cSschwarze  *	Print the arrow key bindings
11224664509cSschwarze  */
11234664509cSschwarze protected void
terminal_print_arrow(EditLine * el,const wchar_t * name)1124e3191321Sschwarze terminal_print_arrow(EditLine *el, const wchar_t *name)
11254664509cSschwarze {
11264664509cSschwarze 	int i;
1127fd40972aSschwarze 	funckey_t *arrow = el->el_terminal.t_fkey;
11284664509cSschwarze 
11294664509cSschwarze 	for (i = 0; i < A_K_NKEYS; i++)
11305c93237dSschwarze 		if (*name == '\0' || wcscmp(name, arrow[i].name) == 0)
11314664509cSschwarze 			if (arrow[i].type != XK_NOD)
113236facb13Sschwarze 				keymacro_kprint(el, arrow[i].name,
113336facb13Sschwarze 				    &arrow[i].fun, arrow[i].type);
11344664509cSschwarze }
11354664509cSschwarze 
11364664509cSschwarze 
1137fd40972aSschwarze /* terminal_bind_arrow():
11384664509cSschwarze  *	Bind the arrow keys
11394664509cSschwarze  */
11404664509cSschwarze protected void
terminal_bind_arrow(EditLine * el)1141fd40972aSschwarze terminal_bind_arrow(EditLine *el)
11424664509cSschwarze {
11434664509cSschwarze 	el_action_t *map;
11444664509cSschwarze 	const el_action_t *dmap;
11454664509cSschwarze 	int i, j;
11464664509cSschwarze 	char *p;
1147fd40972aSschwarze 	funckey_t *arrow = el->el_terminal.t_fkey;
11484664509cSschwarze 
11494664509cSschwarze 	/* Check if the components needed are initialized */
1150fd40972aSschwarze 	if (el->el_terminal.t_buf == NULL || el->el_map.key == NULL)
11514664509cSschwarze 		return;
11524664509cSschwarze 
11534664509cSschwarze 	map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key;
11544664509cSschwarze 	dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs;
11554664509cSschwarze 
1156fd40972aSschwarze 	terminal_reset_arrow(el);
11574664509cSschwarze 
11584664509cSschwarze 	for (i = 0; i < A_K_NKEYS; i++) {
1159e3191321Sschwarze 		wchar_t wt_str[VISUAL_WIDTH_MAX];
1160e3191321Sschwarze 		wchar_t *px;
11614664509cSschwarze 		size_t n;
11624664509cSschwarze 
1163fd40972aSschwarze 		p = el->el_terminal.t_str[arrow[i].key];
11644664509cSschwarze 		if (!p || !*p)
11654664509cSschwarze 			continue;
11664664509cSschwarze 		for (n = 0; n < VISUAL_WIDTH_MAX && p[n]; ++n)
11674664509cSschwarze 			wt_str[n] = p[n];
11684664509cSschwarze 		while (n < VISUAL_WIDTH_MAX)
11694664509cSschwarze 			wt_str[n++] = '\0';
11704664509cSschwarze 		px = wt_str;
11714664509cSschwarze 		j = (unsigned char) *p;
11724664509cSschwarze 		/*
11734664509cSschwarze 		 * Assign the arrow keys only if:
11744664509cSschwarze 		 *
11754664509cSschwarze 		 * 1. They are multi-character arrow keys and the user
11764664509cSschwarze 		 *    has not re-assigned the leading character, or
11774664509cSschwarze 		 *    has re-assigned the leading character to be
11784664509cSschwarze 		 *	  ED_SEQUENCE_LEAD_IN
11794664509cSschwarze 		 * 2. They are single arrow keys pointing to an
11804664509cSschwarze 		 *    unassigned key.
11814664509cSschwarze 		 */
11824664509cSschwarze 		if (arrow[i].type == XK_NOD)
118336facb13Sschwarze 			keymacro_clear(el, map, px);
11844664509cSschwarze 		else {
11854664509cSschwarze 			if (p[1] && (dmap[j] == map[j] ||
11864664509cSschwarze 				map[j] == ED_SEQUENCE_LEAD_IN)) {
118736facb13Sschwarze 				keymacro_add(el, px, &arrow[i].fun,
11884664509cSschwarze 				    arrow[i].type);
11894664509cSschwarze 				map[j] = ED_SEQUENCE_LEAD_IN;
11904664509cSschwarze 			} else if (map[j] == ED_UNASSIGNED) {
119136facb13Sschwarze 				keymacro_clear(el, map, px);
11924664509cSschwarze 				if (arrow[i].type == XK_CMD)
11934664509cSschwarze 					map[j] = arrow[i].fun.cmd;
11944664509cSschwarze 				else
119536facb13Sschwarze 					keymacro_add(el, px, &arrow[i].fun,
11964664509cSschwarze 					    arrow[i].type);
11974664509cSschwarze 			}
11984664509cSschwarze 		}
11994664509cSschwarze 	}
12004664509cSschwarze }
12014664509cSschwarze 
1202fd40972aSschwarze /* terminal_putc():
12034664509cSschwarze  *	Add a character
12044664509cSschwarze  */
1205ddc81437Sschwarze static int
terminal_putc(int c)1206fd40972aSschwarze terminal_putc(int c)
12074664509cSschwarze {
1208fd40972aSschwarze 	if (terminal_outfile == NULL)
12094664509cSschwarze 		return -1;
1210fd40972aSschwarze 	return fputc(c, terminal_outfile);
12114664509cSschwarze }
12124664509cSschwarze 
1213ddc81437Sschwarze static void
terminal_tputs(EditLine * el,const char * cap,int affcnt)1214fd40972aSschwarze terminal_tputs(EditLine *el, const char *cap, int affcnt)
12154664509cSschwarze {
12164664509cSschwarze #ifdef _REENTRANT
1217fd40972aSschwarze 	pthread_mutex_lock(&terminal_mutex);
12184664509cSschwarze #endif
1219fd40972aSschwarze 	terminal_outfile = el->el_outfile;
1220fd40972aSschwarze 	(void)tputs(cap, affcnt, terminal_putc);
12214664509cSschwarze #ifdef _REENTRANT
1222fd40972aSschwarze 	pthread_mutex_unlock(&terminal_mutex);
12234664509cSschwarze #endif
12244664509cSschwarze }
12254664509cSschwarze 
1226fd40972aSschwarze /* terminal__putc():
12274664509cSschwarze  *	Add a character
12284664509cSschwarze  */
12294664509cSschwarze protected int
terminal__putc(EditLine * el,wint_t c)1230b2589f0bSschwarze terminal__putc(EditLine *el, wint_t c)
12314664509cSschwarze {
12324664509cSschwarze 	char buf[MB_LEN_MAX +1];
12334664509cSschwarze 	ssize_t i;
1234b2589f0bSschwarze 	if (c == (wint_t)MB_FILL_CHAR)
12354664509cSschwarze 		return 0;
12364664509cSschwarze 	i = ct_encode_char(buf, MB_LEN_MAX, c);
12374664509cSschwarze 	if (i <= 0)
12384664509cSschwarze 		return (int)i;
12394664509cSschwarze 	buf[i] = '\0';
12404664509cSschwarze 	return fputs(buf, el->el_outfile);
12414664509cSschwarze }
12424664509cSschwarze 
1243fd40972aSschwarze /* terminal__flush():
12444664509cSschwarze  *	Flush output
12454664509cSschwarze  */
12464664509cSschwarze protected void
terminal__flush(EditLine * el)1247fd40972aSschwarze terminal__flush(EditLine *el)
12484664509cSschwarze {
12494664509cSschwarze 
12504664509cSschwarze 	(void) fflush(el->el_outfile);
12514664509cSschwarze }
12524664509cSschwarze 
1253fd40972aSschwarze /* terminal_writec():
12544664509cSschwarze  *	Write the given character out, in a human readable form
12554664509cSschwarze  */
12564664509cSschwarze protected void
terminal_writec(EditLine * el,wint_t c)1257b2589f0bSschwarze terminal_writec(EditLine *el, wint_t c)
12584664509cSschwarze {
1259e3191321Sschwarze 	wchar_t visbuf[VISUAL_WIDTH_MAX +1];
12604664509cSschwarze 	ssize_t vcnt = ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
12614664509cSschwarze 	visbuf[vcnt] = '\0';
1262fd40972aSschwarze 	terminal_overwrite(el, visbuf, (size_t)vcnt);
1263fd40972aSschwarze 	terminal__flush(el);
12644664509cSschwarze }
12654664509cSschwarze 
12664664509cSschwarze 
1267fd40972aSschwarze /* terminal_telltc():
12684664509cSschwarze  *	Print the current termcap characteristics
12694664509cSschwarze  */
12704664509cSschwarze protected int
terminal_telltc(EditLine * el,int argc,const wchar_t ** argv)1271fd40972aSschwarze terminal_telltc(EditLine *el, int argc __attribute__((__unused__)),
1272e3191321Sschwarze     const wchar_t **argv __attribute__((__unused__)))
12734664509cSschwarze {
12744664509cSschwarze 	const struct termcapstr *t;
12754664509cSschwarze 	char **ts;
12764664509cSschwarze 
12774664509cSschwarze 	(void) fprintf(el->el_outfile, "\n\tYour terminal has the\n");
12784664509cSschwarze 	(void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n");
12794664509cSschwarze 	(void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n",
12804664509cSschwarze 	    Val(T_co), Val(T_li));
12814664509cSschwarze 	(void) fprintf(el->el_outfile,
12824664509cSschwarze 	    "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no");
12834664509cSschwarze 	(void) fprintf(el->el_outfile,
12844664509cSschwarze 	    "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not ");
12854664509cSschwarze 	(void) fprintf(el->el_outfile, "\tIt %s automatic margins\n",
12864664509cSschwarze 	    EL_HAS_AUTO_MARGINS ? "has" : "does not have");
12874664509cSschwarze 	if (EL_HAS_AUTO_MARGINS)
12884664509cSschwarze 		(void) fprintf(el->el_outfile, "\tIt %s magic margins\n",
12894664509cSschwarze 		    EL_HAS_MAGIC_MARGINS ? "has" : "does not have");
12904664509cSschwarze 
1291fd40972aSschwarze 	for (t = tstr, ts = el->el_terminal.t_str; t->name != NULL; t++, ts++) {
12924664509cSschwarze 		const char *ub;
12934664509cSschwarze 		if (*ts && **ts) {
12944664509cSschwarze 			ub = ct_encode_string(ct_visual_string(
12954664509cSschwarze 			    ct_decode_string(*ts, &el->el_scratch)),
12964664509cSschwarze 			    &el->el_scratch);
12974664509cSschwarze 		} else {
12984664509cSschwarze 			ub = "(empty)";
12994664509cSschwarze 		}
13004664509cSschwarze 		(void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n",
13014664509cSschwarze 		    t->long_name, t->name, ub);
13024664509cSschwarze 	}
13034664509cSschwarze 	(void) fputc('\n', el->el_outfile);
130428d54ee8Sschwarze 	return 0;
13054664509cSschwarze }
13064664509cSschwarze 
13074664509cSschwarze 
1308fd40972aSschwarze /* terminal_settc():
13094664509cSschwarze  *	Change the current terminal characteristics
13104664509cSschwarze  */
13114664509cSschwarze protected int
terminal_settc(EditLine * el,int argc,const wchar_t ** argv)1312fd40972aSschwarze terminal_settc(EditLine *el, int argc __attribute__((__unused__)),
1313e3191321Sschwarze     const wchar_t **argv)
13144664509cSschwarze {
13154664509cSschwarze 	const struct termcapstr *ts;
13164664509cSschwarze 	const struct termcapval *tv;
13174664509cSschwarze 	char what[8], how[8];
13184664509cSschwarze 
13194664509cSschwarze 	if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
13204664509cSschwarze 		return -1;
13214664509cSschwarze 
13224664509cSschwarze 	strncpy(what, ct_encode_string(argv[1], &el->el_scratch), sizeof(what));
13234664509cSschwarze 	what[sizeof(what) - 1] = '\0';
13244664509cSschwarze 	strncpy(how,  ct_encode_string(argv[2], &el->el_scratch), sizeof(how));
13254664509cSschwarze 	how[sizeof(how) - 1] = '\0';
13264664509cSschwarze 
13274664509cSschwarze 	/*
13284664509cSschwarze          * Do the strings first
13294664509cSschwarze          */
13304664509cSschwarze 	for (ts = tstr; ts->name != NULL; ts++)
13314664509cSschwarze 		if (strcmp(ts->name, what) == 0)
13324664509cSschwarze 			break;
13334664509cSschwarze 
13344664509cSschwarze 	if (ts->name != NULL) {
1335fd40972aSschwarze 		terminal_alloc(el, ts, how);
1336fd40972aSschwarze 		terminal_setflags(el);
13374664509cSschwarze 		return 0;
13384664509cSschwarze 	}
13394664509cSschwarze 	/*
13404664509cSschwarze          * Do the numeric ones second
13414664509cSschwarze          */
13424664509cSschwarze 	for (tv = tval; tv->name != NULL; tv++)
13434664509cSschwarze 		if (strcmp(tv->name, what) == 0)
13444664509cSschwarze 			break;
13454664509cSschwarze 
13464664509cSschwarze 	if (tv->name != NULL)
13474664509cSschwarze 		return -1;
13484664509cSschwarze 
13494664509cSschwarze 	if (tv == &tval[T_pt] || tv == &tval[T_km] ||
13504664509cSschwarze 	    tv == &tval[T_am] || tv == &tval[T_xn]) {
13514664509cSschwarze 		if (strcmp(how, "yes") == 0)
1352fd40972aSschwarze 			el->el_terminal.t_val[tv - tval] = 1;
13534664509cSschwarze 		else if (strcmp(how, "no") == 0)
1354fd40972aSschwarze 			el->el_terminal.t_val[tv - tval] = 0;
13554664509cSschwarze 		else {
13564664509cSschwarze 			(void) fprintf(el->el_errfile,
1357565aa7e8Sschwarze 			    "%ls: Bad value `%s'.\n", argv[0], how);
13584664509cSschwarze 			return -1;
13594664509cSschwarze 		}
1360fd40972aSschwarze 		terminal_setflags(el);
1361fd40972aSschwarze 		if (terminal_change_size(el, Val(T_li), Val(T_co)) == -1)
13624664509cSschwarze 			return -1;
13634664509cSschwarze 		return 0;
13644664509cSschwarze 	} else {
13654664509cSschwarze 		long i;
13664664509cSschwarze 		char *ep;
13674664509cSschwarze 
13684664509cSschwarze 		i = strtol(how, &ep, 10);
13694664509cSschwarze 		if (*ep != '\0') {
13704664509cSschwarze 			(void) fprintf(el->el_errfile,
1371565aa7e8Sschwarze 			    "%ls: Bad value `%s'.\n", argv[0], how);
13724664509cSschwarze 			return -1;
13734664509cSschwarze 		}
1374fd40972aSschwarze 		el->el_terminal.t_val[tv - tval] = (int) i;
1375fd40972aSschwarze 		el->el_terminal.t_size.v = Val(T_co);
1376fd40972aSschwarze 		el->el_terminal.t_size.h = Val(T_li);
13774664509cSschwarze 		if (tv == &tval[T_co] || tv == &tval[T_li])
1378fd40972aSschwarze 			if (terminal_change_size(el, Val(T_li), Val(T_co))
13794664509cSschwarze 			    == -1)
13804664509cSschwarze 				return -1;
13814664509cSschwarze 		return 0;
13824664509cSschwarze 	}
13834664509cSschwarze }
13844664509cSschwarze 
13854664509cSschwarze 
1386fd40972aSschwarze /* terminal_gettc():
13874664509cSschwarze  *	Get the current terminal characteristics
13884664509cSschwarze  */
13894664509cSschwarze protected int
terminal_gettc(EditLine * el,int argc,char ** argv)1390fd40972aSschwarze terminal_gettc(EditLine *el, int argc __attribute__((__unused__)), char **argv)
13914664509cSschwarze {
13924664509cSschwarze 	const struct termcapstr *ts;
13934664509cSschwarze 	const struct termcapval *tv;
13944664509cSschwarze 	char *what;
13954664509cSschwarze 	void *how;
13964664509cSschwarze 
13974664509cSschwarze 	if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
139828d54ee8Sschwarze 		return -1;
13994664509cSschwarze 
14004664509cSschwarze 	what = argv[1];
14014664509cSschwarze 	how = argv[2];
14024664509cSschwarze 
14034664509cSschwarze 	/*
14044664509cSschwarze          * Do the strings first
14054664509cSschwarze          */
14064664509cSschwarze 	for (ts = tstr; ts->name != NULL; ts++)
14074664509cSschwarze 		if (strcmp(ts->name, what) == 0)
14084664509cSschwarze 			break;
14094664509cSschwarze 
14104664509cSschwarze 	if (ts->name != NULL) {
1411fd40972aSschwarze 		*(char **)how = el->el_terminal.t_str[ts - tstr];
14124664509cSschwarze 		return 0;
14134664509cSschwarze 	}
14144664509cSschwarze 	/*
14154664509cSschwarze          * Do the numeric ones second
14164664509cSschwarze          */
14174664509cSschwarze 	for (tv = tval; tv->name != NULL; tv++)
14184664509cSschwarze 		if (strcmp(tv->name, what) == 0)
14194664509cSschwarze 			break;
14204664509cSschwarze 
14214664509cSschwarze 	if (tv->name == NULL)
14224664509cSschwarze 		return -1;
14234664509cSschwarze 
14244664509cSschwarze 	if (tv == &tval[T_pt] || tv == &tval[T_km] ||
14254664509cSschwarze 	    tv == &tval[T_am] || tv == &tval[T_xn]) {
14264664509cSschwarze 		static char yes[] = "yes";
14274664509cSschwarze 		static char no[] = "no";
1428fd40972aSschwarze 		if (el->el_terminal.t_val[tv - tval])
14294664509cSschwarze 			*(char **)how = yes;
14304664509cSschwarze 		else
14314664509cSschwarze 			*(char **)how = no;
14324664509cSschwarze 		return 0;
14334664509cSschwarze 	} else {
1434fd40972aSschwarze 		*(int *)how = el->el_terminal.t_val[tv - tval];
14354664509cSschwarze 		return 0;
14364664509cSschwarze 	}
14374664509cSschwarze }
14384664509cSschwarze 
1439fd40972aSschwarze /* terminal_echotc():
14404664509cSschwarze  *	Print the termcap string out with variable substitution
14414664509cSschwarze  */
14424664509cSschwarze protected int
terminal_echotc(EditLine * el,int argc,const wchar_t ** argv)1443fd40972aSschwarze terminal_echotc(EditLine *el, int argc __attribute__((__unused__)),
1444e3191321Sschwarze     const wchar_t **argv)
14454664509cSschwarze {
14464664509cSschwarze 	char *cap, *scap;
1447e3191321Sschwarze 	wchar_t *ep;
14484664509cSschwarze 	int arg_need, arg_cols, arg_rows;
14494664509cSschwarze 	int verbose = 0, silent = 0;
14504664509cSschwarze 	char *area;
14514664509cSschwarze 	static const char fmts[] = "%s\n", fmtd[] = "%d\n";
14524664509cSschwarze 	const struct termcapstr *t;
14534664509cSschwarze 	char buf[TC_BUFSIZE];
14544664509cSschwarze 	long i;
14554664509cSschwarze 
14564664509cSschwarze 	area = buf;
14574664509cSschwarze 
14584664509cSschwarze 	if (argv == NULL || argv[1] == NULL)
145928d54ee8Sschwarze 		return -1;
14604664509cSschwarze 	argv++;
14614664509cSschwarze 
14624664509cSschwarze 	if (argv[0][0] == '-') {
14634664509cSschwarze 		switch (argv[0][1]) {
14644664509cSschwarze 		case 'v':
14654664509cSschwarze 			verbose = 1;
14664664509cSschwarze 			break;
14674664509cSschwarze 		case 's':
14684664509cSschwarze 			silent = 1;
14694664509cSschwarze 			break;
14704664509cSschwarze 		default:
14714664509cSschwarze 			/* stderror(ERR_NAME | ERR_TCUSAGE); */
14724664509cSschwarze 			break;
14734664509cSschwarze 		}
14744664509cSschwarze 		argv++;
14754664509cSschwarze 	}
14764664509cSschwarze 	if (!*argv || *argv[0] == '\0')
147728d54ee8Sschwarze 		return 0;
14785c93237dSschwarze 	if (wcscmp(*argv, L"tabs") == 0) {
14794664509cSschwarze 		(void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no");
148028d54ee8Sschwarze 		return 0;
14815c93237dSschwarze 	} else if (wcscmp(*argv, L"meta") == 0) {
14824664509cSschwarze 		(void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no");
148328d54ee8Sschwarze 		return 0;
14845c93237dSschwarze 	} else if (wcscmp(*argv, L"xn") == 0) {
14854664509cSschwarze 		(void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ?
14864664509cSschwarze 		    "yes" : "no");
148728d54ee8Sschwarze 		return 0;
14885c93237dSschwarze 	} else if (wcscmp(*argv, L"am") == 0) {
14894664509cSschwarze 		(void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ?
14904664509cSschwarze 		    "yes" : "no");
149128d54ee8Sschwarze 		return 0;
14925c93237dSschwarze 	} else if (wcscmp(*argv, L"baud") == 0) {
14934664509cSschwarze 		(void) fprintf(el->el_outfile, fmtd, (int)el->el_tty.t_speed);
149428d54ee8Sschwarze 		return 0;
14955c93237dSschwarze 	} else if (wcscmp(*argv, L"rows") == 0 ||
14965c93237dSschwarze                    wcscmp(*argv, L"lines") == 0) {
14974664509cSschwarze 		(void) fprintf(el->el_outfile, fmtd, Val(T_li));
149828d54ee8Sschwarze 		return 0;
14995c93237dSschwarze 	} else if (wcscmp(*argv, L"cols") == 0) {
15004664509cSschwarze 		(void) fprintf(el->el_outfile, fmtd, Val(T_co));
150128d54ee8Sschwarze 		return 0;
15024664509cSschwarze 	}
15034664509cSschwarze 	/*
15044664509cSschwarze          * Try to use our local definition first
15054664509cSschwarze          */
15064664509cSschwarze 	scap = NULL;
15074664509cSschwarze 	for (t = tstr; t->name != NULL; t++)
15084664509cSschwarze 		if (strcmp(t->name,
15094664509cSschwarze 		    ct_encode_string(*argv, &el->el_scratch)) == 0) {
1510fd40972aSschwarze 			scap = el->el_terminal.t_str[t - tstr];
15114664509cSschwarze 			break;
15124664509cSschwarze 		}
15134664509cSschwarze 	if (t->name == NULL) {
15144664509cSschwarze 		/* XXX: some systems' tgetstr needs non const */
15154664509cSschwarze                 scap = tgetstr(ct_encode_string(*argv, &el->el_scratch), &area);
15164664509cSschwarze 	}
15174664509cSschwarze 	if (!scap || scap[0] == '\0') {
15184664509cSschwarze 		if (!silent)
15194664509cSschwarze 			(void) fprintf(el->el_errfile,
1520565aa7e8Sschwarze 			    "echotc: Termcap parameter `%ls' not found.\n",
15214664509cSschwarze 			    *argv);
152228d54ee8Sschwarze 		return -1;
15234664509cSschwarze 	}
15244664509cSschwarze 	/*
15254664509cSschwarze          * Count home many values we need for this capability.
15264664509cSschwarze          */
15274664509cSschwarze 	for (cap = scap, arg_need = 0; *cap; cap++)
15284664509cSschwarze 		if (*cap == '%')
15294664509cSschwarze 			switch (*++cap) {
15304664509cSschwarze 			case 'd':
15314664509cSschwarze 			case '2':
15324664509cSschwarze 			case '3':
15334664509cSschwarze 			case '.':
15344664509cSschwarze 			case '+':
15354664509cSschwarze 				arg_need++;
15364664509cSschwarze 				break;
15374664509cSschwarze 			case '%':
15384664509cSschwarze 			case '>':
15394664509cSschwarze 			case 'i':
15404664509cSschwarze 			case 'r':
15414664509cSschwarze 			case 'n':
15424664509cSschwarze 			case 'B':
15434664509cSschwarze 			case 'D':
15444664509cSschwarze 				break;
15454664509cSschwarze 			default:
15464664509cSschwarze 				/*
15474664509cSschwarze 				 * hpux has lot's of them...
15484664509cSschwarze 				 */
15494664509cSschwarze 				if (verbose)
15504664509cSschwarze 					(void) fprintf(el->el_errfile,
15514664509cSschwarze 				"echotc: Warning: unknown termcap %% `%c'.\n",
15524664509cSschwarze 					    *cap);
15534664509cSschwarze 				/* This is bad, but I won't complain */
15544664509cSschwarze 				break;
15554664509cSschwarze 			}
15564664509cSschwarze 
15574664509cSschwarze 	switch (arg_need) {
15584664509cSschwarze 	case 0:
15594664509cSschwarze 		argv++;
15604664509cSschwarze 		if (*argv && *argv[0]) {
15614664509cSschwarze 			if (!silent)
15624664509cSschwarze 				(void) fprintf(el->el_errfile,
1563565aa7e8Sschwarze 				    "echotc: Warning: Extra argument `%ls'.\n",
15644664509cSschwarze 				    *argv);
156528d54ee8Sschwarze 			return -1;
15664664509cSschwarze 		}
1567fd40972aSschwarze 		terminal_tputs(el, scap, 1);
15684664509cSschwarze 		break;
15694664509cSschwarze 	case 1:
15704664509cSschwarze 		argv++;
15714664509cSschwarze 		if (!*argv || *argv[0] == '\0') {
15724664509cSschwarze 			if (!silent)
15734664509cSschwarze 				(void) fprintf(el->el_errfile,
15744664509cSschwarze 				    "echotc: Warning: Missing argument.\n");
157528d54ee8Sschwarze 			return -1;
15764664509cSschwarze 		}
15774664509cSschwarze 		arg_cols = 0;
1578565aa7e8Sschwarze 		i = wcstol(*argv, &ep, 10);
15794664509cSschwarze 		if (*ep != '\0' || i < 0) {
15804664509cSschwarze 			if (!silent)
15814664509cSschwarze 				(void) fprintf(el->el_errfile,
1582565aa7e8Sschwarze 				    "echotc: Bad value `%ls' for rows.\n",
15834664509cSschwarze 				    *argv);
158428d54ee8Sschwarze 			return -1;
15854664509cSschwarze 		}
15864664509cSschwarze 		arg_rows = (int) i;
15874664509cSschwarze 		argv++;
15884664509cSschwarze 		if (*argv && *argv[0]) {
15894664509cSschwarze 			if (!silent)
15904664509cSschwarze 				(void) fprintf(el->el_errfile,
1591565aa7e8Sschwarze 				    "echotc: Warning: Extra argument `%ls"
1592565aa7e8Sschwarze 				    "'.\n", *argv);
159328d54ee8Sschwarze 			return -1;
15944664509cSschwarze 		}
1595fd40972aSschwarze 		terminal_tputs(el, tgoto(scap, arg_cols, arg_rows), 1);
15964664509cSschwarze 		break;
15974664509cSschwarze 	default:
15984664509cSschwarze 		/* This is wrong, but I will ignore it... */
15994664509cSschwarze 		if (verbose)
16004664509cSschwarze 			(void) fprintf(el->el_errfile,
16014664509cSschwarze 			 "echotc: Warning: Too many required arguments (%d).\n",
16024664509cSschwarze 			    arg_need);
16034664509cSschwarze 		/* FALLTHROUGH */
16044664509cSschwarze 	case 2:
16054664509cSschwarze 		argv++;
16064664509cSschwarze 		if (!*argv || *argv[0] == '\0') {
16074664509cSschwarze 			if (!silent)
16084664509cSschwarze 				(void) fprintf(el->el_errfile,
16094664509cSschwarze 				    "echotc: Warning: Missing argument.\n");
161028d54ee8Sschwarze 			return -1;
16114664509cSschwarze 		}
1612565aa7e8Sschwarze 		i = wcstol(*argv, &ep, 10);
16134664509cSschwarze 		if (*ep != '\0' || i < 0) {
16144664509cSschwarze 			if (!silent)
16154664509cSschwarze 				(void) fprintf(el->el_errfile,
1616565aa7e8Sschwarze 				    "echotc: Bad value `%ls' for cols.\n",
16174664509cSschwarze 				    *argv);
161828d54ee8Sschwarze 			return -1;
16194664509cSschwarze 		}
16204664509cSschwarze 		arg_cols = (int) i;
16214664509cSschwarze 		argv++;
16224664509cSschwarze 		if (!*argv || *argv[0] == '\0') {
16234664509cSschwarze 			if (!silent)
16244664509cSschwarze 				(void) fprintf(el->el_errfile,
16254664509cSschwarze 				    "echotc: Warning: Missing argument.\n");
162628d54ee8Sschwarze 			return -1;
16274664509cSschwarze 		}
1628565aa7e8Sschwarze 		i = wcstol(*argv, &ep, 10);
16294664509cSschwarze 		if (*ep != '\0' || i < 0) {
16304664509cSschwarze 			if (!silent)
16314664509cSschwarze 				(void) fprintf(el->el_errfile,
1632565aa7e8Sschwarze 				    "echotc: Bad value `%ls' for rows.\n",
16334664509cSschwarze 				    *argv);
163428d54ee8Sschwarze 			return -1;
16354664509cSschwarze 		}
16364664509cSschwarze 		arg_rows = (int) i;
16374664509cSschwarze 		if (*ep != '\0') {
16384664509cSschwarze 			if (!silent)
16394664509cSschwarze 				(void) fprintf(el->el_errfile,
1640565aa7e8Sschwarze 				    "echotc: Bad value `%ls'.\n", *argv);
164128d54ee8Sschwarze 			return -1;
16424664509cSschwarze 		}
16434664509cSschwarze 		argv++;
16444664509cSschwarze 		if (*argv && *argv[0]) {
16454664509cSschwarze 			if (!silent)
16464664509cSschwarze 				(void) fprintf(el->el_errfile,
1647565aa7e8Sschwarze 				    "echotc: Warning: Extra argument `%ls"
1648565aa7e8Sschwarze 				    "'.\n", *argv);
164928d54ee8Sschwarze 			return -1;
16504664509cSschwarze 		}
1651fd40972aSschwarze 		terminal_tputs(el, tgoto(scap, arg_cols, arg_rows), arg_rows);
16524664509cSschwarze 		break;
16534664509cSschwarze 	}
165428d54ee8Sschwarze 	return 0;
16554664509cSschwarze }
1656