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