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