#include <sched.h> /* sched_yield() */
-#include "busybox.h"
+#include "libbb.h"
#if ENABLE_FEATURE_LESS_REGEXP
#include "xregex.h"
#endif
REAL_KEY_LEFT = 'D',
REAL_PAGE_UP = '5',
REAL_PAGE_DOWN = '6',
- REAL_KEY_HOME = '7',
+ REAL_KEY_HOME = '7', // vt100? linux vt? or what?
REAL_KEY_END = '8',
+ REAL_KEY_HOME_ALT = '1', // ESC [1~ (vt100? linux vt? or what?)
+ REAL_KEY_END_ALT = '4', // ESC [4~
REAL_KEY_HOME_XTERM = 'H',
REAL_KEY_END_XTERM = 'F',
TILDES = 1,
};
-static unsigned max_displayed_line;
-static unsigned width;
-static const char *empty_line_marker = "~";
-
-static char *filename;
-static char **files;
-static unsigned num_files = 1;
-static unsigned current_file = 1;
-static const char **buffer;
-static const char **flines;
-static int cur_fline; /* signed */
-static unsigned max_fline;
-static unsigned max_lineno; /* this one tracks linewrap */
-
-static ssize_t eof_error = 1; /* eof if 0, error if < 0 */
-static char terminated = 1;
-static size_t readpos;
-static size_t readeof;
-/* last position in last line, taking into account tabs */
-static size_t linepos;
-
/* Command line options */
enum {
FLAG_E = 1,
LESS_STATE_MATCH_BACKWARDS = 1 << 15,
};
-#if ENABLE_FEATURE_LESS_MARKS
-static unsigned mark_lines[15][2];
-static unsigned num_marks;
+#if !ENABLE_FEATURE_LESS_REGEXP
+enum { pattern_valid = 0 };
#endif
+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;
+ unsigned max_fline;
+ unsigned max_lineno; /* this one tracks linewrap */
+ unsigned width;
+ ssize_t eof_error; /* eof if 0, error if < 0 */
+ ssize_t readpos;
+ ssize_t readeof; /* must be signed */
+ const char **buffer;
+ const char **flines;
+ const char *empty_line_marker;
+ unsigned num_files;
+ unsigned current_file;
+ char *filename;
+ char **files;
+#if ENABLE_FEATURE_LESS_MARKS
+ unsigned num_marks;
+ unsigned mark_lines[15][2];
+#endif
#if ENABLE_FEATURE_LESS_REGEXP
-static unsigned *match_lines;
-static int match_pos; /* signed! */
-static unsigned num_matches;
-static regex_t pattern;
-static unsigned pattern_valid;
-#else
-enum { pattern_valid = 0 };
+ unsigned *match_lines;
+ int match_pos; /* signed! */
+ int wanted_match; /* signed! */
+ int num_matches;
+ regex_t pattern;
+ smallint pattern_valid;
#endif
-
-static struct termios term_orig, term_vi;
-
-/* File pointer to get input from */
-static int kbd_fd;
+ smallint terminated;
+ struct termios term_orig, term_less;
+};
+#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 )
+#define max_lineno (G.max_lineno )
+#define width (G.width )
+#define eof_error (G.eof_error )
+#define readpos (G.readpos )
+#define readeof (G.readeof )
+#define buffer (G.buffer )
+#define flines (G.flines )
+#define empty_line_marker (G.empty_line_marker )
+#define num_files (G.num_files )
+#define current_file (G.current_file )
+#define filename (G.filename )
+#define files (G.files )
+#define num_marks (G.num_marks )
+#define mark_lines (G.mark_lines )
+#if ENABLE_FEATURE_LESS_REGEXP
+#define match_lines (G.match_lines )
+#define match_pos (G.match_pos )
+#define num_matches (G.num_matches )
+#define wanted_match (G.wanted_match )
+#define pattern (G.pattern )
+#define pattern_valid (G.pattern_valid )
+#endif
+#define terminated (G.terminated )
+#define term_orig (G.term_orig )
+#define term_less (G.term_less )
+#define INIT_G() do { \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+ less_gets_pos = -1; \
+ empty_line_marker = "~"; \
+ num_files = 1; \
+ current_file = 1; \
+ eof_error = 1; \
+ terminated = 1; \
+ USE_FEATURE_LESS_REGEXP(wanted_match = -1;) \
+} while (0)
/* Reset terminal input to normal */
static void set_tty_cooked(void)
tcsetattr(kbd_fd, TCSANOW, &term_orig);
}
-/* Exit the program gracefully */
-static void less_exit(int code)
-{
- /* TODO: We really should save the terminal state when we start,
- * and restore it when we exit. Less does this with the
- * "ti" and "te" termcap commands; can this be done with
- * only termios.h? */
- putchar('\n');
- fflush_stdout_and_exit(code);
-}
-
/* Move the cursor to a position (x,y), where (0,0) is the
top-left corner of the console */
static void move_cursor(int line, int row)
printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str);
}
+/* Exit the program gracefully */
+static void less_exit(int code)
+{
+ set_tty_cooked();
+ clear_line();
+ if (code < 0)
+ kill_myself_with_sig(- code); /* does not return */
+ exit(code);
+}
+
#if ENABLE_FEATURE_LESS_REGEXP
static void fill_match_lines(unsigned pos);
#else
#define fill_match_lines(pos) ((void)0)
#endif
-
+/* Devilishly complex routine.
+ *
+ * Has to deal with EOF and EPIPE on input,
+ * with line wrapping, with last line not ending in '\n'
+ * (possibly not ending YET!), with backspace and tabs.
+ * It reads input again if last time we got an EOF (thus supporting
+ * growing files) or EPIPE (watching output of slow process like make).
+ *
+ * Variables used:
+ * flines[] - array of lines already read. Linewrap may cause
+ * one source file line to occupy several flines[n].
+ * flines[max_fline] - last line, possibly incomplete.
+ * terminated - 1 if flines[max_fline] is 'terminated'
+ * (if there was '\n' [which isn't stored itself, we just remember
+ * that it was seen])
+ * max_lineno - last line's number, this one doesn't increment
+ * on line wrap, only on "real" new lines.
+ * readbuf[0..readeof-1] - small preliminary buffer.
+ * readbuf[readpos] - next character to add to current line.
+ * linepos - screen line position of next char to be read
+ * (takes into account tabs and backspaces)
+ * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error
+ */
static void read_lines(void)
{
#define readbuf bb_common_bufsiz1
char *current_line, *p;
- USE_FEATURE_LESS_REGEXP(unsigned old_max_fline = max_fline;)
int w = width;
char last_terminated = terminated;
+#if ENABLE_FEATURE_LESS_REGEXP
+ unsigned old_max_fline = max_fline;
+ time_t last_time = 0;
+ unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */
+#endif
if (option_mask32 & FLAG_N)
w -= 8;
- current_line = xmalloc(w);
- p = current_line;
+ USE_FEATURE_LESS_REGEXP(again0:)
+
+ p = current_line = xmalloc(w);
max_fline += last_terminated;
if (!last_terminated) {
const char *cp = flines[max_fline];
cp += 8;
strcpy(current_line, cp);
p += strlen(current_line);
+ free((char*)flines[max_fline]);
+ /* linepos is still valid from previous read_lines() */
} else {
linepos = 0;
}
- while (1) {
- again:
+ while (1) { /* read lines until we reach cur_fline or wanted_match */
*p = '\0';
terminated = 0;
- while (1) {
+ while (1) { /* read chars until we have a line */
char c;
/* if no unprocessed chars left, eat more */
if (readpos >= readeof) {
- smallint yielded = 0;
-
ndelay_on(0);
- read_again:
- eof_error = safe_read(0, readbuf, sizeof(readbuf));
+ eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf));
+ ndelay_off(0);
readpos = 0;
readeof = eof_error;
- if (eof_error < 0) {
- if (errno == EAGAIN && !yielded) {
- /* We can hit EAGAIN while searching for regexp match.
- * Yield is not 100% reliable solution in general,
- * but for less it should be good enough.
- * We give stdin supplier some CPU time to produce more.
- * We do it just once. */
- sched_yield();
- yielded = 1;
- goto read_again;
- }
- readeof = 0;
- if (errno != EAGAIN)
- print_statusline("read error");
- }
- ndelay_off(0);
-
- if (eof_error <= 0) {
+ if (eof_error <= 0)
goto reached_eof;
- }
}
c = readbuf[readpos];
/* backspace? [needed for manpages] */
if (c == '\x8' && linepos && p[-1] != '\t') {
readpos++; /* eat it */
linepos--;
+ /* was buggy (p could end up <= current_line)... */
*--p = '\0';
continue;
}
- if (c == '\t')
- linepos += (linepos^7) & 7;
- linepos++;
- if (linepos >= w)
- break;
+ {
+ size_t new_linepos = linepos + 1;
+ if (c == '\t') {
+ new_linepos += 7;
+ new_linepos &= (~7);
+ }
+ if ((int)new_linepos >= w)
+ break;
+ linepos = new_linepos;
+ }
/* ok, we will eat this char */
readpos++;
- if (c == '\n') { terminated = 1; break; }
+ if (c == '\n') {
+ terminated = 1;
+ linepos = 0;
+ break;
+ }
/* NUL is substituted by '\n'! */
if (c == '\0') c = '\n';
*p++ = c;
*p = '\0';
- }
+ } /* end of "read chars until we have a line" loop */
/* Corner case: linewrap with only "" wrapping to next line */
/* Looks ugly on screen, so we do not store this empty line */
if (!last_terminated && !current_line[0]) {
last_terminated = 1;
max_lineno++;
- goto again;
+ continue;
}
reached_eof:
last_terminated = terminated;
- flines = xrealloc(flines, (max_fline+1) * sizeof(char *));
+ flines = xrealloc_vector(flines, 8, max_fline);
if (option_mask32 & FLAG_N) {
/* Width of 7 preserves tab spacing in the text */
flines[max_fline] = xasprintf(
if (terminated)
max_lineno++;
} else {
- flines[max_fline] = xrealloc(current_line, strlen(current_line)+1);
+ flines[max_fline] = xrealloc(current_line, strlen(current_line) + 1);
}
- if (max_fline >= MAXLINES)
+ if (max_fline >= MAXLINES) {
+ eof_error = 0; /* Pretend we saw EOF */
break;
- if (max_fline > cur_fline + max_displayed_line)
+ }
+ if (max_fline > cur_fline + max_displayed_line) {
+#if !ENABLE_FEATURE_LESS_REGEXP
break;
+#else
+ if (wanted_match >= num_matches) { /* goto_match called us */
+ fill_match_lines(old_max_fline);
+ old_max_fline = max_fline;
+ }
+ if (wanted_match < num_matches)
+ break;
+#endif
+ }
if (eof_error <= 0) {
- if (eof_error < 0 && errno == EAGAIN) {
- /* not yet eof or error, reset flag (or else
- * we will hog CPU - select() will return
- * immediately */
- eof_error = 1;
+ if (eof_error < 0) {
+ if (errno == EAGAIN) {
+ /* not yet eof or error, reset flag (or else
+ * we will hog CPU - select() will return
+ * immediately */
+ eof_error = 1;
+ } else {
+ print_statusline("read error");
+ }
}
+#if !ENABLE_FEATURE_LESS_REGEXP
break;
+#else
+ if (wanted_match < num_matches) {
+ break;
+ } else { /* goto_match called us */
+ time_t t = time(NULL);
+ if (t != last_time) {
+ last_time = t;
+ if (--seconds_p1 == 0)
+ break;
+ }
+ sched_yield();
+ goto again0; /* go loop again (max 2 seconds) */
+ }
+#endif
}
max_fline++;
current_line = xmalloc(w);
p = current_line;
linepos = 0;
- }
+ } /* end of "read lines until we reach cur_fline" loop */
fill_match_lines(old_max_fline);
+#if ENABLE_FEATURE_LESS_REGEXP
+ /* prevent us from being stuck in search for a match */
+ wanted_match = -1;
+#endif
#undef readbuf
}
{
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)
printf(" lines %i-%i/%i ",
cur_fline + 1, cur_fline + max_displayed_line + 1,
max_fline + 1);
- if (cur_fline >= max_fline - max_displayed_line) {
+ if (cur_fline >= (int)(max_fline - max_displayed_line)) {
printf("(END)"NORMAL);
if (num_files > 1 && current_file != num_files)
printf(HIGHLIGHT" - next: %s"NORMAL, files[current_file]);
{
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)) {
#endif
clear_line();
- if (cur_fline && cur_fline < max_fline - max_displayed_line) {
- putchar(':');
+ if (cur_fline && cur_fline < (int)(max_fline - max_displayed_line)) {
+ bb_putchar(':');
return;
}
p = "(END)";
}
}
-static char controls[] =
+static const char controls[] ALIGN1 =
/* NUL: never encountered; TAB: not converted */
/**/"\x01\x02\x03\x04\x05\x06\x07\x08" "\x0a\x0b\x0c\x0d\x0e\x0f"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
"\x7f\x9b"; /* DEL and infamous Meta-ESC :( */
-static char ctrlconv[] =
+static const char ctrlconv[] ALIGN1 =
/* '\n': it's a former NUL - subst with '@', not 'J' */
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f"
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f";
start:
/* Most of the time doesn't find the regex, optimize for that */
match_status = regexec(&pattern, line, 1, &match_structs, eflags);
+ /* if even "" matches, treat it as "not a match" */
+ if (match_structs.rm_so >= match_structs.rm_eo)
+ match_status = 1;
}
if (!growline) {
/* Print the buffer */
static void buffer_print(void)
{
- int i;
+ unsigned i;
move_cursor(0, 0);
for (i = 0; i <= max_displayed_line; i++)
static void buffer_fill_and_print(void)
{
- int i;
+ unsigned i;
for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) {
buffer[i] = flines[cur_fline + i];
}
/* Reinitialize everything for a new file - free the memory and start over */
static void reinitialize(void)
{
- int i;
+ unsigned i;
if (flines) {
for (i = 0; i <= max_fline; i++)
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_vi);
- 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
* character is ASCII \033, get more characters and assign certain sequences
* special return codes. Note that this function works best with raw input. */
-static int less_getch(void)
+static int less_getch(int pos)
{
- char input[16];
+ unsigned char input[16];
unsigned i;
+
again:
- getch_nowait(input, sizeof(input));
+ less_gets_pos = pos;
+ memset(input, 0, sizeof(input));
+ getch_nowait((char *)input, sizeof(input));
+ less_gets_pos = -1;
+
/* 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;
return 24 + i;
if (input[2] == REAL_KEY_HOME_XTERM)
return KEY_HOME;
+ if (input[2] == REAL_KEY_HOME_ALT)
+ return KEY_HOME;
if (input[2] == REAL_KEY_END_XTERM)
return KEY_END;
+ if (input[2] == REAL_KEY_END_ALT)
+ return KEY_END;
return 0;
}
/* 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;
}
static char* less_gets(int sz)
{
char c;
- int i = 0;
+ unsigned 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_vi);
-
- read(kbd_fd, &c, 1);
- if (c == 0x0d)
+ while (1) {
+ c = '\0';
+ less_gets_pos = sz + i;
+ getch_nowait(&c, 1);
+ if (c == 0x0d) {
+ result[i] = '\0';
+ less_gets_pos = -1;
return result;
+ }
if (c == 0x7f)
c = 8;
if (c == 8 && i) {
continue;
if (i >= width - sz - 1)
continue; /* len limit */
- putchar(c);
+ bb_putchar(c);
result[i++] = c;
result = xrealloc(result, i+1);
- result[i] = '\0';
}
}
static void examine_file(void)
{
+ char *new_fname;
+
print_statusline("Examine: ");
+ new_fname = less_gets(sizeof("Examine: ") - 1);
+ if (!new_fname[0]) {
+ status_print();
+ err:
+ free(new_fname);
+ return;
+ }
+ if (access(new_fname, R_OK) != 0) {
+ print_statusline("Cannot read this file");
+ goto err;
+ }
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;
static void remove_current_file(void)
{
- int i;
+ unsigned i;
if (num_files < 2)
return;
/* Clear the current line and print a prompt */
print_statusline(" :");
- keypress = less_getch();
+ keypress = less_getch(2);
switch (keypress) {
case 'd':
remove_current_file();
change_file(-1);
break;
case 'q':
- less_exit(0);
+ less_exit(EXIT_SUCCESS);
break;
case 'x':
change_file(0);
match = 0;
/* Try to find next match if eof isn't reached yet */
if (match >= num_matches && eof_error > 0) {
- cur_fline = MAXLINES; /* look as far as needed */
+ wanted_match = match; /* "I want to read until I see N'th match" */
read_lines();
- cap_cur_fline(cur_fline);
}
if (num_matches) {
normalize_match_pos(match);
buffer_line(match_lines[match_pos]);
+ } else {
+ print_statusline("No matches found");
}
}
/* and we didn't match it last time */
&& !(num_matches && match_lines[num_matches-1] == pos)
) {
- match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int));
+ match_lines = xrealloc_vector(match_lines, 4, num_matches);
match_lines[num_matches++] = pos;
}
pos++;
/* Get the uncompiled regular expression from the user */
clear_line();
- putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
+ bb_putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
uncomp_regex = less_gets(1);
if (!uncomp_regex[0]) {
free(uncomp_regex);
match_pos = 0;
fill_match_lines(0);
while (match_pos < num_matches) {
- if (match_lines[match_pos] > cur_fline)
+ if ((int)match_lines[match_pos] > cur_fline)
break;
match_pos++;
}
static void number_process(int first_digit)
{
- int i = 1;
+ unsigned i;
int num;
char num_input[sizeof(int)*4]; /* more than enough */
char keypress;
printf(":%c", first_digit);
/* Receive input until a letter is given */
+ i = 1;
while (i < sizeof(num_input)-1) {
- num_input[i] = less_getch();
+ num_input[i] = less_getch(i + 1);
if (!num_input[i] || !isdigit(num_input[i]))
break;
- putchar(num_input[i]);
+ bb_putchar(num_input[i]);
i++;
}
int keypress;
clear_line();
- putchar('-');
- keypress = less_getch();
+ bb_putchar('-');
+ keypress = less_getch(1);
switch (keypress) {
case 'M':
int flag_val;
clear_line();
- putchar('_');
- keypress = less_getch();
+ bb_putchar('_');
+ keypress = less_getch(1);
switch (keypress) {
case 'M':
{
const char *msg = "";
char *current_line;
- int i;
+ unsigned i;
FILE *fp;
print_statusline("Log file: ");
current_line = less_gets(sizeof("Log file: ")-1);
- if (strlen(current_line) > 0) {
- fp = fopen(current_line, "w");
+ if (current_line[0]) {
+ fp = fopen_for_write(current_line);
if (!fp) {
msg = "Error opening log file";
goto ret;
int letter;
print_statusline("Mark: ");
- letter = less_getch();
+ letter = less_getch(sizeof("Mark: ") - 1);
if (isalpha(letter)) {
/* If we exceed 15 marks, start overwriting previous ones */
int i;
print_statusline("Go to mark: ");
- letter = less_getch();
+ letter = less_getch(sizeof("Go to mark: ") - 1);
clear_line();
if (isalpha(letter)) {
static char opp_bracket(char bracket)
{
switch (bracket) {
- case '{': case '[':
- return bracket + 2;
- case '(':
- return ')';
- case '}': case ']':
- return bracket - 2;
- case ')':
- return '(';
- }
- return 0;
+ case '{': case '[': /* '}' == '{' + 2. Same for '[' */
+ bracket++;
+ case '(': /* ')' == '(' + 1 */
+ bracket++;
+ break;
+ case '}': case ']':
+ bracket--;
+ case ')':
+ bracket--;
+ break;
+ };
+ return bracket;
}
static void match_right_bracket(char bracket)
{
- int bracket_line = -1;
- int i;
+ unsigned i;
if (strchr(flines[cur_fline], bracket) == NULL) {
print_statusline("No bracket in top line");
return;
}
+ bracket = opp_bracket(bracket);
for (i = cur_fline + 1; i < max_fline; i++) {
- if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
- bracket_line = i;
- break;
+ if (strchr(flines[i], bracket) != NULL) {
+ buffer_line(i);
+ return;
}
}
- if (bracket_line == -1)
- print_statusline("No matching bracket found");
- buffer_line(bracket_line - max_displayed_line);
+ print_statusline("No matching bracket found");
}
static void match_left_bracket(char bracket)
{
- int bracket_line = -1;
int i;
if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) {
return;
}
+ bracket = opp_bracket(bracket);
for (i = cur_fline + max_displayed_line; i >= 0; i--) {
- if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
- bracket_line = i;
- break;
+ if (strchr(flines[i], bracket) != NULL) {
+ buffer_line(i);
+ return;
}
}
- if (bracket_line == -1)
- print_statusline("No matching bracket found");
- buffer_line(bracket_line);
+ print_statusline("No matching bracket found");
}
#endif /* FEATURE_LESS_BRACKETS */
case KEY_UP: case 'y': case 'k':
buffer_up(1);
break;
- case PAGE_DOWN: case ' ': case 'z':
+ case PAGE_DOWN: case ' ': case 'z': case 'f':
buffer_down(max_displayed_line + 1);
break;
case PAGE_UP: case 'w': case 'b':
buffer_line(cur_fline);
break;
case 'q': case 'Q':
- less_exit(0);
+ less_exit(EXIT_SUCCESS);
break;
#if ENABLE_FEATURE_LESS_MARKS
case 'm':
number_process(keypress);
}
-static void sig_catcher(int sig ATTRIBUTE_UNUSED)
+static void sig_catcher(int sig)
{
- set_tty_cooked();
- exit(1);
+ less_exit(- sig);
}
-int less_main(int argc, char **argv);
+int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int less_main(int argc, char **argv)
{
int keypress;
+ INIT_G();
+
/* TODO: -x: do not interpret backspace, -xx: tab also */
/* -xxx: newline also */
/* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
- getopt32(argc, argv, "EMmN~");
+ getopt32(argv, "EMmN~");
argc -= optind;
argv += optind;
num_files = argc;
* is not a tty and turns into cat. This makes sense. */
if (!isatty(STDOUT_FILENO))
return bb_cat(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)) {
} else
filename = xstrdup(files[0]);
- kbd_fd = xopen(CURRENT_TTY, O_RDONLY);
-
get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
/* 20: two tabstops + 4 */
if (width < 20 || max_displayed_line < 3)
- bb_error_msg_and_die("too narrow here");
+ return bb_cat(argv);
max_displayed_line -= 2;
buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
empty_line_marker = "";
tcgetattr(kbd_fd, &term_orig);
- signal(SIGTERM, sig_catcher);
- signal(SIGINT, sig_catcher);
- term_vi = term_orig;
- term_vi.c_lflag &= ~(ICANON | ECHO);
- term_vi.c_iflag &= ~(IXON | ICRNL);
- /*term_vi.c_oflag &= ~ONLCR;*/
- term_vi.c_cc[VMIN] = 1;
- term_vi.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_vi);*/
+ term_less = term_orig;
+ term_less.c_lflag &= ~(ICANON | ECHO);
+ term_less.c_iflag &= ~(IXON | ICRNL);
+ /*term_less.c_oflag &= ~ONLCR;*/
+ term_less.c_cc[VMIN] = 1;
+ term_less.c_cc[VTIME] = 0;
+
+ /* We want to restore term_orig on exit */
+ bb_signals(BB_FATAL_SIGS, sig_catcher);
reinitialize();
while (1) {
- keypress = less_getch();
+ keypress = less_getch(-1); /* -1: do not position cursor */
keypress_process(keypress);
}
}