From f4dff773553da8b4a5a66205e920a82732156851 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Sun, 24 Dec 2006 07:14:17 +0000 Subject: less: implement waiting for input using select(). Rather tricky business. But we do not read entire input anymore up-front. --- miscutils/less.c | 970 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 516 insertions(+), 454 deletions(-) (limited to 'miscutils/less.c') diff --git a/miscutils/less.c b/miscutils/less.c index 03b9397..6615889 100644 --- a/miscutils/less.c +++ b/miscutils/less.c @@ -26,26 +26,9 @@ #include "xregex.h" #endif - -/* These are the escape sequences corresponding to special keys */ -#define REAL_KEY_UP 'A' -#define REAL_KEY_DOWN 'B' -#define REAL_KEY_RIGHT 'C' -#define REAL_KEY_LEFT 'D' -#define REAL_PAGE_UP '5' -#define REAL_PAGE_DOWN '6' -#define REAL_KEY_HOME '7' -#define REAL_KEY_END '8' - -/* These are the special codes assigned by this program to the special keys */ -#define KEY_UP 20 -#define KEY_DOWN 21 -#define KEY_RIGHT 22 -#define KEY_LEFT 23 -#define PAGE_UP 24 -#define PAGE_DOWN 25 -#define KEY_HOME 26 -#define KEY_END 27 +/* FIXME: currently doesn't work right */ +#undef ENABLE_FEATURE_LESS_FLAGCS +#define ENABLE_FEATURE_LESS_FLAGCS 0 /* The escape codes for highlighted and normal text */ #define HIGHLIGHT "\033[7m" @@ -55,124 +38,103 @@ /* The escape code to clear to end of line */ #define CLEAR_2_EOL "\033[K" -#define MAXLINES CONFIG_FEATURE_LESS_MAXLINES +/* These are the escape sequences corresponding to special keys */ +enum { + REAL_KEY_UP = 'A', + REAL_KEY_DOWN = 'B', + REAL_KEY_RIGHT = 'C', + REAL_KEY_LEFT = 'D', + REAL_PAGE_UP = '5', + REAL_PAGE_DOWN = '6', + REAL_KEY_HOME = '7', + REAL_KEY_END = '8', + +/* These are the special codes assigned by this program to the special keys */ + KEY_UP = 20, + KEY_DOWN = 21, + KEY_RIGHT = 22, + KEY_LEFT = 23, + PAGE_UP = 24, + PAGE_DOWN = 25, + KEY_HOME = 26, + KEY_END = 27, + +/* Absolute max of lines eaten */ + MAXLINES = CONFIG_FEATURE_LESS_MAXLINES, + +/* This many "after the end" lines we will show (at max) */ + TILDES = 1, +}; + +static unsigned max_displayed_line; +static unsigned width; +static const char *empty_line_marker = "~"; -static int height; -static int width; -static char **files; static char *filename; +static char **files; +static unsigned num_files = 1; +static unsigned current_file = 1; static const char **buffer; -static char **flines; -static int current_file = 1; -static int line_pos; -static int num_flines; -static int num_files = 1; -static const char *empty_line_marker = "~"; +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 */ -#define FLAG_E 1 -#define FLAG_M (1<<1) -#define FLAG_m (1<<2) -#define FLAG_N (1<<3) -#define FLAG_TILDE (1<<4) +enum { + FLAG_E = 1, + FLAG_M = 1 << 1, + FLAG_m = 1 << 2, + FLAG_N = 1 << 3, + FLAG_TILDE = 1 << 4, /* hijack command line options variable for internal state vars */ -#define LESS_STATE_MATCH_BACKWARDS (1<<6) + LESS_STATE_MATCH_BACKWARDS = 1 << 15, +}; #if ENABLE_FEATURE_LESS_MARKS -static int mark_lines[15][2]; -static int num_marks; +static unsigned mark_lines[15][2]; +static unsigned num_marks; #endif #if ENABLE_FEATURE_LESS_REGEXP -static int *match_lines; -static int match_pos; -static int num_matches; +static unsigned *match_lines; +static int match_pos; /* signed! */ +static unsigned num_matches; static regex_t pattern; -static int pattern_valid; +static unsigned pattern_valid; #endif -/* Needed termios structures */ static struct termios term_orig, term_vi; /* File pointer to get input from */ -static FILE *inp; +static int kbd_fd; /* Reset terminal input to normal */ static void set_tty_cooked(void) { fflush(stdout); - tcsetattr(fileno(inp), TCSANOW, &term_orig); + tcsetattr(kbd_fd, TCSANOW, &term_orig); } /* Exit the program gracefully */ -static void tless_exit(int code) +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? */ + * 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); } -/* 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 tless_getch(void) -{ - int input; - /* Set terminal input to raw mode (taken from vi.c) */ - tcsetattr(fileno(inp), TCSANOW, &term_vi); - again: - input = getc(inp); - /* Detect escape sequences (i.e. arrow keys) and handle - them accordingly */ - - if (input == '\033' && getc(inp) == '[') { - unsigned i; - input = getc(inp); - set_tty_cooked(); - - i = input - REAL_KEY_UP; - if (i < 4) - return 20 + i; - i = input - REAL_PAGE_UP; - if (i < 4) - return 24 + i; - return 0; /* ?? */ - } - /* Reject almost all control chars */ - if (input < ' ' && input != 0x0d && input != 8) goto again; - set_tty_cooked(); - return input; -} - -static char* tless_gets(int sz) -{ - int c; - int i = 0; - char *result = xzalloc(1); - while (1) { - c = tless_getch(); - if (c == 0x0d) - return result; - if (c == 0x7f) c = 8; - if (c == 8 && i) { - printf("\x8 \x8"); - i--; - } - if (c < ' ') - continue; - if (i >= width - sz - 1) - continue; /* len limit */ - putchar(c); - result[i++] = c; - result = xrealloc(result, i+1); - result[i] = '\0'; - } -} - /* 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) @@ -182,7 +144,7 @@ static void move_cursor(int line, int row) static void clear_line(void) { - printf("\033[%u;0H" CLEAR_2_EOL, height); + printf("\033[%u;0H" CLEAR_2_EOL, max_displayed_line + 2); } static void print_hilite(const char *str) @@ -193,90 +155,116 @@ static void print_hilite(const char *str) static void print_statusline(const char *str) { clear_line(); - printf(HIGHLIGHT"%.*s"NORMAL, width-1, str); + printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str); } -static void data_readlines(void) +static void read_lines(void) { - unsigned i, pos; - unsigned lineno = 1; - int w = width; - char terminated, last_terminated = 1; - char *current_line, *p; - FILE *fp; + /* TODO: regexp match array should be updated too */ - if (filename) - fp = xfopen(filename, "r"); - else { - /* "less" with no arguments in argv[] */ - fp = stdin; - /* For status line only */ - filename = xstrdup(bb_msg_standard_input); - } +#define readbuf bb_common_bufsiz1 + char *current_line, *p; + int w = width; + char last_terminated = terminated; - flines = NULL; - if (option_mask32 & FLAG_N) { + if (option_mask32 & FLAG_N) w -= 8; + + current_line = xmalloc(w); + p = current_line; + max_fline += last_terminated; + if (!last_terminated) { + const char *cp = flines[max_fline]; + if (option_mask32 & FLAG_N) + cp += 8; + strcpy(current_line, cp); + p += strlen(current_line); } - for (i = 0; !feof(fp) && i <= MAXLINES; i++) { - flines = xrealloc(flines, (i+1) * sizeof(char *)); - current_line = xmalloc(w); + + while (1) { again: - p = current_line; - pos = 0; + *p = '\0'; terminated = 0; while (1) { - int c = getc(fp); - if (c == '\t') pos += (pos^7) & 7; - pos++; - if (pos >= w) { ungetc(c, fp); break; } - if (c == EOF) break; + char c; + if (readpos >= readeof) { +ndelay_on(0); + eof_error = safe_read(0, readbuf, sizeof(readbuf)); +ndelay_off(0); + readpos = 0; + readeof = eof_error; + if (eof_error < 0) { + readeof = 0; + if (errno != EAGAIN) + print_statusline("read error"); + } + if (eof_error <= 0) { + goto reached_eof; + } + } + c = readbuf[readpos]; + if (c == '\t') + linepos += (linepos^7) & 7; + linepos++; + if (linepos >= w) + break; + readpos++; if (c == '\n') { terminated = 1; break; } /* NUL is substituted by '\n'! */ if (c == '\0') c = '\n'; *p++ = c; + *p = '\0'; } - *p = '\0'; - if (fp != stdin) - die_if_ferror(fp, filename); - /* 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; - lineno++; + max_lineno++; goto again; } - + reached_eof: last_terminated = terminated; + flines = xrealloc(flines, (max_fline+1) * sizeof(char *)); if (option_mask32 & FLAG_N) { /* Width of 7 preserves tab spacing in the text */ - flines[i] = xasprintf( - (lineno <= 9999999) ? "%7u %s" : "%07u %s", - lineno % 10000000, current_line); + flines[max_fline] = xasprintf( + (max_lineno <= 9999999) ? "%7u %s" : "%07u %s", + max_lineno % 10000000, current_line); free(current_line); if (terminated) - lineno++; + max_lineno++; } else { - flines[i] = xrealloc(current_line, strlen(current_line)+1); + flines[max_fline] = xrealloc(current_line, strlen(current_line)+1); } + if (max_fline >= MAXLINES) + break; + if (max_fline > cur_fline + max_displayed_line) + break; + 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; + } + break; + } + max_fline++; + current_line = xmalloc(w); + p = current_line; + linepos = 0; } - num_flines = i - 1; /* buggie: 'num_flines' must be 'max_fline' */ - - /* Reset variables for a new file */ - - line_pos = 0; - - fclose(fp); +#undef readbuf } #if ENABLE_FEATURE_LESS_FLAGS -/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes +/* Interestingly, writing calc_percent as a function saves around 32 bytes * on my build. */ static int calc_percent(void) { - return ((100 * (line_pos + height - 2) / num_flines) + 1); + unsigned p = 100 * (cur_fline + max_displayed_line) / (max_fline + 1); + return p <= 100 ? p : 100; } /* Print a status line if -M was specified */ @@ -289,16 +277,16 @@ static void m_status_print(void) if (num_files > 1) printf(" (file %i of %i)", current_file, num_files); printf(" lines %i-%i/%i ", - line_pos + 1, line_pos + height - 1, - num_flines + 1); - if (line_pos >= num_flines - height + 2) { + cur_fline + 1, cur_fline + max_displayed_line + 1, + max_fline + 1); + if (cur_fline >= max_fline - max_displayed_line) { printf("(END)"NORMAL); if (num_files > 1 && current_file != num_files) - printf(HIGHLIGHT" - next: %s "NORMAL, files[current_file]); + printf(HIGHLIGHT" - next: %s"NORMAL, files[current_file]); return; } percentage = calc_percent(); - printf("%i%% "NORMAL, percentage); + printf("%i%%"NORMAL, percentage); } #endif @@ -318,15 +306,15 @@ static void status_print(void) #endif clear_line(); - if (line_pos && line_pos < num_flines - height + 2) { + if (cur_fline && cur_fline < max_fline - max_displayed_line) { putchar(':'); return; } p = "(END)"; - if (!line_pos) + if (!cur_fline) p = filename; if (num_files > 1) { - printf(HIGHLIGHT"%s (file %i of %i) "NORMAL, + printf(HIGHLIGHT"%s (file %i of %i)"NORMAL, p, current_file, num_files); return; } @@ -423,7 +411,7 @@ static void print_ascii(const char *str) do { if (*str == 0x7f) *p++ = '?'; - else if (*str == 0x9b) + else if (*str == (char)0x9b) /* VT100's CSI, aka Meta-ESC. Who's inventor? */ /* I want to know who committed this sin */ *p++ = '{'; @@ -443,7 +431,7 @@ static void buffer_print(void) int i; move_cursor(0, 0); - for (i = 0; i < height - 1; i++) + for (i = 0; i <= max_displayed_line; i++) if (pattern_valid) print_found(buffer[i]); else @@ -451,111 +439,194 @@ static void buffer_print(void) status_print(); } -/* Initialise the buffer */ -static void buffer_init(void) +static void buffer_fill_and_print(void) { int i; - - /* Fill the buffer until the end of the file or the - end of the buffer is reached */ - for (i = 0; i < height - 1 && i <= num_flines; i++) { - buffer[i] = flines[i]; + for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) { + buffer[i] = flines[cur_fline + i]; } - - /* If the buffer still isn't full, fill it with blank lines */ - for (; i < height - 1; i++) { + for (; i <= max_displayed_line; i++) { buffer[i] = empty_line_marker; } + buffer_print(); } /* Move the buffer up and down in the file in order to scroll */ static void buffer_down(int nlines) { - int i; + int diff; + cur_fline += nlines; + read_lines(); - if (line_pos + (height - 3) + nlines < num_flines) { - line_pos += nlines; - for (i = 0; i < (height - 1); i++) { - buffer[i] = flines[line_pos + i]; - } - } else { + if (cur_fline + max_displayed_line > max_fline + TILDES) { + cur_fline -= nlines; + diff = max_fline - (cur_fline + max_displayed_line) + TILDES; /* As the number of lines requested was too large, we just move to the end of the file */ - while (line_pos + (height - 3) + 1 < num_flines) { - line_pos += 1; - for (i = 0; i < (height - 1); i++) { - buffer[i] = flines[line_pos + i]; - } - } + if (diff > 0) + cur_fline += diff; } - - /* We exit if the -E flag has been set */ - if ((option_mask32 & FLAG_E) && (line_pos + (height - 2) == num_flines)) - tless_exit(0); + buffer_fill_and_print(); } static void buffer_up(int nlines) { - int i; + cur_fline -= nlines; + if (cur_fline < 0) cur_fline = 0; + read_lines(); + buffer_fill_and_print(); +} - line_pos -= nlines; - if (line_pos < 0) line_pos = 0; - for (i = 0; i < height - 1; i++) { - if (line_pos + i <= num_flines) { - buffer[i] = flines[line_pos + i]; - } else { - buffer[i] = empty_line_marker; - } +static void buffer_line(int linenum) +{ + if (linenum + max_displayed_line > max_fline) + linenum = max_fline - max_displayed_line + TILDES; + if (linenum < 0) linenum = 0; + cur_fline = linenum; + buffer_fill_and_print(); +} + +static void open_file_and_read_lines(void) +{ + if (filename) { + int fd = xopen(filename, O_RDONLY); + dup2(fd, 0); + if (fd) close(fd); + } else { + /* "less" with no arguments in argv[] */ + /* For status line only */ + filename = xstrdup(bb_msg_standard_input); } + readpos = 0; + readeof = 0; + linepos = 0; + terminated = 1; + read_lines(); } -static void buffer_line(int linenum) +/* Reinitialize everything for a new file - free the memory and start over */ +static void reinitialize(void) { int i; - if (linenum < 0 || linenum > num_flines) { - clear_line(); - printf(HIGHLIGHT"%s%u"NORMAL, "Cannot seek to line ", linenum + 1); - return; + if (flines) { + for (i = 0; i <= max_fline; i++) + free((void*)(flines[i])); + free(flines); + flines = NULL; } - for (i = 0; i < height - 1; i++) { - if (linenum + i <= num_flines) - buffer[i] = flines[linenum + i]; - else { - buffer[i] = empty_line_marker; - } + max_fline = -1; + cur_fline = 0; + max_lineno = 0; + open_file_and_read_lines(); + buffer_fill_and_print(); +} + +static char* getch_nowait(void) +{ + static char input[16]; + ssize_t sz; + fd_set readfds; + again: + fflush(stdout); + FD_ZERO(&readfds); + if (max_fline <= cur_fline + max_displayed_line && eof_error > 0) { + /* We are interested in stdin */ + FD_SET(0, &readfds); } - line_pos = linenum; - buffer_print(); + FD_SET(kbd_fd, &readfds); + tcsetattr(kbd_fd, TCSANOW, &term_vi); + select(kbd_fd + 1, &readfds, NULL, NULL, NULL); + + input[0] = '\0'; + ndelay_on(kbd_fd); + sz = read(kbd_fd, input, sizeof(input)); + ndelay_off(kbd_fd); + if (sz < 0) { + /* No keyboard input, but we have input on stdin! */ + if (errno != EAGAIN) /* Huh?? */ + return input; + read_lines(); + buffer_fill_and_print(); + goto again; + } + return input; } -/* Reinitialise everything for a new file - free the memory and start over */ -static void reinitialise(void) +/* 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) { - int i; + char *input; + unsigned i; + again: + input = getch_nowait(); + /* Detect escape sequences (i.e. arrow keys) and handle + * them accordingly */ - for (i = 0; i <= num_flines; i++) - free(flines[i]); - free(flines); + if (input[0] == '\033' && input[1] == '[') { + set_tty_cooked(); + i = input[2] - REAL_KEY_UP; + if (i < 4) + return 20 + i; + i = input[2] - REAL_PAGE_UP; + if (i < 4) + return 24 + i; + return 0; + } + /* Reject almost all control chars */ + i = input[0]; + if (i < ' ' && i != 0x0d && i != 8) goto again; + set_tty_cooked(); + return i; +} - data_readlines(); - buffer_init(); - buffer_print(); +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_vi); + + read(kbd_fd, &c, 1); + if (c == 0x0d) + return result; + if (c == 0x7f) + c = 8; + if (c == 8 && i) { + printf("\x8 \x8"); + i--; + } + if (c < ' ') + continue; + if (i >= width - sz - 1) + continue; /* len limit */ + putchar(c); + result[i++] = c; + result = xrealloc(result, i+1); + result[i] = '\0'; + } } static void examine_file(void) { print_statusline("Examine: "); free(filename); - filename = tless_gets(sizeof("Examine: ")-1); + filename = less_gets(sizeof("Examine: ")-1); /* files start by = argv. why we assume that argv is infinitely long?? files[num_files] = filename; current_file = num_files + 1; num_files++; */ files[0] = filename; num_files = current_file = 1; - reinitialise(); + reinitialize(); } /* This function changes the file currently being paged. direction can be one of the following: @@ -569,7 +640,7 @@ static void change_file(int direction) current_file = direction ? current_file + direction : 1; free(filename); filename = xstrdup(files[current_file - 1]); - reinitialise(); + reinitialize(); } else { print_statusline(direction > 0 ? "No next file" : "No previous file"); } @@ -579,19 +650,20 @@ static void remove_current_file(void) { int i; + if (num_files < 2) + return; + if (current_file != 1) { change_file(-1); for (i = 3; i <= num_files; i++) files[i - 2] = files[i - 1]; num_files--; - buffer_print(); } else { change_file(1); for (i = 2; i <= num_files; i++) files[i - 2] = files[i - 1]; num_files--; current_file--; - buffer_print(); } } @@ -602,33 +674,31 @@ static void colon_process(void) /* Clear the current line and print a prompt */ print_statusline(" :"); - keypress = tless_getch(); + keypress = less_getch(); switch (keypress) { - case 'd': - remove_current_file(); - break; - case 'e': - examine_file(); - break; + case 'd': + remove_current_file(); + break; + case 'e': + examine_file(); + break; #if ENABLE_FEATURE_LESS_FLAGS - case 'f': - m_status_print(); - break; + case 'f': + m_status_print(); + break; #endif - case 'n': - change_file(1); - break; - case 'p': - change_file(-1); - break; - case 'q': - tless_exit(0); - break; - case 'x': - change_file(0); - break; - default: - break; + case 'n': + change_file(1); + break; + case 'p': + change_file(-1); + break; + case 'q': + less_exit(0); + break; + case 'x': + change_file(0); + break; } } @@ -638,7 +708,7 @@ static int normalize_match_pos(int match) if (match >= num_matches) match_pos = num_matches - 1; if (match < 0) - return (match_pos = 0); + match_pos = 0; return match_pos; } @@ -654,8 +724,8 @@ static void regex_process(void) char *uncomp_regex, *err; /* Reset variables */ - match_lines = xrealloc(match_lines, sizeof(int)); - match_lines[0] = -1; + free(match_lines); + match_lines = NULL; match_pos = 0; num_matches = 0; if (pattern_valid) { @@ -666,8 +736,8 @@ static void regex_process(void) /* Get the uncompiled regular expression from the user */ clear_line(); putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/'); - uncomp_regex = tless_gets(1); - if (/*!uncomp_regex ||*/ !uncomp_regex[0]) { + uncomp_regex = less_gets(1); + if (!uncomp_regex[0]) { free(uncomp_regex); buffer_print(); return; @@ -684,19 +754,19 @@ static void regex_process(void) pattern_valid = 1; /* Run the regex on each line of the current file */ - for (match_pos = 0; match_pos <= num_flines; match_pos++) { + for (match_pos = 0; match_pos <= max_fline; match_pos++) { if (regexec(&pattern, flines[match_pos], 0, NULL, 0) == 0) { match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int)); match_lines[num_matches++] = match_pos; } } - if (num_matches == 0 || num_flines <= height - 2) { + if (num_matches == 0 || max_fline <= max_displayed_line) { buffer_print(); return; } for (match_pos = 0; match_pos < num_matches; match_pos++) { - if (match_lines[match_pos] > line_pos) + if (match_lines[match_pos] > cur_fline) break; } if (option_mask32 & LESS_STATE_MATCH_BACKWARDS) match_pos--; @@ -719,7 +789,7 @@ static void number_process(int first_digit) /* Receive input until a letter is given */ while (i < sizeof(num_input)-1) { - num_input[i] = tless_getch(); + num_input[i] = less_getch(); if (!num_input[i] || !isdigit(num_input[i])) break; putchar(num_input[i]); @@ -738,34 +808,38 @@ static void number_process(int first_digit) /* We now know the number and the letter entered, so we process them */ switch (keypress) { - case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015': - buffer_down(num); - break; - case KEY_UP: case 'b': case 'w': case 'y': case 'u': - buffer_up(num); - break; - case 'g': case '<': case 'G': case '>': - if (num_flines >= height - 2) - buffer_line(num - 1); - break; - case 'p': case '%': - buffer_line(((num / 100) * num_flines) - 1); - break; + case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015': + buffer_down(num); + buffer_print(); + break; + case KEY_UP: case 'b': case 'w': case 'y': case 'u': + buffer_up(num); + buffer_print(); + break; + case 'g': case '<': case 'G': case '>': + cur_fline = num + max_displayed_line; + read_lines(); + buffer_line(num - 1); + break; + case 'p': case '%': + num = num * (max_fline / 100); /* + max_fline / 2; */ + cur_fline = num + max_displayed_line; + read_lines(); + buffer_line(num); + break; #if ENABLE_FEATURE_LESS_REGEXP - case 'n': - goto_match(match_pos + num); - break; - case '/': - option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS; - regex_process(); - break; - case '?': - option_mask32 |= LESS_STATE_MATCH_BACKWARDS; - regex_process(); - break; + case 'n': + goto_match(match_pos + num); + break; + case '/': + option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS; + regex_process(); + break; + case '?': + option_mask32 |= LESS_STATE_MATCH_BACKWARDS; + regex_process(); + break; #endif - default: - break; } } @@ -776,23 +850,21 @@ static void flag_change(void) clear_line(); putchar('-'); - keypress = tless_getch(); + keypress = less_getch(); switch (keypress) { - case 'M': - option_mask32 ^= FLAG_M; - break; - case 'm': - option_mask32 ^= FLAG_m; - break; - case 'E': - option_mask32 ^= FLAG_E; - break; - case '~': - option_mask32 ^= FLAG_TILDE; - break; - default: - break; + case 'M': + option_mask32 ^= FLAG_M; + break; + case 'm': + option_mask32 ^= FLAG_m; + break; + case 'E': + option_mask32 ^= FLAG_E; + break; + case '~': + option_mask32 ^= FLAG_TILDE; + break; } } @@ -803,27 +875,27 @@ static void show_flag_status(void) clear_line(); putchar('_'); - keypress = tless_getch(); + keypress = less_getch(); switch (keypress) { - case 'M': - flag_val = option_mask32 & FLAG_M; - break; - case 'm': - flag_val = option_mask32 & FLAG_m; - break; - case '~': - flag_val = option_mask32 & FLAG_TILDE; - break; - case 'N': - flag_val = option_mask32 & FLAG_N; - break; - case 'E': - flag_val = option_mask32 & FLAG_E; - break; - default: - flag_val = 0; - break; + case 'M': + flag_val = option_mask32 & FLAG_M; + break; + case 'm': + flag_val = option_mask32 & FLAG_m; + break; + case '~': + flag_val = option_mask32 & FLAG_TILDE; + break; + case 'N': + flag_val = option_mask32 & FLAG_N; + break; + case 'E': + flag_val = option_mask32 & FLAG_E; + break; + default: + flag_val = 0; + break; } clear_line(); @@ -831,15 +903,6 @@ static void show_flag_status(void) } #endif -static void full_repaint(void) -{ - int temp_line_pos = line_pos; - data_readlines(); - buffer_init(); - buffer_line(temp_line_pos); -} - - static void save_input_to_file(void) { char *current_line; @@ -847,7 +910,7 @@ static void save_input_to_file(void) FILE *fp; print_statusline("Log file: "); - current_line = tless_gets(sizeof("Log file: ")-1); + current_line = less_gets(sizeof("Log file: ")-1); if (strlen(current_line) > 0) { fp = fopen(current_line, "w"); free(current_line); @@ -855,7 +918,7 @@ static void save_input_to_file(void) print_statusline("Error opening log file"); return; } - for (i = 0; i < num_flines; i++) + for (i = 0; i < max_fline; i++) fprintf(fp, "%s\n", flines[i]); fclose(fp); buffer_print(); @@ -871,16 +934,15 @@ static void add_mark(void) int letter; print_statusline("Mark: "); - letter = tless_getch(); + letter = less_getch(); if (isalpha(letter)) { - /* If we exceed 15 marks, start overwriting previous ones */ if (num_marks == 14) num_marks = 0; mark_lines[num_marks][0] = letter; - mark_lines[num_marks][1] = line_pos; + mark_lines[num_marks][1] = cur_fline; num_marks++; } else { print_statusline("Invalid mark letter"); @@ -893,7 +955,7 @@ static void goto_mark(void) int i; print_statusline("Go to mark: "); - letter = tless_getch(); + letter = less_getch(); clear_line(); if (isalpha(letter)) { @@ -915,17 +977,16 @@ static void goto_mark(void) static char opp_bracket(char bracket) { switch (bracket) { - case '{': case '[': - return bracket + 2; - case '(': - return ')'; - case '}': case ']': - return bracket - 2; - case ')': - return '('; - default: - return 0; + case '{': case '[': + return bracket + 2; + case '(': + return ')'; + case '}': case ']': + return bracket - 2; + case ')': + return '('; } + return 0; } static void match_right_bracket(char bracket) @@ -933,11 +994,11 @@ static void match_right_bracket(char bracket) int bracket_line = -1; int i; - if (strchr(flines[line_pos], bracket) == NULL) { + if (strchr(flines[cur_fline], bracket) == NULL) { print_statusline("No bracket in top line"); return; } - for (i = line_pos + 1; i < num_flines; i++) { + for (i = cur_fline + 1; i < max_fline; i++) { if (strchr(flines[i], opp_bracket(bracket)) != NULL) { bracket_line = i; break; @@ -945,7 +1006,7 @@ static void match_right_bracket(char bracket) } if (bracket_line == -1) print_statusline("No matching bracket found"); - buffer_line(bracket_line - height + 2); + buffer_line(bracket_line - max_displayed_line); } static void match_left_bracket(char bracket) @@ -953,12 +1014,12 @@ static void match_left_bracket(char bracket) int bracket_line = -1; int i; - if (strchr(flines[line_pos + height - 2], bracket) == NULL) { + if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) { print_statusline("No bracket in bottom line"); return; } - for (i = line_pos + height - 2; i >= 0; i--) { + for (i = cur_fline + max_displayed_line; i >= 0; i--) { if (strchr(flines[i], opp_bracket(bracket)) != NULL) { bracket_line = i; break; @@ -974,105 +1035,104 @@ static void match_left_bracket(char bracket) static void keypress_process(int keypress) { switch (keypress) { - case KEY_DOWN: case 'e': case 'j': case 0x0d: - buffer_down(1); - buffer_print(); - break; - case KEY_UP: case 'y': case 'k': - buffer_up(1); - buffer_print(); - break; - case PAGE_DOWN: case ' ': case 'z': - buffer_down(height - 1); - buffer_print(); - break; - case PAGE_UP: case 'w': case 'b': - buffer_up(height - 1); - buffer_print(); - break; - case 'd': - buffer_down((height - 1) / 2); - buffer_print(); - break; - case 'u': - buffer_up((height - 1) / 2); - buffer_print(); - break; - case KEY_HOME: case 'g': case 'p': case '<': case '%': - buffer_line(0); - break; - case KEY_END: case 'G': case '>': - buffer_line(num_flines - height + 2); - break; - case 'q': case 'Q': - tless_exit(0); - break; + case KEY_DOWN: case 'e': case 'j': case 0x0d: + buffer_down(1); + buffer_print(); + break; + case KEY_UP: case 'y': case 'k': + buffer_up(1); + buffer_print(); + break; + case PAGE_DOWN: case ' ': case 'z': + buffer_down(max_displayed_line + 1); + buffer_print(); + break; + case PAGE_UP: case 'w': case 'b': + buffer_up(max_displayed_line + 1); + buffer_print(); + break; + case 'd': + buffer_down((max_displayed_line + 1) / 2); + buffer_print(); + break; + case 'u': + buffer_up((max_displayed_line + 1) / 2); + buffer_print(); + break; + case KEY_HOME: case 'g': case 'p': case '<': case '%': + buffer_line(0); + break; + case KEY_END: case 'G': case '>': + cur_fline = MAXLINES; + read_lines(); + buffer_line(cur_fline); + break; + case 'q': case 'Q': + less_exit(0); + break; #if ENABLE_FEATURE_LESS_MARKS - case 'm': - add_mark(); - buffer_print(); - break; - case '\'': - goto_mark(); - buffer_print(); - break; + case 'm': + add_mark(); + buffer_print(); + break; + case '\'': + goto_mark(); + buffer_print(); + break; #endif - case 'r': - buffer_print(); - break; - case 'R': - full_repaint(); - break; - case 's': - save_input_to_file(); - break; - case 'E': - examine_file(); - break; + case 'r': case 'R': + buffer_print(); + break; + /*case 'R': + full_repaint(); + break;*/ + case 's': + save_input_to_file(); + break; + case 'E': + examine_file(); + break; #if ENABLE_FEATURE_LESS_FLAGS - case '=': - m_status_print(); - break; + case '=': + m_status_print(); + break; #endif #if ENABLE_FEATURE_LESS_REGEXP - case '/': - option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS; - regex_process(); - break; - case 'n': -//printf("HERE 3\n");sleep(1); - goto_match(match_pos + 1); - break; - case 'N': - goto_match(match_pos - 1); - break; - case '?': - option_mask32 |= LESS_STATE_MATCH_BACKWARDS; - regex_process(); - break; + case '/': + option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS; + regex_process(); + break; + case 'n': + goto_match(match_pos + 1); + break; + case 'N': + goto_match(match_pos - 1); + break; + case '?': + option_mask32 |= LESS_STATE_MATCH_BACKWARDS; + regex_process(); + break; #endif #if ENABLE_FEATURE_LESS_FLAGCS - case '-': - flag_change(); - buffer_print(); - break; - case '_': - show_flag_status(); - break; + case '-': + flag_change(); + buffer_print(); + break; + case '_': + show_flag_status(); + break; #endif #if ENABLE_FEATURE_LESS_BRACKETS - case '{': case '(': case '[': - match_right_bracket(keypress); - break; - case '}': case ')': case ']': - match_left_bracket(keypress); - break; + case '{': case '(': case '[': + match_right_bracket(keypress); + break; + case '}': case ')': case ']': + match_left_bracket(keypress); + break; #endif - case ':': - colon_process(); - break; - default: - break; + case ':': + colon_process(); + break; } if (isdigit(keypress)) @@ -1092,8 +1152,8 @@ int less_main(int argc, char **argv) getopt32(argc, argv, "EMmN~"); argc -= optind; argv += optind; - files = argv; num_files = argc; + files = argv; /* Another popular pager, most, detects when stdout * is not a tty and turns into cat. This makes sense. */ @@ -1109,33 +1169,35 @@ int less_main(int argc, char **argv) } else filename = xstrdup(files[0]); - inp = xfopen(CURRENT_TTY, "r"); + kbd_fd = xopen(CURRENT_TTY, O_RDONLY); - get_terminal_width_height(fileno(inp), &width, &height); - if (width < 10 || height < 3) + 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"); + max_displayed_line -= 2; - buffer = xmalloc(height * sizeof(char *)); + buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); if (option_mask32 & FLAG_TILDE) empty_line_marker = ""; - data_readlines(); - - tcgetattr(fileno(inp), &term_orig); + 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_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; - buffer_init(); - buffer_print(); + /* 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);*/ + reinitialize(); while (1) { - keypress = tless_getch(); + keypress = less_getch(); keypress_process(keypress); } } -- cgit v1.1