xref: /freebsd/sys/ddb/db_input.c (revision 732a02b4e77866604a120a275c082bb6221bd2ff)
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 
40 #include <ddb/ddb.h>
41 #include <ddb/db_output.h>
42 
43 /*
44  * Character input and editing.
45  */
46 
47 /*
48  * We don't track output position while editing input,
49  * since input always ends with a new-line.  We just
50  * reset the line position at the end.
51  */
52 static char *	db_lbuf_start;	/* start of input line buffer */
53 static char *	db_lbuf_end;	/* end of input line buffer */
54 static char *	db_lc;		/* current character */
55 static char *	db_le;		/* one past last character */
56 
57 /*
58  * Simple input line history support.
59  */
60 static char	db_lhistory[2048];
61 static int	db_lhistlsize, db_lhistidx, db_lhistcur;
62 static int	db_lhist_nlines;
63 
64 #define	CTRL(c)		((c) & 0x1f)
65 #define	BLANK		' '
66 #define	BACKUP		'\b'
67 
68 static int	cnmaygetc(void);
69 static void	db_delete(int n, int bwd);
70 static int	db_inputchar(int c);
71 static void	db_putnchars(int c, int count);
72 static void	db_putstring(char *s, int count);
73 
74 static void
75 db_putstring(s, count)
76 	char	*s;
77 	int	count;
78 {
79 	while (--count >= 0)
80 	    cnputc(*s++);
81 }
82 
83 static void
84 db_putnchars(c, count)
85 	int	c;
86 	int	count;
87 {
88 	while (--count >= 0)
89 	    cnputc(c);
90 }
91 
92 /*
93  * Delete N characters, forward or backward
94  */
95 #define	DEL_FWD		0
96 #define	DEL_BWD		1
97 static void
98 db_delete(n, bwd)
99 	int	n;
100 	int	bwd;
101 {
102 	char *p;
103 
104 	if (bwd) {
105 	    db_lc -= n;
106 	    db_putnchars(BACKUP, n);
107 	}
108 	for (p = db_lc; p < db_le-n; p++) {
109 	    *p = *(p+n);
110 	    cnputc(*p);
111 	}
112 	db_putnchars(BLANK, n);
113 	db_putnchars(BACKUP, db_le - db_lc);
114 	db_le -= n;
115 }
116 
117 /* returns true at end-of-line */
118 static int
119 db_inputchar(c)
120 	int	c;
121 {
122 	static int escstate;
123 
124 	if (escstate == 1) {
125 		/* ESC seen, look for [ or O */
126 		if (c == '[' || c == 'O')
127 			escstate++;
128 		else
129 			escstate = 0; /* re-init state machine */
130 		return (0);
131 	} else if (escstate == 2) {
132 		escstate = 0;
133 		/*
134 		 * If a valid cursor key has been found, translate
135 		 * into an emacs-style control key, and fall through.
136 		 * Otherwise, drop off.
137 		 */
138 		switch (c) {
139 		case 'A':	/* up */
140 			c = CTRL('p');
141 			break;
142 		case 'B':	/* down */
143 			c = CTRL('n');
144 			break;
145 		case 'C':	/* right */
146 			c = CTRL('f');
147 			break;
148 		case 'D':	/* left */
149 			c = CTRL('b');
150 			break;
151 		default:
152 			return (0);
153 		}
154 	}
155 
156 	switch (c) {
157 	    case CTRL('['):
158 		escstate = 1;
159 		break;
160 	    case CTRL('b'):
161 		/* back up one character */
162 		if (db_lc > db_lbuf_start) {
163 		    cnputc(BACKUP);
164 		    db_lc--;
165 		}
166 		break;
167 	    case CTRL('f'):
168 		/* forward one character */
169 		if (db_lc < db_le) {
170 		    cnputc(*db_lc);
171 		    db_lc++;
172 		}
173 		break;
174 	    case CTRL('a'):
175 		/* beginning of line */
176 		while (db_lc > db_lbuf_start) {
177 		    cnputc(BACKUP);
178 		    db_lc--;
179 		}
180 		break;
181 	    case CTRL('e'):
182 		/* end of line */
183 		while (db_lc < db_le) {
184 		    cnputc(*db_lc);
185 		    db_lc++;
186 		}
187 		break;
188 	    case CTRL('h'):
189 	    case 0177:
190 		/* erase previous character */
191 		if (db_lc > db_lbuf_start)
192 		    db_delete(1, DEL_BWD);
193 		break;
194 	    case CTRL('d'):
195 		/* erase next character */
196 		if (db_lc < db_le)
197 		    db_delete(1, DEL_FWD);
198 		break;
199 	    case CTRL('u'):
200 	    case CTRL('c'):
201 		/* kill entire line: */
202 		/* at first, delete to beginning of line */
203 		if (db_lc > db_lbuf_start)
204 		    db_delete(db_lc - db_lbuf_start, DEL_BWD);
205 		/* FALLTHROUGH */
206 	    case CTRL('k'):
207 		/* delete to end of line */
208 		if (db_lc < db_le)
209 		    db_delete(db_le - db_lc, DEL_FWD);
210 		break;
211 	    case CTRL('t'):
212 		/* twiddle last 2 characters */
213 		if (db_lc >= db_lbuf_start + 2) {
214 		    c = db_lc[-2];
215 		    db_lc[-2] = db_lc[-1];
216 		    db_lc[-1] = c;
217 		    cnputc(BACKUP);
218 		    cnputc(BACKUP);
219 		    cnputc(db_lc[-2]);
220 		    cnputc(db_lc[-1]);
221 		}
222 		break;
223 	    case CTRL('w'):
224 		/* erase previous word */
225 		for (; db_lc > db_lbuf_start;) {
226 		    if (*(db_lc - 1) != ' ')
227 			break;
228 		    db_delete(1, DEL_BWD);
229 		}
230 		for (; db_lc > db_lbuf_start;) {
231 		    if (*(db_lc - 1) == ' ')
232 			break;
233 		    db_delete(1, DEL_BWD);
234 		}
235 		break;
236 	    case CTRL('r'):
237 		db_putstring("^R\n", 3);
238 	    redraw:
239 		if (db_le > db_lbuf_start) {
240 		    db_putstring(db_lbuf_start, db_le - db_lbuf_start);
241 		    db_putnchars(BACKUP, db_le - db_lc);
242 		}
243 		break;
244 	    case CTRL('p'):
245 		/* Make previous history line the active one. */
246 		if (db_lhistcur >= 0) {
247 		    bcopy(db_lhistory + db_lhistcur * db_lhistlsize,
248 			  db_lbuf_start, db_lhistlsize);
249 		    db_lhistcur--;
250 		    goto hist_redraw;
251 		}
252 		break;
253 	    case CTRL('n'):
254 		/* Make next history line the active one. */
255 		if (db_lhistcur < db_lhistidx - 1) {
256 		    db_lhistcur += 2;
257 		    bcopy(db_lhistory + db_lhistcur * db_lhistlsize,
258 			  db_lbuf_start, db_lhistlsize);
259 		} else {
260 		    /*
261 		     * ^N through tail of history, reset the
262 		     * buffer to zero length.
263 		     */
264 		    *db_lbuf_start = '\0';
265 		    db_lhistcur = db_lhistidx;
266 		}
267 
268 	    hist_redraw:
269 		db_putnchars(BACKUP, db_lc - db_lbuf_start);
270 		db_putnchars(BLANK, db_le - db_lbuf_start);
271 		db_putnchars(BACKUP, db_le - db_lbuf_start);
272 		db_le = strchr(db_lbuf_start, '\0');
273 		if (db_le[-1] == '\r' || db_le[-1] == '\n')
274 		    *--db_le = '\0';
275 		db_lc = db_le;
276 		goto redraw;
277 
278 	    case -1:
279 		/*
280 		 * eek! the console returned eof.
281 		 * probably that means we HAVE no console.. we should try bail
282 		 * XXX
283 		 */
284 		c = '\r';
285 	    case '\n':
286 		/* FALLTHROUGH */
287 	    case '\r':
288 		*db_le++ = c;
289 		return (1);
290 	    default:
291 		if (db_le == db_lbuf_end) {
292 		    cnputc('\007');
293 		}
294 		else if (c >= ' ' && c <= '~') {
295 		    char *p;
296 
297 		    for (p = db_le; p > db_lc; p--)
298 			*p = *(p-1);
299 		    *db_lc++ = c;
300 		    db_le++;
301 		    cnputc(c);
302 		    db_putstring(db_lc, db_le - db_lc);
303 		    db_putnchars(BACKUP, db_le - db_lc);
304 		}
305 		break;
306 	}
307 	return (0);
308 }
309 
310 static int
311 cnmaygetc()
312 {
313 	return (-1);
314 }
315 
316 int
317 db_readline(lstart, lsize)
318 	char *	lstart;
319 	int	lsize;
320 {
321 
322 	if (lsize < 2)
323 		return (0);
324 	if (lsize != db_lhistlsize) {
325 		/*
326 		 * (Re)initialize input line history.  Throw away any
327 		 * existing history.
328 		 */
329 		db_lhist_nlines = sizeof(db_lhistory) / lsize;
330 		db_lhistlsize = lsize;
331 		db_lhistidx = -1;
332 	}
333 	db_lhistcur = db_lhistidx;
334 
335 	db_force_whitespace();	/* synch output position */
336 
337 	db_lbuf_start = lstart;
338 	db_lbuf_end   = lstart + lsize - 2;	/* Will append NL and NUL. */
339 	db_lc = lstart;
340 	db_le = lstart;
341 
342 	while (!db_inputchar(cngetc()))
343 	    continue;
344 
345 	db_capture_write(lstart, db_le - db_lbuf_start);
346 	db_printf("\n");	/* synch output position */
347 	*db_le = 0;
348 
349 	if (db_le - db_lbuf_start > 1) {
350 	    /* Maintain input line history for non-empty lines. */
351 	    if (++db_lhistidx == db_lhist_nlines) {
352 		/* Rotate history. */
353 		bcopy(db_lhistory + db_lhistlsize, db_lhistory,
354 		      db_lhistlsize * (db_lhist_nlines - 1));
355 		db_lhistidx--;
356 	    }
357 	    bcopy(lstart, db_lhistory + db_lhistidx * db_lhistlsize,
358 		  db_lhistlsize);
359 	}
360 
361 	return (db_le - db_lbuf_start);
362 }
363 
364 void
365 db_check_interrupt(void)
366 {
367 	int	c;
368 
369 	c = cnmaygetc();
370 	switch (c) {
371 	    case -1:		/* no character */
372 		return;
373 
374 	    case CTRL('c'):
375 		db_error((char *)0);
376 		/*NOTREACHED*/
377 
378 	    case CTRL('s'):
379 		do {
380 		    c = cnmaygetc();
381 		    if (c == CTRL('c'))
382 			db_error((char *)0);
383 		} while (c != CTRL('q'));
384 		break;
385 
386 	    default:
387 		/* drop on floor */
388 		break;
389 	}
390 }
391