less: update line input so that it doesn't interfere with

screen update. Makes "man bash", [enter], [/],
<enter search pattern>, [enter] more usable - manpage
draws as you enter the pattern! Yay!!
less: fix bug where backspace wasn't actually deleting chars
less: "examine file with empty name" doesn't abort anymore.
libbb: add "all fatal signals" mask.

getch_nowait                                           -     207    +207
status_print                                           -     105    +105
examine_file                                          64      87     +23
move_cursor                                            -      16     +16
m_status_print                                       185     195     +10
less_main                                           1656    1663      +7
decode_format_string                                 790     795      +5
test_main                                            403     405      +2
process0_stdin                                       247     249      +2
passwd_main                                         1070    1072      +2
less_gets                                            196     178     -18
buffer_print                                         169      71     -98
less_getch                                           362     138    -224
------------------------------------------------------------------------------
(add/remove: 3/0 grow/shrink: 7/3 up/down: 379/-340)           Total: 39 bytes
   text    data     bss     dec     hex filename
 798329     740    7484  806553   c4e99 busybox_old
 798368     740    7484  806592   c4ec0 busybox_unstripped
This commit is contained in:
Denis Vlasenko 2008-02-23 01:25:38 +00:00
parent 86620756d2
commit 33196372be
2 changed files with 82 additions and 46 deletions

View File

@ -274,9 +274,32 @@ char *xrealloc_getcwd_or_warn(char *cwd);
char *xmalloc_follow_symlinks(const char *path); char *xmalloc_follow_symlinks(const char *path);
//enum { enum {
// BB_SIGS_FATAL = , /* bb_signals(BB_SIGS_FATAL, handler) catches all signals which
//}; * otherwise would kill us, except for those resulting from bugs:
* SIGSEGV, SIGILL, SIGFPE.
* Other fatal signals not included (TODO?):
* SIGBUS Bus error (bad memory access)
* SIGPOLL Pollable event. Synonym of SIGIO
* SIGPROF Profiling timer expired
* SIGSYS Bad argument to routine
* SIGTRAP Trace/breakpoint trap
*/
BB_SIGS_FATAL = 0
+ (1 << SIGHUP)
+ (1 << SIGINT)
+ (1 << SIGTERM)
+ (1 << SIGPIPE) // Write to pipe with no readers
+ (1 << SIGQUIT) // Quit from keyboard
+ (1 << SIGABRT) // Abort signal from abort(3)
+ (1 << SIGALRM) // Timer signal from alarm(2)
+ (1 << SIGVTALRM) // Virtual alarm clock
+ (1 << SIGXCPU) // CPU time limit exceeded
+ (1 << SIGXFSZ) // File size limit exceeded
+ (1 << SIGUSR1) // Yes kids, these are also fatal!
+ (1 << SIGUSR2)
+ 0,
};
void bb_signals(int sigs, void (*f)(int)); void bb_signals(int sigs, void (*f)(int));
/* Unlike signal() and bb_signals, sets handler with sigaction() /* Unlike signal() and bb_signals, sets handler with sigaction()
* and in a way that while signal handler is run, no other signals * and in a way that while signal handler is run, no other signals

View File

@ -90,6 +90,7 @@ enum { pattern_valid = 0 };
struct globals { struct globals {
int cur_fline; /* signed */ int cur_fline; /* signed */
int kbd_fd; /* fd to get input from */ int kbd_fd; /* fd to get input from */
int less_gets_pos;
/* last position in last line, taking into account tabs */ /* last position in last line, taking into account tabs */
size_t linepos; size_t linepos;
unsigned max_displayed_line; unsigned max_displayed_line;
@ -123,6 +124,7 @@ struct globals {
#define G (*ptr_to_globals) #define G (*ptr_to_globals)
#define cur_fline (G.cur_fline ) #define cur_fline (G.cur_fline )
#define kbd_fd (G.kbd_fd ) #define kbd_fd (G.kbd_fd )
#define less_gets_pos (G.less_gets_pos )
#define linepos (G.linepos ) #define linepos (G.linepos )
#define max_displayed_line (G.max_displayed_line) #define max_displayed_line (G.max_displayed_line)
#define max_fline (G.max_fline ) #define max_fline (G.max_fline )
@ -152,6 +154,7 @@ struct globals {
#define term_less (G.term_less ) #define term_less (G.term_less )
#define INIT_G() do { \ #define INIT_G() do { \
PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
less_gets_pos = -1; \
empty_line_marker = "~"; \ empty_line_marker = "~"; \
num_files = 1; \ num_files = 1; \
current_file = 1; \ current_file = 1; \
@ -385,6 +388,9 @@ static void m_status_print(void)
{ {
int percentage; int percentage;
if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
return;
clear_line(); clear_line();
printf(HIGHLIGHT"%s", filename); printf(HIGHLIGHT"%s", filename);
if (num_files > 1) if (num_files > 1)
@ -408,6 +414,9 @@ static void status_print(void)
{ {
const char *p; const char *p;
if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
return;
/* Change the status if flags have been set */ /* Change the status if flags have been set */
#if ENABLE_FEATURE_LESS_FLAGS #if ENABLE_FEATURE_LESS_FLAGS
if (option_mask32 & (FLAG_M|FLAG_m)) { if (option_mask32 & (FLAG_M|FLAG_m)) {
@ -652,43 +661,46 @@ static void reinitialize(void)
buffer_fill_and_print(); buffer_fill_and_print();
} }
static void getch_nowait(char* input, int sz) static ssize_t getch_nowait(char* input, int sz)
{ {
ssize_t rd; ssize_t rd;
fd_set readfds; struct pollfd pfd[2];
again:
fflush(stdout);
/* NB: select returns whenever read will not block. Therefore: pfd[0].fd = STDIN_FILENO;
* (a) with O_NONBLOCK'ed fds select will return immediately pfd[0].events = POLLIN;
* (b) if eof is reached, select will also return pfd[1].fd = kbd_fd;
* because read will immediately return 0 bytes. pfd[1].events = POLLIN;
* Even if select says that input is available, read CAN block again:
tcsetattr(kbd_fd, TCSANOW, &term_less);
/* NB: select/poll returns whenever read will not block. Therefore:
* if eof is reached, select/poll will return immediately
* because read will immediately return 0 bytes.
* Even if select/poll says that input is available, read CAN block
* (switch fd into O_NONBLOCK'ed mode to avoid it) * (switch fd into O_NONBLOCK'ed mode to avoid it)
*/ */
FD_ZERO(&readfds); rd = 1;
if (max_fline <= cur_fline + max_displayed_line if (max_fline <= cur_fline + max_displayed_line
&& eof_error > 0 /* did NOT reach eof yet */ && eof_error > 0 /* did NOT reach eof yet */
) { ) {
/* We are interested in stdin */ /* We are interested in stdin */
FD_SET(0, &readfds); rd = 0;
} }
FD_SET(kbd_fd, &readfds); /* position cursor if line input is done */
tcsetattr(kbd_fd, TCSANOW, &term_less); if (less_gets_pos >= 0)
select(kbd_fd + 1, &readfds, NULL, NULL, NULL); move_cursor(max_displayed_line + 2, less_gets_pos + 1);
fflush(stdout);
safe_poll(pfd + rd, 2 - rd, -1);
input[0] = '\0'; input[0] = '\0';
ndelay_on(kbd_fd); rd = safe_read(kbd_fd, input, sz); /* NB: kbd_fd is in O_NONBLOCK mode */
rd = read(kbd_fd, input, sz); if (rd < 0 && errno == EAGAIN) {
ndelay_off(kbd_fd); /* No keyboard input -> we have input on stdin! */
if (rd < 0) {
/* No keyboard input, but we have input on stdin! */
if (errno != EAGAIN) /* Huh?? */
return;
read_lines(); read_lines();
buffer_fill_and_print(); buffer_fill_and_print();
goto again; goto again;
} }
set_tty_cooked();
return rd;
} }
/* Grab a character from input without requiring the return key. If the /* Grab a character from input without requiring the return key. If the
@ -696,7 +708,7 @@ static void getch_nowait(char* input, int sz)
* special return codes. Note that this function works best with raw input. */ * special return codes. Note that this function works best with raw input. */
static int less_getch(void) static int less_getch(void)
{ {
char input[16]; unsigned char input[16];
unsigned i; unsigned i;
again: again:
memset(input, 0, sizeof(input)); memset(input, 0, sizeof(input));
@ -705,7 +717,6 @@ static int less_getch(void)
/* Detect escape sequences (i.e. arrow keys) and handle /* Detect escape sequences (i.e. arrow keys) and handle
* them accordingly */ * them accordingly */
if (input[0] == '\033' && input[1] == '[') { if (input[0] == '\033' && input[1] == '[') {
set_tty_cooked();
i = input[2] - REAL_KEY_UP; i = input[2] - REAL_KEY_UP;
if (i < 4) if (i < 4)
return 20 + i; return 20 + i;
@ -724,8 +735,8 @@ static int less_getch(void)
} }
/* Reject almost all control chars */ /* Reject almost all control chars */
i = input[0]; i = input[0];
if (i < ' ' && i != 0x0d && i != 8) goto again; if (i < ' ' && i != 0x0d && i != 8)
set_tty_cooked(); goto again;
return i; return i;
} }
@ -734,22 +745,21 @@ static char* less_gets(int sz)
char c; char c;
int i = 0; int i = 0;
char *result = xzalloc(1); char *result = xzalloc(1);
while (1) { while (1) {
fflush(stdout);
/* I be damned if I know why is it needed *repeatedly*,
* but it is needed. Is it because of stdio? */
tcsetattr(kbd_fd, TCSANOW, &term_less);
c = '\0'; c = '\0';
read(kbd_fd, &c, 1); less_gets_pos = sz + i;
if (c == 0x0d) getch_nowait(&c, 1);
if (c == 0x0d) {
less_gets_pos = -1;
return result; return result;
}
if (c == 0x7f) if (c == 0x7f)
c = 8; c = 8;
if (c == 8 && i) { if (c == 8 && i) {
printf("\x8 \x8"); printf("\x8 \x8");
i--; i--;
result[i] = '\0';
} }
if (c < ' ') if (c < ' ')
continue; continue;
@ -764,9 +774,17 @@ static char* less_gets(int sz)
static void examine_file(void) static void examine_file(void)
{ {
char *new_fname;
print_statusline("Examine: "); print_statusline("Examine: ");
new_fname = less_gets(sizeof("Examine: ")-1);
if (!new_fname[0]) {
free(new_fname);
status_print();
return;
}
free(filename); free(filename);
filename = less_gets(sizeof("Examine: ")-1); filename = new_fname;
/* files start by = argv. why we assume that argv is infinitely long?? /* files start by = argv. why we assume that argv is infinitely long??
files[num_files] = filename; files[num_files] = filename;
current_file = num_files + 1; current_file = num_files + 1;
@ -1087,7 +1105,7 @@ static void save_input_to_file(void)
print_statusline("Log file: "); print_statusline("Log file: ");
current_line = less_gets(sizeof("Log file: ")-1); current_line = less_gets(sizeof("Log file: ")-1);
if (strlen(current_line) > 0) { if (current_line[0]) {
fp = fopen(current_line, "w"); fp = fopen(current_line, "w");
if (!fp) { if (!fp) {
msg = "Error opening log file"; msg = "Error opening log file";
@ -1334,6 +1352,7 @@ int less_main(int argc, char **argv)
kbd_fd = open(CURRENT_TTY, O_RDONLY); kbd_fd = open(CURRENT_TTY, O_RDONLY);
if (kbd_fd < 0) if (kbd_fd < 0)
return bb_cat(argv); return bb_cat(argv);
ndelay_on(kbd_fd);
if (!num_files) { if (!num_files) {
if (isatty(STDIN_FILENO)) { if (isatty(STDIN_FILENO)) {
@ -1354,11 +1373,9 @@ int less_main(int argc, char **argv)
if (option_mask32 & FLAG_TILDE) if (option_mask32 & FLAG_TILDE)
empty_line_marker = ""; empty_line_marker = "";
bb_signals(BB_SIGS_FATAL, sig_catcher);
tcgetattr(kbd_fd, &term_orig); tcgetattr(kbd_fd, &term_orig);
bb_signals(0
+ (1 << SIGTERM)
+ (1 << SIGINT)
, sig_catcher);
term_less = term_orig; term_less = term_orig;
term_less.c_lflag &= ~(ICANON | ECHO); term_less.c_lflag &= ~(ICANON | ECHO);
term_less.c_iflag &= ~(IXON | ICRNL); term_less.c_iflag &= ~(IXON | ICRNL);
@ -1366,10 +1383,6 @@ int less_main(int argc, char **argv)
term_less.c_cc[VMIN] = 1; term_less.c_cc[VMIN] = 1;
term_less.c_cc[VTIME] = 0; term_less.c_cc[VTIME] = 0;
/* Want to do it just once, but it doesn't work, */
/* so we are redoing it (see code above). Mystery... */
/*tcsetattr(kbd_fd, TCSANOW, &term_less);*/
reinitialize(); reinitialize();
while (1) { while (1) {
keypress = less_getch(); keypress = less_getch();