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