1/*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 * HISTORY
33 *
34 * Revision 1.1.1.1  1998/09/22 21:05:48  wsanchez
35 * Import of Mac OS X kernel (~semeria)
36 *
37 * Revision 1.1.1.1  1998/03/07 02:26:09  wsanchez
38 * Import of OSF Mach kernel (~mburg)
39 *
40 * Revision 1.3.10.2  1994/09/23  01:19:37  ezf
41 * 	change marker to not FREE
42 * 	[1994/09/22  21:10:05  ezf]
43 *
44 * Revision 1.3.10.1  1994/06/11  21:11:48  bolinger
45 * 	Merge up to NMK17.2.
46 * 	[1994/06/11  20:01:41  bolinger]
47 *
48 * Revision 1.3.8.2  1994/02/11  14:21:41  paire
49 * 	Added string.h header file for strlen declaration.
50 * 	[94/02/09            paire]
51 *
52 * Revision 1.3.8.1  1994/02/08  10:57:55  bernadat
53 * 	Added db_auto_completion variable.
54 * 	[93/08/17            paire]
55 *
56 * 	Added support of symbol completion by typing '\t'.
57 * 	[93/08/14            paire]
58 * 	[94/02/07            bernadat]
59 *
60 * Revision 1.3.2.4  1993/08/11  20:37:51  elliston
61 * 	Add ANSI Prototypes.  CR #9523.
62 * 	[1993/08/11  03:33:21  elliston]
63 *
64 * Revision 1.3.2.3  1993/07/27  18:27:30  elliston
65 * 	Add ANSI prototypes.  CR #9523.
66 * 	[1993/07/27  18:12:01  elliston]
67 *
68 * Revision 1.3.2.2  1993/06/09  02:20:13  gm
69 * 	CR9176 - ANSI C violations: trailing tokens on CPP
70 * 	directives, extra semicolons after decl_ ..., asm keywords
71 * 	[1993/06/07  18:57:14  jeffc]
72 *
73 * 	Added to OSF/1 R1.3 from NMK15.0.
74 * 	[1993/06/02  20:56:26  jeffc]
75 *
76 * Revision 1.3  1993/04/19  16:02:17  devrcs
77 * 	Replaced ^R (redraw) with ^L [barbou@gr.osf.org]
78 *
79 * 	Added ^R and ^S commands for history search commands
80 * 	^U does not erase end of the line anymore. (only erases
81 * 	from the beginning of the line to current position).
82 * 	[barbou@gr.osf.org]
83 *
84 * 	^C now erases the entire line. [barbou@gr.osf.org]
85 * 	[92/12/03            bernadat]
86 *
87 * 	Fixed history management: Do not store repeated typed
88 * 	command. Null terminate current command in case it is a
89 * 	substring of the last command.
90 * 	[92/10/02            bernadat]
91 *
92 * Revision 1.2  1992/11/25  01:04:24  robert
93 * 	integrate changes for norma_14 below
94 *
95 * 	Philippe Bernadat (bernadat) at gr.osf.org 02-Oct-92
96 * 	Fixed history management: Do not store repeated typed
97 * 	command. Null terminate current command in case it is a
98 * 	substring of the last command.
99 * 	[1992/11/20  00:56:07  robert]
100 *
101 * 	integrate changes below for norma_14
102 * 	[1992/11/13  19:21:34  robert]
103 *
104 * Revision 1.1  1992/09/30  02:01:08  robert
105 * 	Initial revision
106 *
107 * $EndLog$
108 */
109/* CMU_HIST */
110/*
111 * Revision 2.7.3.2  92/09/15  17:14:26  jeffreyh
112 * 	Fixed history code. (Only one char. out of 2 was checked to
113 * 	compare to last command)
114 * 	[barbou@gr.osf.org]
115 *
116 * Revision 2.7.3.1  92/03/03  16:13:30  jeffreyh
117 * 	Pick up changes from TRUNK
118 * 	[92/02/26  10:59:36  jeffreyh]
119 *
120 * Revision 2.8  92/02/19  15:07:44  elf
121 * 	Added delete_line (Ctrl-U).
122 * 	[92/02/17            kivinen]
123 *
124 * 	Added command line history. Ctrl-P = previous, Ctrl-N = next. If
125 * 	DB_HISTORY_SIZE is 0 then command history is disabled.
126 * 	[92/02/17            kivinen]
127 *
128 * Revision 2.7  91/10/09  16:00:03  af
129 * 	 Revision 2.6.2.1  91/10/05  13:06:12  jeffreyh
130 * 	 	Fixed incorrect db_lbuf_end setting.
131 * 	 	[91/08/29            tak]
132 *
133 * Revision 2.6.2.1  91/10/05  13:06:12  jeffreyh
134 * 	Fixed incorrect db_lbuf_end setting.
135 * 	[91/08/29            tak]
136 *
137 * Revision 2.6  91/07/09  23:15:49  danner
138 * 	Add include of machine/db_machdep.h to allow machine-specific
139 * 	 overrides via defines.
140 * 	[91/07/08            danner]
141 *
142 * Revision 2.5  91/05/14  15:34:03  mrt
143 * 	Correcting copyright
144 *
145 * Revision 2.4  91/02/14  14:41:53  mrt
146 * 	Add input line editing.
147 * 	[90/11/11            dbg]
148 *
149 * Revision 2.3  91/02/05  17:06:32  mrt
150 * 	Changed to new Mach copyright
151 * 	[91/01/31  16:18:13  mrt]
152 *
153 * Revision 2.2  90/08/27  21:51:03  dbg
154 * 	Reduce lint.
155 * 	[90/08/07            dbg]
156 * 	Created.
157 * 	[90/07/25            dbg]
158 *
159 */
160/* CMU_ENDHIST */
161/*
162 * Mach Operating System
163 * Copyright (c) 1991,1990 Carnegie Mellon University
164 * All Rights Reserved.
165 *
166 * Permission to use, copy, modify and distribute this software and its
167 * documentation is hereby granted, provided that both the copyright
168 * notice and this permission notice appear in all copies of the
169 * software, derivative works or modified versions, and any portions
170 * thereof, and that both notices appear in supporting documentation.
171 *
172 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
173 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
174 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
175 *
176 * Carnegie Mellon requests users of this software to return to
177 *
178 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
179 *  School of Computer Science
180 *  Carnegie Mellon University
181 *  Pittsburgh PA 15213-3890
182 *
183 * any improvements or extensions that they make and grant Carnegie Mellon
184 * the rights to redistribute these changes.
185 */
186/*
187 */
188/*
189 *	Author: David B. Golub, Carnegie Mellon University
190 *	Date:	7/90
191 */
192
193#include <string.h>
194#include <mach/boolean.h>
195#include <machine/db_machdep.h>
196#include <kern/misc_protos.h>
197#include <ddb/db_output.h>
198#include <ddb/db_lex.h>
199#include <ddb/db_command.h>
200#include <ddb/db_input.h>
201#include <ddb/db_sym.h>
202
203#ifndef DB_HISTORY_SIZE
204#define DB_HISTORY_SIZE 4000
205#endif /* DB_HISTORY_SIZE */
206
207/*
208 * Character input and editing.
209 */
210
211/*
212 * We don't track output position while editing input,
213 * since input always ends with a new-line.  We just
214 * reset the line position at the end.
215 */
216char *	db_lbuf_start;	/* start of input line buffer */
217char *	db_lbuf_end;	/* end of input line buffer */
218char *	db_lc;		/* current character */
219char *	db_le;		/* one past last character */
220int	db_completion;	/* number of incomplete symbols matched */
221int	db_auto_completion = 10; /* number of line to display without asking */
222#if DB_HISTORY_SIZE != 0
223char    db_history[DB_HISTORY_SIZE];	/* start of history buffer */
224int     db_history_size = DB_HISTORY_SIZE;/* size of history buffer */
225char *  db_history_curr = db_history;	/* start of current line */
226char *  db_history_last = db_history;	/* start of last line */
227char *  db_history_prev = (char *) 0;	/* start of previous line */
228int	db_hist_unmodified = 0;		/* unmodified line from history */
229int	db_hist_search = 0;		/* are we in hist search mode ? */
230char 	db_hist_search_string[DB_LEX_LINE_SIZE];/* the string to look for */
231int	db_hist_ignore_dups = 0;	/* don't duplicate commands in hist */
232#endif
233
234#define	CTRL(c)		((c) & 0x1f)
235#define	isspace(c)	((c) == ' ' || (c) == '\t')
236#define	BLANK		' '
237#define	BACKUP		'\b'
238
239
240
241/* Prototypes for functions local to this file.  XXX -- should be static!
242 */
243void db_putstring(const char *s, int count);
244
245void db_putnchars(
246	int	c,
247	int	count);
248
249void db_delete(
250	int	n,
251	int	bwd);
252
253void db_delete_line(void);
254
255boolean_t db_hist_substring(
256	char	*string,
257	char	*substring);
258
259boolean_t db_inputchar(int c);
260
261extern jmp_buf_t	*db_recover;
262
263void
264db_putstring(const char *s, int count)
265{
266	while (--count >= 0)
267	    cnputc(*s++);
268}
269
270void
271db_putnchars(
272	int	c,
273	int	count)
274{
275	while (--count >= 0)
276	    cnputc(c);
277}
278
279/*
280 * Delete N characters, forward or backward
281 */
282#define	DEL_FWD		0
283#define	DEL_BWD		1
284void
285db_delete(
286	int	n,
287	int	bwd)
288{
289	register char *p;
290
291	if (bwd) {
292	    db_lc -= n;
293	    db_putnchars(BACKUP, n);
294	}
295	for (p = db_lc; p < db_le-n; p++) {
296	    *p = *(p+n);
297	    cnputc(*p);
298	}
299	db_putnchars(BLANK, n);
300	db_putnchars(BACKUP, db_le - db_lc);
301	db_le -= n;
302}
303
304void
305db_delete_line(void)
306{
307	db_delete(db_le - db_lc, DEL_FWD);
308	db_delete(db_lc - db_lbuf_start, DEL_BWD);
309	db_le = db_lc = db_lbuf_start;
310}
311
312#if DB_HISTORY_SIZE != 0
313#define INC_DB_CURR() \
314    do { \
315	     db_history_curr++; \
316	     if (db_history_curr > \
317		 db_history + db_history_size - 1) \
318		     db_history_curr = db_history; \
319       } while (0)
320#define DEC_DB_CURR() \
321    do { \
322	     db_history_curr--; \
323	     if (db_history_curr < db_history) \
324		 db_history_curr = db_history + \
325		 db_history_size - 1; \
326       } while (0)
327#endif
328
329/* returs TRUE if "substring" is a substring of "string" */
330boolean_t
331db_hist_substring(
332	char	*string,
333	char	*substring)
334{
335	register char *cp1, *cp2;
336
337	cp1 = string;
338	while (*cp1)
339		cp1++;
340	cp2 = substring;
341	while (*cp2)
342		cp2++;
343
344	while (cp2 > substring) {
345		cp1--; cp2--;
346	}
347
348	while (cp1 >= string) {
349		register char *cp3;
350
351		cp2 = substring;
352		cp3 = cp1;
353		while (*cp2 && *cp2 == *cp3) {
354			cp2++; cp3++;
355		}
356		if (*cp2 == '\0') {
357			return TRUE;
358		}
359		cp1--;
360	}
361	return FALSE;
362}
363
364/* returns TRUE at end-of-line */
365boolean_t
366db_inputchar(int c)
367{
368	char *sym;
369	char *start;
370	char *restart;
371	jmp_buf_t db_jmpbuf;
372	jmp_buf_t *local_prev;
373	char *p;
374	int len;
375
376	switch(db_completion) {
377	case -1:
378	    db_putchar('\n');
379	    local_prev = db_recover;
380	    if (_setjmp(db_recover = &db_jmpbuf) == 0 &&
381		(c == 'y' || c == ' ' || c == '\t'))
382		    db_print_completion(db_tok_string);
383	    db_recover = local_prev;
384	    db_completion = 0;
385	    db_reset_more();
386	    db_output_prompt();
387	    if (db_le > db_lbuf_start) {
388		    for (start = db_lbuf_start; start < db_le; start++)
389			    db_putchar(*start);
390		db_putnchars(BACKUP, db_le - db_lc);
391	    }
392	    return(FALSE);
393
394	case 0:
395	    break;
396
397	default:
398	    if (c == '\t') {
399		db_printf("\nThere are %d possibilities. ", db_completion);
400		db_printf("Do you really wish to see them all [n] ? ");
401		db_force_whitespace();
402		db_completion = -1;
403		db_reset_more();
404		return(FALSE);
405	    }
406	    db_completion = 0;
407	    break;
408	}
409
410	switch (c) {
411	    case '\t':
412		/* symbol completion */
413		if (db_lc == db_lbuf_start || db_auto_completion == 0)
414		    break;
415		if (db_le == db_lbuf_end) {
416		    cnputc('\007');
417		    break;
418		}
419		start = db_lc - 1;
420		while (start >= db_lbuf_start &&
421		       ((*start >= 'A' && *start <= 'Z') ||
422			(*start >= 'a' && *start <= 'z') ||
423			(*start >= '0' && *start <= '9') ||
424			*start == '_' || *start == ':'))
425		    start--;
426		if (start == db_lc - 1)
427		    break;
428		if (start > db_lbuf_start && *start == '$') {
429		    cnputc('\007');
430		    break;
431		}
432		sym = db_tok_string;
433		restart = ++start;
434		do {
435		    *sym++ = *start++;
436		} while (start != db_lc &&
437			 sym != db_tok_string + sizeof(db_tok_string));
438		if (sym == db_tok_string + sizeof(db_tok_string)) {
439		    cnputc('\007');
440		    break;
441		}
442		*sym = '\0';
443		db_completion = db_lookup_incomplete(db_tok_string,
444						     sizeof(db_tok_string));
445		if (db_completion == 0) {
446		    /* symbol unknown */
447		    cnputc('\007');
448		    break;
449		}
450
451		len = strlen(db_tok_string) - (start - restart);
452		if (db_completion == 1 &&
453		    (db_le == db_lc ||
454		     ((db_le > db_lc) && *db_lc != ' ')))
455		    len++;
456		for (p = db_le - 1; p >= db_lc; p--)
457		    *(p + len) = *p;
458		db_le += len;
459		for (sym = &db_tok_string[start - restart];
460		     *sym != '\0'; sym++)
461		    *db_lc++ = *sym;
462
463		if (db_completion == 1 || db_completion > db_auto_completion) {
464		    for (sym = &db_tok_string[start - restart];
465			 *sym != '\0'; sym++)
466			cnputc(*sym);
467		    if (db_completion == 1) {
468			if (db_le == db_lc ||
469			    ((db_le > db_lc) && *db_lc != ' ')) {
470			    cnputc(' ');
471			    *db_lc++ = ' ';
472			}
473			db_completion = 0;
474		    }
475		    db_putstring(db_lc, db_le - db_lc);
476		    db_putnchars(BACKUP, db_le - db_lc);
477		}
478
479		if (db_completion > 1) {
480		    cnputc('\007');
481		    if (db_completion <= db_auto_completion) {
482			db_putchar('\n');
483			db_print_completion(db_tok_string);
484			db_completion = 0;
485			db_reset_more();
486			db_output_prompt();
487			if (db_le > db_lbuf_start) {
488			    for (start = db_lbuf_start; start < db_le; start++)
489				db_putchar(*start);
490			    db_putnchars(BACKUP, db_le - db_lc);
491			}
492		    }
493		}
494		break;
495
496	    case CTRL('b'):
497		/* back up one character */
498		if (db_lc > db_lbuf_start) {
499		    cnputc(BACKUP);
500		    db_lc--;
501		}
502		break;
503	    case CTRL('f'):
504		/* forward one character */
505		if (db_lc < db_le) {
506		    cnputc(*db_lc);
507		    db_lc++;
508		}
509		break;
510	    case CTRL('a'):
511		/* beginning of line */
512		while (db_lc > db_lbuf_start) {
513		    cnputc(BACKUP);
514		    db_lc--;
515		}
516		break;
517	    case CTRL('e'):
518		/* end of line */
519		while (db_lc < db_le) {
520		    cnputc(*db_lc);
521		    db_lc++;
522		}
523		break;
524	    case CTRL('h'):
525	    case 0177:
526		/* erase previous character */
527		if (db_lc > db_lbuf_start)
528		    db_delete(1, DEL_BWD);
529		break;
530	    case CTRL('d'):
531		/* erase next character */
532		if (db_lc < db_le)
533		    db_delete(1, DEL_FWD);
534		break;
535	    case CTRL('k'):
536		/* delete to end of line */
537		if (db_lc < db_le)
538		    db_delete(db_le - db_lc, DEL_FWD);
539		break;
540	    case CTRL('u'):
541		/* delete to beginning of line */
542		if (db_lc > db_lbuf_start)
543		    db_delete(db_lc - db_lbuf_start, DEL_BWD);
544		break;
545	    case CTRL('t'):
546		/* twiddle last 2 characters */
547		if (db_lc >= db_lbuf_start + 2) {
548		    c = db_lc[-2];
549		    db_lc[-2] = db_lc[-1];
550		    db_lc[-1] = c;
551		    cnputc(BACKUP);
552		    cnputc(BACKUP);
553		    cnputc(db_lc[-2]);
554		    cnputc(db_lc[-1]);
555		}
556		break;
557	    case CTRL('c'):
558	    case CTRL('g'):
559		db_delete_line();
560#if DB_HISTORY_SIZE != 0
561		db_history_curr = db_history_last;
562		if (c == CTRL('g') && db_hist_search) {
563			for (p = db_hist_search_string, db_le = db_lbuf_start;
564			     *p; ) {
565				*db_le++ = *p++;
566			}
567			db_lc = db_le;
568			*db_le = '\0';
569			db_putstring(db_lbuf_start, db_le - db_lbuf_start);
570		}
571#endif
572		break;
573#if DB_HISTORY_SIZE != 0
574	    case CTRL('r'):
575		if (db_hist_search++ == 0) {
576			/* starting an history lookup */
577			register char *cp1, *cp2;
578			for (cp1 = db_lbuf_start, cp2 = db_hist_search_string;
579			     cp1 < db_le;
580			     cp1++, cp2++)
581				*cp2 = *cp1;
582			*cp2 = '\0';
583			db_hist_search++;
584		}
585		/* FALL THROUGH */
586	    case CTRL('p'):
587		{
588		char * old_history_curr = db_history_curr;
589
590		if (db_hist_unmodified++ == 0)
591			db_hist_unmodified++;
592	        DEC_DB_CURR();
593	        while (db_history_curr != db_history_last) {
594			DEC_DB_CURR();
595			if (*db_history_curr == '\0') {
596				INC_DB_CURR();
597				if (db_hist_search <= 1) {
598					if (*db_history_curr == '\0')
599						cnputc('\007');
600					else
601						DEC_DB_CURR();
602					break;
603				}
604				if (*db_history_curr == '\0') {
605					cnputc('\007');
606					db_history_curr = old_history_curr;
607					DEC_DB_CURR();
608					break;
609				}
610				if (db_history_curr != db_history_last &&
611				    db_hist_substring(db_history_curr,
612						      db_hist_search_string)) {
613					DEC_DB_CURR();
614					break;
615				}
616				DEC_DB_CURR();
617			}
618		}
619		if (db_history_curr == db_history_last) {
620			cnputc('\007');
621			db_history_curr = old_history_curr;
622		} else {
623			INC_DB_CURR();
624			db_delete_line();
625			for (p = db_history_curr, db_le = db_lbuf_start;
626			     *p; ) {
627				*db_le++ = *p++;
628				if (p == db_history + db_history_size) {
629					p = db_history;
630				}
631			}
632			db_lc = db_le;
633			*db_le = '\0';
634			db_putstring(db_lbuf_start, db_le - db_lbuf_start);
635		}
636		break;
637		}
638	    case CTRL('s'):
639		if (db_hist_search++ == 0) {
640			/* starting an history lookup */
641			register char *cp1, *cp2;
642			for (cp1 = db_lbuf_start, cp2 = db_hist_search_string;
643			     cp1 < db_le;
644			     cp1++, cp2++)
645				*cp2 = *cp1;
646			*cp2 = '\0';
647			db_hist_search++;
648		}
649		/* FALL THROUGH */
650	    case CTRL('n'):
651		{
652		char *old_history_curr = db_history_curr;
653
654		if (db_hist_unmodified++ == 0)
655			db_hist_unmodified++;
656	        while (db_history_curr != db_history_last) {
657			if (*db_history_curr == '\0') {
658				if (db_hist_search <= 1)
659					break;
660				INC_DB_CURR();
661				if (db_history_curr != db_history_last &&
662				    db_hist_substring(db_history_curr,
663						      db_hist_search_string)) {
664					DEC_DB_CURR();
665					break;
666				}
667				DEC_DB_CURR();
668			}
669			INC_DB_CURR();
670		}
671		if (db_history_curr != db_history_last) {
672			INC_DB_CURR();
673			if (db_history_curr != db_history_last) {
674				db_delete_line();
675				for (p = db_history_curr,
676				     db_le = db_lbuf_start; *p;) {
677					*db_le++ = *p++;
678					if (p == db_history +
679					    db_history_size) {
680						p = db_history;
681					}
682				}
683				db_lc = db_le;
684				*db_le = '\0';
685				db_putstring(db_lbuf_start,
686					     db_le - db_lbuf_start);
687			} else {
688				cnputc('\007');
689				db_history_curr = old_history_curr;
690			}
691		} else {
692			cnputc('\007');
693			db_history_curr = old_history_curr;
694		}
695		break;
696		}
697#endif
698	    /* refresh the command line */
699	    case CTRL('l'):
700		db_putstring("^L\n", 3);
701		if (db_le > db_lbuf_start) {
702			db_putstring(db_lbuf_start, db_le - db_lbuf_start);
703			db_putnchars(BACKUP, db_le - db_lc);
704		}
705		break;
706	    case '\n':
707	    case '\r':
708#if DB_HISTORY_SIZE != 0
709		/* Check if it same than previous line */
710		if (db_history_prev) {
711			char *pc;
712
713			/* Is it unmodified */
714			for (p = db_history_prev, pc = db_lbuf_start;
715			     pc != db_le && *p;) {
716				if (*p != *pc)
717				    break;
718				if (++p == db_history + db_history_size) {
719					p = db_history;
720				}
721				if (++pc == db_history + db_history_size) {
722					pc = db_history;
723				}
724			}
725			if (!*p && pc == db_le) {
726				/* Repeted previous line, not saved */
727				db_history_curr = db_history_last;
728				*db_le++ = c;
729				db_hist_search = 0;
730				db_hist_unmodified = 0;
731				return (TRUE);
732			}
733		}
734		if (db_le != db_lbuf_start &&
735		    (db_hist_unmodified == 0 || !db_hist_ignore_dups)) {
736			db_history_prev = db_history_last;
737			for (p = db_lbuf_start; p != db_le; p++) {
738				*db_history_last++ = *p;
739				if (db_history_last == db_history +
740				    db_history_size) {
741					db_history_last = db_history;
742				}
743			}
744			*db_history_last++ = '\0';
745		}
746		db_history_curr = db_history_last;
747#endif
748		*db_le++ = c;
749		db_hist_search = 0;
750		db_hist_unmodified = 0;
751		return (TRUE);
752	    default:
753		if (db_le == db_lbuf_end) {
754		    cnputc('\007');
755		}
756		else if (c >= ' ' && c <= '~') {
757		    for (p = db_le; p > db_lc; p--)
758			*p = *(p-1);
759		    *db_lc++ = c;
760		    db_le++;
761		    cnputc(c);
762		    db_putstring(db_lc, db_le - db_lc);
763		    db_putnchars(BACKUP, db_le - db_lc);
764		}
765		break;
766	}
767	if (db_hist_search)
768		db_hist_search--;
769	if (db_hist_unmodified)
770		db_hist_unmodified--;
771	return (FALSE);
772}
773
774int
775db_readline(
776	char *	lstart,
777	int	lsize)
778{
779	db_force_whitespace();	/* synch output position */
780
781	db_lbuf_start = lstart;
782	db_lbuf_end   = lstart + lsize - 1;
783	db_lc = lstart;
784	db_le = lstart;
785
786	while (!db_inputchar(cngetc()))
787	    continue;
788
789	db_putchar('\n');	/* synch output position */
790
791	*db_le = 0;
792	return (db_le - db_lbuf_start);
793}
794
795void
796db_check_interrupt(void)
797{
798	register int	c;
799
800	c = cnmaygetc();
801	switch (c) {
802	    case -1:		/* no character */
803		return;
804
805	    case CTRL('c'):
806		db_error((char *)0);
807		/*NOTREACHED*/
808
809	    case CTRL('s'):
810		do {
811		    c = cnmaygetc();
812		    if (c == CTRL('c'))
813			db_error((char *)0);
814		} while (c != CTRL('q'));
815		break;
816
817	    default:
818		/* drop on floor */
819		break;
820	}
821}
822