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