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