Merge from vendor branch BINUTILS:
[dragonfly.git] / sys / ddb / db_input.c
1 /*
2  * Mach Operating System
3  * Copyright (c) 1991,1990 Carnegie Mellon University
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify and distribute this software and its
7  * documentation is hereby granted, provided that both the copyright
8  * notice and this permission notice appear in all copies of the
9  * software, derivative works or modified versions, and any portions
10  * thereof, and that both notices appear in supporting documentation.
11  *
12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15  *
16  * Carnegie Mellon requests users of this software to return to
17  *
18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19  *  School of Computer Science
20  *  Carnegie Mellon University
21  *  Pittsburgh PA 15213-3890
22  *
23  * any improvements or extensions that they make and grant Carnegie the
24  * rights to redistribute these changes.
25  *
26  * $FreeBSD: src/sys/ddb/db_input.c,v 1.28.2.1 2002/03/08 16:37:10 yar Exp $
27  * $DragonFly: src/sys/ddb/db_input.c,v 1.4 2003/08/27 10:47:13 rob Exp $
28  */
29
30 /*
31  *      Author: David B. Golub, Carnegie Mellon University
32  *      Date:   7/90
33  */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/cons.h>
38
39 #include <ddb/ddb.h>
40 #include <ddb/db_output.h>
41
42 /*
43  * Character input and editing.
44  */
45
46 /*
47  * We don't track output position while editing input,
48  * since input always ends with a new-line.  We just
49  * reset the line position at the end.
50  */
51 static char *   db_lbuf_start;  /* start of input line buffer */
52 static char *   db_lbuf_end;    /* end of input line buffer */
53 static char *   db_lc;          /* current character */
54 static char *   db_le;          /* one past last character */
55
56 /*
57  * Simple input line history support.
58  */
59 static char     db_lhistory[2048];
60 static int      db_lhistlsize, db_lhistidx, db_lhistcur;
61 static int      db_lhist_nlines;
62
63 #define CTRL(c)         ((c) & 0x1f)
64 #define BLANK           ' '
65 #define BACKUP          '\b'
66
67 static int      cnmaygetc (void);
68 static void     db_delete (int n, int bwd);
69 static int      db_inputchar (int c);
70 static void     db_putnchars (int c, int count);
71 static void     db_putstring (char *s, int count);
72
73 void
74 db_putstring(s, count)
75         char    *s;
76         int     count;
77 {
78         while (--count >= 0)
79             cnputc(*s++);
80 }
81
82 void
83 db_putnchars(c, count)
84         int     c;
85         int     count;
86 {
87         while (--count >= 0)
88             cnputc(c);
89 }
90
91 /*
92  * Delete N characters, forward or backward
93  */
94 #define DEL_FWD         0
95 #define DEL_BWD         1
96 void
97 db_delete(n, bwd)
98         int     n;
99         int     bwd;
100 {
101         char *p;
102
103         if (bwd) {
104             db_lc -= n;
105             db_putnchars(BACKUP, n);
106         }
107         for (p = db_lc; p < db_le-n; p++) {
108             *p = *(p+n);
109             cnputc(*p);
110         }
111         db_putnchars(BLANK, n);
112         db_putnchars(BACKUP, db_le - db_lc);
113         db_le -= n;
114 }
115
116 /* returns TRUE at end-of-line */
117 int
118 db_inputchar(c)
119         int     c;
120 {
121         static int escstate;
122
123         if (escstate == 1) {
124                 /* ESC seen, look for [ or O */
125                 if (c == '[' || c == 'O')
126                         escstate++;
127                 else
128                         escstate = 0; /* re-init state machine */
129                 return (0);
130         } else if (escstate == 2) {
131                 escstate = 0;
132                 /*
133                  * If a valid cursor key has been found, translate
134                  * into an emacs-style control key, and fall through.
135                  * Otherwise, drop off.
136                  */
137                 switch (c) {
138                 case 'A':       /* up */
139                         c = CTRL('p');
140                         break;
141                 case 'B':       /* down */
142                         c = CTRL('n');
143                         break;
144                 case 'C':       /* right */
145                         c = CTRL('f');
146                         break;
147                 case 'D':       /* left */
148                         c = CTRL('b');
149                         break;
150                 default:
151                         return (0);
152                 }
153         }
154
155         switch (c) {
156             case CTRL('['):
157                 escstate = 1;
158                 break;
159             case CTRL('b'):
160                 /* back up one character */
161                 if (db_lc > db_lbuf_start) {
162                     cnputc(BACKUP);
163                     db_lc--;
164                 }
165                 break;
166             case CTRL('f'):
167                 /* forward one character */
168                 if (db_lc < db_le) {
169                     cnputc(*db_lc);
170                     db_lc++;
171                 }
172                 break;
173             case CTRL('a'):
174                 /* beginning of line */
175                 while (db_lc > db_lbuf_start) {
176                     cnputc(BACKUP);
177                     db_lc--;
178                 }
179                 break;
180             case CTRL('e'):
181                 /* end of line */
182                 while (db_lc < db_le) {
183                     cnputc(*db_lc);
184                     db_lc++;
185                 }
186                 break;
187             case CTRL('h'):
188             case 0177:
189                 /* erase previous character */
190                 if (db_lc > db_lbuf_start)
191                     db_delete(1, DEL_BWD);
192                 break;
193             case CTRL('d'):
194                 /* erase next character */
195                 if (db_lc < db_le)
196                     db_delete(1, DEL_FWD);
197                 break;
198             case CTRL('u'):
199                 /* kill entire line: */
200                 /* at first, delete to beginning of line */
201                 if (db_lc > db_lbuf_start)
202                     db_delete(db_lc - db_lbuf_start, DEL_BWD);
203                 /* FALLTHROUGH */
204             case CTRL('k'):
205                 /* delete to end of line */
206                 if (db_lc < db_le)
207                     db_delete(db_le - db_lc, DEL_FWD);
208                 break;
209             case CTRL('t'):
210                 /* twiddle last 2 characters */
211                 if (db_lc >= db_lbuf_start + 2) {
212                     c = db_lc[-2];
213                     db_lc[-2] = db_lc[-1];
214                     db_lc[-1] = c;
215                     cnputc(BACKUP);
216                     cnputc(BACKUP);
217                     cnputc(db_lc[-2]);
218                     cnputc(db_lc[-1]);
219                 }
220                 break;
221             case CTRL('r'):
222                 db_putstring("^R\n", 3);
223             redraw:
224                 if (db_le > db_lbuf_start) {
225                     db_putstring(db_lbuf_start, db_le - db_lbuf_start);
226                     db_putnchars(BACKUP, db_le - db_lc);
227                 }
228                 break;
229             case CTRL('p'):
230                 /* Make previous history line the active one. */
231                 if (db_lhistcur >= 0) {
232                     bcopy(db_lhistory + db_lhistcur * db_lhistlsize,
233                           db_lbuf_start, db_lhistlsize);
234                     db_lhistcur--;
235                     goto hist_redraw;
236                 }
237                 break;
238             case CTRL('n'):
239                 /* Make next history line the active one. */
240                 if (db_lhistcur < db_lhistidx - 1) {
241                     db_lhistcur += 2;
242                     bcopy(db_lhistory + db_lhistcur * db_lhistlsize,
243                           db_lbuf_start, db_lhistlsize);
244                 } else {
245                     /*
246                      * ^N through tail of history, reset the
247                      * buffer to zero length.
248                      */
249                     *db_lbuf_start = '\0';
250                     db_lhistcur = db_lhistidx;
251                 }
252
253             hist_redraw:
254                 db_putnchars(BACKUP, db_le - db_lbuf_start);
255                 db_putnchars(BLANK, db_le - db_lbuf_start);
256                 db_putnchars(BACKUP, db_le - db_lbuf_start);
257                 db_le = index(db_lbuf_start, '\0');
258                 if (db_le[-1] == '\r' || db_le[-1] == '\n')
259                     *--db_le = '\0';
260                 db_lc = db_le;
261                 goto redraw;
262
263             case -1:
264                 /*
265                  * eek! the console returned eof.
266                  * probably that means we HAVE no console.. we should try bail
267                  * XXX
268                  */
269                 c = '\r';
270             case '\n':
271             case '\r':
272                 *db_le++ = c;
273                 return (1);
274             default:
275                 if (db_le == db_lbuf_end) {
276                     cnputc('\007');
277                 }
278                 else if (c >= ' ' && c <= '~') {
279                     char *p;
280
281                     for (p = db_le; p > db_lc; p--)
282                         *p = *(p-1);
283                     *db_lc++ = c;
284                     db_le++;
285                     cnputc(c);
286                     db_putstring(db_lc, db_le - db_lc);
287                     db_putnchars(BACKUP, db_le - db_lc);
288                 }
289                 break;
290         }
291         return (0);
292 }
293
294 int
295 cnmaygetc()
296 {
297         return (-1);
298 }
299
300 int
301 db_readline(lstart, lsize)
302         char *  lstart;
303         int     lsize;
304 {
305         if (lsize != db_lhistlsize) {
306                 /*
307                  * (Re)initialize input line history.  Throw away any
308                  * existing history.
309                  */
310                 db_lhist_nlines = sizeof(db_lhistory) / lsize;
311                 db_lhistlsize = lsize;
312                 db_lhistidx = -1;
313         }
314         db_lhistcur = db_lhistidx;
315
316         db_force_whitespace();  /* synch output position */
317
318         db_lbuf_start = lstart;
319         db_lbuf_end   = lstart + lsize;
320         db_lc = lstart;
321         db_le = lstart;
322
323         while (!db_inputchar(cngetc()))
324             continue;
325
326         db_printf("\n");        /* synch output position */
327         *db_le = 0;
328
329         if (db_le - db_lbuf_start > 1) {
330             /* Maintain input line history for non-empty lines. */
331             if (++db_lhistidx == db_lhist_nlines) {
332                 /* Rotate history. */
333                 ovbcopy(db_lhistory + db_lhistlsize, db_lhistory,
334                         db_lhistlsize * (db_lhist_nlines - 1));
335                 db_lhistidx--;
336             }
337             bcopy(lstart, db_lhistory + db_lhistidx * db_lhistlsize,
338                   db_lhistlsize);
339         }
340
341         return (db_le - db_lbuf_start);
342 }
343
344 void
345 db_check_interrupt()
346 {
347         int     c;
348
349         c = cnmaygetc();
350         switch (c) {
351             case -1:            /* no character */
352                 return;
353
354             case CTRL('c'):
355                 db_error((char *)0);
356                 /*NOTREACHED*/
357
358             case CTRL('s'):
359                 do {
360                     c = cnmaygetc();
361                     if (c == CTRL('c'))
362                         db_error((char *)0);
363                 } while (c != CTRL('q'));
364                 break;
365
366             default:
367                 /* drop on floor */
368                 break;
369         }
370 }