Source to osfmk/ddb/db_input.c
/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* @OSF_COPYRIGHT@
*/
/*
* HISTORY
*
* Revision 1.1.1.1 1998/09/22 21:05:48 wsanchez
* Import of Mac OS X kernel (~semeria)
*
* Revision 1.1.1.1 1998/03/07 02:26:09 wsanchez
* Import of OSF Mach kernel (~mburg)
*
* Revision 1.3.10.2 1994/09/23 01:19:37 ezf
* change marker to not FREE
* [1994/09/22 21:10:05 ezf]
*
* Revision 1.3.10.1 1994/06/11 21:11:48 bolinger
* Merge up to NMK17.2.
* [1994/06/11 20:01:41 bolinger]
*
* Revision 1.3.8.2 1994/02/11 14:21:41 paire
* Added string.h header file for strlen declaration.
* [94/02/09 paire]
*
* Revision 1.3.8.1 1994/02/08 10:57:55 bernadat
* Added db_auto_completion variable.
* [93/08/17 paire]
*
* Added support of symbol completion by typing '\t'.
* [93/08/14 paire]
* [94/02/07 bernadat]
*
* Revision 1.3.2.4 1993/08/11 20:37:51 elliston
* Add ANSI Prototypes. CR #9523.
* [1993/08/11 03:33:21 elliston]
*
* Revision 1.3.2.3 1993/07/27 18:27:30 elliston
* Add ANSI prototypes. CR #9523.
* [1993/07/27 18:12:01 elliston]
*
* Revision 1.3.2.2 1993/06/09 02:20:13 gm
* CR9176 - ANSI C violations: trailing tokens on CPP
* directives, extra semicolons after decl_ ..., asm keywords
* [1993/06/07 18:57:14 jeffc]
*
* Added to OSF/1 R1.3 from NMK15.0.
* [1993/06/02 20:56:26 jeffc]
*
* Revision 1.3 1993/04/19 16:02:17 devrcs
* Replaced ^R (redraw) with ^L [[email protected]]
*
* Added ^R and ^S commands for history search commands
* ^U does not erase end of the line anymore. (only erases
* from the beginning of the line to current position).
* [[email protected]]
*
* ^C now erases the entire line. [[email protected]]
* [92/12/03 bernadat]
*
* Fixed history management: Do not store repeated typed
* command. Null terminate current command in case it is a
* substring of the last command.
* [92/10/02 bernadat]
*
* Revision 1.2 1992/11/25 01:04:24 robert
* integrate changes for norma_14 below
*
* Philippe Bernadat (bernadat) at gr.osf.org 02-Oct-92
* Fixed history management: Do not store repeated typed
* command. Null terminate current command in case it is a
* substring of the last command.
* [1992/11/20 00:56:07 robert]
*
* integrate changes below for norma_14
* [1992/11/13 19:21:34 robert]
*
* Revision 1.1 1992/09/30 02:01:08 robert
* Initial revision
*
* $EndLog$
*/
/* CMU_HIST */
/*
* Revision 2.7.3.2 92/09/15 17:14:26 jeffreyh
* Fixed history code. (Only one char. out of 2 was checked to
* compare to last command)
* [[email protected]]
*
* Revision 2.7.3.1 92/03/03 16:13:30 jeffreyh
* Pick up changes from TRUNK
* [92/02/26 10:59:36 jeffreyh]
*
* Revision 2.8 92/02/19 15:07:44 elf
* Added delete_line (Ctrl-U).
* [92/02/17 kivinen]
*
* Added command line history. Ctrl-P = previous, Ctrl-N = next. If
* DB_HISTORY_SIZE is 0 then command history is disabled.
* [92/02/17 kivinen]
*
* Revision 2.7 91/10/09 16:00:03 af
* Revision 2.6.2.1 91/10/05 13:06:12 jeffreyh
* Fixed incorrect db_lbuf_end setting.
* [91/08/29 tak]
*
* Revision 2.6.2.1 91/10/05 13:06:12 jeffreyh
* Fixed incorrect db_lbuf_end setting.
* [91/08/29 tak]
*
* Revision 2.6 91/07/09 23:15:49 danner
* Add include of machine/db_machdep.h to allow machine-specific
* overrides via defines.
* [91/07/08 danner]
*
* Revision 2.5 91/05/14 15:34:03 mrt
* Correcting copyright
*
* Revision 2.4 91/02/14 14:41:53 mrt
* Add input line editing.
* [90/11/11 dbg]
*
* Revision 2.3 91/02/05 17:06:32 mrt
* Changed to new Mach copyright
* [91/01/31 16:18:13 mrt]
*
* Revision 2.2 90/08/27 21:51:03 dbg
* Reduce lint.
* [90/08/07 dbg]
* Created.
* [90/07/25 dbg]
*
*/
/* CMU_ENDHIST */
/*
* Mach Operating System
* Copyright (c) 1991,1990 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or [email protected]
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
/*
*/
/*
* Author: David B. Golub, Carnegie Mellon University
* Date: 7/90
*/
#include <string.h>
#include <mach/boolean.h>
#include <machine/db_machdep.h>
#include <kern/misc_protos.h>
#include <ddb/db_output.h>
#include <ddb/db_lex.h>
#include <ddb/db_command.h>
#include <ddb/db_input.h>
#include <ddb/db_sym.h>
#ifndef DB_HISTORY_SIZE
#define DB_HISTORY_SIZE 4000
#endif /* DB_HISTORY_SIZE */
/*
* Character input and editing.
*/
/*
* We don't track output position while editing input,
* since input always ends with a new-line. We just
* reset the line position at the end.
*/
char * db_lbuf_start; /* start of input line buffer */
char * db_lbuf_end; /* end of input line buffer */
char * db_lc; /* current character */
char * db_le; /* one past last character */
int db_completion; /* number of incomplete symbols matched */
int db_auto_completion = 10; /* number of line to display without asking */
#if DB_HISTORY_SIZE != 0
char db_history[DB_HISTORY_SIZE]; /* start of history buffer */
int db_history_size = DB_HISTORY_SIZE;/* size of history buffer */
char * db_history_curr = db_history; /* start of current line */
char * db_history_last = db_history; /* start of last line */
char * db_history_prev = (char *) 0; /* start of previous line */
int db_hist_unmodified = 0; /* unmodified line from history */
int db_hist_search = 0; /* are we in hist search mode ? */
char db_hist_search_string[DB_LEX_LINE_SIZE];/* the string to look for */
int db_hist_ignore_dups = 0; /* don't duplicate commands in hist */
#endif
#define CTRL(c) ((c) & 0x1f)
#define isspace(c) ((c) == ' ' || (c) == '\t')
#define BLANK ' '
#define BACKUP '\b'
/* Prototypes for functions local to this file. XXX -- should be static!
*/
void db_putstring(
char *s,
int count);
void db_putnchars(
int c,
int count);
void db_delete(
int n,
int bwd);
void db_delete_line(void);
boolean_t db_hist_substring(
char *string,
char *substring);
boolean_t db_inputchar(int c);
extern jmp_buf_t *db_recover;
void
db_putstring(
char *s,
int count)
{
while (--count >= 0)
cnputc(*s++);
}
void
db_putnchars(
int c,
int count)
{
while (--count >= 0)
cnputc(c);
}
/*
* Delete N characters, forward or backward
*/
#define DEL_FWD 0
#define DEL_BWD 1
void
db_delete(
int n,
int bwd)
{
register char *p;
if (bwd) {
db_lc -= n;
db_putnchars(BACKUP, n);
}
for (p = db_lc; p < db_le-n; p++) {
*p = *(p+n);
cnputc(*p);
}
db_putnchars(BLANK, n);
db_putnchars(BACKUP, db_le - db_lc);
db_le -= n;
}
void
db_delete_line(void)
{
db_delete(db_le - db_lc, DEL_FWD);
db_delete(db_lc - db_lbuf_start, DEL_BWD);
db_le = db_lc = db_lbuf_start;
}
#if DB_HISTORY_SIZE != 0
#define INC_DB_CURR() \
do { \
db_history_curr++; \
if (db_history_curr > \
db_history + db_history_size - 1) \
db_history_curr = db_history; \
} while (0)
#define DEC_DB_CURR() \
do { \
db_history_curr--; \
if (db_history_curr < db_history) \
db_history_curr = db_history + \
db_history_size - 1; \
} while (0)
#endif
/* returs TRUE if "substring" is a substring of "string" */
boolean_t
db_hist_substring(
char *string,
char *substring)
{
register char *cp1, *cp2;
cp1 = string;
while (*cp1)
cp1++;
cp2 = substring;
while (*cp2)
cp2++;
while (cp2 > substring) {
cp1--; cp2--;
}
while (cp1 >= string) {
register char *cp3;
cp2 = substring;
cp3 = cp1;
while (*cp2 && *cp2 == *cp3) {
cp2++; cp3++;
}
if (*cp2 == '\0') {
return TRUE;
}
cp1--;
}
return FALSE;
}
/* returns TRUE at end-of-line */
boolean_t
db_inputchar(int c)
{
char *sym;
char *start;
char *restart;
jmp_buf_t db_jmpbuf;
jmp_buf_t *db_prev;
char *p;
int len;
switch(db_completion) {
case -1:
db_putchar('\n');
db_prev = db_recover;
if (_setjmp(db_recover = &db_jmpbuf) == 0 &&
(c == 'y' || c == ' ' || c == '\t'))
db_print_completion(db_tok_string);
db_recover = db_prev;
db_completion = 0;
db_reset_more();
db_output_prompt();
if (db_le > db_lbuf_start) {
for (start = db_lbuf_start; start < db_le; start++)
db_putchar(*start);
db_putnchars(BACKUP, db_le - db_lc);
}
return(FALSE);
case 0:
break;
default:
if (c == '\t') {
db_printf("\nThere are %d possibilities. ", db_completion);
db_printf("Do you really wish to see them all [n] ? ");
db_force_whitespace();
db_completion = -1;
db_reset_more();
return(FALSE);
}
db_completion = 0;
break;
}
switch (c) {
case '\t':
/* symbol completion */
if (db_lc == db_lbuf_start || db_auto_completion == 0)
break;
if (db_le == db_lbuf_end) {
cnputc('\007');
break;
}
start = db_lc - 1;
while (start >= db_lbuf_start &&
((*start >= 'A' && *start <= 'Z') ||
(*start >= 'a' && *start <= 'z') ||
(*start >= '0' && *start <= '9') ||
*start == '_' || *start == ':'))
start--;
if (start == db_lc - 1)
break;
if (start > db_lbuf_start && *start == '$') {
cnputc('\007');
break;
}
sym = db_tok_string;
restart = ++start;
do {
*sym++ = *start++;
} while (start != db_lc &&
sym != db_tok_string + sizeof(db_tok_string));
if (sym == db_tok_string + sizeof(db_tok_string)) {
cnputc('\007');
break;
}
*sym = '\0';
db_completion = db_lookup_incomplete(db_tok_string,
sizeof(db_tok_string));
if (db_completion == 0) {
/* symbol unknown */
cnputc('\007');
break;
}
len = strlen(db_tok_string) - (start - restart);
if (db_completion == 1 &&
(db_le == db_lc ||
(db_le > db_lc) && *db_lc != ' '))
len++;
for (p = db_le - 1; p >= db_lc; p--)
*(p + len) = *p;
db_le += len;
for (sym = &db_tok_string[start - restart];
*sym != '\0'; sym++)
*db_lc++ = *sym;
if (db_completion == 1 || db_completion > db_auto_completion) {
for (sym = &db_tok_string[start - restart];
*sym != '\0'; sym++)
cnputc(*sym);
if (db_completion == 1) {
if (db_le == db_lc ||
(db_le > db_lc) && *db_lc != ' ') {
cnputc(' ');
*db_lc++ = ' ';
}
db_completion = 0;
}
db_putstring(db_lc, db_le - db_lc);
db_putnchars(BACKUP, db_le - db_lc);
}
if (db_completion > 1) {
cnputc('\007');
if (db_completion <= db_auto_completion) {
db_putchar('\n');
db_print_completion(db_tok_string);
db_completion = 0;
db_reset_more();
db_output_prompt();
if (db_le > db_lbuf_start) {
for (start = db_lbuf_start; start < db_le; start++)
db_putchar(*start);
db_putnchars(BACKUP, db_le - db_lc);
}
}
}
break;
case CTRL('b'):
/* back up one character */
if (db_lc > db_lbuf_start) {
cnputc(BACKUP);
db_lc--;
}
break;
case CTRL('f'):
/* forward one character */
if (db_lc < db_le) {
cnputc(*db_lc);
db_lc++;
}
break;
case CTRL('a'):
/* beginning of line */
while (db_lc > db_lbuf_start) {
cnputc(BACKUP);
db_lc--;
}
break;
case CTRL('e'):
/* end of line */
while (db_lc < db_le) {
cnputc(*db_lc);
db_lc++;
}
break;
case CTRL('h'):
case 0177:
/* erase previous character */
if (db_lc > db_lbuf_start)
db_delete(1, DEL_BWD);
break;
case CTRL('d'):
/* erase next character */
if (db_lc < db_le)
db_delete(1, DEL_FWD);
break;
case CTRL('k'):
/* delete to end of line */
if (db_lc < db_le)
db_delete(db_le - db_lc, DEL_FWD);
break;
case CTRL('u'):
/* delete to beginning of line */
if (db_lc > db_lbuf_start)
db_delete(db_lc - db_lbuf_start, DEL_BWD);
break;
case CTRL('t'):
/* twiddle last 2 characters */
if (db_lc >= db_lbuf_start + 2) {
c = db_lc[-2];
db_lc[-2] = db_lc[-1];
db_lc[-1] = c;
cnputc(BACKUP);
cnputc(BACKUP);
cnputc(db_lc[-2]);
cnputc(db_lc[-1]);
}
break;
case CTRL('c'):
case CTRL('g'):
db_delete_line();
#if DB_HISTORY_SIZE != 0
db_history_curr = db_history_last;
if (c == CTRL('g') && db_hist_search) {
char *p;
for (p = db_hist_search_string, db_le = db_lbuf_start;
*p; ) {
*db_le++ = *p++;
}
db_lc = db_le;
*db_le = '\0';
db_putstring(db_lbuf_start, db_le - db_lbuf_start);
}
#endif
break;
#if DB_HISTORY_SIZE != 0
case CTRL('r'):
if (db_hist_search++ == 0) {
/* starting an history lookup */
register char *cp1, *cp2;
for (cp1 = db_lbuf_start, cp2 = db_hist_search_string;
cp1 < db_le;
cp1++, cp2++)
*cp2 = *cp1;
*cp2 = '\0';
db_hist_search++;
}
/* FALL THROUGH */
case CTRL('p'):
{
char * old_history_curr = db_history_curr;
if (db_hist_unmodified++ == 0)
db_hist_unmodified++;
DEC_DB_CURR();
while (db_history_curr != db_history_last) {
DEC_DB_CURR();
if (*db_history_curr == '\0') {
INC_DB_CURR();
if (db_hist_search <= 1) {
if (*db_history_curr == '\0')
cnputc('\007');
else
DEC_DB_CURR();
break;
}
if (*db_history_curr == '\0') {
cnputc('\007');
db_history_curr = old_history_curr;
DEC_DB_CURR();
break;
}
if (db_history_curr != db_history_last &&
db_hist_substring(db_history_curr,
db_hist_search_string)) {
DEC_DB_CURR();
break;
}
DEC_DB_CURR();
}
}
if (db_history_curr == db_history_last) {
cnputc('\007');
db_history_curr = old_history_curr;
} else {
register char *p;
INC_DB_CURR();
db_delete_line();
for (p = db_history_curr, db_le = db_lbuf_start;
*p; ) {
*db_le++ = *p++;
if (p == db_history + db_history_size) {
p = db_history;
}
}
db_lc = db_le;
*db_le = '\0';
db_putstring(db_lbuf_start, db_le - db_lbuf_start);
}
break;
}
case CTRL('s'):
if (db_hist_search++ == 0) {
/* starting an history lookup */
register char *cp1, *cp2;
for (cp1 = db_lbuf_start, cp2 = db_hist_search_string;
cp1 < db_le;
cp1++, cp2++)
*cp2 = *cp1;
*cp2 = '\0';
db_hist_search++;
}
/* FALL THROUGH */
case CTRL('n'):
{
char *old_history_curr = db_history_curr;
if (db_hist_unmodified++ == 0)
db_hist_unmodified++;
while (db_history_curr != db_history_last) {
if (*db_history_curr == '\0') {
if (db_hist_search <= 1)
break;
INC_DB_CURR();
if (db_history_curr != db_history_last &&
db_hist_substring(db_history_curr,
db_hist_search_string)) {
DEC_DB_CURR();
break;
}
DEC_DB_CURR();
}
INC_DB_CURR();
}
if (db_history_curr != db_history_last) {
INC_DB_CURR();
if (db_history_curr != db_history_last) {
register char *p;
db_delete_line();
for (p = db_history_curr,
db_le = db_lbuf_start; *p;) {
*db_le++ = *p++;
if (p == db_history +
db_history_size) {
p = db_history;
}
}
db_lc = db_le;
*db_le = '\0';
db_putstring(db_lbuf_start,
db_le - db_lbuf_start);
} else {
cnputc('\007');
db_history_curr = old_history_curr;
}
} else {
cnputc('\007');
db_history_curr = old_history_curr;
}
break;
}
#endif
/* refresh the command line */
case CTRL('l'):
db_putstring("^L\n", 3);
if (db_le > db_lbuf_start) {
db_putstring(db_lbuf_start, db_le - db_lbuf_start);
db_putnchars(BACKUP, db_le - db_lc);
}
break;
case '\n':
case '\r':
#if DB_HISTORY_SIZE != 0
/* Check if it same than previous line */
if (db_history_prev) {
register char *pp, *pc;
/* Is it unmodified */
for (p = db_history_prev, pc = db_lbuf_start;
pc != db_le && *p;) {
if (*p != *pc)
break;
if (++p == db_history + db_history_size) {
p = db_history;
}
if (++pc == db_history + db_history_size) {
pc = db_history;
}
}
if (!*p && pc == db_le) {
/* Repeted previous line, not saved */
db_history_curr = db_history_last;
*db_le++ = c;
db_hist_search = 0;
db_hist_unmodified = 0;
return (TRUE);
}
}
if (db_le != db_lbuf_start &&
(db_hist_unmodified == 0 || !db_hist_ignore_dups)) {
db_history_prev = db_history_last;
for (p = db_lbuf_start; p != db_le; p++) {
*db_history_last++ = *p;
if (db_history_last == db_history +
db_history_size) {
db_history_last = db_history;
}
}
*db_history_last++ = '\0';
}
db_history_curr = db_history_last;
#endif
*db_le++ = c;
db_hist_search = 0;
db_hist_unmodified = 0;
return (TRUE);
default:
if (db_le == db_lbuf_end) {
cnputc('\007');
}
else if (c >= ' ' && c <= '~') {
for (p = db_le; p > db_lc; p--)
*p = *(p-1);
*db_lc++ = c;
db_le++;
cnputc(c);
db_putstring(db_lc, db_le - db_lc);
db_putnchars(BACKUP, db_le - db_lc);
}
break;
}
if (db_hist_search)
db_hist_search--;
if (db_hist_unmodified)
db_hist_unmodified--;
return (FALSE);
}
int
db_readline(
char * lstart,
int lsize)
{
db_force_whitespace(); /* synch output position */
db_lbuf_start = lstart;
db_lbuf_end = lstart + lsize - 1;
db_lc = lstart;
db_le = lstart;
while (!db_inputchar(cngetc()))
continue;
db_putchar('\n'); /* synch output position */
*db_le = 0;
return (db_le - db_lbuf_start);
}
void
db_check_interrupt(void)
{
register int c;
c = cnmaygetc();
switch (c) {
case -1: /* no character */
return;
case CTRL('c'):
db_error((char *)0);
/*NOTREACHED*/
case CTRL('s'):
do {
c = cnmaygetc();
if (c == CTRL('c'))
db_error((char *)0);
} while (c != CTRL('q'));
break;
default:
/* drop on floor */
break;
}
}