less: somewhat buggy applet, but nice. Muchly reduced

xstrdup'ing and memory consumption. Made linewrap saner.
regex matching code was awful - still buggy, but not as
leaky as before. Made buffer size configurable. Killed
several static and on-stack buffers. Hopefully eliminated
staircase effect on Ctrl-C (unable to reproduce).
This commit is contained in:
Denis Vlasenko 2006-12-20 02:46:48 +00:00
parent b95636c52f
commit 9a7cef930f
3 changed files with 208 additions and 214 deletions

View File

@ -13,7 +13,7 @@
/* This function reads an entire line from a text file, up to a newline /* This function reads an entire line from a text file, up to a newline
* or NUL byte, inclusive. It returns a malloc'ed char * which must be * or NUL byte, inclusive. It returns a malloc'ed char * which must be
* stored and free'ed by the caller. If end is null '\n' isn't considered * stored and free'ed by the caller. If end is null '\n' isn't considered
* end of line. If end isn't null, length of the chunk read is stored in it. */ * end of line. If end isn't null, length of the chunk read is stored in it. */
char *bb_get_chunk_from_file(FILE * file, int *end) char *bb_get_chunk_from_file(FILE * file, int *end)
@ -62,7 +62,7 @@ char *xmalloc_getline(FILE * file)
char *c = bb_get_chunk_from_file(file, &i); char *c = bb_get_chunk_from_file(file, &i);
if (i && c[--i] == '\n') if (i && c[--i] == '\n')
c[i] = 0; c[i] = '\0';
return c; return c;
} }

View File

@ -133,6 +133,11 @@ config LESS
'less' is a pager, meaning that it displays text files. It possesses 'less' is a pager, meaning that it displays text files. It possesses
a wide array of features, and is an improvement over 'more'. a wide array of features, and is an improvement over 'more'.
config FEATURE_LESS_MAXLINES
int "Max number of input lines less will try to eat"
default 9999999
depends on LESS
config FEATURE_LESS_BRACKETS config FEATURE_LESS_BRACKETS
bool "Enable bracket searching" bool "Enable bracket searching"
default y default y

View File

@ -32,7 +32,7 @@
#include "busybox.h" #include "busybox.h"
#ifdef CONFIG_FEATURE_LESS_REGEXP #if ENABLE_FEATURE_LESS_REGEXP
#include "xregex.h" #include "xregex.h"
#endif #endif
@ -64,13 +64,12 @@
/* The escape code to clear the screen */ /* The escape code to clear the screen */
#define CLEAR "\033[H\033[J" #define CLEAR "\033[H\033[J"
/* Maximum number of lines in a file */ #define MAXLINES CONFIG_FEATURE_LESS_MAXLINES
#define MAXLINES 10000
static int height; static int height;
static int width; static int width;
static char **files; static char **files;
static char filename[256]; static char *filename;
static char **buffer; static char **buffer;
static char **flines; static char **flines;
static int current_file = 1; static int current_file = 1;
@ -79,24 +78,21 @@ static int num_flines;
static int num_files = 1; static int num_files = 1;
/* Command line options */ /* Command line options */
static unsigned flags;
#define FLAG_E 1 #define FLAG_E 1
#define FLAG_M (1<<1) #define FLAG_M (1<<1)
#define FLAG_m (1<<2) #define FLAG_m (1<<2)
#define FLAG_N (1<<3) #define FLAG_N (1<<3)
#define FLAG_TILDE (1<<4) #define FLAG_TILDE (1<<4)
/* hijack command line options variable for internal state vars */ /* hijack command line options variable for internal state vars */
#define LESS_STATE_INP_STDIN (1<<5) #define LESS_STATE_PAST_EOF (1<<5)
#define LESS_STATE_PAST_EOF (1<<6) #define LESS_STATE_MATCH_BACKWARDS (1<<6)
#define LESS_STATE_MATCH_BACKWARDS (1<<7)
/* INP_STDIN is used to change behaviour when input comes from stdin */
#ifdef CONFIG_FEATURE_LESS_MARKS #if ENABLE_FEATURE_LESS_MARKS
static int mark_lines[15][2]; static int mark_lines[15][2];
static int num_marks; static int num_marks;
#endif #endif
#ifdef CONFIG_FEATURE_LESS_REGEXP #if ENABLE_FEATURE_LESS_REGEXP
static int match_found; static int match_found;
static int *match_lines; static int *match_lines;
static int match_pos; static int match_pos;
@ -143,7 +139,7 @@ static int tless_getch(void)
them accordingly */ them accordingly */
if (input == '\033' && getc(inp) == '[') { if (input == '\033' && getc(inp) == '[') {
unsigned int i; unsigned i;
input = getc(inp); input = getc(inp);
set_tty_cooked(); set_tty_cooked();
@ -152,13 +148,12 @@ static int tless_getch(void)
return 20 + i; return 20 + i;
else if ((i = input - REAL_PAGE_UP) < 4) else if ((i = input - REAL_PAGE_UP) < 4)
return 24 + i; return 24 + i;
else
return 0; /* ?? */
} }
/* The input is a normal ASCII value */ /* The input is a normal ASCII value */
else { set_tty_cooked();
set_tty_cooked(); return input;
return input;
}
return 0;
} }
/* Move the cursor to a position (x,y), where (0,0) is the /* Move the cursor to a position (x,y), where (0,0) is the
@ -174,51 +169,62 @@ static void clear_line(void)
printf("\033[K"); printf("\033[K");
} }
/* This adds line numbers to every line, as the -N flag necessitates */
static void add_linenumbers(void)
{
int i;
for (i = 0; i <= num_flines; i++) {
char *new = xasprintf("%5d %s", i + 1, flines[i]);
free(flines[i]);
flines[i] = new;
}
}
static void data_readlines(void) static void data_readlines(void)
{ {
int i; unsigned i;
char current_line[256]; unsigned n = 1;
int w = width;
char *last_nl = (char*)1; /* "not NULL" */
char *current_line;
FILE *fp; FILE *fp;
fp = (flags & LESS_STATE_INP_STDIN) ? stdin : xfopen(filename, "r"); fp = filename ? xfopen(filename, "r") : stdin;
flines = NULL; flines = NULL;
for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) { if (option_mask32 & FLAG_N) {
strcpy(current_line, ""); w -= 6;
fgets(current_line, 256, fp); if (w < 1) w = 1; /* paranoia */
}
for (i = 0; !feof(fp) && i <= MAXLINES; i++) {
flines = xrealloc(flines, (i+1) * sizeof(char *));
current_line = xmalloc(w);
again:
current_line[0] = '\0';
fgets(current_line, w, fp);
if (fp != stdin) if (fp != stdin)
die_if_ferror(fp, filename); die_if_ferror(fp, filename);
flines = xrealloc(flines, (i+1) * sizeof(char *));
flines[i] = xstrdup(current_line); /* Corner case: linewrap with only '\n' wrapping */
/* Looks ugly on screen, so we handle it specially */
if (!last_nl && current_line[0] == '\n') {
last_nl = (char*)1; /* "not NULL" */
n++;
goto again;
}
last_nl = last_char_is(current_line, '\n');
if (last_nl)
*last_nl = '\0';
if (option_mask32 & FLAG_N) {
flines[i] = xasprintf((n <= 99999) ? "%5u %s" : "%05u %s",
n % 100000, current_line);
free(current_line);
if (last_nl)
n++;
} else {
flines[i] = xrealloc(current_line, strlen(current_line)+1);
}
} }
num_flines = i - 2; num_flines = i - 2;
/* Reset variables for a new file */ /* Reset variables for a new file */
line_pos = 0; line_pos = 0;
flags &= ~LESS_STATE_PAST_EOF; option_mask32 &= ~LESS_STATE_PAST_EOF;
fclose(fp); fclose(fp);
if (inp == NULL)
inp = (flags & LESS_STATE_INP_STDIN) ? xfopen(CURRENT_TTY, "r") : stdin;
if (flags & FLAG_N)
add_linenumbers();
} }
#ifdef CONFIG_FEATURE_LESS_FLAGS #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 and not a prototype saves around 32 bytes
* on my build. */ * on my build. */
@ -232,19 +238,18 @@ static void m_status_print(void)
{ {
int percentage; int percentage;
if (!(flags & LESS_STATE_PAST_EOF)) { if (!(option_mask32 & LESS_STATE_PAST_EOF)) {
if (!line_pos) { if (!line_pos) {
if (num_files > 1) if (num_files > 1) {
printf("%s%s %s%i%s%i%s%i-%i/%i ", HIGHLIGHT, printf("%s%s %s%i%s%i%s%i-%i/%i ", HIGHLIGHT,
filename, "(file ", current_file, " of ", num_files, ") lines ", filename, "(file ", current_file, " of ", num_files, ") lines ",
line_pos + 1, line_pos + height - 1, num_flines + 1); line_pos + 1, line_pos + height - 1, num_flines + 1);
else { } else {
printf("%s%s lines %i-%i/%i ", HIGHLIGHT, printf("%s%s lines %i-%i/%i ", HIGHLIGHT,
filename, line_pos + 1, line_pos + height - 1, filename, line_pos + 1, line_pos + height - 1,
num_flines + 1); num_flines + 1);
} }
} } else {
else {
printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename,
line_pos + 1, line_pos + height - 1, num_flines + 1); line_pos + 1, line_pos + height - 1, num_flines + 1);
} }
@ -253,13 +258,11 @@ static void m_status_print(void)
printf("(END) %s", NORMAL); printf("(END) %s", NORMAL);
if ((num_files > 1) && (current_file != num_files)) if ((num_files > 1) && (current_file != num_files))
printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL); printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
} } else {
else {
percentage = calc_percent(); percentage = calc_percent();
printf("%i%% %s", percentage, NORMAL); printf("%i%% %s", percentage, NORMAL);
} }
} } else {
else {
printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename,
line_pos + 1, num_flines + 1, num_flines + 1); line_pos + 1, num_flines + 1, num_flines + 1);
if ((num_files > 1) && (current_file != num_files)) if ((num_files > 1) && (current_file != num_files))
@ -287,10 +290,10 @@ static void medium_status_print(void)
static void status_print(void) static void status_print(void)
{ {
/* Change the status if flags have been set */ /* Change the status if flags have been set */
#ifdef CONFIG_FEATURE_LESS_FLAGS #if ENABLE_FEATURE_LESS_FLAGS
if (flags & FLAG_M) if (option_mask32 & FLAG_M)
m_status_print(); m_status_print();
else if (flags & FLAG_m) else if (option_mask32 & FLAG_m)
medium_status_print(); medium_status_print();
/* No flags set */ /* No flags set */
else { else {
@ -300,16 +303,14 @@ static void status_print(void)
if (num_files > 1) if (num_files > 1)
printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ",
current_file, " of ", num_files, ")", NORMAL); current_file, " of ", num_files, ")", NORMAL);
} } else if (line_pos == num_flines - height + 2) {
else if (line_pos == num_flines - height + 2) {
printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL); printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
if ((num_files > 1) && (current_file != num_files)) if ((num_files > 1) && (current_file != num_files))
printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL); printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
} } else {
else {
putchar(':'); putchar(':');
} }
#ifdef CONFIG_FEATURE_LESS_FLAGS #if ENABLE_FEATURE_LESS_FLAGS
} }
#endif #endif
} }
@ -322,13 +323,12 @@ static void buffer_print(void)
printf("%s", CLEAR); printf("%s", CLEAR);
if (num_flines >= height - 2) { if (num_flines >= height - 2) {
for (i = 0; i < height - 1; i++) for (i = 0; i < height - 1; i++)
printf("%s", buffer[i]); printf("%.*s\n", width, buffer[i]);
} } else {
else {
for (i = 1; i < (height - 1 - num_flines); i++) for (i = 1; i < (height - 1 - num_flines); i++)
putchar('\n'); putchar('\n');
for (i = 0; i < height - 1; i++) for (i = 0; i < height - 1; i++)
printf("%s", buffer[i]); printf("%.*s\n", width, buffer[i]);
} }
status_print(); status_print();
@ -341,21 +341,18 @@ static void buffer_init(void)
if (buffer == NULL) { if (buffer == NULL) {
/* malloc the number of lines needed for the buffer */ /* malloc the number of lines needed for the buffer */
buffer = xrealloc(buffer, height * sizeof(char *)); buffer = xmalloc(height * sizeof(char *));
} else {
for (i = 0; i < (height - 1); i++)
free(buffer[i]);
} }
/* Fill the buffer until the end of the file or the /* Fill the buffer until the end of the file or the
end of the buffer is reached */ end of the buffer is reached */
for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) { for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
buffer[i] = xstrdup(flines[i]); buffer[i] = flines[i];
} }
/* If the buffer still isn't full, fill it with blank lines */ /* If the buffer still isn't full, fill it with blank lines */
for (; i < (height - 1); i++) { for (; i < (height - 1); i++) {
buffer[i] = xstrdup(""); buffer[i] = "";
} }
} }
@ -364,28 +361,25 @@ static void buffer_down(int nlines)
{ {
int i; int i;
if (!(flags & LESS_STATE_PAST_EOF)) { if (!(option_mask32 & LESS_STATE_PAST_EOF)) {
if (line_pos + (height - 3) + nlines < num_flines) { if (line_pos + (height - 3) + nlines < num_flines) {
line_pos += nlines; line_pos += nlines;
for (i = 0; i < (height - 1); i++) { for (i = 0; i < (height - 1); i++) {
free(buffer[i]); buffer[i] = flines[line_pos + i];
buffer[i] = xstrdup(flines[line_pos + i]);
} }
} } else {
else {
/* As the number of lines requested was too large, we just move /* As the number of lines requested was too large, we just move
to the end of the file */ to the end of the file */
while (line_pos + (height - 3) + 1 < num_flines) { while (line_pos + (height - 3) + 1 < num_flines) {
line_pos += 1; line_pos += 1;
for (i = 0; i < (height - 1); i++) { for (i = 0; i < (height - 1); i++) {
free(buffer[i]); buffer[i] = flines[line_pos + i];
buffer[i] = xstrdup(flines[line_pos + i]);
} }
} }
} }
/* We exit if the -E flag has been set */ /* We exit if the -E flag has been set */
if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines)) if ((option_mask32 & FLAG_E) && (line_pos + (height - 2) == num_flines))
tless_exit(0); tless_exit(0);
} }
} }
@ -395,22 +389,19 @@ static void buffer_up(int nlines)
int i; int i;
int tilde_line; int tilde_line;
if (!(flags & LESS_STATE_PAST_EOF)) { if (!(option_mask32 & LESS_STATE_PAST_EOF)) {
if (line_pos - nlines >= 0) { if (line_pos - nlines >= 0) {
line_pos -= nlines; line_pos -= nlines;
for (i = 0; i < (height - 1); i++) { for (i = 0; i < (height - 1); i++) {
free(buffer[i]); buffer[i] = flines[line_pos + i];
buffer[i] = xstrdup(flines[line_pos + i]);
} }
} } else {
else {
/* As the requested number of lines to move was too large, we /* As the requested number of lines to move was too large, we
move one line up at a time until we can't. */ move one line up at a time until we can't. */
while (line_pos != 0) { while (line_pos != 0) {
line_pos -= 1; line_pos -= 1;
for (i = 0; i < (height - 1); i++) { for (i = 0; i < (height - 1); i++) {
free(buffer[i]); buffer[i] = flines[line_pos + i];
buffer[i] = xstrdup(flines[line_pos + i]);
} }
} }
} }
@ -423,19 +414,17 @@ static void buffer_up(int nlines)
/* Going backwards nlines lines has taken us to a point where /* Going backwards nlines lines has taken us to a point where
nothing is past the EOF, so we revert to normal. */ nothing is past the EOF, so we revert to normal. */
if (line_pos < num_flines - height + 3) { if (line_pos < num_flines - height + 3) {
flags &= ~LESS_STATE_PAST_EOF; option_mask32 &= ~LESS_STATE_PAST_EOF;
buffer_up(nlines); buffer_up(nlines);
} } else {
else {
/* We only move part of the buffer, as the rest /* We only move part of the buffer, as the rest
is past the EOF */ is past the EOF */
for (i = 0; i < (height - 1); i++) { for (i = 0; i < (height - 1); i++) {
free(buffer[i]); if (i < tilde_line - nlines + 1) {
if (i < tilde_line - nlines + 1) buffer[i] = flines[line_pos + i];
buffer[i] = xstrdup(flines[line_pos + i]); } else {
else {
if (line_pos >= num_flines - height + 2) if (line_pos >= num_flines - height + 2)
buffer[i] = xstrdup("~\n"); buffer[i] = "~";
} }
} }
} }
@ -445,7 +434,7 @@ static void buffer_up(int nlines)
static void buffer_line(int linenum) static void buffer_line(int linenum)
{ {
int i; int i;
flags &= ~LESS_STATE_PAST_EOF; option_mask32 &= ~LESS_STATE_PAST_EOF;
if (linenum < 0 || linenum > num_flines) { if (linenum < 0 || linenum > num_flines) {
clear_line(); clear_line();
@ -453,23 +442,20 @@ static void buffer_line(int linenum)
} }
else if (linenum < (num_flines - height - 2)) { else if (linenum < (num_flines - height - 2)) {
for (i = 0; i < (height - 1); i++) { for (i = 0; i < (height - 1); i++) {
free(buffer[i]); buffer[i] = flines[linenum + i];
buffer[i] = xstrdup(flines[linenum + i]);
} }
line_pos = linenum; line_pos = linenum;
buffer_print(); buffer_print();
} } else {
else {
for (i = 0; i < (height - 1); i++) { for (i = 0; i < (height - 1); i++) {
free(buffer[i]);
if (linenum + i < num_flines + 2) if (linenum + i < num_flines + 2)
buffer[i] = xstrdup(flines[linenum + i]); buffer[i] = flines[linenum + i];
else else
buffer[i] = xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n"); buffer[i] = (option_mask32 & FLAG_TILDE) ? "" : "~";
} }
line_pos = linenum; line_pos = linenum;
/* Set past_eof so buffer_down and buffer_up act differently */ /* Set past_eof so buffer_down and buffer_up act differently */
flags |= LESS_STATE_PAST_EOF; option_mask32 |= LESS_STATE_PAST_EOF;
buffer_print(); buffer_print();
} }
} }
@ -490,22 +476,13 @@ static void reinitialise(void)
static void examine_file(void) static void examine_file(void)
{ {
int newline_offset;
clear_line(); clear_line();
printf("Examine: "); printf("Examine: ");
fgets(filename, 256, inp); free(filename);
filename = xmalloc_getline(inp);
/* As fgets adds a newline to the end of an input string, we files[num_files] = filename;
need to remove it */
newline_offset = strlen(filename) - 1;
filename[newline_offset] = '\0';
files[num_files] = xstrdup(filename);
current_file = num_files + 1; current_file = num_files + 1;
num_files++; num_files++;
flags &= ~LESS_STATE_INP_STDIN;
reinitialise(); reinitialise();
} }
@ -518,10 +495,10 @@ static void change_file(int direction)
{ {
if (current_file != ((direction > 0) ? num_files : 1)) { if (current_file != ((direction > 0) ? num_files : 1)) {
current_file = direction ? current_file + direction : 1; current_file = direction ? current_file + direction : 1;
strcpy(filename, files[current_file - 1]); free(filename);
filename = xstrdup(files[current_file - 1]);
reinitialise(); reinitialise();
} } else {
else {
clear_line(); clear_line();
printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL); printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
} }
@ -537,8 +514,7 @@ static void remove_current_file(void)
files[i - 2] = files[i - 1]; files[i - 2] = files[i - 1];
num_files--; num_files--;
buffer_print(); buffer_print();
} } else {
else {
change_file(1); change_file(1);
for (i = 2; i <= num_files; i++) for (i = 2; i <= num_files; i++)
files[i - 2] = files[i - 1]; files[i - 2] = files[i - 1];
@ -564,7 +540,7 @@ static void colon_process(void)
case 'e': case 'e':
examine_file(); examine_file();
break; break;
#ifdef CONFIG_FEATURE_LESS_FLAGS #if ENABLE_FEATURE_LESS_FLAGS
case 'f': case 'f':
clear_line(); clear_line();
m_status_print(); m_status_print();
@ -587,7 +563,7 @@ static void colon_process(void)
} }
} }
#ifdef CONFIG_FEATURE_LESS_REGEXP #if ENABLE_FEATURE_LESS_REGEXP
/* The below two regular expression handler functions NEED development. */ /* The below two regular expression handler functions NEED development. */
/* Get a regular expression from the user, and then go through the current /* Get a regular expression from the user, and then go through the current
@ -595,48 +571,50 @@ static void colon_process(void)
static char *process_regex_on_line(char *line, regex_t *pattern, int action) static char *process_regex_on_line(char *line, regex_t *pattern, int action)
{ {
/* UNTESTED. LOOKED BUGGY AND LEAKY AS HELL. */
/* FIXED. NEED TESTING. */
/* 'line' should be either returned or free()ed */
/* This function takes the regex and applies it to the line. /* This function takes the regex and applies it to the line.
Each part of the line that matches has the HIGHLIGHT Each part of the line that matches has the HIGHLIGHT
and NORMAL escape sequences placed around it by and NORMAL escape sequences placed around it by
insert_highlights if action = 1, or has the escape sequences insert_highlights if action = 1, or has the escape sequences
removed if action = 0, and then the line is returned. */ removed if action = 0, and then the line is returned. */
int match_status; int match_status;
char *line2 = xmalloc((sizeof(char) * (strlen(line) + 1)) + 64); char *line2 = line;
char *growline = ""; char *growline = xstrdup("");
char *ng;
regmatch_t match_structs; regmatch_t match_structs;
line2 = xstrdup(line);
match_found = 0; match_found = 0;
match_status = regexec(pattern, line2, 1, &match_structs, 0); match_status = regexec(pattern, line2, 1, &match_structs, 0);
while (match_status == 0) { while (match_status == 0) {
if (match_found == 0) match_found = 1;
match_found = 1;
if (action) { if (action) {
growline = xasprintf("%s%.*s%s%.*s%s", growline, ng = xasprintf("%s%.*s%s%.*s%s", growline,
match_structs.rm_so, line2, HIGHLIGHT, match_structs.rm_so, line2, HIGHLIGHT,
match_structs.rm_eo - match_structs.rm_so, match_structs.rm_eo - match_structs.rm_so,
line2 + match_structs.rm_so, NORMAL); line2 + match_structs.rm_so, NORMAL);
} } else {
else { ng = xasprintf("%s%.*s%.*s", growline,
growline = xasprintf("%s%.*s%.*s", growline,
match_structs.rm_so - 4, line2, match_structs.rm_so - 4, line2,
match_structs.rm_eo - match_structs.rm_so, match_structs.rm_eo - match_structs.rm_so,
line2 + match_structs.rm_so); line2 + match_structs.rm_so);
} }
free(growline); growline = ng;
line2 += match_structs.rm_eo; line2 += match_structs.rm_eo;
match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL); match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL);
} }
growline = xasprintf("%s%s", growline, line2); if (match_found) {
ng = xasprintf("%s%s", growline, line2);
return (match_found ? growline : line); free(line);
} else {
ng = line;
}
free(growline); free(growline);
free(line2); return ng;
} }
static void goto_match(int match) static void goto_match(int match)
@ -651,35 +629,33 @@ static void goto_match(int match)
static void regex_process(void) static void regex_process(void)
{ {
char uncomp_regex[100]; char *uncomp_regex;
char *current_line;
int i; int i;
int j = 0; int j = 0;
regex_t pattern; regex_t pattern;
/* Get the uncompiled regular expression from the user */ /* Get the uncompiled regular expression from the user */
clear_line(); clear_line();
putchar((flags & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/'); putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
uncomp_regex[0] = 0; uncomp_regex = xmalloc_getline(inp);
fgets(uncomp_regex, sizeof(uncomp_regex), inp); if (!uncomp_regex || !uncomp_regex[0]) {
free(uncomp_regex);
if (strlen(uncomp_regex) == 1) {
if (num_matches) if (num_matches)
goto_match((flags & LESS_STATE_MATCH_BACKWARDS) goto_match((option_mask32 & LESS_STATE_MATCH_BACKWARDS)
? match_pos - 1 : match_pos + 1); ? match_pos - 1 : match_pos + 1);
else else
buffer_print(); buffer_print();
return; return;
} }
uncomp_regex[strlen(uncomp_regex) - 1] = '\0';
/* Compile the regex and check for errors */ /* Compile the regex and check for errors */
xregcomp(&pattern, uncomp_regex, 0); xregcomp(&pattern, uncomp_regex, 0);
free(uncomp_regex);
if (num_matches) { if (num_matches) {
/* Get rid of all the highlights we added previously */ /* Get rid of all the highlights we added previously */
for (i = 0; i <= num_flines; i++) { for (i = 0; i <= num_flines; i++) {
current_line = process_regex_on_line(flines[i], &old_pattern, 0); flines[i] = process_regex_on_line(flines[i], &old_pattern, 0);
flines[i] = xstrdup(current_line);
} }
} }
old_pattern = pattern; old_pattern = pattern;
@ -692,8 +668,7 @@ static void regex_process(void)
match_found = 0; match_found = 0;
/* Run the regex on each line of the current file here */ /* Run the regex on each line of the current file here */
for (i = 0; i <= num_flines; i++) { for (i = 0; i <= num_flines; i++) {
current_line = process_regex_on_line(flines[i], &pattern, 1); flines[i] = process_regex_on_line(flines[i], &pattern, 1);
flines[i] = xstrdup(current_line);
if (match_found) { if (match_found) {
match_lines = xrealloc(match_lines, (j + 1) * sizeof(int)); match_lines = xrealloc(match_lines, (j + 1) * sizeof(int));
match_lines[j] = i; match_lines[j] = i;
@ -703,7 +678,7 @@ static void regex_process(void)
num_matches = j; num_matches = j;
if ((match_lines[0] != -1) && (num_flines > height - 2)) { if ((match_lines[0] != -1) && (num_flines > height - 2)) {
if (flags & LESS_STATE_MATCH_BACKWARDS) { if (option_mask32 & LESS_STATE_MATCH_BACKWARDS) {
for (i = 0; i < num_matches; i++) { for (i = 0; i < num_matches; i++) {
if (match_lines[i] > line_pos) { if (match_lines[i] > line_pos) {
match_pos = i - 1; match_pos = i - 1;
@ -711,11 +686,9 @@ static void regex_process(void)
break; break;
} }
} }
} } else
else
buffer_line(match_lines[0]); buffer_line(match_lines[0]);
} } else
else
buffer_init(); buffer_init();
} }
#endif #endif
@ -724,9 +697,8 @@ static void number_process(int first_digit)
{ {
int i = 1; int i = 1;
int num; int num;
char num_input[80]; char num_input[sizeof(int)*4]; /* more than enough */
char keypress; char keypress;
char *endptr;
num_input[0] = first_digit; num_input[0] = first_digit;
@ -734,8 +706,11 @@ static void number_process(int first_digit)
clear_line(); clear_line();
printf(":%c", first_digit); printf(":%c", first_digit);
/* Receive input until a letter is given (max 80 chars)*/ /* Receive input until a letter is given */
while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) { while (i < sizeof(num_input)-1) {
num_input[i] = tless_getch();
if (!num_input[i] || !isdigit(num_input[i]))
break;
putchar(num_input[i]); putchar(num_input[i]);
i++; i++;
} }
@ -743,8 +718,9 @@ static void number_process(int first_digit)
/* Take the final letter out of the digits string */ /* Take the final letter out of the digits string */
keypress = num_input[i]; keypress = num_input[i];
num_input[i] = '\0'; num_input[i] = '\0';
num = strtol(num_input, &endptr, 10); num = bb_strtou(num_input, NULL, 10);
if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES) { /* on format error, num == -1 */
if (num < 1 || num > MAXLINES) {
buffer_print(); buffer_print();
return; return;
} }
@ -764,16 +740,16 @@ static void number_process(int first_digit)
case 'p': case '%': case 'p': case '%':
buffer_line(((num / 100) * num_flines) - 1); buffer_line(((num / 100) * num_flines) - 1);
break; break;
#ifdef CONFIG_FEATURE_LESS_REGEXP #if ENABLE_FEATURE_LESS_REGEXP
case 'n': case 'n':
goto_match(match_pos + num); goto_match(match_pos + num);
break; break;
case '/': case '/':
flags &= ~LESS_STATE_MATCH_BACKWARDS; option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
regex_process(); regex_process();
break; break;
case '?': case '?':
flags |= LESS_STATE_MATCH_BACKWARDS; option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
regex_process(); regex_process();
break; break;
#endif #endif
@ -782,7 +758,7 @@ static void number_process(int first_digit)
} }
} }
#ifdef CONFIG_FEATURE_LESS_FLAGCS #if ENABLE_FEATURE_LESS_FLAGCS
static void flag_change(void) static void flag_change(void)
{ {
int keypress; int keypress;
@ -793,16 +769,16 @@ static void flag_change(void)
switch (keypress) { switch (keypress) {
case 'M': case 'M':
flags ^= FLAG_M; option_mask32 ^= FLAG_M;
break; break;
case 'm': case 'm':
flags ^= FLAG_m; option_mask32 ^= FLAG_m;
break; break;
case 'E': case 'E':
flags ^= FLAG_E; option_mask32 ^= FLAG_E;
break; break;
case '~': case '~':
flags ^= FLAG_TILDE; option_mask32 ^= FLAG_TILDE;
break; break;
default: default:
break; break;
@ -820,19 +796,19 @@ static void show_flag_status(void)
switch (keypress) { switch (keypress) {
case 'M': case 'M':
flag_val = flags & FLAG_M; flag_val = option_mask32 & FLAG_M;
break; break;
case 'm': case 'm':
flag_val = flags & FLAG_m; flag_val = option_mask32 & FLAG_m;
break; break;
case '~': case '~':
flag_val = flags & FLAG_TILDE; flag_val = option_mask32 & FLAG_TILDE;
break; break;
case 'N': case 'N':
flag_val = flags & FLAG_N; flag_val = option_mask32 & FLAG_N;
break; break;
case 'E': case 'E':
flag_val = flags & FLAG_E; flag_val = option_mask32 & FLAG_E;
break; break;
default: default:
flag_val = 0; flag_val = 0;
@ -855,26 +831,31 @@ static void full_repaint(void)
static void save_input_to_file(void) static void save_input_to_file(void)
{ {
char current_line[256]; char *current_line;
int i; int i;
FILE *fp; FILE *fp;
clear_line(); clear_line();
printf("Log file: "); printf("Log file: ");
fgets(current_line, 256, inp); current_line = xmalloc_getline(inp);
current_line[strlen(current_line) - 1] = '\0'; if (strlen(current_line) > 0) {
if (strlen(current_line) > 1) { fp = fopen(current_line, "w");
fp = xfopen(current_line, "w"); free(current_line);
if (!fp) {
printf("%s%s%s", HIGHLIGHT, "Error opening log file", NORMAL);
return;
}
for (i = 0; i < num_flines; i++) for (i = 0; i < num_flines; i++)
fprintf(fp, "%s", flines[i]); fprintf(fp, "%s\n", flines[i]);
fclose(fp); fclose(fp);
buffer_print(); buffer_print();
return;
} }
else free(current_line);
printf("%s%s%s", HIGHLIGHT, "No log file", NORMAL); printf("%s%s%s", HIGHLIGHT, "No log file", NORMAL);
} }
#ifdef CONFIG_FEATURE_LESS_MARKS #if ENABLE_FEATURE_LESS_MARKS
static void add_mark(void) static void add_mark(void)
{ {
int letter; int letter;
@ -892,8 +873,7 @@ static void add_mark(void)
mark_lines[num_marks][0] = letter; mark_lines[num_marks][0] = letter;
mark_lines[num_marks][1] = line_pos; mark_lines[num_marks][1] = line_pos;
num_marks++; num_marks++;
} } else {
else {
clear_line(); clear_line();
printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL); printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
} }
@ -917,14 +897,13 @@ static void goto_mark(void)
} }
if ((num_marks == 14) && (letter != mark_lines[14][0])) if ((num_marks == 14) && (letter != mark_lines[14][0]))
printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL); printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
} } else
else
printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL); printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
} }
#endif #endif
#ifdef CONFIG_FEATURE_LESS_BRACKETS #if ENABLE_FEATURE_LESS_BRACKETS
static char opp_bracket(char bracket) static char opp_bracket(char bracket)
{ {
@ -977,8 +956,7 @@ static void match_left_bracket(char bracket)
printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL); printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
printf("%s", flines[line_pos + height]); printf("%s", flines[line_pos + height]);
sleep(4); sleep(4);
} } else {
else {
for (i = line_pos + height - 2; i >= 0; i--) { for (i = line_pos + height - 2; i >= 0; i--) {
if (strchr(flines[i], opp_bracket(bracket)) != NULL) { if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
bracket_line = i; bracket_line = i;
@ -993,7 +971,7 @@ static void match_left_bracket(char bracket)
} }
} }
#endif /* CONFIG_FEATURE_LESS_BRACKETS */ #endif /* FEATURE_LESS_BRACKETS */
static void keypress_process(int keypress) static void keypress_process(int keypress)
{ {
@ -1031,7 +1009,7 @@ static void keypress_process(int keypress)
case 'q': case 'Q': case 'q': case 'Q':
tless_exit(0); tless_exit(0);
break; break;
#ifdef CONFIG_FEATURE_LESS_MARKS #if ENABLE_FEATURE_LESS_MARKS
case 'm': case 'm':
add_mark(); add_mark();
buffer_print(); buffer_print();
@ -1048,21 +1026,20 @@ static void keypress_process(int keypress)
full_repaint(); full_repaint();
break; break;
case 's': case 's':
if (flags & LESS_STATE_INP_STDIN) save_input_to_file();
save_input_to_file();
break; break;
case 'E': case 'E':
examine_file(); examine_file();
break; break;
#ifdef CONFIG_FEATURE_LESS_FLAGS #if ENABLE_FEATURE_LESS_FLAGS
case '=': case '=':
clear_line(); clear_line();
m_status_print(); m_status_print();
break; break;
#endif #endif
#ifdef CONFIG_FEATURE_LESS_REGEXP #if ENABLE_FEATURE_LESS_REGEXP
case '/': case '/':
flags &= ~LESS_STATE_MATCH_BACKWARDS; option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
regex_process(); regex_process();
break; break;
case 'n': case 'n':
@ -1072,11 +1049,11 @@ static void keypress_process(int keypress)
goto_match(match_pos - 1); goto_match(match_pos - 1);
break; break;
case '?': case '?':
flags |= LESS_STATE_MATCH_BACKWARDS; option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
regex_process(); regex_process();
break; break;
#endif #endif
#ifdef CONFIG_FEATURE_LESS_FLAGCS #if ENABLE_FEATURE_LESS_FLAGCS
case '-': case '-':
flag_change(); flag_change();
buffer_print(); buffer_print();
@ -1085,7 +1062,7 @@ static void keypress_process(int keypress)
show_flag_status(); show_flag_status();
break; break;
#endif #endif
#ifdef CONFIG_FEATURE_LESS_BRACKETS #if ENABLE_FEATURE_LESS_BRACKETS
case '{': case '(': case '[': case '{': case '(': case '[':
match_right_bracket(keypress); match_right_bracket(keypress);
break; break;
@ -1104,30 +1081,42 @@ static void keypress_process(int keypress)
number_process(keypress); number_process(keypress);
} }
int less_main(int argc, char **argv) { static void sig_catcher(int sig ATTRIBUTE_UNUSED)
{
set_tty_cooked();
exit(1);
}
int less_main(int argc, char **argv)
{
int keypress; int keypress;
flags = getopt32(argc, argv, "EMmN~"); getopt32(argc, argv, "EMmN~");
argc -= optind; argc -= optind;
argv += optind; argv += optind;
files = argv; files = argv;
num_files = argc; num_files = argc;
if (!num_files) { if (!num_files) {
if (ttyname(STDIN_FILENO) == NULL) if (isatty(STDIN_FILENO)) {
flags |= LESS_STATE_INP_STDIN; /* Just "less"? No file and no redirection? */
else {
bb_error_msg("missing filename"); bb_error_msg("missing filename");
bb_show_usage(); bb_show_usage();
} }
} } else
filename = xstrdup(files[0]);
strcpy(filename, (flags & LESS_STATE_INP_STDIN) ? bb_msg_standard_input : files[0]); /* FIXME: another popular pager, most, detects when stdout
get_terminal_width_height(0, &width, &height); * is not a tty and turns into cat */
inp = xfopen(CURRENT_TTY, "r");
get_terminal_width_height(fileno(inp), &width, &height);
data_readlines(); data_readlines();
signal(SIGTERM, sig_catcher);
signal(SIGINT, sig_catcher);
tcgetattr(fileno(inp), &term_orig); tcgetattr(fileno(inp), &term_orig);
term_vi = term_orig; term_vi = term_orig;
term_vi.c_lflag &= (~ICANON & ~ECHO); term_vi.c_lflag &= (~ICANON & ~ECHO);
term_vi.c_iflag &= (~IXON & ~ICRNL); term_vi.c_iflag &= (~IXON & ~ICRNL);