GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libedit/terminal.c Lines: 209 675 31.0 %
Date: 2017-11-07 Branches: 73 562 13.0 %

Line Branch Exec Source
1
/*	$OpenBSD: terminal.c,v 1.18 2017/04/12 18:24:37 tb Exp $	*/
2
/*	$NetBSD: terminal.c,v 1.17 2016/02/15 15:35:03 christos Exp $	*/
3
4
/*-
5
 * Copyright (c) 1992, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * This code is derived from software contributed to Berkeley by
9
 * Christos Zoulas of Cornell University.
10
 *
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following conditions
13
 * are met:
14
 * 1. Redistributions of source code must retain the above copyright
15
 *    notice, this list of conditions and the following disclaimer.
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in the
18
 *    documentation and/or other materials provided with the distribution.
19
 * 3. Neither the name of the University nor the names of its contributors
20
 *    may be used to endorse or promote products derived from this software
21
 *    without specific prior written permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
 * SUCH DAMAGE.
34
 */
35
36
#include "config.h"
37
38
/*
39
 * terminal.c: Editor/termcap-curses interface
40
 *	       We have to declare a static variable here, since the
41
 *	       termcap putchar routine does not take an argument!
42
 */
43
#include <sys/types.h>
44
#include <sys/ioctl.h>
45
#include <limits.h>
46
#include <signal.h>
47
#include <stdio.h>
48
#include <stdlib.h>
49
#include <string.h>
50
#include <unistd.h>
51
#ifdef HAVE_TERMCAP_H
52
#include <termcap.h>
53
#endif
54
#ifdef HAVE_CURSES_H
55
#include <curses.h>
56
#elif HAVE_NCURSES_H
57
#include <ncurses.h>
58
#endif
59
/* Solaris's term.h does horrid things. */
60
61
#if defined(HAVE_TERM_H) && !defined(__sun)
62
#include <term.h>
63
#endif
64
65
#ifdef _REENTRANT
66
#include <pthread.h>
67
#endif
68
69
#include "el.h"
70
#include "fcns.h"
71
72
/*
73
 * IMPORTANT NOTE: these routines are allowed to look at the current screen
74
 * and the current position assuming that it is correct.  If this is not
75
 * true, then the update will be WRONG!  This is (should be) a valid
76
 * assumption...
77
 */
78
79
#define	TC_BUFSIZE	2048
80
81
#define	GoodStr(a)	(el->el_terminal.t_str[a] != NULL && \
82
			    el->el_terminal.t_str[a][0] != '\0')
83
#define	Str(a)		el->el_terminal.t_str[a]
84
#define	Val(a)		el->el_terminal.t_val[a]
85
86
static const struct termcapstr {
87
	const char *name;
88
	const char *long_name;
89
} tstr[] = {
90
#define	T_al	0
91
	{ "al", "add new blank line" },
92
#define	T_bl	1
93
	{ "bl", "audible bell" },
94
#define	T_cd	2
95
	{ "cd", "clear to bottom" },
96
#define	T_ce	3
97
	{ "ce", "clear to end of line" },
98
#define	T_ch	4
99
	{ "ch", "cursor to horiz pos" },
100
#define	T_cl	5
101
	{ "cl", "clear screen" },
102
#define	T_dc	6
103
	{ "dc", "delete a character" },
104
#define	T_dl	7
105
	{ "dl", "delete a line" },
106
#define	T_dm	8
107
	{ "dm", "start delete mode" },
108
#define	T_ed	9
109
	{ "ed", "end delete mode" },
110
#define	T_ei	10
111
	{ "ei", "end insert mode" },
112
#define	T_fs	11
113
	{ "fs", "cursor from status line" },
114
#define	T_ho	12
115
	{ "ho", "home cursor" },
116
#define	T_ic	13
117
	{ "ic", "insert character" },
118
#define	T_im	14
119
	{ "im", "start insert mode" },
120
#define	T_ip	15
121
	{ "ip", "insert padding" },
122
#define	T_kd	16
123
	{ "kd", "sends cursor down" },
124
#define	T_kl	17
125
	{ "kl", "sends cursor left" },
126
#define	T_kr	18
127
	{ "kr", "sends cursor right" },
128
#define	T_ku	19
129
	{ "ku", "sends cursor up" },
130
#define	T_md	20
131
	{ "md", "begin bold" },
132
#define	T_me	21
133
	{ "me", "end attributes" },
134
#define	T_nd	22
135
	{ "nd", "non destructive space" },
136
#define	T_se	23
137
	{ "se", "end standout" },
138
#define	T_so	24
139
	{ "so", "begin standout" },
140
#define	T_ts	25
141
	{ "ts", "cursor to status line" },
142
#define	T_up	26
143
	{ "up", "cursor up one" },
144
#define	T_us	27
145
	{ "us", "begin underline" },
146
#define	T_ue	28
147
	{ "ue", "end underline" },
148
#define	T_vb	29
149
	{ "vb", "visible bell" },
150
#define	T_DC	30
151
	{ "DC", "delete multiple chars" },
152
#define	T_DO	31
153
	{ "DO", "cursor down multiple" },
154
#define	T_IC	32
155
	{ "IC", "insert multiple chars" },
156
#define	T_LE	33
157
	{ "LE", "cursor left multiple" },
158
#define	T_RI	34
159
	{ "RI", "cursor right multiple" },
160
#define	T_UP	35
161
	{ "UP", "cursor up multiple" },
162
#define	T_kh	36
163
	{ "kh", "send cursor home" },
164
#define	T_at7	37
165
	{ "@7", "send cursor end" },
166
#define	T_str	38
167
	{ NULL, NULL }
168
};
169
170
static const struct termcapval {
171
	const char *name;
172
	const char *long_name;
173
} tval[] = {
174
#define	T_am	0
175
	{ "am", "has automatic margins" },
176
#define	T_pt	1
177
	{ "pt", "has physical tabs" },
178
#define	T_li	2
179
	{ "li", "Number of lines" },
180
#define	T_co	3
181
	{ "co", "Number of columns" },
182
#define	T_km	4
183
	{ "km", "Has meta key" },
184
#define	T_xt	5
185
	{ "xt", "Tab chars destructive" },
186
#define	T_xn	6
187
	{ "xn", "newline ignored at right margin" },
188
#define	T_MT	7
189
	{ "MT", "Has meta key" },			/* XXX? */
190
#define	T_val	8
191
	{ NULL, NULL, }
192
};
193
/* do two or more of the attributes use me */
194
195
static void	terminal_setflags(EditLine *);
196
static int	terminal_rebuffer_display(EditLine *);
197
static void	terminal_free_display(EditLine *);
198
static int	terminal_alloc_display(EditLine *);
199
static void	terminal_alloc(EditLine *, const struct termcapstr *,
200
    const char *);
201
static void	terminal_init_arrow(EditLine *);
202
static void	terminal_reset_arrow(EditLine *);
203
static int	terminal_putc(int);
204
static void	terminal_tputs(EditLine *, const char *, int);
205
206
#ifdef _REENTRANT
207
static pthread_mutex_t terminal_mutex = PTHREAD_MUTEX_INITIALIZER;
208
#endif
209
static FILE *terminal_outfile = NULL;
210
211
212
/* terminal_setflags():
213
 *	Set the terminal capability flags
214
 */
215
static void
216
terminal_setflags(EditLine *el)
217
{
218
12
	EL_FLAGS = 0;
219
6
	if (el->el_tty.t_tabs)
220
		EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0;
221
222
12
	EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0;
223
18
	EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0;
224

18
	EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0;
225


18
	EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ?
226
	    TERM_CAN_INSERT : 0;
227

18
	EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0;
228
6
	EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0;
229
6
	EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0;
230
231


24
	if (GoodStr(T_me) && GoodStr(T_ue))
232
6
		EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ?
233
		    TERM_CAN_ME : 0;
234
	else
235
		EL_FLAGS &= ~TERM_CAN_ME;
236


24
	if (GoodStr(T_me) && GoodStr(T_se))
237
6
		EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ?
238
		    TERM_CAN_ME : 0;
239
240
241
#ifdef DEBUG_SCREEN
242
	if (!EL_CAN_UP) {
243
		(void) fprintf(el->el_errfile,
244
		    "WARNING: Your terminal cannot move up.\n");
245
		(void) fprintf(el->el_errfile,
246
		    "Editing may be odd for long lines.\n");
247
	}
248
	if (!EL_CAN_CEOL)
249
		(void) fprintf(el->el_errfile, "no clear EOL capability.\n");
250
	if (!EL_CAN_DELETE)
251
		(void) fprintf(el->el_errfile, "no delete char capability.\n");
252
	if (!EL_CAN_INSERT)
253
		(void) fprintf(el->el_errfile, "no insert char capability.\n");
254
#endif /* DEBUG_SCREEN */
255
6
}
256
257
/* terminal_init():
258
 *	Initialize the terminal stuff
259
 */
260
protected int
261
terminal_init(EditLine *el)
262
{
263
264
12
	el->el_terminal.t_buf = (char *)malloc(TC_BUFSIZE);
265
6
	if (el->el_terminal.t_buf == NULL)
266
		goto fail1;
267
6
	el->el_terminal.t_cap = (char *)malloc(TC_BUFSIZE);
268
6
	if (el->el_terminal.t_cap == NULL)
269
		goto fail2;
270
6
	el->el_terminal.t_fkey = reallocarray(NULL, A_K_NKEYS,
271
	    sizeof(*el->el_terminal.t_fkey));
272
6
	if (el->el_terminal.t_fkey == NULL)
273
		goto fail3;
274
6
	el->el_terminal.t_loc = 0;
275
6
	el->el_terminal.t_str = calloc(T_str, sizeof(char *));
276
6
	if (el->el_terminal.t_str == NULL)
277
		goto fail4;
278
6
	el->el_terminal.t_val = calloc(T_val, sizeof(int));
279
6
	if (el->el_terminal.t_val == NULL)
280
		goto fail5;
281
6
	(void) terminal_set(el, NULL);
282
6
	terminal_init_arrow(el);
283
6
	return 0;
284
fail5:
285
	free(el->el_terminal.t_str);
286
	el->el_terminal.t_str = NULL;
287
fail4:
288
	free(el->el_terminal.t_fkey);
289
	el->el_terminal.t_fkey = NULL;
290
fail3:
291
	free(el->el_terminal.t_cap);
292
	el->el_terminal.t_cap = NULL;
293
fail2:
294
	free(el->el_terminal.t_buf);
295
	el->el_terminal.t_buf = NULL;
296
fail1:
297
	return -1;
298
6
}
299
300
/* terminal_end():
301
 *	Clean up the terminal stuff
302
 */
303
protected void
304
terminal_end(EditLine *el)
305
{
306
307
	free(el->el_terminal.t_buf);
308
	el->el_terminal.t_buf = NULL;
309
	free(el->el_terminal.t_cap);
310
	el->el_terminal.t_cap = NULL;
311
	el->el_terminal.t_loc = 0;
312
	free(el->el_terminal.t_str);
313
	el->el_terminal.t_str = NULL;
314
	free(el->el_terminal.t_val);
315
	el->el_terminal.t_val = NULL;
316
	free(el->el_terminal.t_fkey);
317
	el->el_terminal.t_fkey = NULL;
318
	terminal_free_display(el);
319
}
320
321
322
/* terminal_alloc():
323
 *	Maintain a string pool for termcap strings
324
 */
325
static void
326
terminal_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
327
{
328
456
	char termbuf[TC_BUFSIZE];
329
	size_t tlen, clen;
330
228
	char **tlist = el->el_terminal.t_str;
331
228
	char **tmp, **str = &tlist[t - tstr];
332
333

414
	if (cap == NULL || *cap == '\0') {
334
42
		*str = NULL;
335
42
		return;
336
	} else
337
186
		clen = strlen(cap);
338
339
372
	tlen = *str == NULL ? 0 : strlen(*str);
340
341
	/*
342
         * New string is shorter; no need to allocate space
343
         */
344
186
	if (clen <= tlen) {
345
		if (*str)
346
			(void) strlcpy(*str, cap, tlen + 1);
347
		return;
348
	}
349
	/*
350
         * New string is longer; see if we have enough space to append
351
         */
352
186
	if (el->el_terminal.t_loc + 3 < TC_BUFSIZE) {
353
186
		tlen = TC_BUFSIZE - el->el_terminal.t_loc;
354
186
		(void) strlcpy(*str = &el->el_terminal.t_buf[
355
		    el->el_terminal.t_loc], cap, tlen);
356
186
		el->el_terminal.t_loc += (int)clen + 1;	/* one for \0 */
357
186
		return;
358
	}
359
	/*
360
         * Compact our buffer; no need to check compaction, cause we know it
361
         * fits...
362
         */
363
	tlen = 0;
364
	for (tmp = tlist; tmp < &tlist[T_str]; tmp++)
365
		if (*tmp != NULL && **tmp != '\0' && *tmp != *str) {
366
			char *ptr;
367
368
			for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++)
369
				continue;
370
			termbuf[tlen++] = '\0';
371
		}
372
	memcpy(el->el_terminal.t_buf, termbuf, TC_BUFSIZE);
373
	el->el_terminal.t_loc = (int)tlen;
374
	if (el->el_terminal.t_loc + 3 >= TC_BUFSIZE) {
375
		(void) fprintf(el->el_errfile,
376
		    "Out of termcap string space.\n");
377
		return;
378
	}
379
	tlen = TC_BUFSIZE - el->el_terminal.t_loc;
380
	(void) strlcpy(*str = &el->el_terminal.t_buf[el->el_terminal.t_loc],
381
	    cap, tlen);
382
	el->el_terminal.t_loc += (int)clen + 1;	/* one for \0 */
383
	return;
384
228
}
385
386
387
/* terminal_rebuffer_display():
388
 *	Rebuffer the display after the screen changed size
389
 */
390
static int
391
terminal_rebuffer_display(EditLine *el)
392
{
393
12
	coord_t *c = &el->el_terminal.t_size;
394
395
6
	terminal_free_display(el);
396
397
6
	c->h = Val(T_co);
398
6
	c->v = Val(T_li);
399
400
6
	if (terminal_alloc_display(el) == -1)
401
		return -1;
402
6
	return 0;
403
6
}
404
405
406
/* terminal_alloc_display():
407
 *	Allocate a new display.
408
 */
409
static int
410
terminal_alloc_display(EditLine *el)
411
{
412
	int i;
413
	wchar_t **b;
414
12
	coord_t *c = &el->el_terminal.t_size;
415
416
6
	b = reallocarray(NULL, c->v + 1, sizeof(*b));
417
6
	if (b == NULL)
418
		goto done;
419
408
	for (i = 0; i < c->v; i++) {
420
198
		b[i] = reallocarray(NULL, c->h + 1, sizeof(**b));
421
198
		if (b[i] == NULL) {
422
			while (--i >= 0)
423
				free(b[i]);
424
			free(b);
425
			goto done;
426
		}
427
	}
428
6
	b[c->v] = NULL;
429
6
	el->el_display = b;
430
431
6
	b = reallocarray(NULL, c->v + 1, sizeof(*b));
432
6
	if (b == NULL)
433
		goto done;
434
408
	for (i = 0; i < c->v; i++) {
435
198
		b[i] = reallocarray(NULL, c->h + 1, sizeof(**b));
436
198
		if (b[i] == NULL) {
437
			while (--i >= 0)
438
				free(b[i]);
439
			free(b);
440
			goto done;
441
		}
442
	}
443
6
	b[c->v] = NULL;
444
6
	el->el_vdisplay = b;
445
6
	return 0;
446
done:
447
	terminal_free_display(el);
448
	return -1;
449
6
}
450
451
452
/* terminal_free_display():
453
 *	Free the display buffers
454
 */
455
static void
456
terminal_free_display(EditLine *el)
457
{
458
	wchar_t **b;
459
	wchar_t **bufp;
460
461
12
	b = el->el_display;
462
6
	el->el_display = NULL;
463
6
	if (b != NULL) {
464
		for (bufp = b; *bufp != NULL; bufp++)
465
			free(*bufp);
466
		free(b);
467
	}
468
6
	b = el->el_vdisplay;
469
6
	el->el_vdisplay = NULL;
470
6
	if (b != NULL) {
471
		for (bufp = b; *bufp != NULL; bufp++)
472
			free(*bufp);
473
		free(b);
474
	}
475
6
}
476
477
478
/* terminal_move_to_line():
479
 *	move to line <where> (first line == 0)
480
 *	as efficiently as possible
481
 */
482
protected void
483
terminal_move_to_line(EditLine *el, int where)
484
{
485
	int del;
486
487
	if (where == el->el_cursor.v)
488
		return;
489
490
	if (where > el->el_terminal.t_size.v) {
491
#ifdef DEBUG_SCREEN
492
		(void) fprintf(el->el_errfile,
493
		    "terminal_move_to_line: where is ridiculous: %d\r\n",
494
		    where);
495
#endif /* DEBUG_SCREEN */
496
		return;
497
	}
498
	if ((del = where - el->el_cursor.v) > 0) {
499
		while (del > 0) {
500
			if (EL_HAS_AUTO_MARGINS &&
501
			    el->el_display[el->el_cursor.v][0] != '\0') {
502
                                size_t h = el->el_terminal.t_size.h - 1;
503
                                for (; h > 0 &&
504
                                         el->el_display[el->el_cursor.v][h] ==
505
                                                 MB_FILL_CHAR;
506
                                         h--)
507
                                                continue;
508
				/* move without newline */
509
				terminal_move_to_char(el, (int)h);
510
				terminal_overwrite(el, &el->el_display
511
				    [el->el_cursor.v][el->el_cursor.h],
512
				    (size_t)(el->el_terminal.t_size.h -
513
				    el->el_cursor.h));
514
				/* updates Cursor */
515
				del--;
516
			} else {
517
				if ((del > 1) && GoodStr(T_DO)) {
518
					terminal_tputs(el, tgoto(Str(T_DO), del,
519
					    del), del);
520
					del = 0;
521
				} else {
522
					for (; del > 0; del--)
523
						terminal__putc(el, '\n');
524
					/* because the \n will become \r\n */
525
					el->el_cursor.h = 0;
526
				}
527
			}
528
		}
529
	} else {		/* del < 0 */
530
		if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up)))
531
			terminal_tputs(el, tgoto(Str(T_UP), -del, -del), -del);
532
		else {
533
			if (GoodStr(T_up))
534
				for (; del < 0; del++)
535
					terminal_tputs(el, Str(T_up), 1);
536
		}
537
	}
538
	el->el_cursor.v = where;/* now where is here */
539
}
540
541
542
/* terminal_move_to_char():
543
 *	Move to the character position specified
544
 */
545
protected void
546
terminal_move_to_char(EditLine *el, int where)
547
{
548
	int del, i;
549
550
mc_again:
551
	if (where == el->el_cursor.h)
552
		return;
553
554
	if (where > el->el_terminal.t_size.h) {
555
#ifdef DEBUG_SCREEN
556
		(void) fprintf(el->el_errfile,
557
		    "terminal_move_to_char: where is riduculous: %d\r\n",
558
		    where);
559
#endif /* DEBUG_SCREEN */
560
		return;
561
	}
562
	if (!where) {		/* if where is first column */
563
		terminal__putc(el, '\r');	/* do a CR */
564
		el->el_cursor.h = 0;
565
		return;
566
	}
567
	del = where - el->el_cursor.h;
568
569
	if ((del < -4 || del > 4) && GoodStr(T_ch))
570
		/* go there directly */
571
		terminal_tputs(el, tgoto(Str(T_ch), where, where), where);
572
	else {
573
		if (del > 0) {	/* moving forward */
574
			if ((del > 4) && GoodStr(T_RI))
575
				terminal_tputs(el, tgoto(Str(T_RI), del, del),
576
				    del);
577
			else {
578
					/* if I can do tabs, use them */
579
				if (EL_CAN_TAB) {
580
					if ((el->el_cursor.h & 0370) !=
581
					    (where & ~0x7)
582
					    && (el->el_display[
583
					    el->el_cursor.v][where & 0370] !=
584
					    MB_FILL_CHAR)
585
					    ) {
586
						/* if not within tab stop */
587
						for (i =
588
						    (el->el_cursor.h & 0370);
589
						    i < (where & ~0x7);
590
						    i += 8)
591
							terminal__putc(el,
592
							    '\t');
593
							/* then tab over */
594
						el->el_cursor.h = where & ~0x7;
595
					}
596
				}
597
				/*
598
				 * it's usually cheaper to just write the
599
				 * chars, so we do.
600
				 */
601
				/*
602
				 * NOTE THAT terminal_overwrite() WILL CHANGE
603
				 * el->el_cursor.h!!!
604
				 */
605
				terminal_overwrite(el, &el->el_display[
606
				    el->el_cursor.v][el->el_cursor.h],
607
				    (size_t)(where - el->el_cursor.h));
608
609
			}
610
		} else {	/* del < 0 := moving backward */
611
			if ((-del > 4) && GoodStr(T_LE))
612
				terminal_tputs(el, tgoto(Str(T_LE), -del, -del),
613
				    -del);
614
			else {	/* can't go directly there */
615
				/*
616
				 * if the "cost" is greater than the "cost"
617
				 * from col 0
618
				 */
619
				if (EL_CAN_TAB ?
620
				    ((unsigned int)-del >
621
				    (((unsigned int) where >> 3) +
622
				     (where & 07)))
623
				    : (-del > where)) {
624
					terminal__putc(el, '\r');/* do a CR */
625
					el->el_cursor.h = 0;
626
					goto mc_again;	/* and try again */
627
				}
628
				for (i = 0; i < -del; i++)
629
					terminal__putc(el, '\b');
630
			}
631
		}
632
	}
633
	el->el_cursor.h = where;		/* now where is here */
634
}
635
636
637
/* terminal_overwrite():
638
 *	Overstrike num characters
639
 *	Assumes MB_FILL_CHARs are present to keep the column count correct
640
 */
641
protected void
642
terminal_overwrite(EditLine *el, const wchar_t *cp, size_t n)
643
{
644
	if (n == 0)
645
		return;
646
647
	if (n > (size_t)el->el_terminal.t_size.h) {
648
#ifdef DEBUG_SCREEN
649
		(void) fprintf(el->el_errfile,
650
		    "terminal_overwrite: n is riduculous: %zu\r\n", n);
651
#endif /* DEBUG_SCREEN */
652
		return;
653
	}
654
655
        do {
656
                /* terminal__putc() ignores any MB_FILL_CHARs */
657
                terminal__putc(el, *cp++);
658
                el->el_cursor.h++;
659
        } while (--n);
660
661
	if (el->el_cursor.h >= el->el_terminal.t_size.h) {	/* wrap? */
662
		if (EL_HAS_AUTO_MARGINS) {	/* yes */
663
			el->el_cursor.h = 0;
664
			el->el_cursor.v++;
665
			if (EL_HAS_MAGIC_MARGINS) {
666
				/* force the wrap to avoid the "magic"
667
				 * situation */
668
				wchar_t c;
669
				if ((c = el->el_display[el->el_cursor.v]
670
				    [el->el_cursor.h]) != '\0') {
671
					terminal_overwrite(el, &c, 1);
672
					while (el->el_display[el->el_cursor.v]
673
					    [el->el_cursor.h] == MB_FILL_CHAR)
674
						el->el_cursor.h++;
675
				} else {
676
					terminal__putc(el, ' ');
677
					el->el_cursor.h = 1;
678
				}
679
			}
680
		} else		/* no wrap, but cursor stays on screen */
681
			el->el_cursor.h = el->el_terminal.t_size.h - 1;
682
	}
683
}
684
685
686
/* terminal_deletechars():
687
 *	Delete num characters
688
 */
689
protected void
690
terminal_deletechars(EditLine *el, int num)
691
{
692
	if (num <= 0)
693
		return;
694
695
	if (!EL_CAN_DELETE) {
696
#ifdef DEBUG_EDIT
697
		(void) fprintf(el->el_errfile, "   ERROR: cannot delete   \n");
698
#endif /* DEBUG_EDIT */
699
		return;
700
	}
701
	if (num > el->el_terminal.t_size.h) {
702
#ifdef DEBUG_SCREEN
703
		(void) fprintf(el->el_errfile,
704
		    "terminal_deletechars: num is riduculous: %d\r\n", num);
705
#endif /* DEBUG_SCREEN */
706
		return;
707
	}
708
	if (GoodStr(T_DC))	/* if I have multiple delete */
709
		if ((num > 1) || !GoodStr(T_dc)) {	/* if dc would be more
710
							 * expen. */
711
			terminal_tputs(el, tgoto(Str(T_DC), num, num), num);
712
			return;
713
		}
714
	if (GoodStr(T_dm))	/* if I have delete mode */
715
		terminal_tputs(el, Str(T_dm), 1);
716
717
	if (GoodStr(T_dc))	/* else do one at a time */
718
		while (num--)
719
			terminal_tputs(el, Str(T_dc), 1);
720
721
	if (GoodStr(T_ed))	/* if I have delete mode */
722
		terminal_tputs(el, Str(T_ed), 1);
723
}
724
725
726
/* terminal_insertwrite():
727
 *	Puts terminal in insert character mode or inserts num
728
 *	characters in the line
729
 *      Assumes MB_FILL_CHARs are present to keep column count correct
730
 */
731
protected void
732
terminal_insertwrite(EditLine *el, wchar_t *cp, int num)
733
{
734
	if (num <= 0)
735
		return;
736
	if (!EL_CAN_INSERT) {
737
#ifdef DEBUG_EDIT
738
		(void) fprintf(el->el_errfile, "   ERROR: cannot insert   \n");
739
#endif /* DEBUG_EDIT */
740
		return;
741
	}
742
	if (num > el->el_terminal.t_size.h) {
743
#ifdef DEBUG_SCREEN
744
		(void) fprintf(el->el_errfile,
745
		    "StartInsert: num is riduculous: %d\r\n", num);
746
#endif /* DEBUG_SCREEN */
747
		return;
748
	}
749
	if (GoodStr(T_IC))	/* if I have multiple insert */
750
		if ((num > 1) || !GoodStr(T_ic)) {
751
				/* if ic would be more expensive */
752
			terminal_tputs(el, tgoto(Str(T_IC), num, num), num);
753
			terminal_overwrite(el, cp, (size_t)num);
754
				/* this updates el_cursor.h */
755
			return;
756
		}
757
	if (GoodStr(T_im) && GoodStr(T_ei)) {	/* if I have insert mode */
758
		terminal_tputs(el, Str(T_im), 1);
759
760
		el->el_cursor.h += num;
761
		do
762
			terminal__putc(el, *cp++);
763
		while (--num);
764
765
		if (GoodStr(T_ip))	/* have to make num chars insert */
766
			terminal_tputs(el, Str(T_ip), 1);
767
768
		terminal_tputs(el, Str(T_ei), 1);
769
		return;
770
	}
771
	do {
772
		if (GoodStr(T_ic))	/* have to make num chars insert */
773
			terminal_tputs(el, Str(T_ic), 1);
774
775
		terminal__putc(el, *cp++);
776
777
		el->el_cursor.h++;
778
779
		if (GoodStr(T_ip))	/* have to make num chars insert */
780
			terminal_tputs(el, Str(T_ip), 1);
781
					/* pad the inserted char */
782
783
	} while (--num);
784
}
785
786
787
/* terminal_clear_EOL():
788
 *	clear to end of line.  There are num characters to clear
789
 */
790
protected void
791
terminal_clear_EOL(EditLine *el, int num)
792
{
793
	int i;
794
795
	if (EL_CAN_CEOL && GoodStr(T_ce))
796
		terminal_tputs(el, Str(T_ce), 1);
797
	else {
798
		for (i = 0; i < num; i++)
799
			terminal__putc(el, ' ');
800
		el->el_cursor.h += num;	/* have written num spaces */
801
	}
802
}
803
804
805
/* terminal_clear_screen():
806
 *	Clear the screen
807
 */
808
protected void
809
terminal_clear_screen(EditLine *el)
810
{				/* clear the whole screen and home */
811
812
	if (GoodStr(T_cl))
813
		/* send the clear screen code */
814
		terminal_tputs(el, Str(T_cl), Val(T_li));
815
	else if (GoodStr(T_ho) && GoodStr(T_cd)) {
816
		terminal_tputs(el, Str(T_ho), Val(T_li));	/* home */
817
		/* clear to bottom of screen */
818
		terminal_tputs(el, Str(T_cd), Val(T_li));
819
	} else {
820
		terminal__putc(el, '\r');
821
		terminal__putc(el, '\n');
822
	}
823
}
824
825
826
/* terminal_beep():
827
 *	Beep the way the terminal wants us
828
 */
829
protected void
830
terminal_beep(EditLine *el)
831
{
832
	if (GoodStr(T_bl))
833
		/* what termcap says we should use */
834
		terminal_tputs(el, Str(T_bl), 1);
835
	else
836
		terminal__putc(el, '\007');	/* an ASCII bell; ^G */
837
}
838
839
840
protected void
841
terminal_get(EditLine *el, const char **term)
842
{
843
12
	*term = el->el_terminal.t_name;
844
6
}
845
846
847
/* terminal_set():
848
 *	Read in the terminal capabilities from the requested terminal
849
 */
850
protected int
851
terminal_set(EditLine *el, const char *term)
852
{
853
	int i;
854
12
	char buf[TC_BUFSIZE];
855
6
	char *area;
856
	const struct termcapstr *t;
857
6
	sigset_t oset, nset;
858
6
	int lins, cols;
859
860
6
	(void) sigemptyset(&nset);
861
6
	(void) sigaddset(&nset, SIGWINCH);
862
6
	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
863
864
6
	area = buf;
865
866
867
6
	if (term == NULL)
868
6
		term = getenv("TERM");
869
870

12
	if (!term || !term[0])
871
		term = "dumb";
872
873
6
	if (strcmp(term, "emacs") == 0)
874
		el->el_flags |= EDIT_DISABLED;
875
876
6
	(void) memset(el->el_terminal.t_cap, 0, TC_BUFSIZE);
877
878
6
	i = tgetent(el->el_terminal.t_cap, term);
879
880
6
	if (i <= 0) {
881
		if (i == -1)
882
			(void) fprintf(el->el_errfile,
883
			    "Cannot read termcap database;\n");
884
		else if (i == 0)
885
			(void) fprintf(el->el_errfile,
886
			    "No entry for terminal type \"%s\";\n", term);
887
		(void) fprintf(el->el_errfile,
888
		    "using dumb terminal settings.\n");
889
		Val(T_co) = 80;	/* do a dumb terminal */
890
		Val(T_pt) = Val(T_km) = Val(T_li) = 0;
891
		Val(T_xt) = Val(T_MT);
892
		for (t = tstr; t->name != NULL; t++)
893
			terminal_alloc(el, t, NULL);
894
	} else {
895
		/* auto/magic margins */
896
6
		Val(T_am) = tgetflag("am");
897
6
		Val(T_xn) = tgetflag("xn");
898
		/* Can we tab */
899
6
		Val(T_pt) = tgetflag("pt");
900
6
		Val(T_xt) = tgetflag("xt");
901
		/* do we have a meta? */
902
6
		Val(T_km) = tgetflag("km");
903
6
		Val(T_MT) = tgetflag("MT");
904
		/* Get the size */
905
6
		Val(T_co) = tgetnum("co");
906
6
		Val(T_li) = tgetnum("li");
907
468
		for (t = tstr; t->name != NULL; t++) {
908
			/* XXX: some systems' tgetstr needs non const */
909
228
			terminal_alloc(el, t, tgetstr(strchr(t->name, *t->name),
910
			    &area));
911
		}
912
	}
913
914
6
	if (Val(T_co) < 2)
915
		Val(T_co) = 80;	/* just in case */
916
6
	if (Val(T_li) < 1)
917
		Val(T_li) = 24;
918
919
6
	el->el_terminal.t_size.v = Val(T_co);
920
6
	el->el_terminal.t_size.h = Val(T_li);
921
922
6
	terminal_setflags(el);
923
924
				/* get the correct window size */
925
6
	(void) terminal_get_size(el, &lins, &cols);
926
6
	if (terminal_change_size(el, lins, cols) == -1)
927
		return -1;
928
6
	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
929
6
	terminal_bind_arrow(el);
930
6
	el->el_terminal.t_name = term;
931
6
	return i <= 0 ? -1 : 0;
932
6
}
933
934
935
/* terminal_get_size():
936
 *	Return the new window size in lines and cols, and
937
 *	true if the size was changed.
938
 */
939
protected int
940
terminal_get_size(EditLine *el, int *lins, int *cols)
941
{
942
943
12
	*cols = Val(T_co);
944
6
	*lins = Val(T_li);
945
946
#ifdef TIOCGWINSZ
947
	{
948
6
		struct winsize ws;
949
6
		if (ioctl(el->el_infd, TIOCGWINSZ, &ws) != -1) {
950
6
			if (ws.ws_col)
951
6
				*cols = ws.ws_col;
952
6
			if (ws.ws_row)
953
6
				*lins = ws.ws_row;
954
		}
955
6
	}
956
#endif
957
#ifdef TIOCGSIZE
958
	{
959
		struct ttysize ts;
960
		if (ioctl(el->el_infd, TIOCGSIZE, &ts) != -1) {
961
			if (ts.ts_cols)
962
				*cols = ts.ts_cols;
963
			if (ts.ts_lines)
964
				*lins = ts.ts_lines;
965
		}
966
	}
967
#endif
968
17
	return Val(T_co) != *cols || Val(T_li) != *lins;
969
}
970
971
972
/* terminal_change_size():
973
 *	Change the size of the terminal
974
 */
975
protected int
976
terminal_change_size(EditLine *el, int lins, int cols)
977
{
978
	/*
979
         * Just in case
980
         */
981
12
	Val(T_co) = (cols < 2) ? 80 : cols;
982
6
	Val(T_li) = (lins < 1) ? 24 : lins;
983
984
	/* re-make display buffers */
985
6
	if (terminal_rebuffer_display(el) == -1)
986
		return -1;
987
6
	re_clear_display(el);
988
6
	return 0;
989
6
}
990
991
992
/* terminal_init_arrow():
993
 *	Initialize the arrow key bindings from termcap
994
 */
995
static void
996
terminal_init_arrow(EditLine *el)
997
{
998
12
	funckey_t *arrow = el->el_terminal.t_fkey;
999
1000
6
	arrow[A_K_DN].name = L"down";
1001
6
	arrow[A_K_DN].key = T_kd;
1002
6
	arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY;
1003
6
	arrow[A_K_DN].type = XK_CMD;
1004
1005
6
	arrow[A_K_UP].name = L"up";
1006
6
	arrow[A_K_UP].key = T_ku;
1007
6
	arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY;
1008
6
	arrow[A_K_UP].type = XK_CMD;
1009
1010
6
	arrow[A_K_LT].name = L"left";
1011
6
	arrow[A_K_LT].key = T_kl;
1012
6
	arrow[A_K_LT].fun.cmd = ED_PREV_CHAR;
1013
6
	arrow[A_K_LT].type = XK_CMD;
1014
1015
6
	arrow[A_K_RT].name = L"right";
1016
6
	arrow[A_K_RT].key = T_kr;
1017
6
	arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR;
1018
6
	arrow[A_K_RT].type = XK_CMD;
1019
1020
6
	arrow[A_K_HO].name = L"home";
1021
6
	arrow[A_K_HO].key = T_kh;
1022
6
	arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG;
1023
6
	arrow[A_K_HO].type = XK_CMD;
1024
1025
6
	arrow[A_K_EN].name = L"end";
1026
6
	arrow[A_K_EN].key = T_at7;
1027
6
	arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END;
1028
6
	arrow[A_K_EN].type = XK_CMD;
1029
6
}
1030
1031
1032
/* terminal_reset_arrow():
1033
 *	Reset arrow key bindings
1034
 */
1035
static void
1036
terminal_reset_arrow(EditLine *el)
1037
{
1038
24
	funckey_t *arrow = el->el_terminal.t_fkey;
1039
	static const wchar_t strA[] = L"\033[A";
1040
	static const wchar_t strB[] = L"\033[B";
1041
	static const wchar_t strC[] = L"\033[C";
1042
	static const wchar_t strD[] = L"\033[D";
1043
	static const wchar_t strH[] = L"\033[H";
1044
	static const wchar_t strF[] = L"\033[F";
1045
	static const wchar_t stOA[] = L"\033OA";
1046
	static const wchar_t stOB[] = L"\033OB";
1047
	static const wchar_t stOC[] = L"\033OC";
1048
	static const wchar_t stOD[] = L"\033OD";
1049
	static const wchar_t stOH[] = L"\033OH";
1050
	static const wchar_t stOF[] = L"\033OF";
1051
1052
12
	keymacro_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1053
12
	keymacro_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1054
12
	keymacro_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1055
12
	keymacro_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1056
12
	keymacro_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1057
12
	keymacro_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1058
12
	keymacro_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1059
12
	keymacro_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1060
12
	keymacro_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1061
12
	keymacro_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1062
12
	keymacro_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1063
12
	keymacro_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1064
1065
12
	if (el->el_map.type != MAP_VI)
1066
6
		return;
1067
6
	keymacro_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1068
6
	keymacro_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1069
6
	keymacro_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1070
6
	keymacro_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1071
6
	keymacro_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1072
6
	keymacro_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1073
6
	keymacro_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
1074
6
	keymacro_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
1075
6
	keymacro_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
1076
6
	keymacro_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
1077
6
	keymacro_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
1078
6
	keymacro_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
1079
18
}
1080
1081
1082
/* terminal_set_arrow():
1083
 *	Set an arrow key binding
1084
 */
1085
protected int
1086
terminal_set_arrow(EditLine *el, const wchar_t *name, keymacro_value_t *fun,
1087
    int type)
1088
{
1089
	funckey_t *arrow = el->el_terminal.t_fkey;
1090
	int i;
1091
1092
	for (i = 0; i < A_K_NKEYS; i++)
1093
		if (wcscmp(name, arrow[i].name) == 0) {
1094
			arrow[i].fun = *fun;
1095
			arrow[i].type = type;
1096
			return 0;
1097
		}
1098
	return -1;
1099
}
1100
1101
1102
/* terminal_clear_arrow():
1103
 *	Clear an arrow key binding
1104
 */
1105
protected int
1106
terminal_clear_arrow(EditLine *el, const wchar_t *name)
1107
{
1108
	funckey_t *arrow = el->el_terminal.t_fkey;
1109
	int i;
1110
1111
	for (i = 0; i < A_K_NKEYS; i++)
1112
		if (wcscmp(name, arrow[i].name) == 0) {
1113
			arrow[i].type = XK_NOD;
1114
			return 0;
1115
		}
1116
	return -1;
1117
}
1118
1119
1120
/* terminal_print_arrow():
1121
 *	Print the arrow key bindings
1122
 */
1123
protected void
1124
terminal_print_arrow(EditLine *el, const wchar_t *name)
1125
{
1126
	int i;
1127
	funckey_t *arrow = el->el_terminal.t_fkey;
1128
1129
	for (i = 0; i < A_K_NKEYS; i++)
1130
		if (*name == '\0' || wcscmp(name, arrow[i].name) == 0)
1131
			if (arrow[i].type != XK_NOD)
1132
				keymacro_kprint(el, arrow[i].name,
1133
				    &arrow[i].fun, arrow[i].type);
1134
}
1135
1136
1137
/* terminal_bind_arrow():
1138
 *	Bind the arrow keys
1139
 */
1140
protected void
1141
terminal_bind_arrow(EditLine *el)
1142
{
1143
	el_action_t *map;
1144
	const el_action_t *dmap;
1145
	int i, j;
1146
	char *p;
1147
36
	funckey_t *arrow = el->el_terminal.t_fkey;
1148
1149
	/* Check if the components needed are initialized */
1150

36
	if (el->el_terminal.t_buf == NULL || el->el_map.key == NULL)
1151
6
		return;
1152
1153
12
	map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key;
1154
12
	dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs;
1155
1156
12
	terminal_reset_arrow(el);
1157
1158
168
	for (i = 0; i < A_K_NKEYS; i++) {
1159
72
		wchar_t wt_str[VISUAL_WIDTH_MAX];
1160
		wchar_t *px;
1161
		size_t n;
1162
1163
72
		p = el->el_terminal.t_str[arrow[i].key];
1164

144
		if (!p || !*p)
1165
			continue;
1166

936
		for (n = 0; n < VISUAL_WIDTH_MAX && p[n]; ++n)
1167
240
			wt_str[n] = p[n];
1168
744
		while (n < VISUAL_WIDTH_MAX)
1169
336
			wt_str[n++] = '\0';
1170
72
		px = wt_str;
1171
72
		j = (unsigned char) *p;
1172
		/*
1173
		 * Assign the arrow keys only if:
1174
		 *
1175
		 * 1. They are multi-character arrow keys and the user
1176
		 *    has not re-assigned the leading character, or
1177
		 *    has re-assigned the leading character to be
1178
		 *	  ED_SEQUENCE_LEAD_IN
1179
		 * 2. They are single arrow keys pointing to an
1180
		 *    unassigned key.
1181
		 */
1182
72
		if (arrow[i].type == XK_NOD)
1183
			keymacro_clear(el, map, px);
1184
		else {
1185

216
			if (p[1] && (dmap[j] == map[j] ||
1186
72
				map[j] == ED_SEQUENCE_LEAD_IN)) {
1187
144
				keymacro_add(el, px, &arrow[i].fun,
1188
72
				    arrow[i].type);
1189
72
				map[j] = ED_SEQUENCE_LEAD_IN;
1190
72
			} else if (map[j] == ED_UNASSIGNED) {
1191
				keymacro_clear(el, map, px);
1192
				if (arrow[i].type == XK_CMD)
1193
					map[j] = arrow[i].fun.cmd;
1194
				else
1195
					keymacro_add(el, px, &arrow[i].fun,
1196
					    arrow[i].type);
1197
			}
1198
		}
1199
144
	}
1200
30
}
1201
1202
/* terminal_putc():
1203
 *	Add a character
1204
 */
1205
static int
1206
terminal_putc(int c)
1207
{
1208
	if (terminal_outfile == NULL)
1209
		return -1;
1210
	return fputc(c, terminal_outfile);
1211
}
1212
1213
static void
1214
terminal_tputs(EditLine *el, const char *cap, int affcnt)
1215
{
1216
#ifdef _REENTRANT
1217
	pthread_mutex_lock(&terminal_mutex);
1218
#endif
1219
	terminal_outfile = el->el_outfile;
1220
	(void)tputs(cap, affcnt, terminal_putc);
1221
#ifdef _REENTRANT
1222
	pthread_mutex_unlock(&terminal_mutex);
1223
#endif
1224
}
1225
1226
/* terminal__putc():
1227
 *	Add a character
1228
 */
1229
protected int
1230
terminal__putc(EditLine *el, wint_t c)
1231
{
1232
	char buf[MB_LEN_MAX +1];
1233
	ssize_t i;
1234
	if (c == (wint_t)MB_FILL_CHAR)
1235
		return 0;
1236
	i = ct_encode_char(buf, MB_LEN_MAX, c);
1237
	if (i <= 0)
1238
		return (int)i;
1239
	buf[i] = '\0';
1240
	return fputs(buf, el->el_outfile);
1241
}
1242
1243
/* terminal__flush():
1244
 *	Flush output
1245
 */
1246
protected void
1247
terminal__flush(EditLine *el)
1248
{
1249
1250
	(void) fflush(el->el_outfile);
1251
}
1252
1253
/* terminal_writec():
1254
 *	Write the given character out, in a human readable form
1255
 */
1256
protected void
1257
terminal_writec(EditLine *el, wint_t c)
1258
{
1259
	wchar_t visbuf[VISUAL_WIDTH_MAX +1];
1260
	ssize_t vcnt = ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
1261
	visbuf[vcnt] = '\0';
1262
	terminal_overwrite(el, visbuf, (size_t)vcnt);
1263
	terminal__flush(el);
1264
}
1265
1266
1267
/* terminal_telltc():
1268
 *	Print the current termcap characteristics
1269
 */
1270
protected int
1271
/*ARGSUSED*/
1272
terminal_telltc(EditLine *el, int argc __attribute__((__unused__)),
1273
    const wchar_t **argv __attribute__((__unused__)))
1274
{
1275
	const struct termcapstr *t;
1276
	char **ts;
1277
1278
	(void) fprintf(el->el_outfile, "\n\tYour terminal has the\n");
1279
	(void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n");
1280
	(void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n",
1281
	    Val(T_co), Val(T_li));
1282
	(void) fprintf(el->el_outfile,
1283
	    "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no");
1284
	(void) fprintf(el->el_outfile,
1285
	    "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not ");
1286
	(void) fprintf(el->el_outfile, "\tIt %s automatic margins\n",
1287
	    EL_HAS_AUTO_MARGINS ? "has" : "does not have");
1288
	if (EL_HAS_AUTO_MARGINS)
1289
		(void) fprintf(el->el_outfile, "\tIt %s magic margins\n",
1290
		    EL_HAS_MAGIC_MARGINS ? "has" : "does not have");
1291
1292
	for (t = tstr, ts = el->el_terminal.t_str; t->name != NULL; t++, ts++) {
1293
		const char *ub;
1294
		if (*ts && **ts) {
1295
			ub = ct_encode_string(ct_visual_string(
1296
			    ct_decode_string(*ts, &el->el_scratch)),
1297
			    &el->el_scratch);
1298
		} else {
1299
			ub = "(empty)";
1300
		}
1301
		(void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n",
1302
		    t->long_name, t->name, ub);
1303
	}
1304
	(void) fputc('\n', el->el_outfile);
1305
	return 0;
1306
}
1307
1308
1309
/* terminal_settc():
1310
 *	Change the current terminal characteristics
1311
 */
1312
protected int
1313
/*ARGSUSED*/
1314
terminal_settc(EditLine *el, int argc __attribute__((__unused__)),
1315
    const wchar_t **argv)
1316
{
1317
	const struct termcapstr *ts;
1318
	const struct termcapval *tv;
1319
	char what[8], how[8];
1320
1321
	if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
1322
		return -1;
1323
1324
	strncpy(what, ct_encode_string(argv[1], &el->el_scratch), sizeof(what));
1325
	what[sizeof(what) - 1] = '\0';
1326
	strncpy(how,  ct_encode_string(argv[2], &el->el_scratch), sizeof(how));
1327
	how[sizeof(how) - 1] = '\0';
1328
1329
	/*
1330
         * Do the strings first
1331
         */
1332
	for (ts = tstr; ts->name != NULL; ts++)
1333
		if (strcmp(ts->name, what) == 0)
1334
			break;
1335
1336
	if (ts->name != NULL) {
1337
		terminal_alloc(el, ts, how);
1338
		terminal_setflags(el);
1339
		return 0;
1340
	}
1341
	/*
1342
         * Do the numeric ones second
1343
         */
1344
	for (tv = tval; tv->name != NULL; tv++)
1345
		if (strcmp(tv->name, what) == 0)
1346
			break;
1347
1348
	if (tv->name != NULL)
1349
		return -1;
1350
1351
	if (tv == &tval[T_pt] || tv == &tval[T_km] ||
1352
	    tv == &tval[T_am] || tv == &tval[T_xn]) {
1353
		if (strcmp(how, "yes") == 0)
1354
			el->el_terminal.t_val[tv - tval] = 1;
1355
		else if (strcmp(how, "no") == 0)
1356
			el->el_terminal.t_val[tv - tval] = 0;
1357
		else {
1358
			(void) fprintf(el->el_errfile,
1359
			    "%ls: Bad value `%s'.\n", argv[0], how);
1360
			return -1;
1361
		}
1362
		terminal_setflags(el);
1363
		if (terminal_change_size(el, Val(T_li), Val(T_co)) == -1)
1364
			return -1;
1365
		return 0;
1366
	} else {
1367
		long i;
1368
		char *ep;
1369
1370
		i = strtol(how, &ep, 10);
1371
		if (*ep != '\0') {
1372
			(void) fprintf(el->el_errfile,
1373
			    "%ls: Bad value `%s'.\n", argv[0], how);
1374
			return -1;
1375
		}
1376
		el->el_terminal.t_val[tv - tval] = (int) i;
1377
		el->el_terminal.t_size.v = Val(T_co);
1378
		el->el_terminal.t_size.h = Val(T_li);
1379
		if (tv == &tval[T_co] || tv == &tval[T_li])
1380
			if (terminal_change_size(el, Val(T_li), Val(T_co))
1381
			    == -1)
1382
				return -1;
1383
		return 0;
1384
	}
1385
}
1386
1387
1388
/* terminal_gettc():
1389
 *	Get the current terminal characteristics
1390
 */
1391
protected int
1392
/*ARGSUSED*/
1393
terminal_gettc(EditLine *el, int argc __attribute__((__unused__)), char **argv)
1394
{
1395
	const struct termcapstr *ts;
1396
	const struct termcapval *tv;
1397
	char *what;
1398
	void *how;
1399
1400
	if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
1401
		return -1;
1402
1403
	what = argv[1];
1404
	how = argv[2];
1405
1406
	/*
1407
         * Do the strings first
1408
         */
1409
	for (ts = tstr; ts->name != NULL; ts++)
1410
		if (strcmp(ts->name, what) == 0)
1411
			break;
1412
1413
	if (ts->name != NULL) {
1414
		*(char **)how = el->el_terminal.t_str[ts - tstr];
1415
		return 0;
1416
	}
1417
	/*
1418
         * Do the numeric ones second
1419
         */
1420
	for (tv = tval; tv->name != NULL; tv++)
1421
		if (strcmp(tv->name, what) == 0)
1422
			break;
1423
1424
	if (tv->name == NULL)
1425
		return -1;
1426
1427
	if (tv == &tval[T_pt] || tv == &tval[T_km] ||
1428
	    tv == &tval[T_am] || tv == &tval[T_xn]) {
1429
		static char yes[] = "yes";
1430
		static char no[] = "no";
1431
		if (el->el_terminal.t_val[tv - tval])
1432
			*(char **)how = yes;
1433
		else
1434
			*(char **)how = no;
1435
		return 0;
1436
	} else {
1437
		*(int *)how = el->el_terminal.t_val[tv - tval];
1438
		return 0;
1439
	}
1440
}
1441
1442
/* terminal_echotc():
1443
 *	Print the termcap string out with variable substitution
1444
 */
1445
protected int
1446
/*ARGSUSED*/
1447
terminal_echotc(EditLine *el, int argc __attribute__((__unused__)),
1448
    const wchar_t **argv)
1449
{
1450
	char *cap, *scap;
1451
	wchar_t *ep;
1452
	int arg_need, arg_cols, arg_rows;
1453
	int verbose = 0, silent = 0;
1454
	char *area;
1455
	static const char fmts[] = "%s\n", fmtd[] = "%d\n";
1456
	const struct termcapstr *t;
1457
	char buf[TC_BUFSIZE];
1458
	long i;
1459
1460
	area = buf;
1461
1462
	if (argv == NULL || argv[1] == NULL)
1463
		return -1;
1464
	argv++;
1465
1466
	if (argv[0][0] == '-') {
1467
		switch (argv[0][1]) {
1468
		case 'v':
1469
			verbose = 1;
1470
			break;
1471
		case 's':
1472
			silent = 1;
1473
			break;
1474
		default:
1475
			/* stderror(ERR_NAME | ERR_TCUSAGE); */
1476
			break;
1477
		}
1478
		argv++;
1479
	}
1480
	if (!*argv || *argv[0] == '\0')
1481
		return 0;
1482
	if (wcscmp(*argv, L"tabs") == 0) {
1483
		(void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no");
1484
		return 0;
1485
	} else if (wcscmp(*argv, L"meta") == 0) {
1486
		(void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no");
1487
		return 0;
1488
	} else if (wcscmp(*argv, L"xn") == 0) {
1489
		(void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ?
1490
		    "yes" : "no");
1491
		return 0;
1492
	} else if (wcscmp(*argv, L"am") == 0) {
1493
		(void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ?
1494
		    "yes" : "no");
1495
		return 0;
1496
	} else if (wcscmp(*argv, L"baud") == 0) {
1497
		(void) fprintf(el->el_outfile, fmtd, (int)el->el_tty.t_speed);
1498
		return 0;
1499
	} else if (wcscmp(*argv, L"rows") == 0 ||
1500
                   wcscmp(*argv, L"lines") == 0) {
1501
		(void) fprintf(el->el_outfile, fmtd, Val(T_li));
1502
		return 0;
1503
	} else if (wcscmp(*argv, L"cols") == 0) {
1504
		(void) fprintf(el->el_outfile, fmtd, Val(T_co));
1505
		return 0;
1506
	}
1507
	/*
1508
         * Try to use our local definition first
1509
         */
1510
	scap = NULL;
1511
	for (t = tstr; t->name != NULL; t++)
1512
		if (strcmp(t->name,
1513
		    ct_encode_string(*argv, &el->el_scratch)) == 0) {
1514
			scap = el->el_terminal.t_str[t - tstr];
1515
			break;
1516
		}
1517
	if (t->name == NULL) {
1518
		/* XXX: some systems' tgetstr needs non const */
1519
                scap = tgetstr(ct_encode_string(*argv, &el->el_scratch), &area);
1520
	}
1521
	if (!scap || scap[0] == '\0') {
1522
		if (!silent)
1523
			(void) fprintf(el->el_errfile,
1524
			    "echotc: Termcap parameter `%ls' not found.\n",
1525
			    *argv);
1526
		return -1;
1527
	}
1528
	/*
1529
         * Count home many values we need for this capability.
1530
         */
1531
	for (cap = scap, arg_need = 0; *cap; cap++)
1532
		if (*cap == '%')
1533
			switch (*++cap) {
1534
			case 'd':
1535
			case '2':
1536
			case '3':
1537
			case '.':
1538
			case '+':
1539
				arg_need++;
1540
				break;
1541
			case '%':
1542
			case '>':
1543
			case 'i':
1544
			case 'r':
1545
			case 'n':
1546
			case 'B':
1547
			case 'D':
1548
				break;
1549
			default:
1550
				/*
1551
				 * hpux has lot's of them...
1552
				 */
1553
				if (verbose)
1554
					(void) fprintf(el->el_errfile,
1555
				"echotc: Warning: unknown termcap %% `%c'.\n",
1556
					    *cap);
1557
				/* This is bad, but I won't complain */
1558
				break;
1559
			}
1560
1561
	switch (arg_need) {
1562
	case 0:
1563
		argv++;
1564
		if (*argv && *argv[0]) {
1565
			if (!silent)
1566
				(void) fprintf(el->el_errfile,
1567
				    "echotc: Warning: Extra argument `%ls'.\n",
1568
				    *argv);
1569
			return -1;
1570
		}
1571
		terminal_tputs(el, scap, 1);
1572
		break;
1573
	case 1:
1574
		argv++;
1575
		if (!*argv || *argv[0] == '\0') {
1576
			if (!silent)
1577
				(void) fprintf(el->el_errfile,
1578
				    "echotc: Warning: Missing argument.\n");
1579
			return -1;
1580
		}
1581
		arg_cols = 0;
1582
		i = wcstol(*argv, &ep, 10);
1583
		if (*ep != '\0' || i < 0) {
1584
			if (!silent)
1585
				(void) fprintf(el->el_errfile,
1586
				    "echotc: Bad value `%ls' for rows.\n",
1587
				    *argv);
1588
			return -1;
1589
		}
1590
		arg_rows = (int) i;
1591
		argv++;
1592
		if (*argv && *argv[0]) {
1593
			if (!silent)
1594
				(void) fprintf(el->el_errfile,
1595
				    "echotc: Warning: Extra argument `%ls"
1596
				    "'.\n", *argv);
1597
			return -1;
1598
		}
1599
		terminal_tputs(el, tgoto(scap, arg_cols, arg_rows), 1);
1600
		break;
1601
	default:
1602
		/* This is wrong, but I will ignore it... */
1603
		if (verbose)
1604
			(void) fprintf(el->el_errfile,
1605
			 "echotc: Warning: Too many required arguments (%d).\n",
1606
			    arg_need);
1607
		/* FALLTHROUGH */
1608
	case 2:
1609
		argv++;
1610
		if (!*argv || *argv[0] == '\0') {
1611
			if (!silent)
1612
				(void) fprintf(el->el_errfile,
1613
				    "echotc: Warning: Missing argument.\n");
1614
			return -1;
1615
		}
1616
		i = wcstol(*argv, &ep, 10);
1617
		if (*ep != '\0' || i < 0) {
1618
			if (!silent)
1619
				(void) fprintf(el->el_errfile,
1620
				    "echotc: Bad value `%ls' for cols.\n",
1621
				    *argv);
1622
			return -1;
1623
		}
1624
		arg_cols = (int) i;
1625
		argv++;
1626
		if (!*argv || *argv[0] == '\0') {
1627
			if (!silent)
1628
				(void) fprintf(el->el_errfile,
1629
				    "echotc: Warning: Missing argument.\n");
1630
			return -1;
1631
		}
1632
		i = wcstol(*argv, &ep, 10);
1633
		if (*ep != '\0' || i < 0) {
1634
			if (!silent)
1635
				(void) fprintf(el->el_errfile,
1636
				    "echotc: Bad value `%ls' for rows.\n",
1637
				    *argv);
1638
			return -1;
1639
		}
1640
		arg_rows = (int) i;
1641
		if (*ep != '\0') {
1642
			if (!silent)
1643
				(void) fprintf(el->el_errfile,
1644
				    "echotc: Bad value `%ls'.\n", *argv);
1645
			return -1;
1646
		}
1647
		argv++;
1648
		if (*argv && *argv[0]) {
1649
			if (!silent)
1650
				(void) fprintf(el->el_errfile,
1651
				    "echotc: Warning: Extra argument `%ls"
1652
				    "'.\n", *argv);
1653
			return -1;
1654
		}
1655
		terminal_tputs(el, tgoto(scap, arg_cols, arg_rows), arg_rows);
1656
		break;
1657
	}
1658
	return 0;
1659
}