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