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 }