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(4I)) 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, 0, 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 1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 758 3500000, 4000000 759 }; 760 761 struct termios *ntios; 762 struct winsize winsz; 763 uint_t speed; 764 765 if (td->tio_suspended == 0) 766 fail("termio_resume called without matching termio_suspend\n"); 767 768 if (--td->tio_suspended != 0) 769 return; /* nested suspends; do not resume yet */ 770 771 td->tio_opgid = -1; /* set to invalid pgid in case TIOCPGRP fails */ 772 (void) termio_ctl(td->tio_io, TIOCGPGRP, &td->tio_opgid); 773 774 /* 775 * If the foreground process group does not include the debugger, reset 776 * the foreground process group so we are in control of the terminal. 777 * We temporarily ignore TTOU because TIOCSPGRP could trigger it. 778 */ 779 if (td->tio_opgid != mdb.m_pgid) { 780 (void) mdb_signal_sethandler(SIGTTOU, SIG_IGN, NULL); 781 (void) termio_ctl(td->tio_io, TIOCSPGRP, &mdb.m_pgid); 782 (void) mdb_signal_sethandler(SIGTTOU, SIG_DFL, NULL); 783 mdb_dprintf(MDB_DBG_CMDBUF, "fg pgid=%d\n", (int)mdb.m_pgid); 784 } 785 786 /* 787 * Read the current set of terminal attributes, and save them in iosp 788 * so we can restore them later. Then derive rtios, dtios, and winsz. 789 */ 790 if (termio_ctl(td->tio_io, TCGETS, iosp) < 0) 791 warn("failed to get terminal attributes"); 792 793 if (termio_ctl(td->tio_io, TIOCGWINSZ, &winsz) == 0) { 794 if (winsz.ws_row != 0) 795 td->tio_rows = (size_t)winsz.ws_row; 796 if (winsz.ws_col != 0) 797 td->tio_cols = (size_t)winsz.ws_col; 798 } 799 800 mdb_iob_resize(td->tio_out, td->tio_rows, td->tio_cols); 801 802 td->tio_intr = td->tio_ptios.c_cc[VINTR]; 803 td->tio_quit = td->tio_ptios.c_cc[VQUIT]; 804 td->tio_erase = td->tio_ptios.c_cc[VERASE]; 805 td->tio_werase = td->tio_ptios.c_cc[VWERASE]; 806 td->tio_kill = td->tio_ptios.c_cc[VKILL]; 807 td->tio_eof = td->tio_ptios.c_cc[VEOF]; 808 td->tio_susp = td->tio_ptios.c_cc[VSUSP]; 809 810 bcopy(&td->tio_ptios, &td->tio_rtios, sizeof (struct termios)); 811 td->tio_rtios.c_iflag &= ~(ISTRIP | INPCK | ICRNL | INLCR | IUCLC); 812 td->tio_rtios.c_oflag &= ~(OCRNL | ONLRET); 813 td->tio_rtios.c_oflag |= ONLCR; 814 td->tio_rtios.c_lflag &= ~(ISIG | ICANON | ECHO); 815 td->tio_rtios.c_cflag |= CS8; 816 td->tio_rtios.c_cc[VTIME] = 0; 817 td->tio_rtios.c_cc[VMIN] = 1; 818 819 bcopy(&td->tio_ptios, &td->tio_dtios, sizeof (struct termios)); 820 td->tio_dtios.c_oflag &= ~(OCRNL | ONLRET); 821 td->tio_dtios.c_oflag |= ONLCR; 822 td->tio_dtios.c_lflag |= ISIG | ICANON | ECHO; 823 824 /* 825 * Select the appropriate modified settings to restore based on our 826 * current state, and then install them. 827 */ 828 if (td->tio_rti_on) 829 ntios = &td->tio_rtios; 830 else 831 ntios = &td->tio_dtios; 832 833 if (termio_ctl(td->tio_io, TCSETSW, ntios) < 0) 834 warn("failed to reset terminal attributes"); 835 836 /* 837 * Compute the terminal speed as described in termio(4I), and then 838 * look up the corresponding microseconds-per-char in our table. 839 */ 840 if (ntios->c_cflag & CBAUDEXT) 841 speed = (ntios->c_cflag & CBAUD) + CBAUD + 1; 842 else 843 speed = (ntios->c_cflag & CBAUD); 844 845 if (speed >= sizeof (baud) / sizeof (baud[0])) { 846 termio_warn(td, TIO_TTYWARN, "invalid speed %u -- assuming " 847 "9600 baud\n", speed); 848 speed = B9600; 849 } 850 851 td->tio_baud = baud[speed]; 852 td->tio_usecpc = MICROSEC / td->tio_baud; 853 854 mdb_dprintf(MDB_DBG_CMDBUF, "speed = %u baud (%u usec / char), " 855 "putp = %s\n", td->tio_baud, td->tio_usecpc, 856 td->tio_putp == &termio_puts ? "fast" : "slow"); 857 858 /* 859 * Send the necessary terminal initialization sequences to enable 860 * enable cursor positioning. Clear the screen afterward if possible. 861 */ 862 if (td->tio_flags & TIO_USECUP) { 863 termio_tput(td, td->tio_info.ti_smcup.at_str, 1); 864 if (td->tio_info.ti_clear.at_str) { 865 termio_tput(td, td->tio_info.ti_clear.at_str, 1); 866 td->tio_x = td->tio_y = 0; 867 } 868 } 869 870 /* 871 * If the terminal is xterm-compatible, enable column mode switching. 872 * Save the previous value in the terminal so we can restore it. 873 */ 874 if (td->tio_flags & TIO_XTERM) { 875 termio_tput(td, TI_DECSAV(TI_COLENAB), 1); 876 termio_tput(td, TI_DECSET(TI_COLENAB), 1); 877 } 878 879 termio_tput(td, td->tio_info.ti_cnorm.at_str, 1); /* cursor visible */ 880 termio_tput(td, td->tio_info.ti_enacs.at_str, 1); /* alt char set */ 881 882 /* 883 * If the terminal is automargin-capable and we have an initialization 884 * sequence to enable automargins, we must send it now. Note that 885 * we don't have a way of querying this mode and restoring it; if 886 * we are fighting with (say) a target that is depending on automargin 887 * being turned off, it will lose. 888 */ 889 if ((td->tio_flags & TIO_AUTOWRAP) && 890 td->tio_info.ti_smam.at_str != NULL) { 891 termio_tput(td, td->tio_info.ti_smam.at_str, 1); 892 } 893 894 mdb_iob_flush(td->tio_out); 895 } 896 897 static void 898 termio_suspend(mdb_io_t *io) 899 { 900 termio_data_t *td = io->io_data; 901 termio_suspend_tty(td, &td->tio_ctios); 902 } 903 904 static void 905 termio_resume(mdb_io_t *io) 906 { 907 termio_data_t *td = io->io_data; 908 termio_resume_tty(td, &td->tio_ctios); 909 } 910 911 /* 912 * Delay for the specified number of microseconds by sending the pad character 913 * to the terminal. We round up by half a frame and then divide by the usecs 914 * per character to determine the number of pad characters to send. 915 */ 916 static void 917 termio_delay(termio_data_t *td, uint_t usec) 918 { 919 char pad = td->tio_info.ti_pad.at_str[0]; 920 uint_t usecpc = td->tio_usecpc; 921 922 for (usec = (usec + usecpc / 2) / usecpc; usec != 0; usec--) { 923 mdb_iob_putc(td->tio_out, pad); 924 mdb_iob_flush(td->tio_out); 925 } 926 } 927 928 /* 929 * Parse the terminfo(5) padding sequence "$<...>" and delay for the specified 930 * amount of time by sending pad characters to the terminal. 931 */ 932 static const char * 933 termio_pad(termio_data_t *td, const char *s, uint_t lines) 934 { 935 int xon = td->tio_info.ti_xon.at_val; 936 int pb = td->tio_info.ti_pb.at_val; 937 938 const char *p = s; 939 uint_t usec = 0; 940 941 /* 942 * The initial string is a number of milliseconds, followed by an 943 * optional decimal point and number of tenths of milliseconds. 944 * We convert this to microseconds for greater accuracy. Only a single 945 * digit is permitted after the decimal point; we ignore any others. 946 */ 947 while (*p >= '0' && *p <= '9') 948 usec = usec * 10 + *p++ - '0'; 949 950 usec *= 1000; /* convert msecs to usecs */ 951 952 if (*p == '.') { 953 if (p[1] >= '0' && p[1] <= '9') 954 usec += (p[1] - '0') * 100; 955 for (p++; *p >= '0' && *p <= '9'; p++) 956 continue; 957 } 958 959 /* 960 * Following the time delay specifier, 961 * 962 * 1. An optional "/" indicates that the delay should be done 963 * regardless of the value of the terminal's xon property, 964 * 2. An optional "*" indicates that the delay is proportional to the 965 * count of affected lines, and 966 * 3. A mandatory ">" terminates the sequence. 967 * 968 * If we encounter any other characters, we assume that we found "$<" 969 * accidentally embedded in another sequence, so we just output "$". 970 */ 971 for (;;) { 972 switch (*p++) { 973 case '/': 974 xon = FALSE; 975 continue; 976 case '*': 977 usec *= lines; 978 continue; 979 case '>': 980 if (xon == FALSE && usec != 0 && td->tio_baud >= pb) 981 termio_delay(td, usec); 982 return (p); 983 default: 984 mdb_iob_putc(td->tio_out, *s); 985 return (s + 1); 986 } 987 } 988 } 989 990 /* 991 * termio_tput() subroutine for terminals that require padding. We look ahead 992 * for "$<>" sequences, and call termio_pad() to process them; all other chars 993 * are output directly to the underlying device and then flushed at the end. 994 */ 995 static void 996 termio_putp(termio_data_t *td, const char *s, uint_t lines) 997 { 998 while (s[0] != '\0') { 999 if (s[0] == '$' && s[1] == '<') 1000 s = termio_pad(td, s + 2, lines); 1001 else 1002 mdb_iob_putc(td->tio_out, *s++); 1003 } 1004 1005 mdb_iob_flush(td->tio_out); 1006 } 1007 1008 /* 1009 * termio_tput() subroutine for terminals that do not require padding. We 1010 * simply output the string to the underlying i/o buffer; we let the caller 1011 * take care of flushing so that multiple sequences can be concatenated. 1012 */ 1013 /*ARGSUSED*/ 1014 static void 1015 termio_puts(termio_data_t *td, const char *s, uint_t lines) 1016 { 1017 mdb_iob_puts(td->tio_out, s); 1018 } 1019 1020 /* 1021 * Print a padded escape sequence string to the terminal. The caller specifies 1022 * the string 's' and a count of the affected lines. If the string contains an 1023 * embedded delay sequence delimited by "$<>" (see terminfo(5)), appropriate 1024 * padding will be included in the output. We determine whether or not padding 1025 * is required during initialization, and set tio_putp to the proper subroutine. 1026 */ 1027 static void 1028 termio_tput(termio_data_t *td, const char *s, uint_t lines) 1029 { 1030 if (s != NULL) 1031 td->tio_putp(td, s, lines); 1032 } 1033 1034 static void 1035 termio_addch(termio_data_t *td, char c, size_t width) 1036 { 1037 if (width == 1) { 1038 mdb_iob_putc(td->tio_out, c); 1039 td->tio_x++; 1040 1041 if (td->tio_x >= td->tio_cols) { 1042 if (!(td->tio_flags & TIO_AUTOWRAP)) 1043 termio_tput(td, td->tio_info.ti_nel.at_str, 1); 1044 td->tio_x = 0; 1045 td->tio_y++; 1046 } 1047 1048 mdb_iob_flush(td->tio_out); 1049 } else 1050 termio_redraw(td); 1051 } 1052 1053 static void 1054 termio_insch(termio_data_t *td, char c, size_t width) 1055 { 1056 if (width == 1 && (td->tio_flags & TIO_INSERT) && 1057 td->tio_y == td->tio_max_y) { 1058 1059 termio_tput(td, td->tio_info.ti_smir.at_str, 1); 1060 termio_tput(td, td->tio_info.ti_ich1.at_str, 1); 1061 1062 mdb_iob_putc(td->tio_out, c); 1063 td->tio_x++; 1064 1065 termio_tput(td, td->tio_info.ti_ip.at_str, 1); 1066 termio_tput(td, td->tio_info.ti_rmir.at_str, 1); 1067 1068 if (td->tio_x >= td->tio_cols) { 1069 if (!(td->tio_flags & TIO_AUTOWRAP)) 1070 termio_tput(td, td->tio_info.ti_nel.at_str, 1); 1071 td->tio_x = 0; 1072 td->tio_y++; 1073 } 1074 1075 mdb_iob_flush(td->tio_out); 1076 } else 1077 termio_redraw(td); 1078 } 1079 1080 static void 1081 termio_mvcur(termio_data_t *td) 1082 { 1083 size_t tipos = td->tio_cmdbuf.cmd_bufidx + td->tio_promptlen; 1084 size_t dst_x = tipos % td->tio_cols; 1085 size_t dst_y = tipos / td->tio_cols; 1086 1087 const char *str; 1088 size_t cnt, i; 1089 1090 if (td->tio_y != dst_y) { 1091 if (td->tio_y < dst_y) { 1092 str = td->tio_info.ti_cud1.at_str; 1093 cnt = dst_y - td->tio_y; 1094 td->tio_x = 0; /* Note: cud1 moves cursor to column 0 */ 1095 } else { 1096 str = td->tio_info.ti_cuu1.at_str; 1097 cnt = td->tio_y - dst_y; 1098 } 1099 1100 for (i = 0; i < cnt; i++) 1101 termio_tput(td, str, 1); 1102 1103 mdb_iob_flush(td->tio_out); 1104 td->tio_y = dst_y; 1105 } 1106 1107 if (td->tio_x != dst_x) { 1108 if (td->tio_x < dst_x) { 1109 str = td->tio_info.ti_cuf1.at_str; 1110 cnt = dst_x - td->tio_x; 1111 } else { 1112 str = td->tio_info.ti_cub1.at_str; 1113 cnt = td->tio_x - dst_x; 1114 } 1115 1116 for (i = 0; i < cnt; i++) 1117 termio_tput(td, str, 1); 1118 1119 mdb_iob_flush(td->tio_out); 1120 td->tio_x = dst_x; 1121 } 1122 } 1123 1124 static void 1125 termio_backleft(termio_data_t *td) 1126 { 1127 size_t i; 1128 1129 if (td->tio_flags & TIO_BACKLEFT) 1130 termio_tput(td, td->tio_info.ti_cub1.at_str, 1); 1131 else { 1132 termio_tput(td, td->tio_info.ti_cuu1.at_str, 1); 1133 for (i = 0; i < td->tio_cols - 1; i++) 1134 termio_tput(td, td->tio_info.ti_cuf1.at_str, 1); 1135 } 1136 } 1137 1138 static void 1139 termio_bspch(termio_data_t *td) 1140 { 1141 if (td->tio_x == 0) { 1142 /* 1143 * If TIO_LAZYWRAP is set, we are regrettably in one of two 1144 * states that we cannot differentiate between: the cursor is 1145 * either on the right margin of the previous line (having not 1146 * advanced due to the lazy wrap) _or_ the cursor is on the 1147 * left margin of the current line because a wrap has already 1148 * been induced due to prior activity. Because these cases are 1149 * impossible to differentiate, we force the latter case by 1150 * emitting a space and then moving the cursor back. If the 1151 * wrap had not been induced, emitting this space will induce 1152 * it -- and will assure that our termio_backleft() is the 1153 * correct behavior with respect to the cursor. 1154 */ 1155 if (td->tio_flags & TIO_LAZYWRAP) { 1156 mdb_iob_putc(td->tio_out, ' '); 1157 termio_tput(td, td->tio_info.ti_cub1.at_str, 1); 1158 } 1159 1160 termio_backleft(td); 1161 td->tio_x = td->tio_cols - 1; 1162 td->tio_y--; 1163 } else { 1164 termio_tput(td, td->tio_info.ti_cub1.at_str, 1); 1165 td->tio_x--; 1166 } 1167 1168 termio_delch(td); 1169 } 1170 1171 static void 1172 termio_delch(termio_data_t *td) 1173 { 1174 mdb_iob_putc(td->tio_out, ' '); 1175 1176 if (td->tio_x == td->tio_cols - 1 && (td->tio_flags & TIO_AUTOWRAP)) { 1177 /* 1178 * We just overwrote a space in the final column, so we know 1179 * that we have autowrapped and we therefore definitely don't 1180 * want to issue the ti_cub1. If we have TIO_LAZYWRAP, we know 1181 * that the cursor remains on the final column, and we want to 1182 * do nothing -- but if TIO_LAZYWRAP isn't set, we have wrapped 1183 * and we need to move the cursor back up and all the way to 1184 * the right via termio_backleft(). 1185 */ 1186 if (!(td->tio_flags & TIO_LAZYWRAP)) 1187 termio_backleft(td); 1188 } else { 1189 termio_tput(td, td->tio_info.ti_cub1.at_str, 1); 1190 } 1191 1192 mdb_iob_flush(td->tio_out); 1193 } 1194 1195 static void 1196 termio_clear(termio_data_t *td) 1197 { 1198 while (td->tio_x-- != 0) 1199 termio_tput(td, td->tio_info.ti_cub1.at_str, 1); 1200 1201 while (td->tio_y < td->tio_max_y) { 1202 termio_tput(td, td->tio_info.ti_cud1.at_str, 1); 1203 td->tio_y++; 1204 } 1205 1206 while (td->tio_y-- != 0) { 1207 termio_tput(td, td->tio_info.ti_el.at_str, 1); 1208 termio_tput(td, td->tio_info.ti_cuu1.at_str, 1); 1209 } 1210 1211 termio_tput(td, td->tio_info.ti_el.at_str, 1); 1212 mdb_iob_flush(td->tio_out); 1213 1214 termio_prompt(td); 1215 } 1216 1217 static void 1218 termio_redraw(termio_data_t *td) 1219 { 1220 const char *buf = td->tio_cmdbuf.cmd_buf; 1221 size_t len = td->tio_cmdbuf.cmd_buflen; 1222 size_t pos, n; 1223 1224 termio_clear(td); 1225 1226 if (len == 0) 1227 return; /* if the buffer is empty, we're done */ 1228 1229 if (td->tio_flags & TIO_AUTOWRAP) 1230 mdb_iob_nputs(td->tio_out, buf, len); 1231 else { 1232 for (pos = td->tio_promptlen; len != 0; pos = 0) { 1233 n = MIN(td->tio_cols - pos, len); 1234 mdb_iob_nputs(td->tio_out, buf, n); 1235 buf += n; 1236 len -= n; 1237 1238 if (pos + n == td->tio_cols) 1239 termio_tput(td, td->tio_info.ti_nel.at_str, 1); 1240 } 1241 } 1242 1243 pos = td->tio_promptlen + td->tio_cmdbuf.cmd_buflen; 1244 td->tio_x = pos % td->tio_cols; 1245 td->tio_y = pos / td->tio_cols; 1246 1247 mdb_iob_flush(td->tio_out); 1248 termio_mvcur(td); 1249 } 1250 1251 static void 1252 termio_prompt(termio_data_t *td) 1253 { 1254 mdb_callb_fire(MDB_CALLB_PROMPT); 1255 1256 /* 1257 * Findhist (^R) overrides the displayed prompt. We should only update 1258 * the main prompt (which may have been changed by the callback) if 1259 * findhist isn't active. 1260 */ 1261 if (!(td->tio_flags & TIO_FINDHIST)) { 1262 td->tio_prompt = mdb.m_prompt; 1263 td->tio_promptlen = mdb.m_promptlen; 1264 } 1265 1266 mdb_iob_puts(td->tio_out, td->tio_prompt); 1267 mdb_iob_flush(td->tio_out); 1268 1269 td->tio_x = td->tio_promptlen; 1270 td->tio_y = 0; 1271 } 1272 1273 /* 1274 * For debugging purposes, iterate over the table of attributes and output them 1275 * in human readable form for verification. 1276 */ 1277 static void 1278 termio_dump(termio_data_t *td, const termio_attr_t *ta) 1279 { 1280 char *str; 1281 1282 for (; ta->ta_name != NULL; ta++) { 1283 switch (ta->ta_type) { 1284 case TIO_ATTR_REQSTR: 1285 case TIO_ATTR_STR: 1286 if (ta->ta_valp->at_str != NULL) { 1287 str = strchr2esc(ta->ta_valp->at_str, 1288 strlen(ta->ta_valp->at_str)); 1289 mdb_dprintf(MDB_DBG_CMDBUF, "%s = \"%s\"\n", 1290 ta->ta_name, str); 1291 strfree(str); 1292 } else { 1293 mdb_dprintf(MDB_DBG_CMDBUF, "%s = <NULL>\n", 1294 ta->ta_name); 1295 } 1296 break; 1297 case TIO_ATTR_INT: 1298 mdb_dprintf(MDB_DBG_CMDBUF, "%s = %d\n", 1299 ta->ta_name, ta->ta_valp->at_val); 1300 break; 1301 case TIO_ATTR_BOOL: 1302 mdb_dprintf(MDB_DBG_CMDBUF, "%s = %s\n", ta->ta_name, 1303 ta->ta_valp->at_val ? "TRUE" : "FALSE"); 1304 break; 1305 } 1306 } 1307 1308 mdb_dprintf(MDB_DBG_CMDBUF, "tio_flags = <%#b>\n", 1309 td->tio_flags, tio_flag_masks); 1310 } 1311 1312 static int 1313 termio_setup_attrs(termio_data_t *td, const char *name) 1314 { 1315 const termio_attr_t *ta; 1316 const char *str; 1317 size_t nbytes; 1318 char *bufp; 1319 1320 int need_padding = 0; 1321 int i; 1322 1323 /* 1324 * Load terminal attributes: 1325 */ 1326 for (nbytes = 0, ta = &termio_attrs[0]; ta->ta_name != NULL; ta++) { 1327 switch (ta->ta_type) { 1328 case TIO_ATTR_REQSTR: 1329 case TIO_ATTR_STR: 1330 str = tigetstr(ta->ta_name); 1331 1332 if (str == (const char *)-1) { 1333 termio_warn(td, TIO_CAPWARN, 1334 "terminal capability '%s' is not of type " 1335 "string as expected\n", ta->ta_name); 1336 return (0); 1337 } 1338 1339 if (str != NULL) 1340 nbytes += strlen(str) + 1; 1341 else if (ta->ta_type == TIO_ATTR_REQSTR) { 1342 termio_warn(td, TIO_CAPWARN, 1343 "terminal capability '%s' is not " 1344 "available\n", ta->ta_name); 1345 return (0); 1346 } 1347 break; 1348 1349 case TIO_ATTR_BOOL: 1350 if (tigetflag(ta->ta_name) == -1) { 1351 termio_warn(td, TIO_CAPWARN, 1352 "terminal capability '%s' is not of type " 1353 "boolean as expected\n", ta->ta_name); 1354 return (0); 1355 } 1356 break; 1357 1358 case TIO_ATTR_INT: 1359 if (tigetnum(ta->ta_name) == -2) { 1360 termio_warn(td, TIO_CAPWARN, 1361 "terminal capability '%s' is not of type " 1362 "integer as expected\n", ta->ta_name); 1363 return (0); 1364 } 1365 break; 1366 } 1367 } 1368 1369 if (nbytes != 0) 1370 td->tio_attrs = mdb_alloc(nbytes, UM_SLEEP); 1371 else 1372 td->tio_attrs = NULL; 1373 1374 td->tio_attrslen = nbytes; 1375 bufp = td->tio_attrs; 1376 1377 /* 1378 * Now make another pass through the terminal attributes and load the 1379 * actual pointers into our static data structure: 1380 */ 1381 for (ta = &termio_attrs[0]; ta->ta_name != NULL; ta++) { 1382 switch (ta->ta_type) { 1383 case TIO_ATTR_REQSTR: 1384 case TIO_ATTR_STR: 1385 if ((str = tigetstr(ta->ta_name)) != NULL) { 1386 /* 1387 * Copy the result string into our contiguous 1388 * buffer, and store a pointer to it in at_str. 1389 */ 1390 (void) strcpy(bufp, str); 1391 ta->ta_valp->at_str = bufp; 1392 bufp += strlen(str) + 1; 1393 /* 1394 * Check the string for a "$<>" pad sequence; 1395 * if none are found, we can optimize later. 1396 */ 1397 if ((str = strstr(ta->ta_valp->at_str, 1398 "$<")) != NULL && strchr(str, '>') != NULL) 1399 need_padding++; 1400 } else { 1401 ta->ta_valp->at_str = NULL; 1402 } 1403 break; 1404 1405 case TIO_ATTR_BOOL: 1406 ta->ta_valp->at_val = tigetflag(ta->ta_name); 1407 break; 1408 1409 case TIO_ATTR_INT: 1410 ta->ta_valp->at_val = tigetnum(ta->ta_name); 1411 break; 1412 } 1413 } 1414 1415 /* 1416 * Copy attribute pointers from temporary struct into td->tio_info: 1417 */ 1418 bcopy(&termio_info, &td->tio_info, sizeof (termio_info_t)); 1419 1420 /* 1421 * Initialize the terminal size based on the terminfo database. If it 1422 * does not have the relevant properties, fall back to the environment 1423 * settings or to a hardcoded default. These settings will only be 1424 * used if we subsequently fail to derive the size with TIOCGWINSZ. 1425 */ 1426 td->tio_rows = MAX(td->tio_info.ti_lines.at_val, 0); 1427 td->tio_cols = MAX(td->tio_info.ti_cols.at_val, 0); 1428 1429 if (td->tio_rows == 0) { 1430 if ((str = getenv("LINES")) != NULL && strisnum(str) != 0 && 1431 (i = strtoi(str)) > 0) 1432 td->tio_rows = i; 1433 else 1434 td->tio_rows = TIO_DEFAULT_ROWS; 1435 } 1436 1437 if (td->tio_cols == 0) { 1438 if ((str = getenv("COLUMNS")) != NULL && strisnum(str) != 0 && 1439 (i = strtoi(str)) > 0) 1440 td->tio_cols = i; 1441 else 1442 td->tio_cols = TIO_DEFAULT_COLS; 1443 } 1444 1445 td->tio_flags = 0; 1446 1447 /* 1448 * We turn on TIO_AUTOWRAP if "am" (automargin) is set on the terminal. 1449 * Previously, this check was too smart for its own good, turning 1450 * TIO_AUTOWRAP on only when both "am" was set _and_ "xenl" was unset. 1451 * "xenl" is one of the (infamous?) so-called "Xanthippe glitches", 1452 * this one pertaining to the Concept 100's feature of not autowrapping 1453 * if the autowrap-inducing character is a newline. (That is, the 1454 * newline was "eaten" in this case -- and "xenl" accordingly stands 1455 * for "Xanthippe eat newline", which itself sounds like a command from 1456 * an Infocom game about line discipline set in ancient Greece.) This 1457 * (now removed) condition misunderstood the semantics of "xenl", 1458 * apparently inferring that its presence denoted that the terminal 1459 * could not autowrap at all. In fact, "xenl" is commonly set, and 1460 * denotes that the terminal will not autowrap on a newline -- a 1461 * feature (nee glitch) that is not particularly relevant with respect 1462 * to our input processing. (Ironically, disabling TIO_AUTOWRAP in 1463 * this case only results in correct-seeming output because of the 1464 * presence of the feature, as the inserted newline to force the wrap 1465 * isn't actually displayed; were it displayed, the input buffer would 1466 * appear double-spaced.) So with respect to "xenl" and autowrapping, 1467 * the correct behavior is to ignore it entirely, and adopt the 1468 * (simpler and less arcane) logic of setting TIO_AUTOWRAP if (and only 1469 * if) "am" is set on the terminal. This does not, however, mean that 1470 * "xenl" is meaningless; see below... 1471 */ 1472 if (td->tio_info.ti_am.at_val) 1473 td->tio_flags |= TIO_AUTOWRAP; 1474 1475 /* 1476 * While "xenl" doesn't dictate our TIO_AUTOWRAP setting, it does have 1477 * a subtle impact on the way we process input: in addition to its 1478 * eponymous behavior of eating newlines, "xenl" denotes a second, 1479 * entirely orthogonal idiosyncracy. As terminfo(5) tells it: "Those 1480 * terminals whose cursor remains on the right-most column until 1481 * another character has been received, rather than wrapping 1482 * immediately upon receiving the right- most character, such as the 1483 * VT100, should also indicate xenl." So yes, "xenl" in fact has two 1484 * entirely different meanings -- it is a glitch-within-a-glitch, in 1485 * what one assumes (hopes?) to be an accident of history rather than a 1486 * deliberate act of sabotage. This second behavior is relevant to us 1487 * because it affects the way we redraw the screen after a backspace in 1488 * the left-most column. We call this second behavior "lazy wrapping", 1489 * and we set it if (and only if) "xenl" is set on the terminal. 1490 */ 1491 if (td->tio_info.ti_xenl.at_val) 1492 td->tio_flags |= TIO_LAZYWRAP; 1493 1494 if (td->tio_info.ti_bw.at_val) 1495 td->tio_flags |= TIO_BACKLEFT; 1496 1497 if (td->tio_info.ti_smir.at_str != NULL || 1498 td->tio_info.ti_ich1.at_str != NULL) 1499 td->tio_flags |= TIO_INSERT; 1500 1501 if (mdb.m_flags & MDB_FL_USECUP) 1502 td->tio_flags |= TIO_USECUP; 1503 1504 if (name != NULL && (strncmp(name, "xterm", 5) == 0 || 1505 strcmp(name, "dtterm") == 0)) 1506 td->tio_flags |= TIO_XTERM; 1507 1508 /* 1509 * Optimizations for padding: (1) if no pad attribute is present, set 1510 * its value to "\0" to avoid testing later; (2) if no pad sequences 1511 * were found, force "npc" to TRUE so we pick the optimized tio_putp; 1512 * (3) if the padding baud property is not present, reset it to zero 1513 * since we need to compare it to an unsigned baud value. 1514 */ 1515 if (td->tio_info.ti_pad.at_str == NULL) 1516 td->tio_info.ti_pad.at_str = ""; /* \0 is the pad char */ 1517 1518 if (need_padding == 0) 1519 td->tio_info.ti_npc.at_val = TRUE; 1520 1521 if (td->tio_info.ti_npc.at_val) 1522 td->tio_putp = &termio_puts; 1523 else 1524 td->tio_putp = &termio_putp; 1525 1526 if (td->tio_info.ti_pb.at_val < 0) 1527 td->tio_info.ti_pb.at_val = 0; 1528 1529 /* 1530 * If no newline capability is available, assume \r\n will work. If no 1531 * carriage return capability is available, assume \r will work. 1532 */ 1533 if (td->tio_info.ti_nel.at_str == NULL) 1534 td->tio_info.ti_nel.at_str = "\r\n"; 1535 if (td->tio_info.ti_cr.at_str == NULL) 1536 td->tio_info.ti_cr.at_str = "\r"; 1537 1538 return (1); 1539 } 1540 1541 mdb_io_t * 1542 mdb_termio_create(const char *name, mdb_io_t *rio, mdb_io_t *wio) 1543 { 1544 struct termios otios; 1545 termio_data_t *td; 1546 int rv, err, i; 1547 1548 td = mdb_zalloc(sizeof (termio_data_t), UM_SLEEP); 1549 td->tio_io = mdb_alloc(sizeof (mdb_io_t), UM_SLEEP); 1550 1551 /* 1552 * Save the original user settings before calling setupterm(), which 1553 * cleverly changes them without telling us what it did or why. 1554 */ 1555 if (IOP_CTL(rio, TCGETS, &otios) == -1) { 1556 warn("failed to read terminal attributes for stdin"); 1557 goto err; 1558 } 1559 1560 rv = setupterm((char *)name, IOP_CTL(rio, MDB_IOC_GETFD, NULL), &err); 1561 IOP_CTL(rio, TCSETSW, &otios); /* undo setupterm() stupidity */ 1562 1563 if (rv == ERR) { 1564 if (err == 0) 1565 warn("no terminal data available for TERM=%s\n", name); 1566 else if (err == -1) 1567 warn("failed to locate terminfo database\n"); 1568 else 1569 warn("failed to initialize terminal (err=%d)\n", err); 1570 goto err; 1571 } 1572 1573 if (!termio_setup_attrs(td, name)) 1574 goto err; 1575 1576 /* 1577 * Do not re-issue terminal capability warnings when mdb re-execs. 1578 */ 1579 if (mdb.m_flags & MDB_FL_EXEC) 1580 td->tio_flags |= TIO_TTYWARN | TIO_CAPWARN; 1581 1582 /* 1583 * Initialize i/o structures and command-line buffer: 1584 */ 1585 td->tio_io->io_ops = &termio_ops; 1586 td->tio_io->io_data = td; 1587 td->tio_io->io_next = NULL; 1588 td->tio_io->io_refcnt = 0; 1589 1590 td->tio_in_io = rio; 1591 td->tio_in = mdb_iob_create(td->tio_in_io, MDB_IOB_RDONLY); 1592 1593 td->tio_out_io = wio; 1594 td->tio_out = mdb_iob_create(td->tio_out_io, MDB_IOB_WRONLY); 1595 mdb_iob_clrflags(td->tio_out, MDB_IOB_AUTOWRAP); 1596 1597 td->tio_link = NULL; 1598 mdb_cmdbuf_create(&td->tio_cmdbuf); 1599 1600 /* 1601 * Fill in all the keymap entries with the insert function: 1602 */ 1603 for (i = 0; i < KEY_MAX; i++) 1604 td->tio_keymap[i] = termio_insert; 1605 1606 /* 1607 * Now override selected entries with editing functions: 1608 */ 1609 td->tio_keymap['\n'] = termio_accept; 1610 td->tio_keymap['\r'] = termio_accept; 1611 1612 td->tio_keymap[CTRL('f')] = termio_fwdchar; 1613 td->tio_keymap[CTRL('b')] = termio_backchar; 1614 td->tio_keymap[CTRL('t')] = termio_transpose; 1615 td->tio_keymap[CTRL('a')] = termio_home; 1616 td->tio_keymap[CTRL('e')] = termio_end; 1617 td->tio_keymap[META('f')] = termio_fwdword; 1618 td->tio_keymap[META('b')] = termio_backword; 1619 td->tio_keymap[META('d')] = termio_killfwdword; 1620 td->tio_keymap[META('\b')] = termio_killbackword; 1621 td->tio_keymap[CTRL('k')] = termio_kill; 1622 td->tio_keymap[CTRL('p')] = termio_prevhist; 1623 td->tio_keymap[CTRL('n')] = termio_nexthist; 1624 td->tio_keymap[CTRL('r')] = termio_findhist; 1625 td->tio_keymap[CTRL('l')] = termio_refresh; 1626 td->tio_keymap[CTRL('d')] = termio_delchar; 1627 td->tio_keymap[CTRL('?')] = termio_widescreen; 1628 1629 td->tio_keymap[KPAD('A')] = termio_prevhist; 1630 td->tio_keymap[KPAD('B')] = termio_nexthist; 1631 td->tio_keymap[KPAD('C')] = termio_fwdchar; 1632 td->tio_keymap[KPAD('D')] = termio_backchar; 1633 1634 /* 1635 * Many modern terminal emulators treat the "Home" and "End" keys on a 1636 * PC keyboard as cursor keys. Some others use a multibyte function 1637 * key control sequence. We handle both styles here: 1638 */ 1639 td->tio_keymap[KPAD('H')] = termio_home; 1640 td->tio_keymap[FKEY('1')] = termio_home; 1641 td->tio_keymap[KPAD('F')] = termio_end; 1642 td->tio_keymap[FKEY('4')] = termio_end; 1643 1644 /* 1645 * We default both ASCII BS and DEL to termio_backspace for safety. We 1646 * want backspace to work whenever possible, regardless of whether or 1647 * not we're able to ask the terminal for the specific character that 1648 * it will use. kmdb, for example, is not able to make this request, 1649 * and must be prepared to accept both. 1650 */ 1651 td->tio_keymap[CTRL('h')] = termio_backspace; 1652 td->tio_keymap[KEY_DEL] = termio_backspace; 1653 1654 /* 1655 * Overrides for single-key accelerators 1656 */ 1657 td->tio_keymap['['] = termio_accel; 1658 td->tio_keymap[']'] = termio_accel; 1659 1660 /* 1661 * Grab tabs 1662 */ 1663 td->tio_keymap['\t'] = termio_tab; 1664 1665 td->tio_x = 0; 1666 td->tio_y = 0; 1667 td->tio_max_x = 0; 1668 td->tio_max_y = 0; 1669 1670 td->tio_active = FALSE; 1671 td->tio_rti_on = FALSE; 1672 td->tio_suspended = 1; 1673 1674 /* 1675 * Perform a resume operation to complete our terminal initialization, 1676 * and then adjust the keymap according to the terminal settings. 1677 */ 1678 termio_resume_tty(td, &td->tio_ptios); 1679 bcopy(&td->tio_ptios, &td->tio_ctios, sizeof (struct termios)); 1680 1681 td->tio_keymap[td->tio_intr] = termio_intr; 1682 td->tio_keymap[td->tio_quit] = termio_quit; 1683 td->tio_keymap[td->tio_erase] = termio_backspace; 1684 td->tio_keymap[td->tio_werase] = termio_killbackword; 1685 td->tio_keymap[td->tio_kill] = termio_reset; 1686 td->tio_keymap[td->tio_susp] = termio_susp; 1687 1688 (void) mdb_signal_sethandler(SIGWINCH, termio_winch, td); 1689 (void) mdb_signal_sethandler(SIGTSTP, termio_tstp, td); 1690 1691 if (mdb.m_debug & MDB_DBG_CMDBUF) 1692 termio_dump(td, &termio_attrs[0]); 1693 1694 return (td->tio_io); 1695 1696 err: 1697 mdb_free(td->tio_io, sizeof (mdb_io_t)); 1698 mdb_free(td, sizeof (termio_data_t)); 1699 1700 return (NULL); 1701 } 1702 1703 int 1704 mdb_iob_isatty(mdb_iob_t *iob) 1705 { 1706 mdb_io_t *io; 1707 1708 if (iob->iob_flags & MDB_IOB_TTYLIKE) 1709 return (1); 1710 1711 for (io = iob->iob_iop; io != NULL; io = io->io_next) { 1712 if (io->io_ops == &termio_ops) 1713 return (1); 1714 } 1715 1716 return (0); 1717 } 1718 1719 static const char * 1720 termio_insert(termio_data_t *td, int c) 1721 { 1722 size_t olen = td->tio_cmdbuf.cmd_buflen; 1723 1724 if (mdb_cmdbuf_insert(&td->tio_cmdbuf, c) == 0) { 1725 if (mdb_cmdbuf_atend(&td->tio_cmdbuf)) 1726 termio_addch(td, c, td->tio_cmdbuf.cmd_buflen - olen); 1727 else 1728 termio_insch(td, c, td->tio_cmdbuf.cmd_buflen - olen); 1729 } 1730 1731 return (NULL); 1732 } 1733 1734 static const char * 1735 termio_accept(termio_data_t *td, int c) 1736 { 1737 if (td->tio_flags & TIO_FINDHIST) { 1738 (void) mdb_cmdbuf_findhist(&td->tio_cmdbuf, c); 1739 1740 td->tio_prompt = mdb.m_prompt; 1741 td->tio_promptlen = mdb.m_promptlen; 1742 td->tio_flags &= ~TIO_FINDHIST; 1743 1744 termio_redraw(td); 1745 return (NULL); 1746 } 1747 1748 /* Ensure that the cursor is at the end of the line */ 1749 (void) termio_end(td, c); 1750 1751 return (mdb_cmdbuf_accept(&td->tio_cmdbuf)); 1752 } 1753 1754 static const char * 1755 termio_backspace(termio_data_t *td, int c) 1756 { 1757 if (mdb_cmdbuf_backspace(&td->tio_cmdbuf, c) == 0) { 1758 if (mdb_cmdbuf_atend(&td->tio_cmdbuf)) 1759 termio_bspch(td); 1760 else 1761 termio_redraw(td); 1762 } 1763 1764 return (NULL); 1765 } 1766 1767 /* 1768 * This function may end up calling termio_read recursively as part of invoking 1769 * the mdb pager. To work around this fact, we need to go through and make sure 1770 * that we change the underlying terminal settings before and after this 1771 * function call. If we don't do this, we invoke the pager, and don't abort 1772 * (which will longjmp us elsewhere) we're going to return to the read loop with 1773 * the wrong termio settings. 1774 * 1775 * Furthermore, because of the fact that we're being invoked in a user context 1776 * that allows us to be interrupted, we need to actually allocate the memory 1777 * that we're using with GC so that it gets cleaned up in case of the pager 1778 * resetting us and never reaching the end. 1779 */ 1780 /*ARGSUSED*/ 1781 static const char * 1782 termio_tab(termio_data_t *td, int c) 1783 { 1784 char *buf; 1785 const char *result; 1786 int nres; 1787 mdb_tab_cookie_t *mtp; 1788 1789 if (termio_ctl(td->tio_io, TCSETSW, &td->tio_dtios) == -1) 1790 warn("failed to restore terminal attributes"); 1791 1792 buf = mdb_alloc(td->tio_cmdbuf.cmd_bufidx + 1, UM_SLEEP | UM_GC); 1793 (void) strncpy(buf, td->tio_cmdbuf.cmd_buf, td->tio_cmdbuf.cmd_bufidx); 1794 buf[td->tio_cmdbuf.cmd_bufidx] = '\0'; 1795 td->tio_flags |= TIO_TAB; 1796 mtp = mdb_tab_init(); 1797 nres = mdb_tab_command(mtp, buf); 1798 1799 if (nres == 0) { 1800 result = NULL; 1801 } else { 1802 result = mdb_tab_match(mtp); 1803 if (nres != 1) { 1804 mdb_printf("\n"); 1805 mdb_tab_print(mtp); 1806 } 1807 } 1808 1809 if (result != NULL) { 1810 int index = 0; 1811 1812 while (result[index] != '\0') { 1813 (void) termio_insert(td, result[index]); 1814 index++; 1815 } 1816 } 1817 1818 termio_redraw(td); 1819 mdb_tab_fini(mtp); 1820 td->tio_flags &= ~TIO_TAB; 1821 if (termio_ctl(td->tio_io, TCSETSW, &td->tio_rtios) == -1) 1822 warn("failed to set terminal attributes"); 1823 1824 1825 return (NULL); 1826 } 1827 1828 static const char * 1829 termio_delchar(termio_data_t *td, int c) 1830 { 1831 if (!(mdb.m_flags & MDB_FL_IGNEOF) && 1832 mdb_cmdbuf_atend(&td->tio_cmdbuf) && 1833 mdb_cmdbuf_atstart(&td->tio_cmdbuf)) 1834 return (termio_quit(td, c)); 1835 1836 if (mdb_cmdbuf_delchar(&td->tio_cmdbuf, c) == 0) { 1837 if (mdb_cmdbuf_atend(&td->tio_cmdbuf)) 1838 termio_delch(td); 1839 else 1840 termio_redraw(td); 1841 } 1842 1843 return (NULL); 1844 } 1845 1846 static const char * 1847 termio_fwdchar(termio_data_t *td, int c) 1848 { 1849 if (mdb_cmdbuf_fwdchar(&td->tio_cmdbuf, c) == 0) 1850 termio_mvcur(td); 1851 1852 return (NULL); 1853 } 1854 1855 static const char * 1856 termio_backchar(termio_data_t *td, int c) 1857 { 1858 if (mdb_cmdbuf_backchar(&td->tio_cmdbuf, c) == 0) 1859 termio_mvcur(td); 1860 1861 return (NULL); 1862 } 1863 1864 static const char * 1865 termio_transpose(termio_data_t *td, int c) 1866 { 1867 if (mdb_cmdbuf_transpose(&td->tio_cmdbuf, c) == 0) 1868 termio_redraw(td); 1869 1870 return (NULL); 1871 } 1872 1873 static const char * 1874 termio_home(termio_data_t *td, int c) 1875 { 1876 if (mdb_cmdbuf_home(&td->tio_cmdbuf, c) == 0) 1877 termio_mvcur(td); 1878 1879 return (NULL); 1880 } 1881 1882 static const char * 1883 termio_end(termio_data_t *td, int c) 1884 { 1885 if (mdb_cmdbuf_end(&td->tio_cmdbuf, c) == 0) 1886 termio_mvcur(td); 1887 1888 return (NULL); 1889 } 1890 1891 static const char * 1892 termio_fwdword(termio_data_t *td, int c) 1893 { 1894 if (mdb_cmdbuf_fwdword(&td->tio_cmdbuf, c) == 0) 1895 termio_mvcur(td); 1896 1897 return (NULL); 1898 } 1899 1900 static const char * 1901 termio_backword(termio_data_t *td, int c) 1902 { 1903 if (mdb_cmdbuf_backword(&td->tio_cmdbuf, c) == 0) 1904 termio_mvcur(td); 1905 1906 return (NULL); 1907 } 1908 1909 static const char * 1910 termio_kill(termio_data_t *td, int c) 1911 { 1912 if (mdb_cmdbuf_kill(&td->tio_cmdbuf, c) == 0) 1913 termio_redraw(td); 1914 1915 return (NULL); 1916 } 1917 1918 static const char * 1919 termio_killfwdword(termio_data_t *td, int c) 1920 { 1921 if (mdb_cmdbuf_killfwdword(&td->tio_cmdbuf, c) == 0) 1922 termio_redraw(td); 1923 1924 return (NULL); 1925 } 1926 1927 static const char * 1928 termio_killbackword(termio_data_t *td, int c) 1929 { 1930 if (mdb_cmdbuf_killbackword(&td->tio_cmdbuf, c) == 0) 1931 termio_redraw(td); 1932 1933 return (NULL); 1934 } 1935 1936 static const char * 1937 termio_reset(termio_data_t *td, int c) 1938 { 1939 if (mdb_cmdbuf_reset(&td->tio_cmdbuf, c) == 0) 1940 termio_clear(td); 1941 1942 return (NULL); 1943 } 1944 1945 /*ARGSUSED*/ 1946 static const char * 1947 termio_widescreen(termio_data_t *td, int c) 1948 { 1949 if (td->tio_flags & TIO_XTERM) { 1950 if (td->tio_cols == 80) 1951 termio_tput(td, TI_DECSET(TI_DECCOLM), 1); 1952 else 1953 termio_tput(td, TI_DECRST(TI_DECCOLM), 1); 1954 mdb_iob_flush(td->tio_out); 1955 termio_winch(SIGWINCH, NULL, NULL, td); 1956 } 1957 1958 return (NULL); 1959 } 1960 1961 static const char * 1962 termio_prevhist(termio_data_t *td, int c) 1963 { 1964 if (mdb_cmdbuf_prevhist(&td->tio_cmdbuf, c) == 0) 1965 termio_redraw(td); 1966 1967 return (NULL); 1968 } 1969 1970 static const char * 1971 termio_nexthist(termio_data_t *td, int c) 1972 { 1973 if (mdb_cmdbuf_nexthist(&td->tio_cmdbuf, c) == 0) 1974 termio_redraw(td); 1975 1976 return (NULL); 1977 } 1978 1979 /* 1980 * Single-key accelerator support. Several commands are so commonly used as to 1981 * require a single-key equivalent. If we see one of these accelerator 1982 * characters at the beginning of an otherwise-empty line, we'll replace it with 1983 * the expansion. 1984 */ 1985 static const char * 1986 termio_accel(termio_data_t *td, int c) 1987 { 1988 const char *p; 1989 1990 if (td->tio_cmdbuf.cmd_buflen != 0 || 1991 (p = termio_accel_lookup(c)) == NULL) 1992 return (termio_insert(td, c)); 1993 1994 while (*p != '\0') 1995 (void) termio_insert(td, *p++); 1996 return (termio_accept(td, '\n')); 1997 } 1998 1999 static const char * 2000 termio_findhist(termio_data_t *td, int c) 2001 { 2002 if (mdb_cmdbuf_reset(&td->tio_cmdbuf, c) == 0) { 2003 td->tio_prompt = "Search: "; 2004 td->tio_promptlen = strlen(td->tio_prompt); 2005 td->tio_flags |= TIO_FINDHIST; 2006 termio_redraw(td); 2007 } 2008 2009 return (NULL); 2010 } 2011 2012 /*ARGSUSED*/ 2013 static const char * 2014 termio_refresh(termio_data_t *td, int c) 2015 { 2016 if (td->tio_info.ti_clear.at_str) { 2017 termio_tput(td, td->tio_info.ti_clear.at_str, 1); 2018 td->tio_x = td->tio_y = 0; 2019 } 2020 termio_redraw(td); 2021 return (NULL); 2022 } 2023 2024 /* 2025 * Leave the terminal read code by longjmp'ing up the stack of mdb_frame_t's 2026 * back to the main parsing loop (see mdb_run() in mdb.c). 2027 */ 2028 static const char * 2029 termio_abort(termio_data_t *td, int c, int err) 2030 { 2031 (void) mdb_cmdbuf_reset(&td->tio_cmdbuf, c); 2032 td->tio_active = FALSE; 2033 td->tio_rti_on = FALSE; 2034 2035 if (termio_ctl(td->tio_io, TCSETSW, &td->tio_dtios) == -1) 2036 warn("failed to restore terminal attributes"); 2037 2038 longjmp(mdb.m_frame->f_pcb, err); 2039 /*NOTREACHED*/ 2040 return (NULL); 2041 } 2042 2043 static const char * 2044 termio_intr(termio_data_t *td, int c) 2045 { 2046 return (termio_abort(td, c, MDB_ERR_SIGINT)); 2047 } 2048 2049 static const char * 2050 termio_quit(termio_data_t *td, int c) 2051 { 2052 return (termio_abort(td, c, MDB_ERR_QUIT)); 2053 } 2054 2055 /*ARGSUSED*/ 2056 static const char * 2057 termio_susp(termio_data_t *td, int c) 2058 { 2059 (void) mdb_signal_sethandler(SIGWINCH, SIG_IGN, NULL); 2060 (void) mdb_signal_sethandler(SIGTSTP, SIG_IGN, NULL); 2061 2062 termio_suspend_tty(td, &td->tio_ptios); 2063 mdb_iob_nl(td->tio_out); 2064 2065 (void) mdb_signal_sethandler(SIGTSTP, SIG_DFL, NULL); 2066 (void) mdb_signal_pgrp(SIGTSTP); 2067 2068 /* 2069 * When we call mdb_signal_pgrp(SIGTSTP), we are expecting the entire 2070 * debugger process group to be stopped by the kernel. Once we return 2071 * from that call, we assume we are resuming from a subsequent SIGCONT. 2072 */ 2073 (void) mdb_signal_sethandler(SIGTSTP, SIG_IGN, NULL); 2074 termio_resume_tty(td, &td->tio_ptios); 2075 2076 (void) mdb_signal_sethandler(SIGWINCH, termio_winch, td); 2077 (void) mdb_signal_sethandler(SIGTSTP, termio_tstp, td); 2078 2079 if (td->tio_active) 2080 siglongjmp(td->tio_env, SIGCONT); 2081 2082 return (NULL); 2083 } 2084 2085 /*ARGSUSED*/ 2086 static void 2087 termio_winch(int sig, siginfo_t *sip, ucontext_t *ucp, void *data) 2088 { 2089 termio_data_t *td = data; 2090 mdb_bool_t change = FALSE; 2091 struct winsize winsz; 2092 2093 if (termio_ctl(td->tio_io, TIOCGWINSZ, &winsz) == -1) 2094 return; /* just ignore this WINCH if the ioctl fails */ 2095 2096 if (td->tio_rows != (size_t)winsz.ws_row || 2097 td->tio_cols != (size_t)winsz.ws_col) { 2098 2099 if (td->tio_active) 2100 termio_clear(td); 2101 2102 if (winsz.ws_row != 0) 2103 td->tio_rows = (size_t)winsz.ws_row; 2104 2105 if (winsz.ws_col != 0) 2106 td->tio_cols = (size_t)winsz.ws_col; 2107 2108 if (td->tio_active) 2109 termio_clear(td); 2110 2111 mdb_iob_resize(td->tio_out, td->tio_rows, td->tio_cols); 2112 change = TRUE; 2113 } 2114 2115 if (change && td->tio_active) 2116 siglongjmp(td->tio_env, sig); 2117 2118 if (change && td->tio_link != NULL) 2119 mdb_iob_resize(td->tio_link, td->tio_rows, td->tio_cols); 2120 } 2121 2122 /*ARGSUSED*/ 2123 static void 2124 termio_tstp(int sig, siginfo_t *sip, ucontext_t *ucp, void *data) 2125 { 2126 (void) termio_susp(data, CTRL('Z')); 2127 } 2128