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