xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_termio.c (revision c559157643fef9f9afb0414e00a3579407ba3052)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright (c) 2012 by Delphix. All rights reserved.
29  * Copyright 2017 Joyent, Inc.
30  */
31 
32 /*
33  * Terminal I/O Backend
34  *
35  * Terminal editing backend for standard input.  The terminal i/o backend is
36  * actually built on top of two other i/o backends: one for raw input and
37  * another for raw output (presumably stdin and stdout).  When IOP_READ is
38  * invoked, the terminal backend enters a read-loop in which it can perform
39  * command-line editing and access a history buffer.  Once a newline is read,
40  * the entire buffered command-line is returned to the caller.  The termio
41  * code makes use of a command buffer (see mdb_cmdbuf.c) to maintain and
42  * manipulate the state of a command line, and store it for re-use in a
43  * history list.  The termio code manipulates the terminal to keep it in
44  * sync with the contents of the command buffer, and moves the cursor in
45  * response to editing commands.
46  *
47  * The terminal backend is also responsible for maintaining and manipulating
48  * the settings (see stty(1) and termio(7I)) associated with the terminal.
49  * The debugger makes use of four distinct sets of terminal attributes:
50  *
51  * (1) the settings used by the debugger's parent process (tio_ptios),
52  * (2) the settings used by a controlled child process (tio_ctios),
53  * (3) the settings used for reading and command-line editing (tio_rtios), and
54  * (4) the settings used when mdb dcmds are executing (tio_dtios).
55  *
56  * The parent settings (1) are read from the terminal during initialization.
57  * These settings are restored before the debugger exits or when it is stopped
58  * by SIGTSTP.  The child settings (2) are initially a copy of (1), but are
59  * then restored prior to continuing execution of a victim process.  The new
60  * settings (3) and (4) are both derived from (1).  The raw settings (3) used
61  * for reading from the terminal allow the terminal code to respond instantly
62  * to keypresses and perform all the necessary handling.  The dcmd settings (4)
63  * are essentially the same as (1), except that we make sure ISIG is enabled
64  * so that we will receive asynchronous SIGINT notification from the terminal
65  * driver if the user types the interrupt character (typically ^C).
66  */
67 
68 #include <setjmp.h>
69 #include <unistd.h>
70 #include <stdlib.h>
71 #include <limits.h>
72 
73 #include <mdb/mdb_types.h>
74 #include <mdb/mdb_cmdbuf.h>
75 #include <mdb/mdb_err.h>
76 #include <mdb/mdb_io_impl.h>
77 #include <mdb/mdb_debug.h>
78 #include <mdb/mdb_signal.h>
79 #include <mdb/mdb_callb.h>
80 #include <mdb/mdb_stdlib.h>
81 #include <mdb/mdb_string.h>
82 #include <mdb/mdb_modapi.h>
83 #include <mdb/mdb_frame.h>
84 #include <mdb/mdb_tab.h>
85 #include <mdb/mdb.h>
86 
87 #ifdef ERR
88 #undef ERR
89 #endif
90 
91 #include <curses.h>
92 
93 #define	KEY_ESC	(0x01b)			/* Escape key code */
94 #define	KEY_DEL (0x07f)			/* ASCII DEL key code */
95 
96 /*
97  * These macros support the use of various ranges within the "tio_keymap"
98  * member of "termio_data_t" objects.  This array maps from an input byte, or
99  * special control code, to the appropriate terminal handling callback.  The
100  * array has KEY_MAX (0x1ff) entries, partitioned as follows:
101  *
102  *     0 -  7f		7-bit ASCII byte
103  *    80 -  ff	META()	ASCII byte with Meta key modifier
104  *   100 - 119	KPAD()	Alphabetic character received as part of a single-byte
105  *			cursor control sequence, e.g. ESC [ A
106  *   11a - 123	FKEY()	Numeric character received as part of a function key
107  *			control sequence, e.g. ESC [ 4 ~
108  *   124 - 1ff		Unused
109  */
110 #define	META(c)		(((c) & 0x7f) | 0x80)
111 #define	KPAD(c)		(((c) < 'A' || (c) > 'Z') ? 0 : ((c) - 'A' + 0x100))
112 #define	FKEY(c)		(((c) < '0' || (c) > '9') ? 0 : ((c) - '0' + 0x11a))
113 
114 /*
115  * These macros allow for composition of control sequences for xterm and other
116  * terminals that support certain features of the VT102 and later VT terminals.
117  * Refer to the classic monograph "Xterm Control Sequences" for more info.
118  */
119 #define	TI_DECSET(Pm) "\033[?" Pm "h"	/* Compose DEC private mode set */
120 #define	TI_DECRST(Pm) "\033[?" Pm "l"	/* Compose DEC private mode reset */
121 #define	TI_DECSAV(Pm) "\033[?" Pm "s"	/* Compose DEC private mode save */
122 #define	TI_DECRES(Pm) "\033[?" Pm "r"	/* Compose DEC private mode restore */
123 
124 #define	TI_DECCOLM		"3"	/* Ps = DEC 80/132 column mode */
125 #define	TI_COLENAB		"40"	/* Ps = 80/132 column switch enable */
126 
127 #define	TIO_DEFAULT_ROWS	24	/* Default number of rows */
128 #define	TIO_DEFAULT_COLS	80	/* Default number of columns */
129 
130 typedef union termio_attr_val {
131 	const char *at_str;		/* String value */
132 	int at_val;			/* Integer or boolean value */
133 } termio_attr_val_t;
134 
135 typedef struct termio_info {
136 	termio_attr_val_t ti_cub1;	/* Move back one space */
137 	termio_attr_val_t ti_cuf1;	/* Move forward one space */
138 	termio_attr_val_t ti_cuu1;	/* Move up one line */
139 	termio_attr_val_t ti_cud1;	/* Move down one line */
140 	termio_attr_val_t ti_pad;	/* Pad character */
141 	termio_attr_val_t ti_el;	/* Clear to end-of-line */
142 	termio_attr_val_t ti_am;	/* Automatic right margin? */
143 	termio_attr_val_t ti_bw;	/* Backward motion at left edge? */
144 	termio_attr_val_t ti_npc;	/* No padding character? */
145 	termio_attr_val_t ti_xenl;	/* Newline ignored at autowrap? */
146 	termio_attr_val_t ti_xon;	/* Use xon/xoff handshaking? */
147 	termio_attr_val_t ti_cols;	/* # of columns */
148 	termio_attr_val_t ti_lines;	/* # of rows */
149 	termio_attr_val_t ti_pb;	/* Lowest baud rate that requires pad */
150 	termio_attr_val_t ti_smso;	/* Set standout mode */
151 	termio_attr_val_t ti_rmso;	/* Remove standout mode */
152 	termio_attr_val_t ti_smul;	/* Set underline mode */
153 	termio_attr_val_t ti_rmul;	/* Remove underline mode */
154 	termio_attr_val_t ti_enacs;	/* Enable alternate character set */
155 	termio_attr_val_t ti_smacs;	/* Set alternate character set */
156 	termio_attr_val_t ti_rmacs;	/* Remove alternate character set */
157 	termio_attr_val_t ti_smcup;	/* Set mode where cup is active */
158 	termio_attr_val_t ti_rmcup;	/* Remove mode where cup is active */
159 	termio_attr_val_t ti_rev;	/* Set reverse video mode */
160 	termio_attr_val_t ti_bold;	/* Set bold text mode */
161 	termio_attr_val_t ti_dim;	/* Set dim text mode */
162 	termio_attr_val_t ti_sgr0;	/* Remove all video attributes */
163 	termio_attr_val_t ti_smir;	/* Set insert mode */
164 	termio_attr_val_t ti_rmir;	/* Remove insert mode */
165 	termio_attr_val_t ti_ich1;	/* Insert character */
166 	termio_attr_val_t ti_ip;	/* Insert pad delay in msecs */
167 	termio_attr_val_t ti_clear;	/* Clear screen and home cursor */
168 	termio_attr_val_t ti_cnorm;	/* Make cursor appear normal */
169 	termio_attr_val_t ti_nel;	/* Newline */
170 	termio_attr_val_t ti_cr;	/* Carriage return */
171 	termio_attr_val_t ti_smam;	/* Turn on automatic margins */
172 } termio_info_t;
173 
174 typedef enum {
175 	TIO_ATTR_REQSTR,		/* String attribute that is required */
176 	TIO_ATTR_STR,			/* String attribute */
177 	TIO_ATTR_BOOL,			/* Boolean attribute */
178 	TIO_ATTR_INT			/* Integer attribute */
179 } termio_attr_type_t;
180 
181 typedef struct termio_attr {
182 	const char *ta_name;		/* Capability name */
183 	termio_attr_type_t ta_type;	/* Capability type */
184 	termio_attr_val_t *ta_valp;	/* String pointer location */
185 } termio_attr_t;
186 
187 struct termio_data;
188 typedef const char *(*keycb_t)(struct termio_data *, int);
189 typedef void (*putp_t)(struct termio_data *, const char *, uint_t);
190 
191 #define	TIO_FINDHIST	0x01		/* Find-history-mode */
192 #define	TIO_AUTOWRAP	0x02		/* Terminal has autowrap */
193 #define	TIO_BACKLEFT	0x04		/* Terminal can go back at left edge */
194 #define	TIO_INSERT	0x08		/* Terminal has insert mode */
195 #define	TIO_USECUP	0x10		/* Use smcup/rmcup sequences */
196 #define	TIO_TTYWARN	0x20		/* Warnings about tty issued */
197 #define	TIO_CAPWARN	0x40		/* Warnings about terminfo issued */
198 #define	TIO_XTERM	0x80		/* Terminal is xterm compatible */
199 #define	TIO_TAB		0x100		/* Tab completion mode */
200 #define	TIO_LAZYWRAP	0x200		/* Lazy cursor on autowrap */
201 
202 static const mdb_bitmask_t tio_flag_masks[] = {
203 	{ "FINDHIST", TIO_FINDHIST, TIO_FINDHIST },
204 	{ "AUTOWRAP", TIO_AUTOWRAP, TIO_AUTOWRAP },
205 	{ "BACKLEFT", TIO_BACKLEFT, TIO_BACKLEFT },
206 	{ "INSERT", TIO_INSERT, TIO_INSERT },
207 	{ "USECUP", TIO_USECUP, TIO_USECUP },
208 	{ "TTYWARN", TIO_TTYWARN, TIO_TTYWARN },
209 	{ "CAPWARN", TIO_CAPWARN, TIO_CAPWARN },
210 	{ "XTERM", TIO_XTERM, TIO_XTERM },
211 	{ "TAB", TIO_TAB, TIO_TAB },
212 	{ "LAZYWRAP", TIO_LAZYWRAP, TIO_LAZYWRAP },
213 	{ NULL, 0, 0 }
214 };
215 
216 typedef struct termio_data {
217 	mdb_io_t *tio_io;		/* Pointer back to containing i/o */
218 	mdb_io_t *tio_out_io;		/* Terminal output backend */
219 	mdb_io_t *tio_in_io;		/* Terminal input backend */
220 	mdb_iob_t *tio_out;		/* I/o buffer for terminal output */
221 	mdb_iob_t *tio_in;		/* I/o buffer for terminal input */
222 	mdb_iob_t *tio_link;		/* I/o buffer to resize on WINCH */
223 	keycb_t tio_keymap[KEY_MAX];	/* Keymap (see comments atop file) */
224 	mdb_cmdbuf_t tio_cmdbuf;	/* Editable command-line buffer */
225 	struct termios tio_ptios;	/* Parent terminal settings */
226 	struct termios tio_ctios;	/* Child terminal settings */
227 	struct termios tio_rtios;	/* Settings for read loop */
228 	struct termios tio_dtios;	/* Settings for dcmd execution */
229 	sigjmp_buf tio_env;		/* Read loop setjmp(3c) environment */
230 	termio_info_t tio_info;		/* Terminal attribute strings */
231 	char *tio_attrs;		/* Attribute string buffer */
232 	size_t tio_attrslen;		/* Length in bytes of tio_attrs */
233 	const char *tio_prompt;		/* Prompt string for this read */
234 	size_t tio_promptlen;		/* Length of prompt string */
235 	size_t tio_rows;		/* Terminal height */
236 	size_t tio_cols;		/* Terminal width */
237 	size_t tio_x;			/* Cursor x coordinate */
238 	size_t tio_y;			/* Cursor y coordinate */
239 	size_t tio_max_x;		/* Previous maximum x coordinate */
240 	size_t tio_max_y;		/* Previous maximum y coordinate */
241 	int tio_intr;			/* Interrupt char */
242 	int tio_quit;			/* Quit char */
243 	int tio_erase;			/* Erase char */
244 	int tio_werase;			/* Word-erase char */
245 	int tio_kill;			/* Kill char */
246 	int tio_eof;			/* End-of-file char */
247 	int tio_susp;			/* Suspend char */
248 	uint_t tio_flags;		/* Miscellaneous flags */
249 	volatile mdb_bool_t tio_active;	/* Flag denoting read loop active */
250 	volatile mdb_bool_t tio_rti_on;	/* Flag denoting rtios in use */
251 	putp_t tio_putp;		/* termio_tput() subroutine */
252 	uint_t tio_baud;		/* Baud rate (chars per second) */
253 	uint_t tio_usecpc;		/* Usecs per char at given baud rate */
254 	pid_t tio_opgid;		/* Old process group id for terminal */
255 	uint_t tio_suspended;		/* termio_suspend_tty() nesting count */
256 } termio_data_t;
257 
258 static ssize_t termio_read(mdb_io_t *, void *, size_t);
259 static ssize_t termio_write(mdb_io_t *, const void *, size_t);
260 static off64_t termio_seek(mdb_io_t *, off64_t, int);
261 static int termio_ctl(mdb_io_t *, int, void *);
262 static void termio_close(mdb_io_t *);
263 static const char *termio_name(mdb_io_t *);
264 static void termio_link(mdb_io_t *, mdb_iob_t *);
265 static void termio_unlink(mdb_io_t *, mdb_iob_t *);
266 static int termio_setattr(mdb_io_t *, int, uint_t);
267 static void termio_suspend(mdb_io_t *);
268 static void termio_resume(mdb_io_t *);
269 
270 static void termio_suspend_tty(termio_data_t *, struct termios *);
271 static void termio_resume_tty(termio_data_t *, struct termios *);
272 
273 static void termio_putp(termio_data_t *, const char *, uint_t);
274 static void termio_puts(termio_data_t *, const char *, uint_t);
275 static void termio_tput(termio_data_t *, const char *, uint_t);
276 static void termio_addch(termio_data_t *, char, size_t);
277 static void termio_insch(termio_data_t *, char, size_t);
278 static void termio_mvcur(termio_data_t *);
279 static void termio_bspch(termio_data_t *);
280 static void termio_delch(termio_data_t *);
281 static void termio_clear(termio_data_t *);
282 static void termio_redraw(termio_data_t *);
283 static void termio_prompt(termio_data_t *);
284 
285 static const char *termio_tab(termio_data_t *, int);
286 static const char *termio_insert(termio_data_t *, int);
287 static const char *termio_accept(termio_data_t *, int);
288 static const char *termio_backspace(termio_data_t *, int);
289 static const char *termio_delchar(termio_data_t *, int);
290 static const char *termio_fwdchar(termio_data_t *, int);
291 static const char *termio_backchar(termio_data_t *, int);
292 static const char *termio_transpose(termio_data_t *, int);
293 static const char *termio_home(termio_data_t *, int);
294 static const char *termio_end(termio_data_t *, int);
295 static const char *termio_fwdword(termio_data_t *, int);
296 static const char *termio_backword(termio_data_t *, int);
297 static const char *termio_kill(termio_data_t *, int);
298 static const char *termio_killfwdword(termio_data_t *, int);
299 static const char *termio_killbackword(termio_data_t *, int);
300 static const char *termio_reset(termio_data_t *, int);
301 static const char *termio_widescreen(termio_data_t *, int);
302 static const char *termio_prevhist(termio_data_t *, int);
303 static const char *termio_nexthist(termio_data_t *, int);
304 static const char *termio_accel(termio_data_t *, int);
305 static const char *termio_findhist(termio_data_t *, int);
306 static const char *termio_refresh(termio_data_t *, int);
307 
308 static const char *termio_intr(termio_data_t *, int);
309 static const char *termio_quit(termio_data_t *, int);
310 static const char *termio_susp(termio_data_t *, int);
311 
312 static void termio_winch(int, siginfo_t *, ucontext_t *, void *);
313 static void termio_tstp(int, siginfo_t *, ucontext_t *, void *);
314 
315 extern const char *tigetstr(const char *);
316 extern int tigetflag(const char *);
317 extern int tigetnum(const char *);
318 
319 static const mdb_io_ops_t termio_ops = {
320 	termio_read,
321 	termio_write,
322 	termio_seek,
323 	termio_ctl,
324 	termio_close,
325 	termio_name,
326 	termio_link,
327 	termio_unlink,
328 	termio_setattr,
329 	termio_suspend,
330 	termio_resume
331 };
332 
333 static termio_info_t termio_info;
334 
335 static const termio_attr_t termio_attrs[] = {
336 	{ "cub1", TIO_ATTR_REQSTR, &termio_info.ti_cub1 },
337 	{ "cuf1", TIO_ATTR_REQSTR, &termio_info.ti_cuf1 },
338 	{ "cuu1", TIO_ATTR_REQSTR, &termio_info.ti_cuu1 },
339 	{ "cud1", TIO_ATTR_REQSTR, &termio_info.ti_cud1 },
340 	{ "pad", TIO_ATTR_STR, &termio_info.ti_pad },
341 	{ "el", TIO_ATTR_REQSTR, &termio_info.ti_el },
342 	{ "am", TIO_ATTR_BOOL, &termio_info.ti_am },
343 	{ "bw", TIO_ATTR_BOOL, &termio_info.ti_bw },
344 	{ "npc", TIO_ATTR_BOOL, &termio_info.ti_npc },
345 	{ "xenl", TIO_ATTR_BOOL, &termio_info.ti_xenl },
346 	{ "xon", TIO_ATTR_BOOL, &termio_info.ti_xon },
347 	{ "cols", TIO_ATTR_INT, &termio_info.ti_cols },
348 	{ "lines", TIO_ATTR_INT, &termio_info.ti_lines },
349 	{ "pb", TIO_ATTR_INT, &termio_info.ti_pb },
350 	{ "smso", TIO_ATTR_STR, &termio_info.ti_smso },
351 	{ "rmso", TIO_ATTR_STR, &termio_info.ti_rmso },
352 	{ "smul", TIO_ATTR_STR, &termio_info.ti_smul },
353 	{ "rmul", TIO_ATTR_STR, &termio_info.ti_rmul },
354 	{ "enacs", TIO_ATTR_STR, &termio_info.ti_enacs },
355 	{ "smacs", TIO_ATTR_STR, &termio_info.ti_smacs },
356 	{ "rmacs", TIO_ATTR_STR, &termio_info.ti_rmacs },
357 	{ "smcup", TIO_ATTR_STR, &termio_info.ti_smcup },
358 	{ "rmcup", TIO_ATTR_STR, &termio_info.ti_rmcup },
359 	{ "rev", TIO_ATTR_STR, &termio_info.ti_rev },
360 	{ "bold", TIO_ATTR_STR, &termio_info.ti_bold },
361 	{ "dim", TIO_ATTR_STR, &termio_info.ti_dim },
362 	{ "sgr0", TIO_ATTR_STR, &termio_info.ti_sgr0 },
363 	{ "smir", TIO_ATTR_STR, &termio_info.ti_smir },
364 	{ "rmir", TIO_ATTR_STR, &termio_info.ti_rmir },
365 	{ "ich1", TIO_ATTR_STR, &termio_info.ti_ich1 },
366 	{ "ip", TIO_ATTR_STR, &termio_info.ti_ip },
367 	{ "clear", TIO_ATTR_STR, &termio_info.ti_clear },
368 	{ "cnorm", TIO_ATTR_STR, &termio_info.ti_cnorm },
369 	{ "nel", TIO_ATTR_STR, &termio_info.ti_nel },
370 	{ "cr", TIO_ATTR_STR, &termio_info.ti_cr },
371 	{ "smam", TIO_ATTR_STR, &termio_info.ti_smam },
372 	{ NULL, 0, NULL }
373 };
374 
375 /*
376  * One-key accelerators.  Some commands are used so frequently as to need
377  * single-key equivalents.  termio_accelkeys contains a list of the accelerator
378  * keys, with termio_accel listing the accelerated commands.  The array is
379  * indexed by the offset of the accelerator in the macro string, and as such
380  * *must* stay in the same order.
381  */
382 static const char *const termio_accelkeys = "[]";
383 
384 static const char *const termio_accelstrings[] = {
385 	"::step over",	/* [ */
386 	"::step"	/* ] */
387 };
388 
389 static const char *
390 termio_accel_lookup(int c)
391 {
392 	const char *acc;
393 
394 	if ((acc = strchr(termio_accelkeys, c)) == NULL)
395 		return (NULL);
396 
397 	return (termio_accelstrings[(int)(acc - termio_accelkeys)]);
398 }
399 
400 static ssize_t
401 termio_read(mdb_io_t *io, void *buf, size_t nbytes)
402 {
403 	termio_data_t *td = io->io_data;
404 
405 	mdb_bool_t esc = FALSE, pad = FALSE;
406 	ssize_t rlen = 0;
407 	int c, fkey = 0;
408 
409 	const char *s;
410 	size_t len;
411 
412 	if (io->io_next != NULL)
413 		return (IOP_READ(io->io_next, buf, nbytes));
414 
415 	td->tio_rti_on = TRUE;
416 	if (termio_ctl(td->tio_io, TCSETSW, &td->tio_rtios) == -1)
417 		warn("failed to set terminal attributes");
418 
419 	if (nbytes == 1) {
420 		if ((c = mdb_iob_getc(td->tio_in)) == EOF)
421 			goto out;
422 
423 		*((uchar_t *)buf) = (uchar_t)c;
424 
425 		rlen = 1;
426 		goto out;
427 	}
428 
429 	if (td->tio_flags & TIO_TAB)
430 		termio_redraw(td);
431 	else
432 		termio_prompt(td);
433 
434 	/*
435 	 * We need to redraw the entire command-line and restart our read loop
436 	 * in the event of a SIGWINCH or resume following SIGTSTP (SIGCONT).
437 	 */
438 	if (sigsetjmp(td->tio_env, 1) != 0) {
439 		td->tio_active = FALSE;
440 		td->tio_x = td->tio_y = 0;
441 
442 		len = td->tio_cmdbuf.cmd_buflen + td->tio_promptlen;
443 		td->tio_max_x = len % td->tio_cols;
444 		td->tio_max_y = len / td->tio_cols;
445 
446 		esc = pad = FALSE;
447 
448 		termio_tput(td, td->tio_info.ti_cr.at_str, 1);
449 		mdb_iob_flush(td->tio_out);
450 		termio_redraw(td);
451 	}
452 
453 	/*
454 	 * Since we're about to start the read loop, we know our linked iob
455 	 * is quiescent. We can now safely resize it to the latest term size.
456 	 */
457 	if (td->tio_link != NULL)
458 		mdb_iob_resize(td->tio_link, td->tio_rows, td->tio_cols);
459 
460 	td->tio_active = TRUE;
461 
462 	/*
463 	 * We may have had some error while in tab completion mode which sent us
464 	 * longjmping all over the place. If that's the case, come back here and
465 	 * make sure the flag is off.
466 	 */
467 	td->tio_flags &= ~TIO_TAB;
468 
469 	do {
470 char_loop:
471 		if ((c = mdb_iob_getc(td->tio_in)) == EOF) {
472 			td->tio_active = FALSE;
473 			goto out;
474 		}
475 
476 		if (c == KEY_ESC && esc == FALSE) {
477 			esc = TRUE;
478 			goto char_loop;
479 		}
480 
481 		if (esc) {
482 			esc = FALSE;
483 
484 			if (c == '[') {
485 				pad++;
486 				goto char_loop;
487 			}
488 
489 			c = META(c);
490 		}
491 
492 		if (pad) {
493 			pad = FALSE;
494 
495 			if ((fkey = FKEY(c)) != 0) {
496 				/*
497 				 * Some terminals send a multibyte control
498 				 * sequence for particular function keys.
499 				 * These sequences are of the form:
500 				 *
501 				 *	ESC [ n ~
502 				 *
503 				 * where "n" is a numeric character from
504 				 * '0' to '9'.
505 				 */
506 				goto char_loop;
507 			}
508 
509 			if ((c = KPAD(c)) == 0) {
510 				/*
511 				 * This was not a valid keypad control
512 				 * sequence.
513 				 */
514 				goto char_loop;
515 			}
516 		}
517 
518 		if (fkey != 0) {
519 			if (c == '~') {
520 				/*
521 				 * This is a valid special function key
522 				 * sequence.  Use the value we stashed
523 				 * earlier.
524 				 */
525 				c = fkey;
526 			}
527 
528 			fkey = 0;
529 		}
530 
531 		len = td->tio_cmdbuf.cmd_buflen + td->tio_promptlen;
532 
533 		td->tio_max_x = len % td->tio_cols;
534 		td->tio_max_y = len / td->tio_cols;
535 
536 	} while ((s = (*td->tio_keymap[c])(td, c)) == NULL);
537 
538 	td->tio_active = FALSE;
539 	mdb_iob_nl(td->tio_out);
540 
541 	if ((rlen = strlen(s)) >= nbytes - 1)
542 		rlen = nbytes - 1;
543 
544 	(void) strncpy(buf, s, rlen);
545 	((char *)buf)[rlen++] = '\n';
546 
547 out:
548 	td->tio_rti_on = FALSE;
549 	if (termio_ctl(td->tio_io, TCSETSW, &td->tio_dtios) == -1)
550 		warn("failed to restore terminal attributes");
551 
552 	return (rlen);
553 }
554 
555 static ssize_t
556 termio_write(mdb_io_t *io, const void *buf, size_t nbytes)
557 {
558 	termio_data_t *td = io->io_data;
559 
560 	if (io->io_next != NULL)
561 		return (IOP_WRITE(io->io_next, buf, nbytes));
562 
563 	return (IOP_WRITE(td->tio_out_io, buf, nbytes));
564 }
565 
566 /*ARGSUSED*/
567 static off64_t
568 termio_seek(mdb_io_t *io, off64_t offset, int whence)
569 {
570 	return (set_errno(ENOTSUP));
571 }
572 
573 static int
574 termio_ctl(mdb_io_t *io, int req, void *arg)
575 {
576 	termio_data_t *td = io->io_data;
577 
578 	if (io->io_next != NULL)
579 		return (IOP_CTL(io->io_next, req, arg));
580 
581 	if (req == MDB_IOC_CTTY) {
582 		bcopy(&td->tio_ptios, &td->tio_ctios, sizeof (struct termios));
583 		return (0);
584 	}
585 
586 	return (IOP_CTL(td->tio_in_io, req, arg));
587 }
588 
589 static void
590 termio_close(mdb_io_t *io)
591 {
592 	termio_data_t *td = io->io_data;
593 
594 	(void) mdb_signal_sethandler(SIGWINCH, SIG_DFL, NULL);
595 	(void) mdb_signal_sethandler(SIGTSTP, SIG_DFL, NULL);
596 
597 	termio_suspend_tty(td, &td->tio_ptios);
598 
599 	if (td->tio_attrs)
600 		mdb_free(td->tio_attrs, td->tio_attrslen);
601 
602 	mdb_cmdbuf_destroy(&td->tio_cmdbuf);
603 
604 	mdb_iob_destroy(td->tio_out);
605 	mdb_iob_destroy(td->tio_in);
606 
607 	mdb_free(td, sizeof (termio_data_t));
608 }
609 
610 static const char *
611 termio_name(mdb_io_t *io)
612 {
613 	termio_data_t *td = io->io_data;
614 
615 	if (io->io_next != NULL)
616 		return (IOP_NAME(io->io_next));
617 
618 	return (IOP_NAME(td->tio_in_io));
619 }
620 
621 static void
622 termio_link(mdb_io_t *io, mdb_iob_t *iob)
623 {
624 	termio_data_t *td = io->io_data;
625 
626 	if (io->io_next == NULL) {
627 		mdb_iob_resize(iob, td->tio_rows, td->tio_cols);
628 		td->tio_link = iob;
629 	} else
630 		IOP_LINK(io->io_next, iob);
631 }
632 
633 static void
634 termio_unlink(mdb_io_t *io, mdb_iob_t *iob)
635 {
636 	termio_data_t *td = io->io_data;
637 
638 	if (io->io_next == NULL) {
639 		if (td->tio_link == iob)
640 			td->tio_link = NULL;
641 	} else
642 		IOP_UNLINK(io->io_next, iob);
643 }
644 
645 static int
646 termio_setattr(mdb_io_t *io, int req, uint_t attrs)
647 {
648 	termio_data_t *td = io->io_data;
649 
650 	if (io->io_next != NULL)
651 		return (IOP_SETATTR(io->io_next, req, attrs));
652 
653 	if ((req != ATT_ON && req != ATT_OFF) || (attrs & ~ATT_ALL) != 0)
654 		return (set_errno(EINVAL));
655 
656 	if (req == ATT_ON) {
657 		if (attrs & ATT_STANDOUT)
658 			termio_tput(td, td->tio_info.ti_smso.at_str, 1);
659 		if (attrs & ATT_UNDERLINE)
660 			termio_tput(td, td->tio_info.ti_smul.at_str, 1);
661 		if (attrs & ATT_REVERSE)
662 			termio_tput(td, td->tio_info.ti_rev.at_str, 1);
663 		if (attrs & ATT_BOLD)
664 			termio_tput(td, td->tio_info.ti_bold.at_str, 1);
665 		if (attrs & ATT_DIM)
666 			termio_tput(td, td->tio_info.ti_dim.at_str, 1);
667 		if (attrs & ATT_ALTCHARSET)
668 			termio_tput(td, td->tio_info.ti_smacs.at_str, 1);
669 	} else {
670 		if (attrs & ATT_STANDOUT)
671 			termio_tput(td, td->tio_info.ti_rmso.at_str, 1);
672 		if (attrs & ATT_UNDERLINE)
673 			termio_tput(td, td->tio_info.ti_rmul.at_str, 1);
674 		if (attrs & ATT_ALTCHARSET)
675 			termio_tput(td, td->tio_info.ti_rmacs.at_str, 1);
676 		if (attrs & (ATT_REVERSE | ATT_BOLD | ATT_DIM))
677 			termio_tput(td, td->tio_info.ti_sgr0.at_str, 1);
678 	}
679 
680 	mdb_iob_flush(td->tio_out);
681 	return (0);
682 }
683 
684 /*
685  * Issue a warning message if the given warning flag is clear.  Then set the
686  * flag bit so that we do not issue multiple instances of the same warning.
687  */
688 static void
689 termio_warn(termio_data_t *td, uint_t flag, const char *format, ...)
690 {
691 	if (!(td->tio_flags & flag)) {
692 		va_list alist;
693 
694 		va_start(alist, format);
695 		vwarn(format, alist);
696 		va_end(alist);
697 
698 		td->tio_flags |= flag;
699 	}
700 }
701 
702 /*
703  * Restore the terminal to its previous state before relinquishing control of
704  * it to the shell (on a SIGTSTP) or the victim process (on a continue).  If
705  * we need to change the foreground process group, we must temporarily ignore
706  * SIGTTOU because TIOCSPGRP could trigger it.
707  */
708 static void
709 termio_suspend_tty(termio_data_t *td, struct termios *iosp)
710 {
711 	if (td->tio_suspended++ != 0)
712 		return; /* already suspended; do not restore state */
713 
714 	if (td->tio_flags & TIO_XTERM)
715 		termio_tput(td, TI_DECRES(TI_COLENAB), 1);
716 
717 	if (td->tio_flags & TIO_USECUP)
718 		termio_tput(td, td->tio_info.ti_rmcup.at_str, 1);
719 
720 	termio_tput(td, td->tio_info.ti_sgr0.at_str, 1);
721 	mdb_iob_flush(td->tio_out);
722 
723 	if (termio_ctl(td->tio_io, TCSETSW, iosp) == -1)
724 		warn("failed to restore terminal attributes");
725 
726 	if (td->tio_opgid > 0 && td->tio_opgid != mdb.m_pgid) {
727 		mdb_dprintf(MDB_DBG_CMDBUF, "fg pgid=%d\n", (int)td->tio_opgid);
728 		(void) mdb_signal_sethandler(SIGTTOU, SIG_IGN, NULL);
729 		(void) termio_ctl(td->tio_io, TIOCSPGRP, &td->tio_opgid);
730 		(void) mdb_signal_sethandler(SIGTTOU, SIG_DFL, NULL);
731 	}
732 }
733 
734 /*
735  * Resume the debugger's terminal state.  We first save the existing terminal
736  * state so we can restore it later, and then install our own state.  We
737  * derive our state dynamically from the existing terminal state so that we
738  * always reflect the latest modifications made by the user with stty(1).
739  */
740 static void
741 termio_resume_tty(termio_data_t *td, struct termios *iosp)
742 {
743 	/*
744 	 * We use this table of bauds to convert the baud constant returned by
745 	 * the terminal code to a baud rate in characters per second.  The
746 	 * values are in the order of the B* speed defines in <sys/termios.h>.
747 	 * We then compute tio_usecpc (microseconds-per-char) in order to
748 	 * determine how many pad characters need to be issued at the current
749 	 * terminal speed to delay for a given number of microseconds.  For
750 	 * example, at 300 baud (B300 = 7), we look up baud[7] = 300, and then
751 	 * compute usecpc as MICROSEC / 300 = 3333 microseconds per character.
752 	 */
753 	static const uint_t baud[] = {
754 		0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
755 		1800, 2400, 4800, 9600, 19200, 38400, 57600,
756 		76800, 115200, 153600, 230400, 307200, 460800, 921600
757 	};
758 
759 	struct termios *ntios;
760 	struct winsize winsz;
761 	uint_t speed;
762 
763 	if (td->tio_suspended == 0)
764 		fail("termio_resume called without matching termio_suspend\n");
765 
766 	if (--td->tio_suspended != 0)
767 		return; /* nested suspends; do not resume yet */
768 
769 	td->tio_opgid = -1; /* set to invalid pgid in case TIOCPGRP fails */
770 	(void) termio_ctl(td->tio_io, TIOCGPGRP, &td->tio_opgid);
771 
772 	/*
773 	 * If the foreground process group does not include the debugger, reset
774 	 * the foreground process group so we are in control of the terminal.
775 	 * We temporarily ignore TTOU because TIOCSPGRP could trigger it.
776 	 */
777 	if (td->tio_opgid != mdb.m_pgid) {
778 		(void) mdb_signal_sethandler(SIGTTOU, SIG_IGN, NULL);
779 		(void) termio_ctl(td->tio_io, TIOCSPGRP, &mdb.m_pgid);
780 		(void) mdb_signal_sethandler(SIGTTOU, SIG_DFL, NULL);
781 		mdb_dprintf(MDB_DBG_CMDBUF, "fg pgid=%d\n", (int)mdb.m_pgid);
782 	}
783 
784 	/*
785 	 * Read the current set of terminal attributes, and save them in iosp
786 	 * so we can restore them later.  Then derive rtios, dtios, and winsz.
787 	 */
788 	if (termio_ctl(td->tio_io, TCGETS, iosp) < 0)
789 		warn("failed to get terminal attributes");
790 
791 	if (termio_ctl(td->tio_io, TIOCGWINSZ, &winsz) == 0) {
792 		if (winsz.ws_row != 0)
793 			td->tio_rows = (size_t)winsz.ws_row;
794 		if (winsz.ws_col != 0)
795 			td->tio_cols = (size_t)winsz.ws_col;
796 	}
797 
798 	mdb_iob_resize(td->tio_out, td->tio_rows, td->tio_cols);
799 
800 	td->tio_intr = td->tio_ptios.c_cc[VINTR];
801 	td->tio_quit = td->tio_ptios.c_cc[VQUIT];
802 	td->tio_erase = td->tio_ptios.c_cc[VERASE];
803 	td->tio_werase = td->tio_ptios.c_cc[VWERASE];
804 	td->tio_kill = td->tio_ptios.c_cc[VKILL];
805 	td->tio_eof = td->tio_ptios.c_cc[VEOF];
806 	td->tio_susp = td->tio_ptios.c_cc[VSUSP];
807 
808 	bcopy(&td->tio_ptios, &td->tio_rtios, sizeof (struct termios));
809 	td->tio_rtios.c_iflag &= ~(ISTRIP | INPCK | ICRNL | INLCR | IUCLC);
810 	td->tio_rtios.c_oflag &= ~(OCRNL | ONLRET);
811 	td->tio_rtios.c_oflag |= ONLCR;
812 	td->tio_rtios.c_lflag &= ~(ISIG | ICANON | ECHO);
813 	td->tio_rtios.c_cflag |= CS8;
814 	td->tio_rtios.c_cc[VTIME] = 0;
815 	td->tio_rtios.c_cc[VMIN] = 1;
816 
817 	bcopy(&td->tio_ptios, &td->tio_dtios, sizeof (struct termios));
818 	td->tio_dtios.c_oflag &= ~(OCRNL | ONLRET);
819 	td->tio_dtios.c_oflag |= ONLCR;
820 	td->tio_dtios.c_lflag |= ISIG | ICANON | ECHO;
821 
822 	/*
823 	 * Select the appropriate modified settings to restore based on our
824 	 * current state, and then install them.
825 	 */
826 	if (td->tio_rti_on)
827 		ntios = &td->tio_rtios;
828 	else
829 		ntios = &td->tio_dtios;
830 
831 	if (termio_ctl(td->tio_io, TCSETSW, ntios) < 0)
832 		warn("failed to reset terminal attributes");
833 
834 	/*
835 	 * Compute the terminal speed as described in termio(7I), and then
836 	 * look up the corresponding microseconds-per-char in our table.
837 	 */
838 	if (ntios->c_cflag & CBAUDEXT)
839 		speed = (ntios->c_cflag & CBAUD) + CBAUD + 1;
840 	else
841 		speed = (ntios->c_cflag & CBAUD);
842 
843 	if (speed >= sizeof (baud) / sizeof (baud[0])) {
844 		termio_warn(td, TIO_TTYWARN, "invalid speed %u -- assuming "
845 		    "9600 baud\n", speed);
846 		speed = B9600;
847 	}
848 
849 	td->tio_baud = baud[speed];
850 	td->tio_usecpc = MICROSEC / td->tio_baud;
851 
852 	mdb_dprintf(MDB_DBG_CMDBUF, "speed = %u baud (%u usec / char), "
853 	    "putp = %s\n", td->tio_baud, td->tio_usecpc,
854 	    td->tio_putp == &termio_puts ? "fast" : "slow");
855 
856 	/*
857 	 * Send the necessary terminal initialization sequences to enable
858 	 * enable cursor positioning.  Clear the screen afterward if possible.
859 	 */
860 	if (td->tio_flags & TIO_USECUP) {
861 		termio_tput(td, td->tio_info.ti_smcup.at_str, 1);
862 		if (td->tio_info.ti_clear.at_str) {
863 			termio_tput(td, td->tio_info.ti_clear.at_str, 1);
864 			td->tio_x = td->tio_y = 0;
865 		}
866 	}
867 
868 	/*
869 	 * If the terminal is xterm-compatible, enable column mode switching.
870 	 * Save the previous value in the terminal so we can restore it.
871 	 */
872 	if (td->tio_flags & TIO_XTERM) {
873 		termio_tput(td, TI_DECSAV(TI_COLENAB), 1);
874 		termio_tput(td, TI_DECSET(TI_COLENAB), 1);
875 	}
876 
877 	termio_tput(td, td->tio_info.ti_cnorm.at_str, 1); /* cursor visible */
878 	termio_tput(td, td->tio_info.ti_enacs.at_str, 1); /* alt char set */
879 
880 	/*
881 	 * If the terminal is automargin-capable and we have an initialization
882 	 * sequence to enable automargins, we must send it now.  Note that
883 	 * we don't have a way of querying this mode and restoring it; if
884 	 * we are fighting with (say) a target that is depending on automargin
885 	 * being turned off, it will lose.
886 	 */
887 	if ((td->tio_flags & TIO_AUTOWRAP) &&
888 	    td->tio_info.ti_smam.at_str != NULL) {
889 		termio_tput(td, td->tio_info.ti_smam.at_str, 1);
890 	}
891 
892 	mdb_iob_flush(td->tio_out);
893 }
894 
895 static void
896 termio_suspend(mdb_io_t *io)
897 {
898 	termio_data_t *td = io->io_data;
899 	termio_suspend_tty(td, &td->tio_ctios);
900 }
901 
902 static void
903 termio_resume(mdb_io_t *io)
904 {
905 	termio_data_t *td = io->io_data;
906 	termio_resume_tty(td, &td->tio_ctios);
907 }
908 
909 /*
910  * Delay for the specified number of microseconds by sending the pad character
911  * to the terminal.  We round up by half a frame and then divide by the usecs
912  * per character to determine the number of pad characters to send.
913  */
914 static void
915 termio_delay(termio_data_t *td, uint_t usec)
916 {
917 	char pad = td->tio_info.ti_pad.at_str[0];
918 	uint_t usecpc = td->tio_usecpc;
919 
920 	for (usec = (usec + usecpc / 2) / usecpc; usec != 0; usec--) {
921 		mdb_iob_putc(td->tio_out, pad);
922 		mdb_iob_flush(td->tio_out);
923 	}
924 }
925 
926 /*
927  * Parse the terminfo(4) padding sequence "$<...>" and delay for the specified
928  * amount of time by sending pad characters to the terminal.
929  */
930 static const char *
931 termio_pad(termio_data_t *td, const char *s, uint_t lines)
932 {
933 	int xon = td->tio_info.ti_xon.at_val;
934 	int pb = td->tio_info.ti_pb.at_val;
935 
936 	const char *p = s;
937 	uint_t usec = 0;
938 
939 	/*
940 	 * The initial string is a number of milliseconds, followed by an
941 	 * optional decimal point and number of tenths of milliseconds.
942 	 * We convert this to microseconds for greater accuracy.  Only a single
943 	 * digit is permitted after the decimal point; we ignore any others.
944 	 */
945 	while (*p >= '0' && *p <= '9')
946 		usec = usec * 10 + *p++ - '0';
947 
948 	usec *= 1000; /* convert msecs to usecs */
949 
950 	if (*p == '.') {
951 		if (p[1] >= '0' && p[1] <= '9')
952 			usec += (p[1] - '0') * 100;
953 		for (p++; *p >= '0' && *p <= '9'; p++)
954 			continue;
955 	}
956 
957 	/*
958 	 * Following the time delay specifier,
959 	 *
960 	 * 1. An optional "/" indicates that the delay should be done
961 	 *    regardless of the value of the terminal's xon property,
962 	 * 2. An optional "*" indicates that the delay is proportional to the
963 	 *    count of affected lines, and
964 	 * 3. A mandatory ">" terminates the sequence.
965 	 *
966 	 * If we encounter any other characters, we assume that we found "$<"
967 	 * accidentally embedded in another sequence, so we just output "$".
968 	 */
969 	for (;;) {
970 		switch (*p++) {
971 		case '/':
972 			xon = FALSE;
973 			continue;
974 		case '*':
975 			usec *= lines;
976 			continue;
977 		case '>':
978 			if (xon == FALSE && usec != 0 && td->tio_baud >= pb)
979 				termio_delay(td, usec);
980 			return (p);
981 		default:
982 			mdb_iob_putc(td->tio_out, *s);
983 			return (s + 1);
984 		}
985 	}
986 }
987 
988 /*
989  * termio_tput() subroutine for terminals that require padding.  We look ahead
990  * for "$<>" sequences, and call termio_pad() to process them; all other chars
991  * are output directly to the underlying device and then flushed at the end.
992  */
993 static void
994 termio_putp(termio_data_t *td, const char *s, uint_t lines)
995 {
996 	while (s[0] != '\0') {
997 		if (s[0] == '$' && s[1] == '<')
998 			s = termio_pad(td, s + 2, lines);
999 		else
1000 			mdb_iob_putc(td->tio_out, *s++);
1001 	}
1002 
1003 	mdb_iob_flush(td->tio_out);
1004 }
1005 
1006 /*
1007  * termio_tput() subroutine for terminals that do not require padding.  We
1008  * simply output the string to the underlying i/o buffer; we let the caller
1009  * take care of flushing so that multiple sequences can be concatenated.
1010  */
1011 /*ARGSUSED*/
1012 static void
1013 termio_puts(termio_data_t *td, const char *s, uint_t lines)
1014 {
1015 	mdb_iob_puts(td->tio_out, s);
1016 }
1017 
1018 /*
1019  * Print a padded escape sequence string to the terminal.  The caller specifies
1020  * the string 's' and a count of the affected lines.  If the string contains an
1021  * embedded delay sequence delimited by "$<>" (see terminfo(4)), appropriate
1022  * padding will be included in the output.  We determine whether or not padding
1023  * is required during initialization, and set tio_putp to the proper subroutine.
1024  */
1025 static void
1026 termio_tput(termio_data_t *td, const char *s, uint_t lines)
1027 {
1028 	if (s != NULL)
1029 		td->tio_putp(td, s, lines);
1030 }
1031 
1032 static void
1033 termio_addch(termio_data_t *td, char c, size_t width)
1034 {
1035 	if (width == 1) {
1036 		mdb_iob_putc(td->tio_out, c);
1037 		td->tio_x++;
1038 
1039 		if (td->tio_x >= td->tio_cols) {
1040 			if (!(td->tio_flags & TIO_AUTOWRAP))
1041 				termio_tput(td, td->tio_info.ti_nel.at_str, 1);
1042 			td->tio_x = 0;
1043 			td->tio_y++;
1044 		}
1045 
1046 		mdb_iob_flush(td->tio_out);
1047 	} else
1048 		termio_redraw(td);
1049 }
1050 
1051 static void
1052 termio_insch(termio_data_t *td, char c, size_t width)
1053 {
1054 	if (width == 1 && (td->tio_flags & TIO_INSERT) &&
1055 	    td->tio_y == td->tio_max_y) {
1056 
1057 		termio_tput(td, td->tio_info.ti_smir.at_str, 1);
1058 		termio_tput(td, td->tio_info.ti_ich1.at_str, 1);
1059 
1060 		mdb_iob_putc(td->tio_out, c);
1061 		td->tio_x++;
1062 
1063 		termio_tput(td, td->tio_info.ti_ip.at_str, 1);
1064 		termio_tput(td, td->tio_info.ti_rmir.at_str, 1);
1065 
1066 		if (td->tio_x >= td->tio_cols) {
1067 			if (!(td->tio_flags & TIO_AUTOWRAP))
1068 				termio_tput(td, td->tio_info.ti_nel.at_str, 1);
1069 			td->tio_x = 0;
1070 			td->tio_y++;
1071 		}
1072 
1073 		mdb_iob_flush(td->tio_out);
1074 	} else
1075 		termio_redraw(td);
1076 }
1077 
1078 static void
1079 termio_mvcur(termio_data_t *td)
1080 {
1081 	size_t tipos = td->tio_cmdbuf.cmd_bufidx + td->tio_promptlen;
1082 	size_t dst_x = tipos % td->tio_cols;
1083 	size_t dst_y = tipos / td->tio_cols;
1084 
1085 	const char *str;
1086 	size_t cnt, i;
1087 
1088 	if (td->tio_y != dst_y) {
1089 		if (td->tio_y < dst_y) {
1090 			str = td->tio_info.ti_cud1.at_str;
1091 			cnt = dst_y - td->tio_y;
1092 			td->tio_x = 0; /* Note: cud1 moves cursor to column 0 */
1093 		} else {
1094 			str = td->tio_info.ti_cuu1.at_str;
1095 			cnt = td->tio_y - dst_y;
1096 		}
1097 
1098 		for (i = 0; i < cnt; i++)
1099 			termio_tput(td, str, 1);
1100 
1101 		mdb_iob_flush(td->tio_out);
1102 		td->tio_y = dst_y;
1103 	}
1104 
1105 	if (td->tio_x != dst_x) {
1106 		if (td->tio_x < dst_x) {
1107 			str = td->tio_info.ti_cuf1.at_str;
1108 			cnt = dst_x - td->tio_x;
1109 		} else {
1110 			str = td->tio_info.ti_cub1.at_str;
1111 			cnt = td->tio_x - dst_x;
1112 		}
1113 
1114 		for (i = 0; i < cnt; i++)
1115 			termio_tput(td, str, 1);
1116 
1117 		mdb_iob_flush(td->tio_out);
1118 		td->tio_x = dst_x;
1119 	}
1120 }
1121 
1122 static void
1123 termio_backleft(termio_data_t *td)
1124 {
1125 	size_t i;
1126 
1127 	if (td->tio_flags & TIO_BACKLEFT)
1128 		termio_tput(td, td->tio_info.ti_cub1.at_str, 1);
1129 	else {
1130 		termio_tput(td, td->tio_info.ti_cuu1.at_str, 1);
1131 		for (i = 0; i < td->tio_cols - 1; i++)
1132 			termio_tput(td, td->tio_info.ti_cuf1.at_str, 1);
1133 	}
1134 }
1135 
1136 static void
1137 termio_bspch(termio_data_t *td)
1138 {
1139 	if (td->tio_x == 0) {
1140 		/*
1141 		 * If TIO_LAZYWRAP is set, we are regrettably in one of two
1142 		 * states that we cannot differentiate between:  the cursor is
1143 		 * either on the right margin of the previous line (having not
1144 		 * advanced due to the lazy wrap) _or_ the cursor is on the
1145 		 * left margin of the current line because a wrap has already
1146 		 * been induced due to prior activity.  Because these cases are
1147 		 * impossible to differentiate, we force the latter case by
1148 		 * emitting a space and then moving the cursor back.  If the
1149 		 * wrap had not been induced, emitting this space will induce
1150 		 * it -- and will assure that our termio_backleft() is the
1151 		 * correct behavior with respect to the cursor.
1152 		 */
1153 		if (td->tio_flags & TIO_LAZYWRAP) {
1154 			mdb_iob_putc(td->tio_out, ' ');
1155 			termio_tput(td, td->tio_info.ti_cub1.at_str, 1);
1156 		}
1157 
1158 		termio_backleft(td);
1159 		td->tio_x = td->tio_cols - 1;
1160 		td->tio_y--;
1161 	} else {
1162 		termio_tput(td, td->tio_info.ti_cub1.at_str, 1);
1163 		td->tio_x--;
1164 	}
1165 
1166 	termio_delch(td);
1167 }
1168 
1169 static void
1170 termio_delch(termio_data_t *td)
1171 {
1172 	mdb_iob_putc(td->tio_out, ' ');
1173 
1174 	if (td->tio_x == td->tio_cols - 1 && (td->tio_flags & TIO_AUTOWRAP)) {
1175 		/*
1176 		 * We just overwrote a space in the final column, so we know
1177 		 * that we have autowrapped and we therefore definitely don't
1178 		 * want to issue the ti_cub1.  If we have TIO_LAZYWRAP, we know
1179 		 * that the cursor remains on the final column, and we want to
1180 		 * do nothing -- but if TIO_LAZYWRAP isn't set, we have wrapped
1181 		 * and we need to move the cursor back up and all the way to
1182 		 * the right via termio_backleft().
1183 		 */
1184 		if (!(td->tio_flags & TIO_LAZYWRAP))
1185 			termio_backleft(td);
1186 	} else {
1187 		termio_tput(td, td->tio_info.ti_cub1.at_str, 1);
1188 	}
1189 
1190 	mdb_iob_flush(td->tio_out);
1191 }
1192 
1193 static void
1194 termio_clear(termio_data_t *td)
1195 {
1196 	while (td->tio_x-- != 0)
1197 		termio_tput(td, td->tio_info.ti_cub1.at_str, 1);
1198 
1199 	while (td->tio_y < td->tio_max_y) {
1200 		termio_tput(td, td->tio_info.ti_cud1.at_str, 1);
1201 		td->tio_y++;
1202 	}
1203 
1204 	while (td->tio_y-- != 0) {
1205 		termio_tput(td, td->tio_info.ti_el.at_str, 1);
1206 		termio_tput(td, td->tio_info.ti_cuu1.at_str, 1);
1207 	}
1208 
1209 	termio_tput(td, td->tio_info.ti_el.at_str, 1);
1210 	mdb_iob_flush(td->tio_out);
1211 
1212 	termio_prompt(td);
1213 }
1214 
1215 static void
1216 termio_redraw(termio_data_t *td)
1217 {
1218 	const char *buf = td->tio_cmdbuf.cmd_buf;
1219 	size_t len = td->tio_cmdbuf.cmd_buflen;
1220 	size_t pos, n;
1221 
1222 	termio_clear(td);
1223 
1224 	if (len == 0)
1225 		return; /* if the buffer is empty, we're done */
1226 
1227 	if (td->tio_flags & TIO_AUTOWRAP)
1228 		mdb_iob_nputs(td->tio_out, buf, len);
1229 	else {
1230 		for (pos = td->tio_promptlen; len != 0; pos = 0) {
1231 			n = MIN(td->tio_cols - pos, len);
1232 			mdb_iob_nputs(td->tio_out, buf, n);
1233 			buf += n;
1234 			len -= n;
1235 
1236 			if (pos + n == td->tio_cols)
1237 				termio_tput(td, td->tio_info.ti_nel.at_str, 1);
1238 		}
1239 	}
1240 
1241 	pos = td->tio_promptlen + td->tio_cmdbuf.cmd_buflen;
1242 	td->tio_x = pos % td->tio_cols;
1243 	td->tio_y = pos / td->tio_cols;
1244 
1245 	mdb_iob_flush(td->tio_out);
1246 	termio_mvcur(td);
1247 }
1248 
1249 static void
1250 termio_prompt(termio_data_t *td)
1251 {
1252 	mdb_callb_fire(MDB_CALLB_PROMPT);
1253 
1254 	/*
1255 	 * Findhist (^R) overrides the displayed prompt.  We should only update
1256 	 * the main prompt (which may have been changed by the callback) if
1257 	 * findhist isn't active.
1258 	 */
1259 	if (!(td->tio_flags & TIO_FINDHIST)) {
1260 		td->tio_prompt = mdb.m_prompt;
1261 		td->tio_promptlen = mdb.m_promptlen;
1262 	}
1263 
1264 	mdb_iob_puts(td->tio_out, td->tio_prompt);
1265 	mdb_iob_flush(td->tio_out);
1266 
1267 	td->tio_x = td->tio_promptlen;
1268 	td->tio_y = 0;
1269 }
1270 
1271 /*
1272  * For debugging purposes, iterate over the table of attributes and output them
1273  * in human readable form for verification.
1274  */
1275 static void
1276 termio_dump(termio_data_t *td, const termio_attr_t *ta)
1277 {
1278 	char *str;
1279 
1280 	for (; ta->ta_name != NULL; ta++) {
1281 		switch (ta->ta_type) {
1282 		case TIO_ATTR_REQSTR:
1283 		case TIO_ATTR_STR:
1284 			if (ta->ta_valp->at_str != NULL) {
1285 				str = strchr2esc(ta->ta_valp->at_str,
1286 				    strlen(ta->ta_valp->at_str));
1287 				mdb_dprintf(MDB_DBG_CMDBUF, "%s = \"%s\"\n",
1288 				    ta->ta_name, str);
1289 				strfree(str);
1290 			} else {
1291 				mdb_dprintf(MDB_DBG_CMDBUF, "%s = <NULL>\n",
1292 				    ta->ta_name);
1293 			}
1294 			break;
1295 		case TIO_ATTR_INT:
1296 			mdb_dprintf(MDB_DBG_CMDBUF, "%s = %d\n",
1297 			    ta->ta_name, ta->ta_valp->at_val);
1298 			break;
1299 		case TIO_ATTR_BOOL:
1300 			mdb_dprintf(MDB_DBG_CMDBUF, "%s = %s\n", ta->ta_name,
1301 			    ta->ta_valp->at_val ? "TRUE" : "FALSE");
1302 			break;
1303 		}
1304 	}
1305 
1306 	mdb_dprintf(MDB_DBG_CMDBUF, "tio_flags = <%#b>\n",
1307 	    td->tio_flags, tio_flag_masks);
1308 }
1309 
1310 static int
1311 termio_setup_attrs(termio_data_t *td, const char *name)
1312 {
1313 	const termio_attr_t *ta;
1314 	const char *str;
1315 	size_t nbytes;
1316 	char *bufp;
1317 
1318 	int need_padding = 0;
1319 	int i;
1320 
1321 	/*
1322 	 * Load terminal attributes:
1323 	 */
1324 	for (nbytes = 0, ta = &termio_attrs[0]; ta->ta_name != NULL; ta++) {
1325 		switch (ta->ta_type) {
1326 		case TIO_ATTR_REQSTR:
1327 		case TIO_ATTR_STR:
1328 			str = tigetstr(ta->ta_name);
1329 
1330 			if (str == (const char *)-1) {
1331 				termio_warn(td, TIO_CAPWARN,
1332 				    "terminal capability '%s' is not of type "
1333 				    "string as expected\n", ta->ta_name);
1334 				return (0);
1335 			}
1336 
1337 			if (str != NULL)
1338 				nbytes += strlen(str) + 1;
1339 			else if (ta->ta_type == TIO_ATTR_REQSTR) {
1340 				termio_warn(td, TIO_CAPWARN,
1341 				    "terminal capability '%s' is not "
1342 				    "available\n", ta->ta_name);
1343 				return (0);
1344 			}
1345 			break;
1346 
1347 		case TIO_ATTR_BOOL:
1348 			if (tigetflag(ta->ta_name) == -1) {
1349 				termio_warn(td, TIO_CAPWARN,
1350 				    "terminal capability '%s' is not of type "
1351 				    "boolean as expected\n", ta->ta_name);
1352 				return (0);
1353 			}
1354 			break;
1355 
1356 		case TIO_ATTR_INT:
1357 			if (tigetnum(ta->ta_name) == -2) {
1358 				termio_warn(td, TIO_CAPWARN,
1359 				    "terminal capability '%s' is not of type "
1360 				    "integer as expected\n", ta->ta_name);
1361 				return (0);
1362 			}
1363 			break;
1364 		}
1365 	}
1366 
1367 	if (nbytes != 0)
1368 		td->tio_attrs = mdb_alloc(nbytes, UM_SLEEP);
1369 	else
1370 		td->tio_attrs = NULL;
1371 
1372 	td->tio_attrslen = nbytes;
1373 	bufp = td->tio_attrs;
1374 
1375 	/*
1376 	 * Now make another pass through the terminal attributes and load the
1377 	 * actual pointers into our static data structure:
1378 	 */
1379 	for (ta = &termio_attrs[0]; ta->ta_name != NULL; ta++) {
1380 		switch (ta->ta_type) {
1381 		case TIO_ATTR_REQSTR:
1382 		case TIO_ATTR_STR:
1383 			if ((str = tigetstr(ta->ta_name)) != NULL) {
1384 				/*
1385 				 * Copy the result string into our contiguous
1386 				 * buffer, and store a pointer to it in at_str.
1387 				 */
1388 				(void) strcpy(bufp, str);
1389 				ta->ta_valp->at_str = bufp;
1390 				bufp += strlen(str) + 1;
1391 				/*
1392 				 * Check the string for a "$<>" pad sequence;
1393 				 * if none are found, we can optimize later.
1394 				 */
1395 				if ((str = strstr(ta->ta_valp->at_str,
1396 				    "$<")) != NULL && strchr(str, '>') != NULL)
1397 					need_padding++;
1398 			} else {
1399 				ta->ta_valp->at_str = NULL;
1400 			}
1401 			break;
1402 
1403 		case TIO_ATTR_BOOL:
1404 			ta->ta_valp->at_val = tigetflag(ta->ta_name);
1405 			break;
1406 
1407 		case TIO_ATTR_INT:
1408 			ta->ta_valp->at_val = tigetnum(ta->ta_name);
1409 			break;
1410 		}
1411 	}
1412 
1413 	/*
1414 	 * Copy attribute pointers from temporary struct into td->tio_info:
1415 	 */
1416 	bcopy(&termio_info, &td->tio_info, sizeof (termio_info_t));
1417 
1418 	/*
1419 	 * Initialize the terminal size based on the terminfo database.  If it
1420 	 * does not have the relevant properties, fall back to the environment
1421 	 * settings or to a hardcoded default.  These settings will only be
1422 	 * used if we subsequently fail to derive the size with TIOCGWINSZ.
1423 	 */
1424 	td->tio_rows = MAX(td->tio_info.ti_lines.at_val, 0);
1425 	td->tio_cols = MAX(td->tio_info.ti_cols.at_val, 0);
1426 
1427 	if (td->tio_rows == 0) {
1428 		if ((str = getenv("LINES")) != NULL && strisnum(str) != 0 &&
1429 		    (i = strtoi(str)) > 0)
1430 			td->tio_rows = i;
1431 		else
1432 			td->tio_rows = TIO_DEFAULT_ROWS;
1433 	}
1434 
1435 	if (td->tio_cols == 0) {
1436 		if ((str = getenv("COLUMNS")) != NULL && strisnum(str) != 0 &&
1437 		    (i = strtoi(str)) > 0)
1438 			td->tio_cols = i;
1439 		else
1440 			td->tio_cols = TIO_DEFAULT_COLS;
1441 	}
1442 
1443 	td->tio_flags = 0;
1444 
1445 	/*
1446 	 * We turn on TIO_AUTOWRAP if "am" (automargin) is set on the terminal.
1447 	 * Previously, this check was too smart for its own good, turning
1448 	 * TIO_AUTOWRAP on only when both "am" was set _and_ "xenl" was unset.
1449 	 * "xenl" is one of the (infamous?) so-called "Xanthippe glitches",
1450 	 * this one pertaining to the Concept 100's feature of not autowrapping
1451 	 * if the autowrap-inducing character is a newline.  (That is, the
1452 	 * newline was "eaten" in this case -- and "xenl" accordingly stands
1453 	 * for "Xanthippe eat newline", which itself sounds like a command from
1454 	 * an Infocom game about line discipline set in ancient Greece.) This
1455 	 * (now removed) condition misunderstood the semantics of "xenl",
1456 	 * apparently inferring that its presence denoted that the terminal
1457 	 * could not autowrap at all.  In fact, "xenl" is commonly set, and
1458 	 * denotes that the terminal will not autowrap on a newline -- a
1459 	 * feature (nee glitch) that is not particularly relevant with respect
1460 	 * to our input processing.  (Ironically, disabling TIO_AUTOWRAP in
1461 	 * this case only results in correct-seeming output because of the
1462 	 * presence of the feature, as the inserted newline to force the wrap
1463 	 * isn't actually displayed; were it displayed, the input buffer would
1464 	 * appear double-spaced.)  So with respect to "xenl" and autowrapping,
1465 	 * the correct behavior is to ignore it entirely, and adopt the
1466 	 * (simpler and less arcane) logic of setting TIO_AUTOWRAP if (and only
1467 	 * if) "am" is set on the terminal.  This does not, however, mean that
1468 	 * "xenl" is meaningless; see below...
1469 	 */
1470 	if (td->tio_info.ti_am.at_val)
1471 		td->tio_flags |= TIO_AUTOWRAP;
1472 
1473 	/*
1474 	 * While "xenl" doesn't dictate our TIO_AUTOWRAP setting, it does have
1475 	 * a subtle impact on the way we process input:  in addition to its
1476 	 * eponymous behavior of eating newlines, "xenl" denotes a second,
1477 	 * entirely orthogonal idiosyncracy.  As terminfo(4) tells it: "Those
1478 	 * terminals whose cursor remains on the right-most column until
1479 	 * another character has been received, rather than wrapping
1480 	 * immediately upon receiving the right- most character, such as the
1481 	 * VT100, should also indicate xenl."  So yes, "xenl" in fact has two
1482 	 * entirely different meanings -- it is a glitch-within-a-glitch, in
1483 	 * what one assumes (hopes?) to be an accident of history rather than a
1484 	 * deliberate act of sabotage.  This second behavior is relevant to us
1485 	 * because it affects the way we redraw the screen after a backspace in
1486 	 * the left-most column.  We call this second behavior "lazy wrapping",
1487 	 * and we set it if (and only if) "xenl" is set on the terminal.
1488 	 */
1489 	if (td->tio_info.ti_xenl.at_val)
1490 		td->tio_flags |= TIO_LAZYWRAP;
1491 
1492 	if (td->tio_info.ti_bw.at_val)
1493 		td->tio_flags |= TIO_BACKLEFT;
1494 
1495 	if (td->tio_info.ti_smir.at_str != NULL ||
1496 	    td->tio_info.ti_ich1.at_str != NULL)
1497 		td->tio_flags |= TIO_INSERT;
1498 
1499 	if (mdb.m_flags & MDB_FL_USECUP)
1500 		td->tio_flags |= TIO_USECUP;
1501 
1502 	if (name != NULL && (strncmp(name, "xterm", 5) == 0 ||
1503 	    strcmp(name, "dtterm") == 0))
1504 		td->tio_flags |= TIO_XTERM;
1505 
1506 	/*
1507 	 * Optimizations for padding: (1) if no pad attribute is present, set
1508 	 * its value to "\0" to avoid testing later; (2) if no pad sequences
1509 	 * were found, force "npc" to TRUE so we pick the optimized tio_putp;
1510 	 * (3) if the padding baud property is not present, reset it to zero
1511 	 * since we need to compare it to an unsigned baud value.
1512 	 */
1513 	if (td->tio_info.ti_pad.at_str == NULL)
1514 		td->tio_info.ti_pad.at_str = ""; /* \0 is the pad char */
1515 
1516 	if (need_padding == 0)
1517 		td->tio_info.ti_npc.at_val = TRUE;
1518 
1519 	if (td->tio_info.ti_npc.at_val)
1520 		td->tio_putp = &termio_puts;
1521 	else
1522 		td->tio_putp = &termio_putp;
1523 
1524 	if (td->tio_info.ti_pb.at_val < 0)
1525 		td->tio_info.ti_pb.at_val = 0;
1526 
1527 	/*
1528 	 * If no newline capability is available, assume \r\n will work.  If no
1529 	 * carriage return capability is available, assume \r will work.
1530 	 */
1531 	if (td->tio_info.ti_nel.at_str == NULL)
1532 		td->tio_info.ti_nel.at_str = "\r\n";
1533 	if (td->tio_info.ti_cr.at_str == NULL)
1534 		td->tio_info.ti_cr.at_str = "\r";
1535 
1536 	return (1);
1537 }
1538 
1539 mdb_io_t *
1540 mdb_termio_create(const char *name, mdb_io_t *rio, mdb_io_t *wio)
1541 {
1542 	struct termios otios;
1543 	termio_data_t *td;
1544 	int rv, err, i;
1545 
1546 	td = mdb_zalloc(sizeof (termio_data_t), UM_SLEEP);
1547 	td->tio_io = mdb_alloc(sizeof (mdb_io_t), UM_SLEEP);
1548 
1549 	/*
1550 	 * Save the original user settings before calling setupterm(), which
1551 	 * cleverly changes them without telling us what it did or why.
1552 	 */
1553 	if (IOP_CTL(rio, TCGETS, &otios) == -1) {
1554 		warn("failed to read terminal attributes for stdin");
1555 		goto err;
1556 	}
1557 
1558 	rv = setupterm((char *)name, IOP_CTL(rio, MDB_IOC_GETFD, NULL), &err);
1559 	IOP_CTL(rio, TCSETSW, &otios); /* undo setupterm() stupidity */
1560 
1561 	if (rv == ERR) {
1562 		if (err == 0)
1563 			warn("no terminal data available for TERM=%s\n", name);
1564 		else if (err == -1)
1565 			warn("failed to locate terminfo database\n");
1566 		else
1567 			warn("failed to initialize terminal (err=%d)\n", err);
1568 		goto err;
1569 	}
1570 
1571 	if (!termio_setup_attrs(td, name))
1572 		goto err;
1573 
1574 	/*
1575 	 * Do not re-issue terminal capability warnings when mdb re-execs.
1576 	 */
1577 	if (mdb.m_flags & MDB_FL_EXEC)
1578 		td->tio_flags |= TIO_TTYWARN | TIO_CAPWARN;
1579 
1580 	/*
1581 	 * Initialize i/o structures and command-line buffer:
1582 	 */
1583 	td->tio_io->io_ops = &termio_ops;
1584 	td->tio_io->io_data = td;
1585 	td->tio_io->io_next = NULL;
1586 	td->tio_io->io_refcnt = 0;
1587 
1588 	td->tio_in_io = rio;
1589 	td->tio_in = mdb_iob_create(td->tio_in_io, MDB_IOB_RDONLY);
1590 
1591 	td->tio_out_io = wio;
1592 	td->tio_out = mdb_iob_create(td->tio_out_io, MDB_IOB_WRONLY);
1593 	mdb_iob_clrflags(td->tio_out, MDB_IOB_AUTOWRAP);
1594 
1595 	td->tio_link = NULL;
1596 	mdb_cmdbuf_create(&td->tio_cmdbuf);
1597 
1598 	/*
1599 	 * Fill in all the keymap entries with the insert function:
1600 	 */
1601 	for (i = 0; i < KEY_MAX; i++)
1602 		td->tio_keymap[i] = termio_insert;
1603 
1604 	/*
1605 	 * Now override selected entries with editing functions:
1606 	 */
1607 	td->tio_keymap['\n'] = termio_accept;
1608 	td->tio_keymap['\r'] = termio_accept;
1609 
1610 	td->tio_keymap[CTRL('f')] = termio_fwdchar;
1611 	td->tio_keymap[CTRL('b')] = termio_backchar;
1612 	td->tio_keymap[CTRL('t')] = termio_transpose;
1613 	td->tio_keymap[CTRL('a')] = termio_home;
1614 	td->tio_keymap[CTRL('e')] = termio_end;
1615 	td->tio_keymap[META('f')] = termio_fwdword;
1616 	td->tio_keymap[META('b')] = termio_backword;
1617 	td->tio_keymap[META('d')] = termio_killfwdword;
1618 	td->tio_keymap[META('\b')] = termio_killbackword;
1619 	td->tio_keymap[CTRL('k')] = termio_kill;
1620 	td->tio_keymap[CTRL('p')] = termio_prevhist;
1621 	td->tio_keymap[CTRL('n')] = termio_nexthist;
1622 	td->tio_keymap[CTRL('r')] = termio_findhist;
1623 	td->tio_keymap[CTRL('l')] = termio_refresh;
1624 	td->tio_keymap[CTRL('d')] = termio_delchar;
1625 	td->tio_keymap[CTRL('?')] = termio_widescreen;
1626 
1627 	td->tio_keymap[KPAD('A')] = termio_prevhist;
1628 	td->tio_keymap[KPAD('B')] = termio_nexthist;
1629 	td->tio_keymap[KPAD('C')] = termio_fwdchar;
1630 	td->tio_keymap[KPAD('D')] = termio_backchar;
1631 
1632 	/*
1633 	 * Many modern terminal emulators treat the "Home" and "End" keys on a
1634 	 * PC keyboard as cursor keys.  Some others use a multibyte function
1635 	 * key control sequence.  We handle both styles here:
1636 	 */
1637 	td->tio_keymap[KPAD('H')] = termio_home;
1638 	td->tio_keymap[FKEY('1')] = termio_home;
1639 	td->tio_keymap[KPAD('F')] = termio_end;
1640 	td->tio_keymap[FKEY('4')] = termio_end;
1641 
1642 	/*
1643 	 * We default both ASCII BS and DEL to termio_backspace for safety.  We
1644 	 * want backspace to work whenever possible, regardless of whether or
1645 	 * not we're able to ask the terminal for the specific character that
1646 	 * it will use.  kmdb, for example, is not able to make this request,
1647 	 * and must be prepared to accept both.
1648 	 */
1649 	td->tio_keymap[CTRL('h')] = termio_backspace;
1650 	td->tio_keymap[KEY_DEL] = termio_backspace;
1651 
1652 	/*
1653 	 * Overrides for single-key accelerators
1654 	 */
1655 	td->tio_keymap['['] = termio_accel;
1656 	td->tio_keymap[']'] = termio_accel;
1657 
1658 	/*
1659 	 * Grab tabs
1660 	 */
1661 	td->tio_keymap['\t'] = termio_tab;
1662 
1663 	td->tio_x = 0;
1664 	td->tio_y = 0;
1665 	td->tio_max_x = 0;
1666 	td->tio_max_y = 0;
1667 
1668 	td->tio_active = FALSE;
1669 	td->tio_rti_on = FALSE;
1670 	td->tio_suspended = 1;
1671 
1672 	/*
1673 	 * Perform a resume operation to complete our terminal initialization,
1674 	 * and then adjust the keymap according to the terminal settings.
1675 	 */
1676 	termio_resume_tty(td, &td->tio_ptios);
1677 	bcopy(&td->tio_ptios, &td->tio_ctios, sizeof (struct termios));
1678 
1679 	td->tio_keymap[td->tio_intr] = termio_intr;
1680 	td->tio_keymap[td->tio_quit] = termio_quit;
1681 	td->tio_keymap[td->tio_erase] = termio_backspace;
1682 	td->tio_keymap[td->tio_werase] = termio_killbackword;
1683 	td->tio_keymap[td->tio_kill] = termio_reset;
1684 	td->tio_keymap[td->tio_susp] = termio_susp;
1685 
1686 	(void) mdb_signal_sethandler(SIGWINCH, termio_winch, td);
1687 	(void) mdb_signal_sethandler(SIGTSTP, termio_tstp, td);
1688 
1689 	if (mdb.m_debug & MDB_DBG_CMDBUF)
1690 		termio_dump(td, &termio_attrs[0]);
1691 
1692 	return (td->tio_io);
1693 
1694 err:
1695 	mdb_free(td->tio_io, sizeof (mdb_io_t));
1696 	mdb_free(td, sizeof (termio_data_t));
1697 
1698 	return (NULL);
1699 }
1700 
1701 int
1702 mdb_iob_isatty(mdb_iob_t *iob)
1703 {
1704 	mdb_io_t *io;
1705 
1706 	if (iob->iob_flags & MDB_IOB_TTYLIKE)
1707 		return (1);
1708 
1709 	for (io = iob->iob_iop; io != NULL; io = io->io_next) {
1710 		if (io->io_ops == &termio_ops)
1711 			return (1);
1712 	}
1713 
1714 	return (0);
1715 }
1716 
1717 static const char *
1718 termio_insert(termio_data_t *td, int c)
1719 {
1720 	size_t olen = td->tio_cmdbuf.cmd_buflen;
1721 
1722 	if (mdb_cmdbuf_insert(&td->tio_cmdbuf, c) == 0) {
1723 		if (mdb_cmdbuf_atend(&td->tio_cmdbuf))
1724 			termio_addch(td, c, td->tio_cmdbuf.cmd_buflen - olen);
1725 		else
1726 			termio_insch(td, c, td->tio_cmdbuf.cmd_buflen - olen);
1727 	}
1728 
1729 	return (NULL);
1730 }
1731 
1732 static const char *
1733 termio_accept(termio_data_t *td, int c)
1734 {
1735 	if (td->tio_flags & TIO_FINDHIST) {
1736 		(void) mdb_cmdbuf_findhist(&td->tio_cmdbuf, c);
1737 
1738 		td->tio_prompt = mdb.m_prompt;
1739 		td->tio_promptlen = mdb.m_promptlen;
1740 		td->tio_flags &= ~TIO_FINDHIST;
1741 
1742 		termio_redraw(td);
1743 		return (NULL);
1744 	}
1745 
1746 	/* Ensure that the cursor is at the end of the line */
1747 	(void) termio_end(td, c);
1748 
1749 	return (mdb_cmdbuf_accept(&td->tio_cmdbuf));
1750 }
1751 
1752 static const char *
1753 termio_backspace(termio_data_t *td, int c)
1754 {
1755 	if (mdb_cmdbuf_backspace(&td->tio_cmdbuf, c) == 0) {
1756 		if (mdb_cmdbuf_atend(&td->tio_cmdbuf))
1757 			termio_bspch(td);
1758 		else
1759 			termio_redraw(td);
1760 	}
1761 
1762 	return (NULL);
1763 }
1764 
1765 /*
1766  * This function may end up calling termio_read recursively as part of invoking
1767  * the mdb pager. To work around this fact, we need to go through and make sure
1768  * that we change the underlying terminal settings before and after this
1769  * function call. If we don't do this, we invoke the pager, and don't abort
1770  * (which will longjmp us elsewhere) we're going to return to the read loop with
1771  * the wrong termio settings.
1772  *
1773  * Furthermore, because of the fact that we're being invoked in a user context
1774  * that allows us to be interrupted, we need to actually allocate the memory
1775  * that we're using with GC so that it gets cleaned up in case of the pager
1776  * resetting us and never reaching the end.
1777  */
1778 /*ARGSUSED*/
1779 static const char *
1780 termio_tab(termio_data_t *td, int c)
1781 {
1782 	char *buf;
1783 	const char *result;
1784 	int nres;
1785 	mdb_tab_cookie_t *mtp;
1786 
1787 	if (termio_ctl(td->tio_io, TCSETSW, &td->tio_dtios) == -1)
1788 		warn("failed to restore terminal attributes");
1789 
1790 	buf = mdb_alloc(td->tio_cmdbuf.cmd_bufidx + 1, UM_SLEEP | UM_GC);
1791 	(void) strncpy(buf, td->tio_cmdbuf.cmd_buf, td->tio_cmdbuf.cmd_bufidx);
1792 	buf[td->tio_cmdbuf.cmd_bufidx] = '\0';
1793 	td->tio_flags |= TIO_TAB;
1794 	mtp = mdb_tab_init();
1795 	nres = mdb_tab_command(mtp, buf);
1796 
1797 	if (nres == 0) {
1798 		result = NULL;
1799 	} else {
1800 		result = mdb_tab_match(mtp);
1801 		if (nres != 1) {
1802 			mdb_printf("\n");
1803 			mdb_tab_print(mtp);
1804 		}
1805 	}
1806 
1807 	if (result != NULL) {
1808 		int index = 0;
1809 
1810 		while (result[index] != '\0') {
1811 			(void) termio_insert(td, result[index]);
1812 			index++;
1813 		}
1814 	}
1815 
1816 	termio_redraw(td);
1817 	mdb_tab_fini(mtp);
1818 	td->tio_flags &= ~TIO_TAB;
1819 	if (termio_ctl(td->tio_io, TCSETSW, &td->tio_rtios) == -1)
1820 		warn("failed to set terminal attributes");
1821 
1822 
1823 	return (NULL);
1824 }
1825 
1826 static const char *
1827 termio_delchar(termio_data_t *td, int c)
1828 {
1829 	if (!(mdb.m_flags & MDB_FL_IGNEOF) &&
1830 	    mdb_cmdbuf_atend(&td->tio_cmdbuf) &&
1831 	    mdb_cmdbuf_atstart(&td->tio_cmdbuf))
1832 		return (termio_quit(td, c));
1833 
1834 	if (mdb_cmdbuf_delchar(&td->tio_cmdbuf, c) == 0) {
1835 		if (mdb_cmdbuf_atend(&td->tio_cmdbuf))
1836 			termio_delch(td);
1837 		else
1838 			termio_redraw(td);
1839 	}
1840 
1841 	return (NULL);
1842 }
1843 
1844 static const char *
1845 termio_fwdchar(termio_data_t *td, int c)
1846 {
1847 	if (mdb_cmdbuf_fwdchar(&td->tio_cmdbuf, c) == 0)
1848 		termio_mvcur(td);
1849 
1850 	return (NULL);
1851 }
1852 
1853 static const char *
1854 termio_backchar(termio_data_t *td, int c)
1855 {
1856 	if (mdb_cmdbuf_backchar(&td->tio_cmdbuf, c) == 0)
1857 		termio_mvcur(td);
1858 
1859 	return (NULL);
1860 }
1861 
1862 static const char *
1863 termio_transpose(termio_data_t *td, int c)
1864 {
1865 	if (mdb_cmdbuf_transpose(&td->tio_cmdbuf, c) == 0)
1866 		termio_redraw(td);
1867 
1868 	return (NULL);
1869 }
1870 
1871 static const char *
1872 termio_home(termio_data_t *td, int c)
1873 {
1874 	if (mdb_cmdbuf_home(&td->tio_cmdbuf, c) == 0)
1875 		termio_mvcur(td);
1876 
1877 	return (NULL);
1878 }
1879 
1880 static const char *
1881 termio_end(termio_data_t *td, int c)
1882 {
1883 	if (mdb_cmdbuf_end(&td->tio_cmdbuf, c) == 0)
1884 		termio_mvcur(td);
1885 
1886 	return (NULL);
1887 }
1888 
1889 static const char *
1890 termio_fwdword(termio_data_t *td, int c)
1891 {
1892 	if (mdb_cmdbuf_fwdword(&td->tio_cmdbuf, c) == 0)
1893 		termio_mvcur(td);
1894 
1895 	return (NULL);
1896 }
1897 
1898 static const char *
1899 termio_backword(termio_data_t *td, int c)
1900 {
1901 	if (mdb_cmdbuf_backword(&td->tio_cmdbuf, c) == 0)
1902 		termio_mvcur(td);
1903 
1904 	return (NULL);
1905 }
1906 
1907 static const char *
1908 termio_kill(termio_data_t *td, int c)
1909 {
1910 	if (mdb_cmdbuf_kill(&td->tio_cmdbuf, c) == 0)
1911 		termio_redraw(td);
1912 
1913 	return (NULL);
1914 }
1915 
1916 static const char *
1917 termio_killfwdword(termio_data_t *td, int c)
1918 {
1919 	if (mdb_cmdbuf_killfwdword(&td->tio_cmdbuf, c) == 0)
1920 		termio_redraw(td);
1921 
1922 	return (NULL);
1923 }
1924 
1925 static const char *
1926 termio_killbackword(termio_data_t *td, int c)
1927 {
1928 	if (mdb_cmdbuf_killbackword(&td->tio_cmdbuf, c) == 0)
1929 		termio_redraw(td);
1930 
1931 	return (NULL);
1932 }
1933 
1934 static const char *
1935 termio_reset(termio_data_t *td, int c)
1936 {
1937 	if (mdb_cmdbuf_reset(&td->tio_cmdbuf, c) == 0)
1938 		termio_clear(td);
1939 
1940 	return (NULL);
1941 }
1942 
1943 /*ARGSUSED*/
1944 static const char *
1945 termio_widescreen(termio_data_t *td, int c)
1946 {
1947 	if (td->tio_flags & TIO_XTERM) {
1948 		if (td->tio_cols == 80)
1949 			termio_tput(td, TI_DECSET(TI_DECCOLM), 1);
1950 		else
1951 			termio_tput(td, TI_DECRST(TI_DECCOLM), 1);
1952 		mdb_iob_flush(td->tio_out);
1953 		termio_winch(SIGWINCH, NULL, NULL, td);
1954 	}
1955 
1956 	return (NULL);
1957 }
1958 
1959 static const char *
1960 termio_prevhist(termio_data_t *td, int c)
1961 {
1962 	if (mdb_cmdbuf_prevhist(&td->tio_cmdbuf, c) == 0)
1963 		termio_redraw(td);
1964 
1965 	return (NULL);
1966 }
1967 
1968 static const char *
1969 termio_nexthist(termio_data_t *td, int c)
1970 {
1971 	if (mdb_cmdbuf_nexthist(&td->tio_cmdbuf, c) == 0)
1972 		termio_redraw(td);
1973 
1974 	return (NULL);
1975 }
1976 
1977 /*
1978  * Single-key accelerator support.  Several commands are so commonly used as to
1979  * require a single-key equivalent.  If we see one of these accelerator
1980  * characters at the beginning of an otherwise-empty line, we'll replace it with
1981  * the expansion.
1982  */
1983 static const char *
1984 termio_accel(termio_data_t *td, int c)
1985 {
1986 	const char *p;
1987 
1988 	if (td->tio_cmdbuf.cmd_buflen != 0 ||
1989 	    (p = termio_accel_lookup(c)) == NULL)
1990 		return (termio_insert(td, c));
1991 
1992 	while (*p != '\0')
1993 		(void) termio_insert(td, *p++);
1994 	return (termio_accept(td, '\n'));
1995 }
1996 
1997 static const char *
1998 termio_findhist(termio_data_t *td, int c)
1999 {
2000 	if (mdb_cmdbuf_reset(&td->tio_cmdbuf, c) == 0) {
2001 		td->tio_prompt = "Search: ";
2002 		td->tio_promptlen = strlen(td->tio_prompt);
2003 		td->tio_flags |= TIO_FINDHIST;
2004 		termio_redraw(td);
2005 	}
2006 
2007 	return (NULL);
2008 }
2009 
2010 /*ARGSUSED*/
2011 static const char *
2012 termio_refresh(termio_data_t *td, int c)
2013 {
2014 	if (td->tio_info.ti_clear.at_str) {
2015 		termio_tput(td, td->tio_info.ti_clear.at_str, 1);
2016 		td->tio_x = td->tio_y = 0;
2017 	}
2018 	termio_redraw(td);
2019 	return (NULL);
2020 }
2021 
2022 /*
2023  * Leave the terminal read code by longjmp'ing up the stack of mdb_frame_t's
2024  * back to the main parsing loop (see mdb_run() in mdb.c).
2025  */
2026 static const char *
2027 termio_abort(termio_data_t *td, int c, int err)
2028 {
2029 	(void) mdb_cmdbuf_reset(&td->tio_cmdbuf, c);
2030 	td->tio_active = FALSE;
2031 	td->tio_rti_on = FALSE;
2032 
2033 	if (termio_ctl(td->tio_io, TCSETSW, &td->tio_dtios) == -1)
2034 		warn("failed to restore terminal attributes");
2035 
2036 	longjmp(mdb.m_frame->f_pcb, err);
2037 	/*NOTREACHED*/
2038 	return (NULL);
2039 }
2040 
2041 static const char *
2042 termio_intr(termio_data_t *td, int c)
2043 {
2044 	return (termio_abort(td, c, MDB_ERR_SIGINT));
2045 }
2046 
2047 static const char *
2048 termio_quit(termio_data_t *td, int c)
2049 {
2050 	return (termio_abort(td, c, MDB_ERR_QUIT));
2051 }
2052 
2053 /*ARGSUSED*/
2054 static const char *
2055 termio_susp(termio_data_t *td, int c)
2056 {
2057 	(void) mdb_signal_sethandler(SIGWINCH, SIG_IGN, NULL);
2058 	(void) mdb_signal_sethandler(SIGTSTP, SIG_IGN, NULL);
2059 
2060 	termio_suspend_tty(td, &td->tio_ptios);
2061 	mdb_iob_nl(td->tio_out);
2062 
2063 	(void) mdb_signal_sethandler(SIGTSTP, SIG_DFL, NULL);
2064 	(void) mdb_signal_pgrp(SIGTSTP);
2065 
2066 	/*
2067 	 * When we call mdb_signal_pgrp(SIGTSTP), we are expecting the entire
2068 	 * debugger process group to be stopped by the kernel.  Once we return
2069 	 * from that call, we assume we are resuming from a subsequent SIGCONT.
2070 	 */
2071 	(void) mdb_signal_sethandler(SIGTSTP, SIG_IGN, NULL);
2072 	termio_resume_tty(td, &td->tio_ptios);
2073 
2074 	(void) mdb_signal_sethandler(SIGWINCH, termio_winch, td);
2075 	(void) mdb_signal_sethandler(SIGTSTP, termio_tstp, td);
2076 
2077 	if (td->tio_active)
2078 		siglongjmp(td->tio_env, SIGCONT);
2079 
2080 	return (NULL);
2081 }
2082 
2083 /*ARGSUSED*/
2084 static void
2085 termio_winch(int sig, siginfo_t *sip, ucontext_t *ucp, void *data)
2086 {
2087 	termio_data_t *td = data;
2088 	mdb_bool_t change = FALSE;
2089 	struct winsize winsz;
2090 
2091 	if (termio_ctl(td->tio_io, TIOCGWINSZ, &winsz) == -1)
2092 		return; /* just ignore this WINCH if the ioctl fails */
2093 
2094 	if (td->tio_rows != (size_t)winsz.ws_row ||
2095 	    td->tio_cols != (size_t)winsz.ws_col) {
2096 
2097 		if (td->tio_active)
2098 			termio_clear(td);
2099 
2100 		if (winsz.ws_row != 0)
2101 			td->tio_rows = (size_t)winsz.ws_row;
2102 
2103 		if (winsz.ws_col != 0)
2104 			td->tio_cols = (size_t)winsz.ws_col;
2105 
2106 		if (td->tio_active)
2107 			termio_clear(td);
2108 
2109 		mdb_iob_resize(td->tio_out, td->tio_rows, td->tio_cols);
2110 		change = TRUE;
2111 	}
2112 
2113 	if (change && td->tio_active)
2114 		siglongjmp(td->tio_env, sig);
2115 
2116 	if (change && td->tio_link != NULL)
2117 		mdb_iob_resize(td->tio_link, td->tio_rows, td->tio_cols);
2118 }
2119 
2120 /*ARGSUSED*/
2121 static void
2122 termio_tstp(int sig, siginfo_t *sip, ucontext_t *ucp, void *data)
2123 {
2124 	(void) termio_susp(data, CTRL('Z'));
2125 }
2126