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