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: tty.c,v 1.25 2006/03/18 09:09:41 christos Exp $
33 */
34
35#if !defined(lint) && !defined(SCCSID)
36static char sccsid[] = "@(#)tty.c	8.1 (Berkeley) 6/4/93";
37#endif /* not lint && not SCCSID */
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD$");
40
41/*
42 * tty.c: tty interface stuff
43 */
44#include <assert.h>
45#include "sys.h"
46#include "tty.h"
47#include "el.h"
48
49typedef struct ttymodes_t {
50	const char *m_name;
51	unsigned int m_value;
52	int m_type;
53}          ttymodes_t;
54
55typedef struct ttymap_t {
56	int nch, och;		/* Internal and termio rep of chars */
57	el_action_t bind[3];	/* emacs, vi, and vi-cmd */
58} ttymap_t;
59
60
61private const ttyperm_t ttyperm = {
62	{
63		{"iflag:", ICRNL, (INLCR | IGNCR)},
64		{"oflag:", (OPOST | ONLCR), ONLRET},
65		{"cflag:", 0, 0},
66		{"lflag:", (ISIG | ICANON | ECHO | ECHOE | ECHOCTL | IEXTEN),
67		(NOFLSH | ECHONL | EXTPROC | FLUSHO)},
68		{"chars:", 0, 0},
69	},
70	{
71		{"iflag:", (INLCR | ICRNL), IGNCR},
72		{"oflag:", (OPOST | ONLCR), ONLRET},
73		{"cflag:", 0, 0},
74		{"lflag:", ISIG,
75		(NOFLSH | ICANON | ECHO | ECHOK | ECHONL | EXTPROC | IEXTEN | FLUSHO)},
76		{"chars:", (C_SH(C_MIN) | C_SH(C_TIME) | C_SH(C_SWTCH) | C_SH(C_DSWTCH) |
77			    C_SH(C_SUSP) | C_SH(C_DSUSP) | C_SH(C_EOL) | C_SH(C_DISCARD) |
78		    C_SH(C_PGOFF) | C_SH(C_PAGE) | C_SH(C_STATUS)), 0}
79	},
80	{
81		{"iflag:", 0, IXON | IXOFF | INLCR | ICRNL},
82		{"oflag:", 0, 0},
83		{"cflag:", 0, 0},
84		{"lflag:", 0, ISIG | IEXTEN},
85		{"chars:", 0, 0},
86	}
87};
88
89private const ttychar_t ttychar = {
90	{
91		CINTR, CQUIT, CERASE, CKILL,
92		CEOF, CEOL, CEOL2, CSWTCH,
93		CDSWTCH, CERASE2, CSTART, CSTOP,
94		CWERASE, CSUSP, CDSUSP, CREPRINT,
95		CDISCARD, CLNEXT, CSTATUS, CPAGE,
96		CPGOFF, CKILL2, CBRK, CMIN,
97		CTIME
98	},
99	{
100		CINTR, CQUIT, CERASE, CKILL,
101		_POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE,
102		_POSIX_VDISABLE, CERASE2, CSTART, CSTOP,
103		_POSIX_VDISABLE, CSUSP, _POSIX_VDISABLE, _POSIX_VDISABLE,
104		CDISCARD, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE,
105		_POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, 1,
106		0
107	},
108	{
109		0, 0, 0, 0,
110		0, 0, 0, 0,
111		0, 0, 0, 0,
112		0, 0, 0, 0,
113		0, 0, 0, 0,
114		0, 0, 0, 0,
115		0
116	}
117};
118
119private const ttymap_t tty_map[] = {
120#ifdef VERASE
121	{C_ERASE, VERASE,
122	{EM_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}},
123#endif /* VERASE */
124#ifdef VERASE2
125	{C_ERASE2, VERASE2,
126	{EM_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}},
127#endif /* VERASE2 */
128#ifdef VKILL
129	{C_KILL, VKILL,
130	{EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}},
131#endif /* VKILL */
132#ifdef VKILL2
133	{C_KILL2, VKILL2,
134	{EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}},
135#endif /* VKILL2 */
136#ifdef VEOF
137	{C_EOF, VEOF,
138	{EM_DELETE_OR_LIST, VI_LIST_OR_EOF, ED_UNASSIGNED}},
139#endif /* VEOF */
140#ifdef VWERASE
141	{C_WERASE, VWERASE,
142	{ED_DELETE_PREV_WORD, ED_DELETE_PREV_WORD, ED_PREV_WORD}},
143#endif /* VWERASE */
144#ifdef VREPRINT
145	{C_REPRINT, VREPRINT,
146	{ED_REDISPLAY, ED_INSERT, ED_REDISPLAY}},
147#endif /* VREPRINT */
148#ifdef VLNEXT
149	{C_LNEXT, VLNEXT,
150	{ED_QUOTED_INSERT, ED_QUOTED_INSERT, ED_UNASSIGNED}},
151#endif /* VLNEXT */
152	{-1, -1,
153	{ED_UNASSIGNED, ED_UNASSIGNED, ED_UNASSIGNED}}
154};
155
156private const ttymodes_t ttymodes[] = {
157#ifdef	IGNBRK
158	{"ignbrk", IGNBRK, MD_INP},
159#endif /* IGNBRK */
160#ifdef	BRKINT
161	{"brkint", BRKINT, MD_INP},
162#endif /* BRKINT */
163#ifdef	IGNPAR
164	{"ignpar", IGNPAR, MD_INP},
165#endif /* IGNPAR */
166#ifdef	PARMRK
167	{"parmrk", PARMRK, MD_INP},
168#endif /* PARMRK */
169#ifdef	INPCK
170	{"inpck", INPCK, MD_INP},
171#endif /* INPCK */
172#ifdef	ISTRIP
173	{"istrip", ISTRIP, MD_INP},
174#endif /* ISTRIP */
175#ifdef	INLCR
176	{"inlcr", INLCR, MD_INP},
177#endif /* INLCR */
178#ifdef	IGNCR
179	{"igncr", IGNCR, MD_INP},
180#endif /* IGNCR */
181#ifdef	ICRNL
182	{"icrnl", ICRNL, MD_INP},
183#endif /* ICRNL */
184#ifdef	IUCLC
185	{"iuclc", IUCLC, MD_INP},
186#endif /* IUCLC */
187#ifdef	IXON
188	{"ixon", IXON, MD_INP},
189#endif /* IXON */
190#ifdef	IXANY
191	{"ixany", IXANY, MD_INP},
192#endif /* IXANY */
193#ifdef	IXOFF
194	{"ixoff", IXOFF, MD_INP},
195#endif /* IXOFF */
196#ifdef  IMAXBEL
197	{"imaxbel", IMAXBEL, MD_INP},
198#endif /* IMAXBEL */
199
200#ifdef	OPOST
201	{"opost", OPOST, MD_OUT},
202#endif /* OPOST */
203#ifdef	OLCUC
204	{"olcuc", OLCUC, MD_OUT},
205#endif /* OLCUC */
206#ifdef	ONLCR
207	{"onlcr", ONLCR, MD_OUT},
208#endif /* ONLCR */
209#ifdef	OCRNL
210	{"ocrnl", OCRNL, MD_OUT},
211#endif /* OCRNL */
212#ifdef	ONOCR
213	{"onocr", ONOCR, MD_OUT},
214#endif /* ONOCR */
215#ifdef ONOEOT
216	{"onoeot", ONOEOT, MD_OUT},
217#endif /* ONOEOT */
218#ifdef	ONLRET
219	{"onlret", ONLRET, MD_OUT},
220#endif /* ONLRET */
221#ifdef	OFILL
222	{"ofill", OFILL, MD_OUT},
223#endif /* OFILL */
224#ifdef	OFDEL
225	{"ofdel", OFDEL, MD_OUT},
226#endif /* OFDEL */
227#ifdef	NLDLY
228	{"nldly", NLDLY, MD_OUT},
229#endif /* NLDLY */
230#ifdef	CRDLY
231	{"crdly", CRDLY, MD_OUT},
232#endif /* CRDLY */
233#ifdef	TABDLY
234	{"tabdly", TABDLY, MD_OUT},
235#endif /* TABDLY */
236#ifdef	XTABS
237	{"xtabs", XTABS, MD_OUT},
238#endif /* XTABS */
239#ifdef	BSDLY
240	{"bsdly", BSDLY, MD_OUT},
241#endif /* BSDLY */
242#ifdef	VTDLY
243	{"vtdly", VTDLY, MD_OUT},
244#endif /* VTDLY */
245#ifdef	FFDLY
246	{"ffdly", FFDLY, MD_OUT},
247#endif /* FFDLY */
248#ifdef	PAGEOUT
249	{"pageout", PAGEOUT, MD_OUT},
250#endif /* PAGEOUT */
251#ifdef	WRAP
252	{"wrap", WRAP, MD_OUT},
253#endif /* WRAP */
254
255#ifdef	CIGNORE
256	{"cignore", CIGNORE, MD_CTL},
257#endif /* CBAUD */
258#ifdef	CBAUD
259	{"cbaud", CBAUD, MD_CTL},
260#endif /* CBAUD */
261#ifdef	CSTOPB
262	{"cstopb", CSTOPB, MD_CTL},
263#endif /* CSTOPB */
264#ifdef	CREAD
265	{"cread", CREAD, MD_CTL},
266#endif /* CREAD */
267#ifdef	PARENB
268	{"parenb", PARENB, MD_CTL},
269#endif /* PARENB */
270#ifdef	PARODD
271	{"parodd", PARODD, MD_CTL},
272#endif /* PARODD */
273#ifdef	HUPCL
274	{"hupcl", HUPCL, MD_CTL},
275#endif /* HUPCL */
276#ifdef	CLOCAL
277	{"clocal", CLOCAL, MD_CTL},
278#endif /* CLOCAL */
279#ifdef	LOBLK
280	{"loblk", LOBLK, MD_CTL},
281#endif /* LOBLK */
282#ifdef	CIBAUD
283	{"cibaud", CIBAUD, MD_CTL},
284#endif /* CIBAUD */
285#ifdef CRTSCTS
286#ifdef CCTS_OFLOW
287	{"ccts_oflow", CCTS_OFLOW, MD_CTL},
288#else
289	{"crtscts", CRTSCTS, MD_CTL},
290#endif /* CCTS_OFLOW */
291#endif /* CRTSCTS */
292#ifdef CRTS_IFLOW
293	{"crts_iflow", CRTS_IFLOW, MD_CTL},
294#endif /* CRTS_IFLOW */
295#ifdef CDTRCTS
296	{"cdtrcts", CDTRCTS, MD_CTL},
297#endif /* CDTRCTS */
298#ifdef MDMBUF
299	{"mdmbuf", MDMBUF, MD_CTL},
300#endif /* MDMBUF */
301#ifdef RCV1EN
302	{"rcv1en", RCV1EN, MD_CTL},
303#endif /* RCV1EN */
304#ifdef XMT1EN
305	{"xmt1en", XMT1EN, MD_CTL},
306#endif /* XMT1EN */
307
308#ifdef	ISIG
309	{"isig", ISIG, MD_LIN},
310#endif /* ISIG */
311#ifdef	ICANON
312	{"icanon", ICANON, MD_LIN},
313#endif /* ICANON */
314#ifdef	XCASE
315	{"xcase", XCASE, MD_LIN},
316#endif /* XCASE */
317#ifdef	ECHO
318	{"echo", ECHO, MD_LIN},
319#endif /* ECHO */
320#ifdef	ECHOE
321	{"echoe", ECHOE, MD_LIN},
322#endif /* ECHOE */
323#ifdef	ECHOK
324	{"echok", ECHOK, MD_LIN},
325#endif /* ECHOK */
326#ifdef	ECHONL
327	{"echonl", ECHONL, MD_LIN},
328#endif /* ECHONL */
329#ifdef	NOFLSH
330	{"noflsh", NOFLSH, MD_LIN},
331#endif /* NOFLSH */
332#ifdef	TOSTOP
333	{"tostop", TOSTOP, MD_LIN},
334#endif /* TOSTOP */
335#ifdef	ECHOCTL
336	{"echoctl", ECHOCTL, MD_LIN},
337#endif /* ECHOCTL */
338#ifdef	ECHOPRT
339	{"echoprt", ECHOPRT, MD_LIN},
340#endif /* ECHOPRT */
341#ifdef	ECHOKE
342	{"echoke", ECHOKE, MD_LIN},
343#endif /* ECHOKE */
344#ifdef	DEFECHO
345	{"defecho", DEFECHO, MD_LIN},
346#endif /* DEFECHO */
347#ifdef	FLUSHO
348	{"flusho", FLUSHO, MD_LIN},
349#endif /* FLUSHO */
350#ifdef	PENDIN
351	{"pendin", PENDIN, MD_LIN},
352#endif /* PENDIN */
353#ifdef	IEXTEN
354	{"iexten", IEXTEN, MD_LIN},
355#endif /* IEXTEN */
356#ifdef	NOKERNINFO
357	{"nokerninfo", NOKERNINFO, MD_LIN},
358#endif /* NOKERNINFO */
359#ifdef	ALTWERASE
360	{"altwerase", ALTWERASE, MD_LIN},
361#endif /* ALTWERASE */
362#ifdef	EXTPROC
363	{"extproc", EXTPROC, MD_LIN},
364#endif /* EXTPROC */
365
366#if defined(VINTR)
367	{"intr", C_SH(C_INTR), MD_CHAR},
368#endif /* VINTR */
369#if defined(VQUIT)
370	{"quit", C_SH(C_QUIT), MD_CHAR},
371#endif /* VQUIT */
372#if defined(VERASE)
373	{"erase", C_SH(C_ERASE), MD_CHAR},
374#endif /* VERASE */
375#if defined(VKILL)
376	{"kill", C_SH(C_KILL), MD_CHAR},
377#endif /* VKILL */
378#if defined(VEOF)
379	{"eof", C_SH(C_EOF), MD_CHAR},
380#endif /* VEOF */
381#if defined(VEOL)
382	{"eol", C_SH(C_EOL), MD_CHAR},
383#endif /* VEOL */
384#if defined(VEOL2)
385	{"eol2", C_SH(C_EOL2), MD_CHAR},
386#endif /* VEOL2 */
387#if defined(VSWTCH)
388	{"swtch", C_SH(C_SWTCH), MD_CHAR},
389#endif /* VSWTCH */
390#if defined(VDSWTCH)
391	{"dswtch", C_SH(C_DSWTCH), MD_CHAR},
392#endif /* VDSWTCH */
393#if defined(VERASE2)
394	{"erase2", C_SH(C_ERASE2), MD_CHAR},
395#endif /* VERASE2 */
396#if defined(VSTART)
397	{"start", C_SH(C_START), MD_CHAR},
398#endif /* VSTART */
399#if defined(VSTOP)
400	{"stop", C_SH(C_STOP), MD_CHAR},
401#endif /* VSTOP */
402#if defined(VWERASE)
403	{"werase", C_SH(C_WERASE), MD_CHAR},
404#endif /* VWERASE */
405#if defined(VSUSP)
406	{"susp", C_SH(C_SUSP), MD_CHAR},
407#endif /* VSUSP */
408#if defined(VDSUSP)
409	{"dsusp", C_SH(C_DSUSP), MD_CHAR},
410#endif /* VDSUSP */
411#if defined(VREPRINT)
412	{"reprint", C_SH(C_REPRINT), MD_CHAR},
413#endif /* VREPRINT */
414#if defined(VDISCARD)
415	{"discard", C_SH(C_DISCARD), MD_CHAR},
416#endif /* VDISCARD */
417#if defined(VLNEXT)
418	{"lnext", C_SH(C_LNEXT), MD_CHAR},
419#endif /* VLNEXT */
420#if defined(VSTATUS)
421	{"status", C_SH(C_STATUS), MD_CHAR},
422#endif /* VSTATUS */
423#if defined(VPAGE)
424	{"page", C_SH(C_PAGE), MD_CHAR},
425#endif /* VPAGE */
426#if defined(VPGOFF)
427	{"pgoff", C_SH(C_PGOFF), MD_CHAR},
428#endif /* VPGOFF */
429#if defined(VKILL2)
430	{"kill2", C_SH(C_KILL2), MD_CHAR},
431#endif /* VKILL2 */
432#if defined(VBRK)
433	{"brk", C_SH(C_BRK), MD_CHAR},
434#endif /* VBRK */
435#if defined(VMIN)
436	{"min", C_SH(C_MIN), MD_CHAR},
437#endif /* VMIN */
438#if defined(VTIME)
439	{"time", C_SH(C_TIME), MD_CHAR},
440#endif /* VTIME */
441	{NULL, 0, -1},
442};
443
444
445
446#define	tty_getty(el, td)	tcgetattr((el)->el_infd, (td))
447#define	tty_setty(el, td)	tcsetattr((el)->el_infd, TCSADRAIN, (td))
448
449#define	tty__gettabs(td)	((((td)->c_oflag & TAB3) == TAB3) ? 0 : 1)
450#define	tty__geteightbit(td)	(((td)->c_cflag & CSIZE) == CS8)
451#define	tty__cooked_mode(td)	((td)->c_lflag & ICANON)
452
453private int	tty__getcharindex(int);
454private void	tty__getchar(struct termios *, unsigned char *);
455private void	tty__setchar(struct termios *, unsigned char *);
456private speed_t	tty__getspeed(struct termios *);
457private int	tty_setup(EditLine *);
458
459#define	t_qu	t_ts
460
461
462/* tty_setup():
463 *	Get the tty parameters and initialize the editing state
464 */
465private int
466tty_setup(EditLine *el)
467{
468	int rst = 1;
469
470	if (el->el_flags & EDIT_DISABLED)
471		return (0);
472
473	if (tty_getty(el, &el->el_tty.t_ed) == -1) {
474#ifdef DEBUG_TTY
475		(void) fprintf(el->el_errfile,
476		    "tty_setup: tty_getty: %s\n", strerror(errno));
477#endif /* DEBUG_TTY */
478		return (-1);
479	}
480	el->el_tty.t_ts = el->el_tty.t_ex = el->el_tty.t_ed;
481
482	el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ex);
483	el->el_tty.t_tabs = tty__gettabs(&el->el_tty.t_ex);
484	el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ex);
485
486	/*
487         * Reset the tty chars to reasonable defaults
488         * If they are disabled, then enable them.
489         */
490	if (rst) {
491		if (tty__cooked_mode(&el->el_tty.t_ts)) {
492			tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]);
493			/*
494	                 * Don't affect CMIN and CTIME for the editor mode
495	                 */
496			for (rst = 0; rst < C_NCC - 2; rst++)
497				if (el->el_tty.t_c[TS_IO][rst] !=
498				      el->el_tty.t_vdisable
499				    && el->el_tty.t_c[ED_IO][rst] !=
500				      el->el_tty.t_vdisable)
501					el->el_tty.t_c[ED_IO][rst] =
502					    el->el_tty.t_c[TS_IO][rst];
503			for (rst = 0; rst < C_NCC; rst++)
504				if (el->el_tty.t_c[TS_IO][rst] !=
505				    el->el_tty.t_vdisable)
506					el->el_tty.t_c[EX_IO][rst] =
507					    el->el_tty.t_c[TS_IO][rst];
508		}
509	}
510
511	el->el_tty.t_ed.c_iflag &= ~el->el_tty.t_t[ED_IO][MD_INP].t_clrmask;
512	el->el_tty.t_ed.c_iflag |= el->el_tty.t_t[ED_IO][MD_INP].t_setmask;
513
514	el->el_tty.t_ed.c_oflag &= ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask;
515	el->el_tty.t_ed.c_oflag |= el->el_tty.t_t[ED_IO][MD_OUT].t_setmask;
516
517	el->el_tty.t_ed.c_cflag &= ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask;
518	el->el_tty.t_ed.c_cflag |= el->el_tty.t_t[ED_IO][MD_CTL].t_setmask;
519
520	el->el_tty.t_ed.c_lflag &= ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask;
521	el->el_tty.t_ed.c_lflag |= el->el_tty.t_t[ED_IO][MD_LIN].t_setmask;
522
523	tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]);
524	tty_bind_char(el, 1);
525	return (0);
526}
527
528protected int
529tty_init(EditLine *el)
530{
531
532	el->el_tty.t_mode = EX_IO;
533	el->el_tty.t_vdisable = _POSIX_VDISABLE;
534	(void) memcpy(el->el_tty.t_t, ttyperm, sizeof(ttyperm_t));
535	(void) memcpy(el->el_tty.t_c, ttychar, sizeof(ttychar_t));
536	return (tty_setup(el));
537}
538
539
540/* tty_end():
541 *	Restore the tty to its original settings
542 */
543protected void
544/*ARGSUSED*/
545tty_end(EditLine *el __unused)
546{
547
548	/* XXX: Maybe reset to an initial state? */
549}
550
551
552/* tty__getspeed():
553 *	Get the tty speed
554 */
555private speed_t
556tty__getspeed(struct termios *td)
557{
558	speed_t spd;
559
560	if ((spd = cfgetispeed(td)) == 0)
561		spd = cfgetospeed(td);
562	return (spd);
563}
564
565/* tty__getspeed():
566 *	Return the index of the asked char in the c_cc array
567 */
568private int
569tty__getcharindex(int i)
570{
571	switch (i) {
572#ifdef VINTR
573	case C_INTR:
574		return VINTR;
575#endif /* VINTR */
576#ifdef VQUIT
577	case C_QUIT:
578		return VQUIT;
579#endif /* VQUIT */
580#ifdef VERASE
581	case C_ERASE:
582		return VERASE;
583#endif /* VERASE */
584#ifdef VKILL
585	case C_KILL:
586		return VKILL;
587#endif /* VKILL */
588#ifdef VEOF
589	case C_EOF:
590		return VEOF;
591#endif /* VEOF */
592#ifdef VEOL
593	case C_EOL:
594		return VEOL;
595#endif /* VEOL */
596#ifdef VEOL2
597	case C_EOL2:
598		return VEOL2;
599#endif /* VEOL2 */
600#ifdef VSWTCH
601	case C_SWTCH:
602		return VSWTCH;
603#endif /* VSWTCH */
604#ifdef VDSWTCH
605	case C_DSWTCH:
606		return VDSWTCH;
607#endif /* VDSWTCH */
608#ifdef VERASE2
609	case C_ERASE2:
610		return VERASE2;
611#endif /* VERASE2 */
612#ifdef VSTART
613	case C_START:
614		return VSTART;
615#endif /* VSTART */
616#ifdef VSTOP
617	case C_STOP:
618		return VSTOP;
619#endif /* VSTOP */
620#ifdef VWERASE
621	case C_WERASE:
622		return VWERASE;
623#endif /* VWERASE */
624#ifdef VSUSP
625	case C_SUSP:
626		return VSUSP;
627#endif /* VSUSP */
628#ifdef VDSUSP
629	case C_DSUSP:
630		return VDSUSP;
631#endif /* VDSUSP */
632#ifdef VREPRINT
633	case C_REPRINT:
634		return VREPRINT;
635#endif /* VREPRINT */
636#ifdef VDISCARD
637	case C_DISCARD:
638		return VDISCARD;
639#endif /* VDISCARD */
640#ifdef VLNEXT
641	case C_LNEXT:
642		return VLNEXT;
643#endif /* VLNEXT */
644#ifdef VSTATUS
645	case C_STATUS:
646		return VSTATUS;
647#endif /* VSTATUS */
648#ifdef VPAGE
649	case C_PAGE:
650		return VPAGE;
651#endif /* VPAGE */
652#ifdef VPGOFF
653	case C_PGOFF:
654		return VPGOFF;
655#endif /* VPGOFF */
656#ifdef VKILL2
657	case C_KILL2:
658		return VKILL2;
659#endif /* KILL2 */
660#ifdef VMIN
661	case C_MIN:
662		return VMIN;
663#endif /* VMIN */
664#ifdef VTIME
665	case C_TIME:
666		return VTIME;
667#endif /* VTIME */
668	default:
669		return -1;
670	}
671}
672
673/* tty__getchar():
674 *	Get the tty characters
675 */
676private void
677tty__getchar(struct termios *td, unsigned char *s)
678{
679
680#ifdef VINTR
681	s[C_INTR] = td->c_cc[VINTR];
682#endif /* VINTR */
683#ifdef VQUIT
684	s[C_QUIT] = td->c_cc[VQUIT];
685#endif /* VQUIT */
686#ifdef VERASE
687	s[C_ERASE] = td->c_cc[VERASE];
688#endif /* VERASE */
689#ifdef VKILL
690	s[C_KILL] = td->c_cc[VKILL];
691#endif /* VKILL */
692#ifdef VEOF
693	s[C_EOF] = td->c_cc[VEOF];
694#endif /* VEOF */
695#ifdef VEOL
696	s[C_EOL] = td->c_cc[VEOL];
697#endif /* VEOL */
698#ifdef VEOL2
699	s[C_EOL2] = td->c_cc[VEOL2];
700#endif /* VEOL2 */
701#ifdef VSWTCH
702	s[C_SWTCH] = td->c_cc[VSWTCH];
703#endif /* VSWTCH */
704#ifdef VDSWTCH
705	s[C_DSWTCH] = td->c_cc[VDSWTCH];
706#endif /* VDSWTCH */
707#ifdef VERASE2
708	s[C_ERASE2] = td->c_cc[VERASE2];
709#endif /* VERASE2 */
710#ifdef VSTART
711	s[C_START] = td->c_cc[VSTART];
712#endif /* VSTART */
713#ifdef VSTOP
714	s[C_STOP] = td->c_cc[VSTOP];
715#endif /* VSTOP */
716#ifdef VWERASE
717	s[C_WERASE] = td->c_cc[VWERASE];
718#endif /* VWERASE */
719#ifdef VSUSP
720	s[C_SUSP] = td->c_cc[VSUSP];
721#endif /* VSUSP */
722#ifdef VDSUSP
723	s[C_DSUSP] = td->c_cc[VDSUSP];
724#endif /* VDSUSP */
725#ifdef VREPRINT
726	s[C_REPRINT] = td->c_cc[VREPRINT];
727#endif /* VREPRINT */
728#ifdef VDISCARD
729	s[C_DISCARD] = td->c_cc[VDISCARD];
730#endif /* VDISCARD */
731#ifdef VLNEXT
732	s[C_LNEXT] = td->c_cc[VLNEXT];
733#endif /* VLNEXT */
734#ifdef VSTATUS
735	s[C_STATUS] = td->c_cc[VSTATUS];
736#endif /* VSTATUS */
737#ifdef VPAGE
738	s[C_PAGE] = td->c_cc[VPAGE];
739#endif /* VPAGE */
740#ifdef VPGOFF
741	s[C_PGOFF] = td->c_cc[VPGOFF];
742#endif /* VPGOFF */
743#ifdef VKILL2
744	s[C_KILL2] = td->c_cc[VKILL2];
745#endif /* KILL2 */
746#ifdef VMIN
747	s[C_MIN] = td->c_cc[VMIN];
748#endif /* VMIN */
749#ifdef VTIME
750	s[C_TIME] = td->c_cc[VTIME];
751#endif /* VTIME */
752}				/* tty__getchar */
753
754
755/* tty__setchar():
756 *	Set the tty characters
757 */
758private void
759tty__setchar(struct termios *td, unsigned char *s)
760{
761
762#ifdef VINTR
763	td->c_cc[VINTR] = s[C_INTR];
764#endif /* VINTR */
765#ifdef VQUIT
766	td->c_cc[VQUIT] = s[C_QUIT];
767#endif /* VQUIT */
768#ifdef VERASE
769	td->c_cc[VERASE] = s[C_ERASE];
770#endif /* VERASE */
771#ifdef VKILL
772	td->c_cc[VKILL] = s[C_KILL];
773#endif /* VKILL */
774#ifdef VEOF
775	td->c_cc[VEOF] = s[C_EOF];
776#endif /* VEOF */
777#ifdef VEOL
778	td->c_cc[VEOL] = s[C_EOL];
779#endif /* VEOL */
780#ifdef VEOL2
781	td->c_cc[VEOL2] = s[C_EOL2];
782#endif /* VEOL2 */
783#ifdef VSWTCH
784	td->c_cc[VSWTCH] = s[C_SWTCH];
785#endif /* VSWTCH */
786#ifdef VDSWTCH
787	td->c_cc[VDSWTCH] = s[C_DSWTCH];
788#endif /* VDSWTCH */
789#ifdef VERASE2
790	td->c_cc[VERASE2] = s[C_ERASE2];
791#endif /* VERASE2 */
792#ifdef VSTART
793	td->c_cc[VSTART] = s[C_START];
794#endif /* VSTART */
795#ifdef VSTOP
796	td->c_cc[VSTOP] = s[C_STOP];
797#endif /* VSTOP */
798#ifdef VWERASE
799	td->c_cc[VWERASE] = s[C_WERASE];
800#endif /* VWERASE */
801#ifdef VSUSP
802	td->c_cc[VSUSP] = s[C_SUSP];
803#endif /* VSUSP */
804#ifdef VDSUSP
805	td->c_cc[VDSUSP] = s[C_DSUSP];
806#endif /* VDSUSP */
807#ifdef VREPRINT
808	td->c_cc[VREPRINT] = s[C_REPRINT];
809#endif /* VREPRINT */
810#ifdef VDISCARD
811	td->c_cc[VDISCARD] = s[C_DISCARD];
812#endif /* VDISCARD */
813#ifdef VLNEXT
814	td->c_cc[VLNEXT] = s[C_LNEXT];
815#endif /* VLNEXT */
816#ifdef VSTATUS
817	td->c_cc[VSTATUS] = s[C_STATUS];
818#endif /* VSTATUS */
819#ifdef VPAGE
820	td->c_cc[VPAGE] = s[C_PAGE];
821#endif /* VPAGE */
822#ifdef VPGOFF
823	td->c_cc[VPGOFF] = s[C_PGOFF];
824#endif /* VPGOFF */
825#ifdef VKILL2
826	td->c_cc[VKILL2] = s[C_KILL2];
827#endif /* VKILL2 */
828#ifdef VMIN
829	td->c_cc[VMIN] = s[C_MIN];
830#endif /* VMIN */
831#ifdef VTIME
832	td->c_cc[VTIME] = s[C_TIME];
833#endif /* VTIME */
834}				/* tty__setchar */
835
836
837/* tty_bind_char():
838 *	Rebind the editline functions
839 */
840protected void
841tty_bind_char(EditLine *el, int force)
842{
843
844	unsigned char *t_n = el->el_tty.t_c[ED_IO];
845	unsigned char *t_o = el->el_tty.t_ed.c_cc;
846	unsigned char new[2], old[2];
847	const ttymap_t *tp;
848	el_action_t *map, *alt;
849	const el_action_t *dmap, *dalt;
850	new[1] = old[1] = '\0';
851
852	map = el->el_map.key;
853	alt = el->el_map.alt;
854	if (el->el_map.type == MAP_VI) {
855		dmap = el->el_map.vii;
856		dalt = el->el_map.vic;
857	} else {
858		dmap = el->el_map.emacs;
859		dalt = NULL;
860	}
861
862	for (tp = tty_map; tp->nch != -1; tp++) {
863		new[0] = t_n[tp->nch];
864		old[0] = t_o[tp->och];
865		if (new[0] == old[0] && !force)
866			continue;
867		/* Put the old default binding back, and set the new binding */
868		key_clear(el, map, (char *)old);
869		map[old[0]] = dmap[old[0]];
870		key_clear(el, map, (char *)new);
871		/* MAP_VI == 1, MAP_EMACS == 0... */
872		map[new[0]] = tp->bind[el->el_map.type];
873		if (dalt) {
874			key_clear(el, alt, (char *)old);
875			alt[old[0]] = dalt[old[0]];
876			key_clear(el, alt, (char *)new);
877			alt[new[0]] = tp->bind[el->el_map.type + 1];
878		}
879	}
880}
881
882
883/* tty_rawmode():
884 * 	Set terminal into 1 character at a time mode.
885 */
886protected int
887tty_rawmode(EditLine *el)
888{
889
890	if (el->el_tty.t_mode == ED_IO || el->el_tty.t_mode == QU_IO)
891		return (0);
892
893	if (el->el_flags & EDIT_DISABLED)
894		return (0);
895
896	if (tty_getty(el, &el->el_tty.t_ts) == -1) {
897#ifdef DEBUG_TTY
898		(void) fprintf(el->el_errfile, "tty_rawmode: tty_getty: %s\n",
899		    strerror(errno));
900#endif /* DEBUG_TTY */
901		return (-1);
902	}
903	/*
904         * We always keep up with the eight bit setting and the speed of the
905         * tty. But only we only believe changes that are made to cooked mode!
906         */
907	el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ts);
908	el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ts);
909
910	if (tty__getspeed(&el->el_tty.t_ed) != el->el_tty.t_speed) {
911		(void) cfsetispeed(&el->el_tty.t_ed, el->el_tty.t_speed);
912		(void) cfsetospeed(&el->el_tty.t_ed, el->el_tty.t_speed);
913	}
914	if (tty__cooked_mode(&el->el_tty.t_ts)) {
915		if ((el->el_tty.t_ts.c_cflag != el->el_tty.t_ex.c_cflag) &&
916		    (el->el_tty.t_ts.c_cflag != el->el_tty.t_ed.c_cflag)) {
917			el->el_tty.t_ed.c_cflag =
918			    el->el_tty.t_ts.c_cflag;
919			el->el_tty.t_ed.c_cflag &=
920			    ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask;
921			el->el_tty.t_ed.c_cflag |=
922			    el->el_tty.t_t[ED_IO][MD_CTL].t_setmask;
923		}
924		if ((el->el_tty.t_ts.c_lflag != el->el_tty.t_ex.c_lflag) &&
925		    (el->el_tty.t_ts.c_lflag != el->el_tty.t_ed.c_lflag)) {
926			el->el_tty.t_ed.c_lflag =
927			    el->el_tty.t_ts.c_lflag;
928			el->el_tty.t_ed.c_lflag &=
929			    ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask;
930			el->el_tty.t_ed.c_lflag |=
931			    el->el_tty.t_t[ED_IO][MD_LIN].t_setmask;
932		}
933		if ((el->el_tty.t_ts.c_iflag != el->el_tty.t_ex.c_iflag) &&
934		    (el->el_tty.t_ts.c_iflag != el->el_tty.t_ed.c_iflag)) {
935			el->el_tty.t_ed.c_iflag =
936			    el->el_tty.t_ts.c_iflag;
937			el->el_tty.t_ed.c_iflag &=
938			    ~el->el_tty.t_t[ED_IO][MD_INP].t_clrmask;
939			el->el_tty.t_ed.c_iflag |=
940			    el->el_tty.t_t[ED_IO][MD_INP].t_setmask;
941		}
942		if ((el->el_tty.t_ts.c_oflag != el->el_tty.t_ex.c_oflag) &&
943		    (el->el_tty.t_ts.c_oflag != el->el_tty.t_ed.c_oflag)) {
944			el->el_tty.t_ed.c_oflag =
945			    el->el_tty.t_ts.c_oflag;
946			el->el_tty.t_ed.c_oflag &=
947			    ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask;
948			el->el_tty.t_ed.c_oflag |=
949			    el->el_tty.t_t[ED_IO][MD_OUT].t_setmask;
950		}
951		if (tty__gettabs(&el->el_tty.t_ex) == 0)
952			el->el_tty.t_tabs = 0;
953		else
954			el->el_tty.t_tabs = EL_CAN_TAB ? 1 : 0;
955
956		{
957			int i;
958
959			tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]);
960			/*
961		         * Check if the user made any changes.
962		         * If he did, then propagate the changes to the
963		         * edit and execute data structures.
964		         */
965			for (i = 0; i < C_NCC; i++)
966				if (el->el_tty.t_c[TS_IO][i] !=
967				    el->el_tty.t_c[EX_IO][i])
968					break;
969
970			if (i != C_NCC) {
971				/*
972				 * Propagate changes only to the unprotected
973				 * chars that have been modified just now.
974				 */
975				for (i = 0; i < C_NCC; i++) {
976					if (!((el->el_tty.t_t[ED_IO][MD_CHAR].t_setmask & C_SH(i)))
977					    && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]))
978						el->el_tty.t_c[ED_IO][i] = el->el_tty.t_c[TS_IO][i];
979					if (el->el_tty.t_t[ED_IO][MD_CHAR].t_clrmask & C_SH(i))
980						el->el_tty.t_c[ED_IO][i] = el->el_tty.t_vdisable;
981				}
982				tty_bind_char(el, 0);
983				tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]);
984
985				for (i = 0; i < C_NCC; i++) {
986					if (!((el->el_tty.t_t[EX_IO][MD_CHAR].t_setmask & C_SH(i)))
987					    && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]))
988						el->el_tty.t_c[EX_IO][i] = el->el_tty.t_c[TS_IO][i];
989					if (el->el_tty.t_t[EX_IO][MD_CHAR].t_clrmask & C_SH(i))
990						el->el_tty.t_c[EX_IO][i] = el->el_tty.t_vdisable;
991				}
992			}
993		}
994	}
995
996	if (el->el_tty.t_mode == EX_IO)
997		el->el_tty.t_ex = el->el_tty.t_ts;
998
999	if (tty_setty(el, &el->el_tty.t_ed) == -1) {
1000#ifdef DEBUG_TTY
1001		(void) fprintf(el->el_errfile, "tty_rawmode: tty_setty: %s\n",
1002		    strerror(errno));
1003#endif /* DEBUG_TTY */
1004		return (-1);
1005	}
1006	el->el_tty.t_mode = ED_IO;
1007	return (0);
1008}
1009
1010
1011/* tty_cookedmode():
1012 *	Set the tty back to normal mode
1013 */
1014protected int
1015tty_cookedmode(EditLine *el)
1016{				/* set tty in normal setup */
1017
1018	if (el->el_tty.t_mode == EX_IO)
1019		return (0);
1020
1021	if (el->el_flags & EDIT_DISABLED)
1022		return (0);
1023
1024	if (tty_setty(el, &el->el_tty.t_ex) == -1) {
1025#ifdef DEBUG_TTY
1026		(void) fprintf(el->el_errfile,
1027		    "tty_cookedmode: tty_setty: %s\n",
1028		    strerror(errno));
1029#endif /* DEBUG_TTY */
1030		return (-1);
1031	}
1032	el->el_tty.t_mode = EX_IO;
1033	return (0);
1034}
1035
1036
1037/* tty_quotemode():
1038 *	Turn on quote mode
1039 */
1040protected int
1041tty_quotemode(EditLine *el)
1042{
1043	if (el->el_tty.t_mode == QU_IO)
1044		return (0);
1045
1046	el->el_tty.t_qu = el->el_tty.t_ed;
1047
1048	el->el_tty.t_qu.c_iflag &= ~el->el_tty.t_t[QU_IO][MD_INP].t_clrmask;
1049	el->el_tty.t_qu.c_iflag |= el->el_tty.t_t[QU_IO][MD_INP].t_setmask;
1050
1051	el->el_tty.t_qu.c_oflag &= ~el->el_tty.t_t[QU_IO][MD_OUT].t_clrmask;
1052	el->el_tty.t_qu.c_oflag |= el->el_tty.t_t[QU_IO][MD_OUT].t_setmask;
1053
1054	el->el_tty.t_qu.c_cflag &= ~el->el_tty.t_t[QU_IO][MD_CTL].t_clrmask;
1055	el->el_tty.t_qu.c_cflag |= el->el_tty.t_t[QU_IO][MD_CTL].t_setmask;
1056
1057	el->el_tty.t_qu.c_lflag &= ~el->el_tty.t_t[QU_IO][MD_LIN].t_clrmask;
1058	el->el_tty.t_qu.c_lflag |= el->el_tty.t_t[QU_IO][MD_LIN].t_setmask;
1059
1060	if (tty_setty(el, &el->el_tty.t_qu) == -1) {
1061#ifdef DEBUG_TTY
1062		(void) fprintf(el->el_errfile, "QuoteModeOn: tty_setty: %s\n",
1063		    strerror(errno));
1064#endif /* DEBUG_TTY */
1065		return (-1);
1066	}
1067	el->el_tty.t_mode = QU_IO;
1068	return (0);
1069}
1070
1071
1072/* tty_noquotemode():
1073 *	Turn off quote mode
1074 */
1075protected int
1076tty_noquotemode(EditLine *el)
1077{
1078
1079	if (el->el_tty.t_mode != QU_IO)
1080		return (0);
1081	if (tty_setty(el, &el->el_tty.t_ed) == -1) {
1082#ifdef DEBUG_TTY
1083		(void) fprintf(el->el_errfile, "QuoteModeOff: tty_setty: %s\n",
1084		    strerror(errno));
1085#endif /* DEBUG_TTY */
1086		return (-1);
1087	}
1088	el->el_tty.t_mode = ED_IO;
1089	return (0);
1090}
1091
1092
1093/* tty_stty():
1094 *	Stty builtin
1095 */
1096protected int
1097/*ARGSUSED*/
1098tty_stty(EditLine *el, int argc __unused, const char **argv)
1099{
1100	const ttymodes_t *m;
1101	char x;
1102	int aflag = 0;
1103	const char *s, *d;
1104	const char *name;
1105	struct termios *tios = &el->el_tty.t_ex;
1106	int z = EX_IO;
1107
1108	if (argv == NULL)
1109		return (-1);
1110	name = *argv++;
1111
1112	while (argv && *argv && argv[0][0] == '-' && argv[0][2] == '\0')
1113		switch (argv[0][1]) {
1114		case 'a':
1115			aflag++;
1116			argv++;
1117			break;
1118		case 'd':
1119			argv++;
1120			tios = &el->el_tty.t_ed;
1121			z = ED_IO;
1122			break;
1123		case 'x':
1124			argv++;
1125			tios = &el->el_tty.t_ex;
1126			z = EX_IO;
1127			break;
1128		case 'q':
1129			argv++;
1130			tios = &el->el_tty.t_ts;
1131			z = QU_IO;
1132			break;
1133		default:
1134			(void) fprintf(el->el_errfile,
1135			    "%s: Unknown switch `%c'.\n",
1136			    name, argv[0][1]);
1137			return (-1);
1138		}
1139
1140	if (!argv || !*argv) {
1141		int i = -1;
1142		int len = 0, st = 0, cu;
1143		for (m = ttymodes; m->m_name; m++) {
1144			if (m->m_type != i) {
1145				(void) fprintf(el->el_outfile, "%s%s",
1146				    i != -1 ? "\n" : "",
1147				    el->el_tty.t_t[z][m->m_type].t_name);
1148				i = m->m_type;
1149				st = len =
1150				    strlen(el->el_tty.t_t[z][m->m_type].t_name);
1151			}
1152			if (i != -1) {
1153			    x = (el->el_tty.t_t[z][i].t_setmask & m->m_value)
1154				?  '+' : '\0';
1155			    x = (el->el_tty.t_t[z][i].t_clrmask & m->m_value)
1156				? '-' : x;
1157			} else {
1158			    x = '\0';
1159			}
1160
1161			if (x != '\0' || aflag) {
1162
1163				cu = strlen(m->m_name) + (x != '\0') + 1;
1164
1165				if (len + cu >= el->el_term.t_size.h) {
1166					(void) fprintf(el->el_outfile, "\n%*s",
1167					    st, "");
1168					len = st + cu;
1169				} else
1170					len += cu;
1171
1172				if (x != '\0')
1173					(void) fprintf(el->el_outfile, "%c%s ",
1174					    x, m->m_name);
1175				else
1176					(void) fprintf(el->el_outfile, "%s ",
1177					    m->m_name);
1178			}
1179		}
1180		(void) fprintf(el->el_outfile, "\n");
1181		return (0);
1182	}
1183	while (argv && (s = *argv++)) {
1184		const char *p;
1185		switch (*s) {
1186		case '+':
1187		case '-':
1188			x = *s++;
1189			break;
1190		default:
1191			x = '\0';
1192			break;
1193		}
1194		d = s;
1195		p = strchr(s, '=');
1196		for (m = ttymodes; m->m_name; m++)
1197			if ((p ? strncmp(m->m_name, d, (size_t)(p - d)) :
1198			    strcmp(m->m_name, d)) == 0 &&
1199			    (p == NULL || m->m_type == MD_CHAR))
1200				break;
1201
1202		if (!m->m_name) {
1203			(void) fprintf(el->el_errfile,
1204			    "%s: Invalid argument `%s'.\n", name, d);
1205			return (-1);
1206		}
1207		if (p) {
1208			int c = ffs((int)m->m_value);
1209			int v = *++p ? parse__escape((const char **) &p) :
1210			    el->el_tty.t_vdisable;
1211			assert(c-- != 0);
1212			c = tty__getcharindex(c);
1213			assert(c != -1);
1214			tios->c_cc[c] = v;
1215			continue;
1216		}
1217		switch (x) {
1218		case '+':
1219			el->el_tty.t_t[z][m->m_type].t_setmask |= m->m_value;
1220			el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value;
1221			break;
1222		case '-':
1223			el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value;
1224			el->el_tty.t_t[z][m->m_type].t_clrmask |= m->m_value;
1225			break;
1226		default:
1227			el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value;
1228			el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value;
1229			break;
1230		}
1231	}
1232	return (0);
1233}
1234
1235
1236#ifdef notyet
1237/* tty_printchar():
1238 *	DEbugging routine to print the tty characters
1239 */
1240private void
1241tty_printchar(EditLine *el, unsigned char *s)
1242{
1243	ttyperm_t *m;
1244	int i;
1245
1246	for (i = 0; i < C_NCC; i++) {
1247		for (m = el->el_tty.t_t; m->m_name; m++)
1248			if (m->m_type == MD_CHAR && C_SH(i) == m->m_value)
1249				break;
1250		if (m->m_name)
1251			(void) fprintf(el->el_errfile, "%s ^%c ",
1252			    m->m_name, s[i] + 'A' - 1);
1253		if (i % 5 == 0)
1254			(void) fprintf(el->el_errfile, "\n");
1255	}
1256	(void) fprintf(el->el_errfile, "\n");
1257}
1258#endif /* notyet */
1259