FreeBSD/Linux Kernel Cross Reference
sys/ddb/db_input.c
1 /*-
2 * SPDX-License-Identifier: MIT-CMU
3 *
4 * Mach Operating System
5 * Copyright (c) 1991,1990 Carnegie Mellon University
6 * All Rights Reserved.
7 *
8 * Permission to use, copy, modify and distribute this software and its
9 * documentation is hereby granted, provided that both the copyright
10 * notice and this permission notice appear in all copies of the
11 * software, derivative works or modified versions, and any portions
12 * thereof, and that both notices appear in supporting documentation.
13 *
14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17 *
18 * Carnegie Mellon requests users of this software to return to
19 *
20 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
21 * School of Computer Science
22 * Carnegie Mellon University
23 * Pittsburgh PA 15213-3890
24 *
25 * any improvements or extensions that they make and grant Carnegie the
26 * rights to redistribute these changes.
27 */
28 /*
29 * Author: David B. Golub, Carnegie Mellon University
30 * Date: 7/90
31 */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/cons.h>
39 #include <sys/sysctl.h>
40
41 #include <ddb/ddb.h>
42 #include <ddb/db_output.h>
43
44 /*
45 * Character input and editing.
46 */
47
48 /*
49 * We don't track output position while editing input,
50 * since input always ends with a new-line. We just
51 * reset the line position at the end.
52 */
53 static char * db_lbuf_start; /* start of input line buffer */
54 static char * db_lbuf_end; /* end of input line buffer */
55 static char * db_lc; /* current character */
56 static char * db_le; /* one past last character */
57
58 /*
59 * Raw input buffer, processed only for certain control characters.
60 */
61 #define DB_RAW_SIZE 512
62 static char db_raw[DB_RAW_SIZE];
63 static u_int db_raw_pos;
64 static u_int db_raw_cnt;
65 static int db_raw_warned;
66 static int ddb_prioritize_control_input = 1;
67 SYSCTL_INT(_debug_ddb, OID_AUTO, prioritize_control_input, CTLFLAG_RWTUN,
68 &ddb_prioritize_control_input, 0,
69 "Drop input when the buffer fills in order to keep servicing ^C/^S/^Q");
70
71 /*
72 * Simple input line history support.
73 */
74 static char db_lhistory[2048];
75 static int db_lhistlsize, db_lhistidx, db_lhistcur;
76 static int db_lhist_nlines;
77
78 #define CTRL(c) ((c) & 0x1f)
79 #define BLANK ' '
80 #define BACKUP '\b'
81
82 static void db_delete(int n, int bwd);
83 static int db_inputchar(int c);
84 static void db_putnchars(int c, int count);
85 static void db_putstring(char *s, int count);
86 static int db_raw_pop(void);
87 static void db_raw_push(int);
88 static int db_raw_space(void);
89
90 static void
91 db_putstring(s, count)
92 char *s;
93 int count;
94 {
95 while (--count >= 0)
96 cnputc(*s++);
97 }
98
99 static void
100 db_putnchars(c, count)
101 int c;
102 int count;
103 {
104 while (--count >= 0)
105 cnputc(c);
106 }
107
108 /*
109 * Delete N characters, forward or backward
110 */
111 #define DEL_FWD 0
112 #define DEL_BWD 1
113 static void
114 db_delete(n, bwd)
115 int n;
116 int bwd;
117 {
118 char *p;
119
120 if (bwd) {
121 db_lc -= n;
122 db_putnchars(BACKUP, n);
123 }
124 for (p = db_lc; p < db_le-n; p++) {
125 *p = *(p+n);
126 cnputc(*p);
127 }
128 db_putnchars(BLANK, n);
129 db_putnchars(BACKUP, db_le - db_lc);
130 db_le -= n;
131 }
132
133 /* returns true at end-of-line */
134 static int
135 db_inputchar(c)
136 int c;
137 {
138 static int escstate;
139
140 if (escstate == 1) {
141 /* ESC seen, look for [ or O */
142 if (c == '[' || c == 'O')
143 escstate++;
144 else
145 escstate = 0; /* re-init state machine */
146 return (0);
147 } else if (escstate == 2) {
148 escstate = 0;
149 /*
150 * If a valid cursor key has been found, translate
151 * into an emacs-style control key, and fall through.
152 * Otherwise, drop off.
153 */
154 switch (c) {
155 case 'A': /* up */
156 c = CTRL('p');
157 break;
158 case 'B': /* down */
159 c = CTRL('n');
160 break;
161 case 'C': /* right */
162 c = CTRL('f');
163 break;
164 case 'D': /* left */
165 c = CTRL('b');
166 break;
167 default:
168 return (0);
169 }
170 }
171
172 switch (c) {
173 case CTRL('['):
174 escstate = 1;
175 break;
176 case CTRL('b'):
177 /* back up one character */
178 if (db_lc > db_lbuf_start) {
179 cnputc(BACKUP);
180 db_lc--;
181 }
182 break;
183 case CTRL('f'):
184 /* forward one character */
185 if (db_lc < db_le) {
186 cnputc(*db_lc);
187 db_lc++;
188 }
189 break;
190 case CTRL('a'):
191 /* beginning of line */
192 while (db_lc > db_lbuf_start) {
193 cnputc(BACKUP);
194 db_lc--;
195 }
196 break;
197 case CTRL('e'):
198 /* end of line */
199 while (db_lc < db_le) {
200 cnputc(*db_lc);
201 db_lc++;
202 }
203 break;
204 case CTRL('h'):
205 case 0177:
206 /* erase previous character */
207 if (db_lc > db_lbuf_start)
208 db_delete(1, DEL_BWD);
209 break;
210 case CTRL('d'):
211 /* erase next character */
212 if (db_lc < db_le)
213 db_delete(1, DEL_FWD);
214 break;
215 case CTRL('u'):
216 case CTRL('c'):
217 /* kill entire line: */
218 /* at first, delete to beginning of line */
219 if (db_lc > db_lbuf_start)
220 db_delete(db_lc - db_lbuf_start, DEL_BWD);
221 /* FALLTHROUGH */
222 case CTRL('k'):
223 /* delete to end of line */
224 if (db_lc < db_le)
225 db_delete(db_le - db_lc, DEL_FWD);
226 break;
227 case CTRL('t'):
228 /* twiddle last 2 characters */
229 if (db_lc >= db_lbuf_start + 2) {
230 c = db_lc[-2];
231 db_lc[-2] = db_lc[-1];
232 db_lc[-1] = c;
233 cnputc(BACKUP);
234 cnputc(BACKUP);
235 cnputc(db_lc[-2]);
236 cnputc(db_lc[-1]);
237 }
238 break;
239 case CTRL('w'):
240 /* erase previous word */
241 for (; db_lc > db_lbuf_start;) {
242 if (*(db_lc - 1) != ' ')
243 break;
244 db_delete(1, DEL_BWD);
245 }
246 for (; db_lc > db_lbuf_start;) {
247 if (*(db_lc - 1) == ' ')
248 break;
249 db_delete(1, DEL_BWD);
250 }
251 break;
252 case CTRL('r'):
253 db_putstring("^R\n", 3);
254 redraw:
255 if (db_le > db_lbuf_start) {
256 db_putstring(db_lbuf_start, db_le - db_lbuf_start);
257 db_putnchars(BACKUP, db_le - db_lc);
258 }
259 break;
260 case CTRL('p'):
261 /* Make previous history line the active one. */
262 if (db_lhistcur >= 0) {
263 bcopy(db_lhistory + db_lhistcur * db_lhistlsize,
264 db_lbuf_start, db_lhistlsize);
265 db_lhistcur--;
266 goto hist_redraw;
267 }
268 break;
269 case CTRL('n'):
270 /* Make next history line the active one. */
271 if (db_lhistcur < db_lhistidx - 1) {
272 db_lhistcur += 2;
273 bcopy(db_lhistory + db_lhistcur * db_lhistlsize,
274 db_lbuf_start, db_lhistlsize);
275 } else {
276 /*
277 * ^N through tail of history, reset the
278 * buffer to zero length.
279 */
280 *db_lbuf_start = '\0';
281 db_lhistcur = db_lhistidx;
282 }
283
284 hist_redraw:
285 db_putnchars(BACKUP, db_lc - db_lbuf_start);
286 db_putnchars(BLANK, db_le - db_lbuf_start);
287 db_putnchars(BACKUP, db_le - db_lbuf_start);
288 db_le = strchr(db_lbuf_start, '\0');
289 if (db_le[-1] == '\r' || db_le[-1] == '\n')
290 *--db_le = '\0';
291 db_lc = db_le;
292 goto redraw;
293
294 case -1:
295 /*
296 * eek! the console returned eof.
297 * probably that means we HAVE no console.. we should try bail
298 * XXX
299 */
300 c = '\r';
301 case '\n':
302 /* FALLTHROUGH */
303 case '\r':
304 *db_le++ = c;
305 return (1);
306 default:
307 if (db_le == db_lbuf_end) {
308 cnputc('\007');
309 }
310 else if (c >= ' ' && c <= '~') {
311 char *p;
312
313 for (p = db_le; p > db_lc; p--)
314 *p = *(p-1);
315 *db_lc++ = c;
316 db_le++;
317 cnputc(c);
318 db_putstring(db_lc, db_le - db_lc);
319 db_putnchars(BACKUP, db_le - db_lc);
320 }
321 break;
322 }
323 return (0);
324 }
325
326 /* Get a character from the console, first checking the raw input buffer. */
327 int
328 db_getc(void)
329 {
330 int c;
331
332 if (db_raw_cnt == 0) {
333 c = cngetc();
334 } else {
335 c = db_raw_pop();
336 if (c == '\r')
337 c = '\n';
338 }
339 return (c);
340 }
341
342 /* Whether the raw input buffer has space to accept another character. */
343 static int
344 db_raw_space(void)
345 {
346
347 return (db_raw_cnt < DB_RAW_SIZE);
348 }
349
350 /* Un-get a character from the console by buffering it. */
351 static void
352 db_raw_push(int c)
353 {
354
355 if (!db_raw_space())
356 db_error(NULL);
357 db_raw[(db_raw_pos + db_raw_cnt++) % DB_RAW_SIZE] = c;
358 }
359
360 /* Drain a character from the raw input buffer. */
361 static int
362 db_raw_pop(void)
363 {
364
365 if (db_raw_cnt == 0)
366 return (-1);
367 db_raw_cnt--;
368 db_raw_warned = 0;
369 return (db_raw[db_raw_pos++ % DB_RAW_SIZE]);
370 }
371
372 int
373 db_readline(lstart, lsize)
374 char * lstart;
375 int lsize;
376 {
377
378 if (lsize < 2)
379 return (0);
380 if (lsize != db_lhistlsize) {
381 /*
382 * (Re)initialize input line history. Throw away any
383 * existing history.
384 */
385 db_lhist_nlines = sizeof(db_lhistory) / lsize;
386 db_lhistlsize = lsize;
387 db_lhistidx = -1;
388 }
389 db_lhistcur = db_lhistidx;
390
391 db_force_whitespace(); /* synch output position */
392
393 db_lbuf_start = lstart;
394 db_lbuf_end = lstart + lsize - 2; /* Will append NL and NUL. */
395 db_lc = lstart;
396 db_le = lstart;
397
398 while (!db_inputchar(db_getc()))
399 continue;
400
401 db_capture_write(lstart, db_le - db_lbuf_start);
402 db_printf("\n"); /* synch output position */
403 *db_le = 0;
404
405 if (db_le - db_lbuf_start > 1) {
406 /* Maintain input line history for non-empty lines. */
407 if (++db_lhistidx == db_lhist_nlines) {
408 /* Rotate history. */
409 bcopy(db_lhistory + db_lhistlsize, db_lhistory,
410 db_lhistlsize * (db_lhist_nlines - 1));
411 db_lhistidx--;
412 }
413 bcopy(lstart, db_lhistory + db_lhistidx * db_lhistlsize,
414 db_lhistlsize);
415 }
416
417 return (db_le - db_lbuf_start);
418 }
419
420 static void
421 db_do_interrupt(const char *reason)
422 {
423
424 /* Do a pager quit too because some commands have jmpbuf handling. */
425 db_disable_pager();
426 db_pager_quit = 1;
427 db_error(reason);
428 }
429
430 void
431 db_check_interrupt(void)
432 {
433 int c;
434
435 /*
436 * Check console input for control characters. Non-control input is
437 * buffered. When buffer space is exhausted, either stop responding to
438 * control input or drop further non-control input on the floor.
439 */
440 for (;;) {
441 if (!ddb_prioritize_control_input && !db_raw_space())
442 return;
443 c = cncheckc();
444 switch (c) {
445 case -1: /* no character */
446 return;
447
448 case CTRL('c'):
449 db_do_interrupt("^C");
450 /*NOTREACHED*/
451
452 case CTRL('s'):
453 do {
454 c = cncheckc();
455 if (c == CTRL('c'))
456 db_do_interrupt("^C");
457 } while (c != CTRL('q'));
458 break;
459
460 default:
461 if (db_raw_space()) {
462 db_raw_push(c);
463 } else if (!db_raw_warned) {
464 db_raw_warned = 1;
465 db_printf("\n--Exceeded input buffer--\n");
466 }
467 break;
468 }
469 }
470 }
Cache object: c695592d7b3ca5db63c48bc4b9cc9b15
|