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