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