forgot about avn add... :(
[oweals/busybox.git] / miscutils / less.c
index ce34701d624de0f3079d6a2d09d5b74b1350ccbd..de97ba6b93fc3bf894db7701e18dd8efe629154e 100644 (file)
@@ -2,24 +2,12 @@
 /*
  * 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
@@ -82,9 +63,6 @@
 /* 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;
@@ -98,7 +76,7 @@ static int num_files = 1;
 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)
@@ -116,11 +94,11 @@ static int num_marks;
 
 #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 */
@@ -207,12 +185,12 @@ static void clear_line(void)
 /* 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;
        }
 }
 
@@ -222,15 +200,15 @@ static void data_readlines(void)
        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;
 
@@ -241,21 +219,13 @@ static void data_readlines(void)
 
        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
@@ -273,13 +243,18 @@ static void m_status_print(void)
        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) {
@@ -293,7 +268,8 @@ static void m_status_print(void)
                }
        }
        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);
@@ -330,7 +306,8 @@ static void status_print(void)
                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);
@@ -370,7 +347,7 @@ static void buffer_init(void)
 {
        int i;
 
-       if(buffer == NULL) {
+       if (buffer == NULL) {
                /* malloc the number of lines needed for the buffer */
                buffer = xrealloc(buffer, height * sizeof(char *));
        } else {
@@ -381,12 +358,12 @@ static void buffer_init(void)
        /* 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("");
        }
 }
 
@@ -400,7 +377,7 @@ static void buffer_down(int nlines)
                        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 {
@@ -410,7 +387,7 @@ static void buffer_down(int nlines)
                                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]);
                                }
                        }
                }
@@ -431,7 +408,7 @@ static void buffer_up(int nlines)
                        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 {
@@ -441,7 +418,7 @@ static void buffer_up(int nlines)
                                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]);
                                }
                        }
                }
@@ -463,10 +440,10 @@ static void buffer_up(int nlines)
                        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");
                                }
                        }
                }
@@ -476,31 +453,32 @@ static void buffer_up(int nlines)
 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();
        }
 }
 
@@ -531,7 +509,7 @@ static void examine_file(void)
        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++;
 
@@ -623,128 +601,129 @@ static void colon_process(void)
 /* 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
 
@@ -772,8 +751,10 @@ static void number_process(int first_digit)
        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) {
@@ -788,26 +769,24 @@ static void number_process(int first_digit)
                                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
@@ -878,7 +857,6 @@ static void full_repaint(void)
        data_readlines();
        buffer_init();
        buffer_line(temp_line_pos);
-       buffer_print();
 }
 
 
@@ -892,8 +870,8 @@ static void save_input_to_file(void)
        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);
@@ -907,14 +885,12 @@ static void save_input_to_file(void)
 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)
@@ -962,19 +938,14 @@ static char opp_bracket(char bracket)
        switch (bracket) {
                case '{': case '[':
                        return bracket + 2;
-                       break;
                case '(':
                        return ')';
-                       break;
                case '}': case ']':
                        return bracket - 2;
-                       break;
                case ')':
                        return '(';
-                       break;
                default:
                        return 0;
-                       break;
        }
 }
 
@@ -999,7 +970,6 @@ static void match_right_bracket(char bracket)
                        printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
 
                buffer_line(bracket_line - height + 2);
-               buffer_print();
        }
 }
 
@@ -1027,7 +997,6 @@ static void match_left_bracket(char bracket)
                        printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
 
                buffer_line(bracket_line);
-               buffer_print();
        }
 }
 
@@ -1061,12 +1030,10 @@ static void keypress_process(int keypress)
                        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);
@@ -1102,20 +1069,18 @@ static void keypress_process(int keypress)
 #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
@@ -1150,7 +1115,7 @@ int less_main(int argc, char **argv) {
 
        int keypress;
 
-       flags = bb_getopt_ulflags(argc, argv, "EMmN~");
+       flags = getopt32(argc, argv, "EMmN~");
 
        argc -= optind;
        argv += optind;
@@ -1161,13 +1126,13 @@ int less_main(int argc, char **argv) {
                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;