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, 0, 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 }