]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/ddb/db_input.c
ng_ppp(4): Fix a typo in a comment
[FreeBSD/FreeBSD.git] / 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 }