/*
* Mini less implementation for busybox
*
- *
* Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307 USA
- *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/*
* This program needs a lot of development, so consider it in a beta stage
* at best.
*
* redirected input has been read from stdin
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <termios.h>
-#include <unistd.h>
-#include <ctype.h>
-
#include "busybox.h"
#ifdef CONFIG_FEATURE_LESS_REGEXP
/* Maximum number of lines in a file */
#define MAXLINES 10000
-/* Get height and width of terminal */
-#define tty_width_height() get_terminal_width_height(0, &width, &height)
-
static int height;
static int width;
static char **files;
static int past_eof;
/* Command line options */
-static unsigned long flags;
+static unsigned flags;
#define FLAG_E 1
#define FLAG_M (1<<1)
#define FLAG_m (1<<2)
#ifdef CONFIG_FEATURE_LESS_REGEXP
static int match_found;
-static int match_lines[100];
+static int *match_lines;
static int match_pos;
static int num_matches;
static int match_backwards;
-static int num_back_match = 1;
+static regex_t old_pattern;
#endif
/* Needed termios structures */
/* This adds line numbers to every line, as the -N flag necessitates */
static void add_linenumbers(void)
{
- char current_line[256];
int i;
for (i = 0; i <= num_flines; i++) {
- safe_strncpy(current_line, flines[i], 256);
- flines[i] = bb_xasprintf("%5d %s", i + 1, current_line);
+ char *new = xasprintf("%5d %s", i + 1, flines[i]);
+ free(flines[i]);
+ flines[i] = new;
}
}
char current_line[256];
FILE *fp;
- fp = (inp_stdin) ? stdin : bb_xfopen(filename, "rt");
+ fp = (inp_stdin) ? stdin : xfopen(filename, "r");
flines = NULL;
for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) {
strcpy(current_line, "");
fgets(current_line, 256, fp);
- if(fp != stdin)
- bb_xferror(fp, filename);
+ if (fp != stdin)
+ die_if_ferror(fp, filename);
flines = xrealloc(flines, (i+1) * sizeof(char *));
- flines[i] = bb_xstrdup(current_line);
+ flines[i] = xstrdup(current_line);
}
num_flines = i - 2;
fclose(fp);
- if(inp == NULL)
- inp = (inp_stdin) ? bb_xfopen(CURRENT_TTY, "r") : stdin;
+ if (inp == NULL)
+ inp = (inp_stdin) ? xfopen(CURRENT_TTY, "r") : stdin;
if (flags & FLAG_N)
add_linenumbers();
}
-/* Turn a percentage into a line number */
-static int reverse_percent(int percentage)
-{
- double linenum = percentage;
- linenum = ((linenum / 100) * num_flines) - 1;
- return(linenum);
-}
-
#ifdef CONFIG_FEATURE_LESS_FLAGS
/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
if (!past_eof) {
if (!line_pos) {
if (num_files > 1)
- printf("%s%s %s%i%s%i%s%i-%i/%i ", HIGHLIGHT, filename, "(file ", current_file, " of ", num_files, ") lines ", line_pos + 1, line_pos + height - 1, num_flines + 1);
+ printf("%s%s %s%i%s%i%s%i-%i/%i ", HIGHLIGHT,
+ filename, "(file ", current_file, " of ", num_files, ") lines ",
+ line_pos + 1, line_pos + height - 1, num_flines + 1);
else {
- printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
+ printf("%s%s lines %i-%i/%i ", HIGHLIGHT,
+ filename, line_pos + 1, line_pos + height - 1,
+ num_flines + 1);
}
}
else {
- printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
+ printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename,
+ line_pos + 1, line_pos + height - 1, num_flines + 1);
}
if (line_pos == num_flines - height + 2) {
}
}
else {
- printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
+ printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename,
+ line_pos + 1, num_flines + 1, num_flines + 1);
if ((num_files > 1) && (current_file != num_files))
printf("- Next: %s", files[current_file]);
printf("%s", NORMAL);
if (!line_pos) {
printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
if (num_files > 1)
- printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
+ printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ",
+ current_file, " of ", num_files, ")", NORMAL);
}
else if (line_pos == num_flines - height + 2) {
printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
{
int i;
- if(buffer == NULL) {
+ if (buffer == NULL) {
/* malloc the number of lines needed for the buffer */
buffer = xrealloc(buffer, height * sizeof(char *));
} else {
/* 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] = bb_xstrdup(flines[i]);
+ buffer[i] = xstrdup(flines[i]);
}
/* If the buffer still isn't full, fill it with blank lines */
for (; i < (height - 1); i++) {
- buffer[i] = bb_xstrdup("");
+ buffer[i] = xstrdup("");
}
}
line_pos += nlines;
for (i = 0; i < (height - 1); i++) {
free(buffer[i]);
- buffer[i] = bb_xstrdup(flines[line_pos + i]);
+ buffer[i] = xstrdup(flines[line_pos + i]);
}
}
else {
line_pos += 1;
for (i = 0; i < (height - 1); i++) {
free(buffer[i]);
- buffer[i] = bb_xstrdup(flines[line_pos + i]);
+ buffer[i] = xstrdup(flines[line_pos + i]);
}
}
}
line_pos -= nlines;
for (i = 0; i < (height - 1); i++) {
free(buffer[i]);
- buffer[i] = bb_xstrdup(flines[line_pos + i]);
+ buffer[i] = xstrdup(flines[line_pos + i]);
}
}
else {
line_pos -= 1;
for (i = 0; i < (height - 1); i++) {
free(buffer[i]);
- buffer[i] = bb_xstrdup(flines[line_pos + i]);
+ buffer[i] = xstrdup(flines[line_pos + i]);
}
}
}
for (i = 0; i < (height - 1); i++) {
free(buffer[i]);
if (i < tilde_line - nlines + 1)
- buffer[i] = bb_xstrdup(flines[line_pos + i]);
+ buffer[i] = xstrdup(flines[line_pos + i]);
else {
if (line_pos >= num_flines - height + 2)
- buffer[i] = bb_xstrdup("~\n");
+ buffer[i] = xstrdup("~\n");
}
}
}
static void buffer_line(int linenum)
{
int i;
-
past_eof = 0;
- if (linenum < 1 || linenum > num_flines) {
+ if (linenum < 0 || linenum > num_flines) {
clear_line();
- printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum, NORMAL);
+ printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum + 1, NORMAL);
}
else if (linenum < (num_flines - height - 2)) {
for (i = 0; i < (height - 1); i++) {
free(buffer[i]);
- buffer[i] = bb_xstrdup(flines[linenum + i]);
+ buffer[i] = xstrdup(flines[linenum + i]);
}
line_pos = linenum;
+ buffer_print();
}
else {
for (i = 0; i < (height - 1); i++) {
free(buffer[i]);
if (linenum + i < num_flines + 2)
- buffer[i] = bb_xstrdup(flines[linenum + i]);
+ buffer[i] = xstrdup(flines[linenum + i]);
else
- buffer[i] = bb_xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
+ buffer[i] = xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
}
line_pos = linenum;
/* Set past_eof so buffer_down and buffer_up act differently */
past_eof = 1;
+ buffer_print();
}
}
newline_offset = strlen(filename) - 1;
filename[newline_offset] = '\0';
- files[num_files] = bb_xstrdup(filename);
+ files[num_files] = xstrdup(filename);
current_file = num_files + 1;
num_files++;
/* Get a regular expression from the user, and then go through the current
file line by line, running a processing regex function on each one. */
-static char *insert_highlights(char *line, int start, int end)
-{
- return bb_xasprintf("%.*s%s%.*s%s%s", start, line, HIGHLIGHT,
- end - start, line + start, NORMAL, line + end);
-}
-
-static char *process_regex_on_line(char *line, regex_t *pattern)
+static char *process_regex_on_line(char *line, regex_t *pattern, int action)
{
/* This function takes the regex and applies it to the line.
Each part of the line that matches has the HIGHLIGHT
and NORMAL escape sequences placed around it by
- insert_highlights, and then the line is returned. */
-
+ insert_highlights if action = 1, or has the escape sequences
+ removed if action = 0, and then the line is returned. */
int match_status;
- char *line2 = (char *) malloc((sizeof(char) * (strlen(line) + 1)) + 64);
- char sub_line[256];
- int prev_eo = 0;
+ char *line2 = xmalloc((sizeof(char) * (strlen(line) + 1)) + 64);
+ char *growline = "";
regmatch_t match_structs;
- memset(sub_line, 0, 256);
- strcpy(line2, line);
+ line2 = xstrdup(line);
match_found = 0;
match_status = regexec(pattern, line2, 1, &match_structs, 0);
while (match_status == 0) {
-
- memset(sub_line, 0, 256);
-
if (match_found == 0)
match_found = 1;
- line2 = insert_highlights(line2, match_structs.rm_so + prev_eo, match_structs.rm_eo + prev_eo);
- if ((size_t)match_structs.rm_eo + 11 + prev_eo < strlen(line2))
- strcat(sub_line, line2 + match_structs.rm_eo + 11 + prev_eo);
+ if (action) {
+ growline = xasprintf("%s%.*s%s%.*s%s", growline,
+ match_structs.rm_so, line2, HIGHLIGHT,
+ match_structs.rm_eo - match_structs.rm_so,
+ line2 + match_structs.rm_so, NORMAL);
+ }
+ else {
+ growline = xasprintf("%s%.*s%.*s", growline,
+ match_structs.rm_so - 4, line2,
+ match_structs.rm_eo - match_structs.rm_so,
+ line2 + match_structs.rm_so);
+ }
- prev_eo += match_structs.rm_eo + 11;
- match_status = regexec(pattern, sub_line, 1, &match_structs, REG_NOTBOL);
+ line2 += match_structs.rm_eo;
+ match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL);
}
- return line2;
+ growline = xasprintf("%s%s", growline, line2);
+
+ return (match_found ? growline : line);
+
+ free(growline);
+ free(line2);
+}
+
+static void goto_match(int match)
+{
+ /* This goes to a specific match - all line positions of matches are
+ stored within the match_lines[] array. */
+ if ((match < num_matches) && (match >= 0)) {
+ buffer_line(match_lines[match]);
+ match_pos = match;
+ }
}
static void regex_process(void)
{
char uncomp_regex[100];
- char current_line[256];
+ char *current_line;
int i;
int j = 0;
- regex_t *pattern;
-
- /* Reset variables */
- match_lines[0] = -1;
- match_pos = 0;
- num_matches = 0;
- match_found = 0;
-
- pattern = (regex_t *) malloc(sizeof(regex_t));
- memset(pattern, 0, sizeof(regex_t));
-
+ regex_t pattern;
/* Get the uncompiled regular expression from the user */
clear_line();
putchar((match_backwards) ? '?' : '/');
uncomp_regex[0] = 0;
- fgets(uncomp_regex, sizeof(uncomp_regex), stdin);
- i = strlen(uncomp_regex);
- if(i > 0) {
- if(uncomp_regex[i-1] == '\n')
- uncomp_regex[i-1] = '\0';
+ fgets(uncomp_regex, sizeof(uncomp_regex), inp);
+
+ if (strlen(uncomp_regex) == 1) {
+ if (num_matches)
+ goto_match(match_backwards ? match_pos - 1 : match_pos + 1);
else
- while((i = getchar()) != '\n' && i != EOF);
+ buffer_print();
+ return;
}
+ uncomp_regex[strlen(uncomp_regex) - 1] = '\0';
/* Compile the regex and check for errors */
- xregcomp(pattern, uncomp_regex, 0);
+ xregcomp(&pattern, uncomp_regex, 0);
+
+ if (num_matches) {
+ /* Get rid of all the highlights we added previously */
+ for (i = 0; i <= num_flines; i++) {
+ current_line = process_regex_on_line(flines[i], &old_pattern, 0);
+ flines[i] = xstrdup(current_line);
+ }
+ }
+ old_pattern = pattern;
+ /* Reset variables */
+ match_lines = xrealloc(match_lines, sizeof(int));
+ match_lines[0] = -1;
+ match_pos = 0;
+ num_matches = 0;
+ match_found = 0;
/* Run the regex on each line of the current file here */
for (i = 0; i <= num_flines; i++) {
- strcpy(current_line, process_regex_on_line(flines[i], pattern));
- flines[i] = bb_xstrdup(current_line);
+ current_line = process_regex_on_line(flines[i], &pattern, 1);
+ flines[i] = xstrdup(current_line);
if (match_found) {
+ match_lines = xrealloc(match_lines, (j + 1) * sizeof(int));
match_lines[j] = i;
j++;
}
}
num_matches = j;
- if ((match_lines[0] != -1) && (num_flines > height - 2))
- buffer_line(match_lines[0]);
- else
- buffer_init();
-}
-
-static void goto_match(int match)
-{
- /* This goes to a specific match - all line positions of matches are
- stored within the match_lines[] array. */
- if ((match < num_matches) && (match >= 0)) {
- buffer_line(match_lines[match]);
- match_pos = match;
- }
-}
-
-static void search_backwards(void)
-{
- int current_linepos = line_pos;
- int i;
-
- match_backwards = 1;
- regex_process();
-
- for (i = 0; i < num_matches; i++) {
- if (match_lines[i] > current_linepos) {
- buffer_line(match_lines[i - num_back_match]);
- break;
+ if ((match_lines[0] != -1) && (num_flines > height - 2)) {
+ if (match_backwards) {
+ for (i = 0; i < num_matches; i++) {
+ if (match_lines[i] > line_pos) {
+ match_pos = i - 1;
+ buffer_line(match_lines[match_pos]);
+ break;
+ }
+ }
}
+ else
+ buffer_line(match_lines[0]);
}
-
- /* Reset variables */
- match_backwards = 0;
- num_back_match = 1;
-
+ else
+ buffer_init();
}
#endif
keypress = num_input[i];
num_input[i] = '\0';
num = strtol(num_input, &endptr, 10);
- if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES)
- goto END;
+ if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES) {
+ buffer_print();
+ return;
+ }
/* We now know the number and the letter entered, so we process them */
switch (keypress) {
buffer_line(num - 1);
break;
case 'p': case '%':
- buffer_line(reverse_percent(num));
+ buffer_line(((num / 100) * num_flines) - 1);
break;
#ifdef CONFIG_FEATURE_LESS_REGEXP
case 'n':
- goto_match(match_pos + num - 1);
+ goto_match(match_pos + num);
break;
case '/':
+ match_backwards = 0;
regex_process();
- goto_match(num - 1);
break;
case '?':
- num_back_match = num;
- search_backwards();
+ match_backwards = 1;
+ regex_process();
break;
#endif
default:
break;
}
-END:
- buffer_print();
}
#ifdef CONFIG_FEATURE_LESS_FLAGCS
data_readlines();
buffer_init();
buffer_line(temp_line_pos);
- buffer_print();
}
printf("Log file: ");
fgets(current_line, 256, inp);
current_line[strlen(current_line) - 1] = '\0';
- if (strlen(current_line)) {
- fp = bb_xfopen(current_line, "w");
+ if (strlen(current_line) > 1) {
+ fp = xfopen(current_line, "w");
for (i = 0; i < num_flines; i++)
fprintf(fp, "%s", flines[i]);
fclose(fp);
static void add_mark(void)
{
int letter;
- int mark_line;
clear_line();
printf("Mark: ");
letter = tless_getch();
if (isalpha(letter)) {
- mark_line = line_pos;
/* If we exceed 15 marks, start overwriting previous ones */
if (num_marks == 14)
switch (bracket) {
case '{': case '[':
return bracket + 2;
- break;
case '(':
return ')';
- break;
case '}': case ']':
return bracket - 2;
- break;
case ')':
return '(';
- break;
default:
return 0;
- break;
}
}
printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
buffer_line(bracket_line - height + 2);
- buffer_print();
}
}
printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
buffer_line(bracket_line);
- buffer_print();
}
}
buffer_print();
break;
case 'g': case 'p': case '<': case '%':
- buffer_up(num_flines + 1);
- buffer_print();
+ buffer_line(0);
break;
case 'G': case '>':
- buffer_down(num_flines + 1);
- buffer_print();
+ buffer_line(num_flines - height + 2);
break;
case 'q': case 'Q':
tless_exit(0);
#endif
#ifdef CONFIG_FEATURE_LESS_REGEXP
case '/':
+ match_backwards = 0;
regex_process();
- buffer_print();
break;
case 'n':
goto_match(match_pos + 1);
- buffer_print();
break;
case 'N':
goto_match(match_pos - 1);
- buffer_print();
break;
case '?':
- search_backwards();
- buffer_print();
+ match_backwards = 1;
+ regex_process();
break;
#endif
#ifdef CONFIG_FEATURE_LESS_FLAGCS
int keypress;
- flags = bb_getopt_ulflags(argc, argv, "EMmN~");
+ flags = getopt32(argc, argv, "EMmN~");
argc -= optind;
argv += optind;
if (ttyname(STDIN_FILENO) == NULL)
inp_stdin = 1;
else {
- bb_error_msg("Missing filename");
+ bb_error_msg("missing filename");
bb_show_usage();
}
}
strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
- tty_width_height();
+ get_terminal_width_height(0, &width, &height);
data_readlines();
tcgetattr(fileno(inp), &term_orig);
term_vi = term_orig;