aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libbb.h29
-rw-r--r--miscutils/less.c99
2 files changed, 82 insertions, 46 deletions
diff --git a/include/libbb.h b/include/libbb.h
index eb8b2f620..873ab8798 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -274,9 +274,32 @@ char *xrealloc_getcwd_or_warn(char *cwd);
char *xmalloc_follow_symlinks(const char *path);
-//enum {
-// BB_SIGS_FATAL = ,
-//};
+enum {
+ /* 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));
/* Unlike signal() and bb_signals, sets handler with sigaction()
* and in a way that while signal handler is run, no other signals
diff --git a/miscutils/less.c b/miscutils/less.c
index 85c5ec536..4229dfe15 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -90,6 +90,7 @@ enum { pattern_valid = 0 };
struct globals {
int cur_fline; /* signed */
int kbd_fd; /* fd to get input from */
+ int less_gets_pos;
/* last position in last line, taking into account tabs */
size_t linepos;
unsigned max_displayed_line;
@@ -123,6 +124,7 @@ struct globals {
#define G (*ptr_to_globals)
#define cur_fline (G.cur_fline )
#define kbd_fd (G.kbd_fd )
+#define less_gets_pos (G.less_gets_pos )
#define linepos (G.linepos )
#define max_displayed_line (G.max_displayed_line)
#define max_fline (G.max_fline )
@@ -152,6 +154,7 @@ struct globals {
#define term_less (G.term_less )
#define INIT_G() do { \
PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
+ less_gets_pos = -1; \
empty_line_marker = "~"; \
num_files = 1; \
current_file = 1; \
@@ -385,6 +388,9 @@ static void m_status_print(void)
{
int percentage;
+ if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
+ return;
+
clear_line();
printf(HIGHLIGHT"%s", filename);
if (num_files > 1)
@@ -408,6 +414,9 @@ static void status_print(void)
{
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 */
#if ENABLE_FEATURE_LESS_FLAGS
if (option_mask32 & (FLAG_M|FLAG_m)) {
@@ -652,43 +661,46 @@ static void reinitialize(void)
buffer_fill_and_print();
}
-static void getch_nowait(char* input, int sz)
+static ssize_t getch_nowait(char* input, int sz)
{
ssize_t rd;
- fd_set readfds;
- again:
- fflush(stdout);
+ struct pollfd pfd[2];
- /* NB: select returns whenever read will not block. Therefore:
- * (a) with O_NONBLOCK'ed fds select will return immediately
- * (b) if eof is reached, select will also return
- * because read will immediately return 0 bytes.
- * Even if select says that input is available, read CAN block
+ pfd[0].fd = STDIN_FILENO;
+ pfd[0].events = POLLIN;
+ pfd[1].fd = kbd_fd;
+ pfd[1].events = POLLIN;
+ 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)
*/
- FD_ZERO(&readfds);
+ rd = 1;
if (max_fline <= cur_fline + max_displayed_line
&& eof_error > 0 /* did NOT reach eof yet */
) {
/* We are interested in stdin */
- FD_SET(0, &readfds);
+ rd = 0;
}
- FD_SET(kbd_fd, &readfds);
- tcsetattr(kbd_fd, TCSANOW, &term_less);
- select(kbd_fd + 1, &readfds, NULL, NULL, NULL);
+ /* position cursor if line input is done */
+ if (less_gets_pos >= 0)
+ move_cursor(max_displayed_line + 2, less_gets_pos + 1);
+ fflush(stdout);
+ safe_poll(pfd + rd, 2 - rd, -1);
input[0] = '\0';
- ndelay_on(kbd_fd);
- rd = read(kbd_fd, input, sz);
- ndelay_off(kbd_fd);
- if (rd < 0) {
- /* No keyboard input, but we have input on stdin! */
- if (errno != EAGAIN) /* Huh?? */
- return;
+ rd = safe_read(kbd_fd, input, sz); /* NB: kbd_fd is in O_NONBLOCK mode */
+ if (rd < 0 && errno == EAGAIN) {
+ /* No keyboard input -> we have input on stdin! */
read_lines();
buffer_fill_and_print();
goto again;
}
+ set_tty_cooked();
+ return rd;
}
/* 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. */
static int less_getch(void)
{
- char input[16];
+ unsigned char input[16];
unsigned i;
again:
memset(input, 0, sizeof(input));
@@ -705,7 +717,6 @@ static int less_getch(void)
/* Detect escape sequences (i.e. arrow keys) and handle
* them accordingly */
if (input[0] == '\033' && input[1] == '[') {
- set_tty_cooked();
i = input[2] - REAL_KEY_UP;
if (i < 4)
return 20 + i;
@@ -724,8 +735,8 @@ static int less_getch(void)
}
/* Reject almost all control chars */
i = input[0];
- if (i < ' ' && i != 0x0d && i != 8) goto again;
- set_tty_cooked();
+ if (i < ' ' && i != 0x0d && i != 8)
+ goto again;
return i;
}
@@ -734,22 +745,21 @@ static char* less_gets(int sz)
char c;
int i = 0;
char *result = xzalloc(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);
+ while (1) {
c = '\0';
- read(kbd_fd, &c, 1);
- if (c == 0x0d)
+ less_gets_pos = sz + i;
+ getch_nowait(&c, 1);
+ if (c == 0x0d) {
+ less_gets_pos = -1;
return result;
+ }
if (c == 0x7f)
c = 8;
if (c == 8 && i) {
printf("\x8 \x8");
i--;
+ result[i] = '\0';
}
if (c < ' ')
continue;
@@ -764,9 +774,17 @@ static char* less_gets(int sz)
static void examine_file(void)
{
+ char *new_fname;
+
print_statusline("Examine: ");
+ new_fname = less_gets(sizeof("Examine: ")-1);
+ if (!new_fname[0]) {
+ free(new_fname);
+ status_print();
+ return;
+ }
free(filename);
- filename = less_gets(sizeof("Examine: ")-1);
+ filename = new_fname;
/* files start by = argv. why we assume that argv is infinitely long??
files[num_files] = filename;
current_file = num_files + 1;
@@ -1087,7 +1105,7 @@ static void save_input_to_file(void)
print_statusline("Log file: ");
current_line = less_gets(sizeof("Log file: ")-1);
- if (strlen(current_line) > 0) {
+ if (current_line[0]) {
fp = fopen(current_line, "w");
if (!fp) {
msg = "Error opening log file";
@@ -1334,6 +1352,7 @@ int less_main(int argc, char **argv)
kbd_fd = open(CURRENT_TTY, O_RDONLY);
if (kbd_fd < 0)
return bb_cat(argv);
+ ndelay_on(kbd_fd);
if (!num_files) {
if (isatty(STDIN_FILENO)) {
@@ -1354,11 +1373,9 @@ int less_main(int argc, char **argv)
if (option_mask32 & FLAG_TILDE)
empty_line_marker = "";
+ bb_signals(BB_SIGS_FATAL, sig_catcher);
+
tcgetattr(kbd_fd, &term_orig);
- bb_signals(0
- + (1 << SIGTERM)
- + (1 << SIGINT)
- , sig_catcher);
term_less = term_orig;
term_less.c_lflag &= ~(ICANON | ECHO);
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[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();
while (1) {
keypress = less_getch();