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