less: implement waiting for input using select(). Rather tricky business. But we do not read entire input anymore up-front.
No binary (build failed)
—
(log yok)
| @@ -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); | |
| } | |
| } | |
| @@ -12693,7 +12693,7 @@ readcmd(int argc, char **argv) | |
| FD_ZERO (&set); | |
| FD_SET (0, &set); | |
| - | i = select (FD_SETSIZE, &set, NULL, NULL, &ts); |
| + | i = select(FD_SETSIZE, &set, NULL, NULL, &ts); |
| if (!i) { | |
| #if defined(CONFIG_ASH_READ_NCHARS) | |
| if (nch_flag) | |