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