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