Changeset View
Standalone View
sys/ddb/db_input.c
Show All 30 Lines | |||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/cons.h> | #include <sys/cons.h> | ||||
#include <sys/sysctl.h> | |||||
#include <ddb/ddb.h> | #include <ddb/ddb.h> | ||||
#include <ddb/db_output.h> | #include <ddb/db_output.h> | ||||
/* | /* | ||||
* Character input and editing. | * Character input and editing. | ||||
*/ | */ | ||||
/* | /* | ||||
* We don't track output position while editing input, | * We don't track output position while editing input, | ||||
* since input always ends with a new-line. We just | * since input always ends with a new-line. We just | ||||
* reset the line position at the end. | * reset the line position at the end. | ||||
*/ | */ | ||||
static char * db_lbuf_start; /* start of input line buffer */ | static char * db_lbuf_start; /* start of input line buffer */ | ||||
static char * db_lbuf_end; /* end of input line buffer */ | static char * db_lbuf_end; /* end of input line buffer */ | ||||
static char * db_lc; /* current character */ | static char * db_lc; /* current character */ | ||||
static char * db_le; /* one past last character */ | static char * db_le; /* one past last character */ | ||||
/* | /* | ||||
* Raw input buffer, processed only for certain control characters. | |||||
*/ | |||||
#define DB_RAW_SIZE 512 | |||||
rlibby: A guess at a size that is small enough not to be too wasteful and large enough that most people… | |||||
Not Done Inline ActionsI'd have selected this or 1024.... but I'd think that fifo sizes would limit this to tens of bytes plus overflow unless hardware flow control was setup... imp: I'd have selected this or 1024.... but I'd think that fifo sizes would limit this to tens of… | |||||
Done Inline ActionsI'm pretty ignorant on the console side so I'll defer to your knowledge there. Let me know if your preference is to bump it up. Empirically, I can hit this 512 byte limit with a large paste on a qemu VM at least. rlibby: I'm pretty ignorant on the console side so I'll defer to your knowledge there. Let me know if… | |||||
static char db_raw[DB_RAW_SIZE]; | |||||
static u_int db_raw_pos; | |||||
static u_int db_raw_cnt; | |||||
static int db_raw_warned; | |||||
static int ddb_prioritize_control_input = 1; | |||||
Done Inline ActionsProbably this should be RWTUN (really that should be the default). I think this probably belongs under debug.ddb? As-is this creates an OID directly under debug. markj: Probably this should be RWTUN (really that should be the default).
I think this probably… | |||||
Done Inline ActionsWill do. rlibby: Will do. | |||||
SYSCTL_INT(_debug_ddb, OID_AUTO, prioritize_control_input, CTLFLAG_RWTUN, | |||||
&ddb_prioritize_control_input, 0, | |||||
"Drop input when the buffer fills in order to keep servicing ^C/^S/^Q"); | |||||
/* | |||||
* Simple input line history support. | * Simple input line history support. | ||||
*/ | */ | ||||
static char db_lhistory[2048]; | static char db_lhistory[2048]; | ||||
static int db_lhistlsize, db_lhistidx, db_lhistcur; | static int db_lhistlsize, db_lhistidx, db_lhistcur; | ||||
static int db_lhist_nlines; | static int db_lhist_nlines; | ||||
#define CTRL(c) ((c) & 0x1f) | #define CTRL(c) ((c) & 0x1f) | ||||
#define BLANK ' ' | #define BLANK ' ' | ||||
#define BACKUP '\b' | #define BACKUP '\b' | ||||
static int cnmaygetc(void); | |||||
static void db_delete(int n, int bwd); | static void db_delete(int n, int bwd); | ||||
static int db_inputchar(int c); | static int db_inputchar(int c); | ||||
static void db_putnchars(int c, int count); | static void db_putnchars(int c, int count); | ||||
static void db_putstring(char *s, int count); | static void db_putstring(char *s, int count); | ||||
static int db_raw_pop(void); | |||||
static void db_raw_push(int); | |||||
static int db_raw_space(void); | |||||
static void | static void | ||||
db_putstring(s, count) | db_putstring(s, count) | ||||
char *s; | char *s; | ||||
int count; | int count; | ||||
{ | { | ||||
while (--count >= 0) | while (--count >= 0) | ||||
cnputc(*s++); | cnputc(*s++); | ||||
▲ Show 20 Lines • Show All 221 Lines • ▼ Show 20 Lines | else if (c >= ' ' && c <= '~') { | ||||
db_putstring(db_lc, db_le - db_lc); | db_putstring(db_lc, db_le - db_lc); | ||||
db_putnchars(BACKUP, db_le - db_lc); | db_putnchars(BACKUP, db_le - db_lc); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Get a character from the console, first checking the raw input buffer. */ | |||||
int | |||||
db_getc(void) | |||||
{ | |||||
int c; | |||||
if (db_raw_cnt == 0) { | |||||
Done Inline ActionsThis is an excellent choke point for having a variable to disable this feature, which would allow the script pasters turn amuck. imp: This is an excellent choke point for having a variable to disable this feature, which would… | |||||
Done Inline ActionsThanks. I added a switch near the cncheckc() instead so that ^C will usually still work with the switch disabled, at least until the input buffer is exhausted, which most of the time it won't be. I've made it a sysctl for now, but it could also be a ddb variable if you think that would be useful (but on the other hand, someone who knew the voodoo could just write the variable with ddb...). I struggled to name the switch and am open to suggestions there. rlibby: Thanks. I added a switch near the cncheckc() instead so that ^C will usually still work with… | |||||
c = cngetc(); | |||||
} else { | |||||
c = db_raw_pop(); | |||||
if (c == '\r') | |||||
c = '\n'; | |||||
} | |||||
return (c); | |||||
} | |||||
/* Whether the raw input buffer has space to accept another character. */ | |||||
static int | static int | ||||
cnmaygetc() | db_raw_space(void) | ||||
{ | { | ||||
return (db_raw_cnt < DB_RAW_SIZE); | |||||
} | |||||
/* Un-get a character from the console by buffering it. */ | |||||
static void | |||||
db_raw_push(int c) | |||||
{ | |||||
if (!db_raw_space()) | |||||
db_error(NULL); | |||||
db_raw[(db_raw_pos + db_raw_cnt++) % DB_RAW_SIZE] = c; | |||||
} | |||||
/* Drain a character from the raw input buffer. */ | |||||
static int | |||||
db_raw_pop(void) | |||||
{ | |||||
if (db_raw_cnt == 0) | |||||
return (-1); | return (-1); | ||||
db_raw_cnt--; | |||||
db_raw_warned = 0; | |||||
return (db_raw[db_raw_pos++ % DB_RAW_SIZE]); | |||||
} | } | ||||
int | int | ||||
db_readline(lstart, lsize) | db_readline(lstart, lsize) | ||||
char * lstart; | char * lstart; | ||||
int lsize; | int lsize; | ||||
{ | { | ||||
Show All 12 Lines | db_readline(lstart, lsize) | ||||
db_force_whitespace(); /* synch output position */ | db_force_whitespace(); /* synch output position */ | ||||
db_lbuf_start = lstart; | db_lbuf_start = lstart; | ||||
db_lbuf_end = lstart + lsize - 2; /* Will append NL and NUL. */ | db_lbuf_end = lstart + lsize - 2; /* Will append NL and NUL. */ | ||||
db_lc = lstart; | db_lc = lstart; | ||||
db_le = lstart; | db_le = lstart; | ||||
while (!db_inputchar(cngetc())) | while (!db_inputchar(db_getc())) | ||||
continue; | continue; | ||||
db_capture_write(lstart, db_le - db_lbuf_start); | db_capture_write(lstart, db_le - db_lbuf_start); | ||||
db_printf("\n"); /* synch output position */ | db_printf("\n"); /* synch output position */ | ||||
*db_le = 0; | *db_le = 0; | ||||
if (db_le - db_lbuf_start > 1) { | if (db_le - db_lbuf_start > 1) { | ||||
/* Maintain input line history for non-empty lines. */ | /* Maintain input line history for non-empty lines. */ | ||||
if (++db_lhistidx == db_lhist_nlines) { | if (++db_lhistidx == db_lhist_nlines) { | ||||
/* Rotate history. */ | /* Rotate history. */ | ||||
bcopy(db_lhistory + db_lhistlsize, db_lhistory, | bcopy(db_lhistory + db_lhistlsize, db_lhistory, | ||||
db_lhistlsize * (db_lhist_nlines - 1)); | db_lhistlsize * (db_lhist_nlines - 1)); | ||||
db_lhistidx--; | db_lhistidx--; | ||||
} | } | ||||
bcopy(lstart, db_lhistory + db_lhistidx * db_lhistlsize, | bcopy(lstart, db_lhistory + db_lhistidx * db_lhistlsize, | ||||
db_lhistlsize); | db_lhistlsize); | ||||
} | } | ||||
return (db_le - db_lbuf_start); | return (db_le - db_lbuf_start); | ||||
} | } | ||||
static void | |||||
db_do_interrupt(const char *reason) | |||||
{ | |||||
/* Do a pager quit too because some commands have jmpbuf handling. */ | |||||
db_disable_pager(); | |||||
db_pager_quit = 1; | |||||
db_error(reason); | |||||
Done Inline ActionsMaybe this deserves a comment? For some commands, like alltrace, db_error() is not enough because they have their own jmpbufs. rlibby: Maybe this deserves a comment? For some commands, like alltrace, db_error() is not enough… | |||||
} | |||||
void | void | ||||
db_check_interrupt(void) | db_check_interrupt(void) | ||||
{ | { | ||||
int c; | int c; | ||||
c = cnmaygetc(); | /* | ||||
* Check console input for control characters. Non-control input is | |||||
* buffered. When buffer space is exhausted, either stop responding to | |||||
* control input or drop further non-control input on the floor. | |||||
*/ | |||||
for (;;) { | |||||
Done Inline ActionsYou might consider documenting this in ddb.4, under DESCRIPTION or HINTS. markj: You might consider documenting this in ddb.4, under DESCRIPTION or HINTS. | |||||
Done Inline ActionsWill consider how to word this. rlibby: Will consider how to word this. | |||||
if (!ddb_prioritize_control_input && !db_raw_space()) | |||||
return; | |||||
c = cncheckc(); | |||||
switch (c) { | switch (c) { | ||||
case -1: /* no character */ | case -1: /* no character */ | ||||
return; | return; | ||||
case CTRL('c'): | case CTRL('c'): | ||||
db_error((char *)0); | db_do_interrupt("^C"); | ||||
Not Done Inline ActionsI think I'd expect ^C to flush the typeahead buffer. Did you consider doing that here? markj: I think I'd expect ^C to flush the typeahead buffer. Did you consider doing that here? | |||||
Done Inline ActionsHmm. No I didn't think much about that. Unfortunately I think we may see odd stuff either way. If the buffer is flushed, then as input continues to be received it will be buffered, so for illustration if the buffer size were 4 and this were received x/s version we'd end up with ion in the buffer. On the other hand, the way it is currently coded can also result in a weird thing where x/s is buffered, then some number of characters are dropped as they are received, then if we are still receiving input as the buffer is drained characters are alternately dropped and buffered. I'm not sure what the least surprising behavior would be. Maybe it would be to drop all input until the buffer is completely drained and then to drain the console too? If you don't have a strong feeling about this, I might like to just go ahead with the current behavior and see if it's a problem in practice before doing anything elaborate. rlibby: Hmm. No I didn't think much about that. Unfortunately I think we may see odd stuff either way. | |||||
Not Done Inline ActionsI'm ok with committing the change as-is. I don't have any strong feelings about this either, I don't think I rely much on typeahead in ddb in any case. markj: I'm ok with committing the change as-is. I don't have any strong feelings about this either, I… | |||||
/*NOTREACHED*/ | /*NOTREACHED*/ | ||||
case CTRL('s'): | case CTRL('s'): | ||||
do { | do { | ||||
c = cnmaygetc(); | c = cncheckc(); | ||||
if (c == CTRL('c')) | if (c == CTRL('c')) | ||||
db_error((char *)0); | db_do_interrupt("^C"); | ||||
} while (c != CTRL('q')); | } while (c != CTRL('q')); | ||||
break; | break; | ||||
default: | default: | ||||
/* drop on floor */ | if (db_raw_space()) { | ||||
db_raw_push(c); | |||||
} else if (!db_raw_warned) { | |||||
db_raw_warned = 1; | |||||
db_printf("\n--Exceeded input buffer--\n"); | |||||
} | |||||
break; | break; | ||||
} | |||||
} | } | ||||
} | } |
A guess at a size that is small enough not to be too wasteful and large enough that most people pasting multiple commands into the db> prompt won't notice.