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