1 | /* $NetBSD: db_input.c,v 1.26 2010/08/31 07:48:23 enami Exp $ */ |
2 | |
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 "AS IS" |
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 | * Author: David B. Golub, Carnegie Mellon University |
29 | * Date: 7/90 |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: db_input.c,v 1.26 2010/08/31 07:48:23 enami Exp $" ); |
34 | |
35 | #ifdef _KERNEL_OPT |
36 | #include "opt_ddbparam.h" |
37 | #endif |
38 | |
39 | #include <sys/param.h> |
40 | #include <sys/proc.h> |
41 | #include <sys/cpu.h> |
42 | |
43 | #include <ddb/ddb.h> |
44 | |
45 | #include <dev/cons.h> |
46 | |
47 | #ifndef DDB_HISTORY_SIZE |
48 | #define DDB_HISTORY_SIZE 0 |
49 | #endif /* DDB_HISTORY_SIZE */ |
50 | |
51 | /* |
52 | * Character input and editing. |
53 | */ |
54 | |
55 | /* |
56 | * We don't track output position while editing input, |
57 | * since input always ends with a new-line. We just |
58 | * reset the line position at the end. |
59 | */ |
60 | static char *db_lbuf_start; /* start of input line buffer */ |
61 | static char *db_lbuf_end; /* end of input line buffer */ |
62 | static char *db_lc; /* current character */ |
63 | static char *db_le; /* one past last character */ |
64 | #if DDB_HISTORY_SIZE != 0 |
65 | static char db_history[DDB_HISTORY_SIZE]; /* start of history buffer */ |
66 | static char *db_history_curr = db_history; /* start of current line */ |
67 | static char *db_history_last = db_history; /* start of last line */ |
68 | static char *db_history_prev = (char *) 0; /* start of previous line */ |
69 | #endif |
70 | |
71 | |
72 | #define CTRL(c) ((c) & 0x1f) |
73 | #define isspace(c) ((c) == ' ' || (c) == '\t') |
74 | #define BLANK ' ' |
75 | #define BACKUP '\b' |
76 | |
77 | static int cnmaygetc(void); |
78 | static void db_putstring(const char *, int); |
79 | static void db_putnchars(int, int); |
80 | static void db_delete(int, int); |
81 | static void db_delete_line(void); |
82 | static int db_inputchar(int); |
83 | |
84 | static void |
85 | db_putstring(const char *s, int count) |
86 | { |
87 | |
88 | while (--count >= 0) |
89 | cnputc(*s++); |
90 | } |
91 | |
92 | static void |
93 | db_putnchars(int c, int count) |
94 | { |
95 | |
96 | while (--count >= 0) |
97 | cnputc(c); |
98 | } |
99 | |
100 | /* |
101 | * Delete N characters, forward or backward |
102 | */ |
103 | #define DEL_FWD 0 |
104 | #define DEL_BWD 1 |
105 | static void |
106 | db_delete(int n, int bwd) |
107 | { |
108 | char *p; |
109 | |
110 | if (bwd) { |
111 | db_lc -= n; |
112 | db_putnchars(BACKUP, n); |
113 | } |
114 | for (p = db_lc; p < db_le-n; p++) { |
115 | *p = *(p+n); |
116 | cnputc(*p); |
117 | } |
118 | db_putnchars(BLANK, n); |
119 | db_putnchars(BACKUP, db_le - db_lc); |
120 | db_le -= n; |
121 | } |
122 | |
123 | static void |
124 | db_delete_line(void) |
125 | { |
126 | |
127 | db_delete(db_le - db_lc, DEL_FWD); |
128 | db_delete(db_lc - db_lbuf_start, DEL_BWD); |
129 | db_le = db_lc = db_lbuf_start; |
130 | } |
131 | |
132 | #if DDB_HISTORY_SIZE != 0 |
133 | |
134 | #define INC_DB_CURR() do { \ |
135 | ++db_history_curr; \ |
136 | if (db_history_curr > db_history + DDB_HISTORY_SIZE - 1) \ |
137 | db_history_curr = db_history; \ |
138 | } while (0) |
139 | #define DEC_DB_CURR() do { \ |
140 | --db_history_curr; \ |
141 | if (db_history_curr < db_history) \ |
142 | db_history_curr = db_history + DDB_HISTORY_SIZE - 1; \ |
143 | } while (0) |
144 | |
145 | static inline void |
146 | db_hist_put(int c) |
147 | { |
148 | KASSERT(&db_history[0] <= db_history_last); |
149 | KASSERT(db_history_last <= &db_history[DDB_HISTORY_SIZE-1]); |
150 | |
151 | *db_history_last++ = c; |
152 | |
153 | if (db_history_last > &db_history[DDB_HISTORY_SIZE-1]) |
154 | db_history_last = db_history; |
155 | } |
156 | #endif |
157 | |
158 | |
159 | /* returns true at end-of-line */ |
160 | static int |
161 | db_inputchar(int c) |
162 | { |
163 | switch (c) { |
164 | case CTRL('b'): |
165 | /* back up one character */ |
166 | if (db_lc > db_lbuf_start) { |
167 | cnputc(BACKUP); |
168 | db_lc--; |
169 | } |
170 | break; |
171 | case CTRL('f'): |
172 | /* forward one character */ |
173 | if (db_lc < db_le) { |
174 | cnputc(*db_lc); |
175 | db_lc++; |
176 | } |
177 | break; |
178 | case CTRL('a'): |
179 | /* beginning of line */ |
180 | while (db_lc > db_lbuf_start) { |
181 | cnputc(BACKUP); |
182 | db_lc--; |
183 | } |
184 | break; |
185 | case CTRL('e'): |
186 | /* end of line */ |
187 | while (db_lc < db_le) { |
188 | cnputc(*db_lc); |
189 | db_lc++; |
190 | } |
191 | break; |
192 | case CTRL('h'): |
193 | case 0177: |
194 | /* erase previous character */ |
195 | if (db_lc > db_lbuf_start) |
196 | db_delete(1, DEL_BWD); |
197 | break; |
198 | case CTRL('d'): |
199 | /* erase next character */ |
200 | if (db_lc < db_le) |
201 | db_delete(1, DEL_FWD); |
202 | break; |
203 | case CTRL('k'): |
204 | /* delete to end of line */ |
205 | if (db_lc < db_le) |
206 | db_delete(db_le - db_lc, DEL_FWD); |
207 | break; |
208 | case CTRL('u'): |
209 | /* delete line */ |
210 | db_delete_line(); |
211 | break; |
212 | case CTRL('t'): |
213 | /* twiddle last 2 characters */ |
214 | if (db_lc >= db_lbuf_start + 1) { |
215 | if (db_lc < db_le) { |
216 | c = db_lc[-1]; |
217 | db_lc[-1] = db_lc[0]; |
218 | db_lc[0] = c; |
219 | cnputc(BACKUP); |
220 | cnputc(db_lc[-1]); |
221 | cnputc(db_lc[0]); |
222 | db_lc++; |
223 | } else if (db_lc >= db_lbuf_start + 2) { |
224 | c = db_lc[-2]; |
225 | db_lc[-2] = db_lc[-1]; |
226 | db_lc[-1] = c; |
227 | cnputc(BACKUP); |
228 | cnputc(BACKUP); |
229 | cnputc(db_lc[-2]); |
230 | cnputc(db_lc[-1]); |
231 | } |
232 | } |
233 | break; |
234 | #if DDB_HISTORY_SIZE != 0 |
235 | case CTRL('p'): |
236 | DEC_DB_CURR(); |
237 | while (db_history_curr != db_history_last) { |
238 | DEC_DB_CURR(); |
239 | if (*db_history_curr == '\0') |
240 | break; |
241 | } |
242 | db_delete_line(); |
243 | if (db_history_curr == db_history_last) { |
244 | INC_DB_CURR(); |
245 | db_le = db_lc = db_lbuf_start; |
246 | } else { |
247 | char *p; |
248 | INC_DB_CURR(); |
249 | for (p = db_history_curr, db_le = db_lbuf_start; |
250 | *p; ) { |
251 | *db_le++ = *p++; |
252 | if (p >= db_history + DDB_HISTORY_SIZE) { |
253 | p = db_history; |
254 | } |
255 | } |
256 | db_lc = db_le; |
257 | } |
258 | db_putstring(db_lbuf_start, db_le - db_lbuf_start); |
259 | break; |
260 | case CTRL('n'): |
261 | while (db_history_curr != db_history_last) { |
262 | if (*db_history_curr == '\0') |
263 | break; |
264 | INC_DB_CURR(); |
265 | } |
266 | if (db_history_curr != db_history_last) { |
267 | INC_DB_CURR(); |
268 | db_delete_line(); |
269 | if (db_history_curr != db_history_last) { |
270 | char *p; |
271 | for (p = db_history_curr, |
272 | db_le = db_lbuf_start; *p;) { |
273 | *db_le++ = *p++; |
274 | if (p >= db_history + DDB_HISTORY_SIZE) { |
275 | p = db_history; |
276 | } |
277 | } |
278 | db_lc = db_le; |
279 | } |
280 | db_putstring(db_lbuf_start, db_le - db_lbuf_start); |
281 | } |
282 | break; |
283 | #endif |
284 | case CTRL('r'): |
285 | db_putstring("^R\n" , 3); |
286 | if (db_le > db_lbuf_start) { |
287 | db_putstring(db_lbuf_start, db_le - db_lbuf_start); |
288 | db_putnchars(BACKUP, db_le - db_lc); |
289 | } |
290 | break; |
291 | case '\n': |
292 | case '\r': |
293 | #if DDB_HISTORY_SIZE != 0 |
294 | /* Check if it same than previous line */ |
295 | if (db_history_curr == db_history_prev) { |
296 | char *pp, *pc; |
297 | |
298 | /* Is it unmodified */ |
299 | for (pp = db_history_prev, pc = db_lbuf_start; |
300 | pc != db_le && *pp; pp++, pc++) { |
301 | if (*pp != *pc) |
302 | break; |
303 | if (++pp >= db_history + DDB_HISTORY_SIZE) { |
304 | pp = db_history; |
305 | } |
306 | if (++pc >= db_history + DDB_HISTORY_SIZE) { |
307 | pc = db_history; |
308 | } |
309 | } |
310 | if (!*pp && pc == db_le) { |
311 | /* Repeted previous line, not saved */ |
312 | db_history_curr = db_history_last; |
313 | *db_le++ = c; |
314 | return (true); |
315 | } |
316 | } |
317 | if (db_le != db_lbuf_start) { |
318 | char *p; |
319 | |
320 | db_history_prev = db_history_last; |
321 | |
322 | for (p = db_lbuf_start; p != db_le; ) { |
323 | db_hist_put(*p++); |
324 | } |
325 | db_hist_put(0); |
326 | } |
327 | db_history_curr = db_history_last; |
328 | #endif |
329 | *db_le++ = c; |
330 | return (1); |
331 | default: |
332 | if (db_le == db_lbuf_end) { |
333 | cnputc('\007'); |
334 | } |
335 | else if (c >= ' ' && c <= '~') { |
336 | char *p; |
337 | |
338 | for (p = db_le; p > db_lc; p--) |
339 | *p = *(p-1); |
340 | *db_lc++ = c; |
341 | db_le++; |
342 | cnputc(c); |
343 | db_putstring(db_lc, db_le - db_lc); |
344 | db_putnchars(BACKUP, db_le - db_lc); |
345 | } |
346 | break; |
347 | } |
348 | return (0); |
349 | } |
350 | |
351 | int |
352 | db_readline(char *lstart, int lsize) |
353 | { |
354 | |
355 | # ifdef MULTIPROCESSOR |
356 | db_printf("db{%ld}> " , (long)cpu_number()); |
357 | # else |
358 | db_printf("db> " ); |
359 | # endif |
360 | db_force_whitespace(); /* synch output position */ |
361 | |
362 | db_lbuf_start = lstart; |
363 | db_lbuf_end = lstart + lsize; |
364 | db_lc = lstart; |
365 | db_le = lstart; |
366 | |
367 | while (!db_inputchar(cngetc())) |
368 | continue; |
369 | |
370 | db_putchar('\n'); /* synch output position */ |
371 | |
372 | *db_le = 0; |
373 | return (db_le - db_lbuf_start); |
374 | } |
375 | |
376 | void |
377 | db_check_interrupt(void) |
378 | { |
379 | int c; |
380 | |
381 | c = cnmaygetc(); |
382 | switch (c) { |
383 | case -1: /* no character */ |
384 | return; |
385 | |
386 | case CTRL('c'): |
387 | db_error((char *)0); |
388 | /*NOTREACHED*/ |
389 | |
390 | case CTRL('s'): |
391 | do { |
392 | c = cnmaygetc(); |
393 | if (c == CTRL('c')) { |
394 | db_error((char *)0); |
395 | /*NOTREACHED*/ |
396 | } |
397 | } while (c != CTRL('q')); |
398 | break; |
399 | |
400 | default: |
401 | /* drop on floor */ |
402 | break; |
403 | } |
404 | } |
405 | |
406 | static int |
407 | cnmaygetc(void) |
408 | { |
409 | |
410 | return (-1); |
411 | } |
412 | |