Big cleanup in config help and description
[oweals/busybox.git] / miscutils / less.c
index b38fcf76625c7fff16c53f312a964660f6b493d8..0b0a9aed4be26e7290d9813cf946e5b25e2d44ca 100644 (file)
@@ -87,7 +87,6 @@
 //config:        this option makes less perform a last-ditch effort to find it:
 //config:        position cursor to 999,999 and ask terminal to report real
 //config:        cursor position using "ESC [ 6 n" escape sequence, then read stdin.
-//config:
 //config:        This is not clean but helps a lot on serial lines and such.
 //config:
 //config:config FEATURE_LESS_DASHCMD
 //config:        less itself ('-' keyboard command).
 //config:
 //config:config FEATURE_LESS_LINENUMS
-//config:      bool "Enable dynamic switching of line numbers"
+//config:      bool "Enable -N (dynamic switching of line numbers)"
 //config:      default y
 //config:      depends on FEATURE_LESS_DASHCMD
-//config:      help
-//config:        Enables "-N" command.
+
+//applet:IF_LESS(APPLET(less, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LESS) += less.o
 
 //usage:#define less_trivial_usage
 //usage:       "[-E" IF_FEATURE_LESS_REGEXP("I")IF_FEATURE_LESS_FLAGS("Mm")
 #include <sched.h>  /* sched_yield() */
 
 #include "libbb.h"
+#include "common_bufsiz.h"
 #if ENABLE_FEATURE_LESS_REGEXP
 #include "xregex.h"
 #endif
@@ -165,15 +167,10 @@ enum {
 enum { pattern_valid = 0 };
 #endif
 
-enum {
-       READING_FILE = -1,
-       READING_STDIN = -2,
-       READING_NONREG = -3
-};
-
 struct globals {
        int cur_fline; /* signed */
        int kbd_fd;  /* fd to get input from */
+       int kbd_fd_orig_flags;
        int less_gets_pos;
 /* last position in last line, taking into account tabs */
        size_t last_line_pos;
@@ -195,7 +192,10 @@ struct globals {
        char *filename;
        char **files;
 #if ENABLE_FEATURE_LESS_FLAGS
-       int num_lines; /* input source if < 0, line count if >= 0 */
+       int num_lines; /* a flag if < 0, line count if >= 0 */
+# define REOPEN_AND_COUNT (-1)
+# define REOPEN_STDIN     (-2)
+# define NOT_REGULAR_FILE (-3)
 #endif
 #if ENABLE_FEATURE_LESS_MARKS
        unsigned num_marks;
@@ -306,6 +306,8 @@ static void print_statusline(const char *str)
 static void less_exit(int code)
 {
        set_tty_cooked();
+       if (!(G.kbd_fd_orig_flags & O_NONBLOCK))
+               ndelay_off(kbd_fd);
        clear_line();
        if (code < 0)
                kill_myself_with_sig(- code); /* does not return */
@@ -442,7 +444,6 @@ static int at_end(void)
  */
 static void read_lines(void)
 {
-#define readbuf bb_common_bufsiz1
        char *current_line, *p;
        int w = width;
        char last_terminated = terminated;
@@ -452,6 +453,9 @@ static void read_lines(void)
        unsigned old_max_fline = max_fline;
 #endif
 
+#define readbuf bb_common_bufsiz1
+       setup_common_bufsiz();
+
        /* (careful: max_fline can be -1) */
        if (max_fline + 1 > MAXLINES)
                return;
@@ -459,7 +463,7 @@ static void read_lines(void)
        if (option_mask32 & FLAG_N)
                w -= 8;
 
-       p = current_line = ((char*)xmalloc(w + 4)) + 4;
+       p = current_line = ((char*)xmalloc(w + 5)) + 4;
        if (!last_terminated) {
                const char *cp = flines[max_fline];
                p = stpcpy(p, cp);
@@ -483,7 +487,7 @@ static void read_lines(void)
                                        time_t t;
 
                                        errno = 0;
-                                       eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf));
+                                       eof_error = safe_read(STDIN_FILENO, readbuf, COMMON_BUFSIZE);
                                        if (errno != EAGAIN)
                                                break;
                                        t = time(NULL);
@@ -512,6 +516,16 @@ static void read_lines(void)
                                *--p = '\0';
                                continue;
                        }
+                       {
+                               size_t new_last_line_pos = last_line_pos + 1;
+                               if (c == '\t') {
+                                       new_last_line_pos += 7;
+                                       new_last_line_pos &= (~7);
+                               }
+                               if ((int)new_last_line_pos > w)
+                                       break;
+                               last_line_pos = new_last_line_pos;
+                       }
                        /* ok, we will eat this char */
                        readpos++;
                        if (c == '\n') {
@@ -523,16 +537,6 @@ static void read_lines(void)
                        if (c == '\0') c = '\n';
                        *p++ = c;
                        *p = '\0';
-                       {
-                               size_t new_last_line_pos = last_line_pos + 1;
-                               if (c == '\t') {
-                                       new_last_line_pos += 7;
-                                       new_last_line_pos &= (~7);
-                               }
-                               if ((int)new_last_line_pos >= w)
-                                       break;
-                               last_line_pos = new_last_line_pos;
-                       }
                } /* end of "read chars until we have a line" loop */
 #if 0
 //BUG: also triggers on this:
@@ -576,7 +580,7 @@ static void read_lines(void)
                        break;
                }
                max_fline++;
-               current_line = ((char*)xmalloc(w + 4)) + 4;
+               current_line = ((char*)xmalloc(w + 5)) + 4;
                p = current_line;
                last_line_pos = 0;
        } /* end of "read lines until we reach cur_fline" loop */
@@ -618,18 +622,27 @@ static int safe_lineno(int fline)
 static void update_num_lines(void)
 {
        int count, fd;
+       struct stat stbuf;
        ssize_t len, i;
        char buf[4096];
 
        /* only do this for regular files */
-       if (num_lines == READING_FILE) {
+       if (num_lines == REOPEN_AND_COUNT || num_lines == REOPEN_STDIN) {
                count = 0;
-               fd = open(filename, O_RDONLY);
+               fd = open("/proc/self/fd/0", O_RDONLY);
+               if (fd < 0 && num_lines == REOPEN_AND_COUNT) {
+                       /* "filename" is valid only if REOPEN_AND_COUNT */
+                       fd = open(filename, O_RDONLY);
+               }
                if (fd < 0) {
                        /* somebody stole my file! */
-                       num_lines = READING_NONREG;
+                       num_lines = NOT_REGULAR_FILE;
                        return;
                }
+               if (fstat(fd, &stbuf) != 0 || !S_ISREG(stbuf.st_mode)) {
+                       num_lines = NOT_REGULAR_FILE;
+                       goto do_close;
+               }
                while ((len = safe_read(fd, buf, sizeof(buf))) > 0) {
                        for (i = 0; i < len; ++i) {
                                if (buf[i] == '\n' && ++count == MAXLINES)
@@ -638,6 +651,7 @@ static void update_num_lines(void)
                }
  done:
                num_lines = count;
+ do_close:
                close(fd);
        }
 }
@@ -748,7 +762,7 @@ static void print_found(const char *line)
        char *growline;
        regmatch_t match_structs;
 
-       char buf[width];
+       char buf[width+1];
        const char *str = line;
        char *p = buf;
        size_t n;
@@ -807,7 +821,7 @@ void print_found(const char *line);
 
 static void print_ascii(const char *str)
 {
-       char buf[width];
+       char buf[width+1];
        char *p;
        size_t n;
 
@@ -991,18 +1005,17 @@ static void buffer_lineno(int lineno)
 static void open_file_and_read_lines(void)
 {
        if (filename) {
+               xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO);
 #if ENABLE_FEATURE_LESS_FLAGS
-               struct stat stbuf;
-
-               xstat(filename, &stbuf);
-               if (!S_ISREG(stbuf.st_mode))
-                       num_lines = READING_NONREG;
+               num_lines = REOPEN_AND_COUNT;
 #endif
-               xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO);
        } else {
                /* "less" with no arguments in argv[] */
                /* For status line only */
                filename = xstrdup(bb_msg_standard_input);
+#if ENABLE_FEATURE_LESS_FLAGS
+               num_lines = REOPEN_STDIN;
+#endif
        }
        readpos = 0;
        readeof = 0;
@@ -1026,9 +1039,6 @@ static void reinitialize(void)
        max_fline = -1;
        cur_fline = 0;
        max_lineno = 0;
-#if ENABLE_FEATURE_LESS_FLAGS
-       num_lines = filename ? READING_FILE : READING_STDIN;
-#endif
        open_file_and_read_lines();
 #if ENABLE_FEATURE_LESS_ASK_TERMINAL
        if (G.winsize_err)
@@ -1583,16 +1593,23 @@ static char opp_bracket(char bracket)
 
 static void match_right_bracket(char bracket)
 {
-       unsigned i;
+       unsigned i = cur_fline;
 
-       if (strchr(flines[cur_fline], bracket) == NULL) {
+       if (i >= max_fline
+        || strchr(flines[i], bracket) == NULL
+       ) {
                print_statusline("No bracket in top line");
                return;
        }
+
        bracket = opp_bracket(bracket);
-       for (i = cur_fline + 1; i < max_fline; i++) {
+       for (; i < max_fline; i++) {
                if (strchr(flines[i], bracket) != NULL) {
-                       buffer_line(i);
+                       /*
+                        * Line with matched right bracket becomes
+                        * last visible line
+                        */
+                       buffer_line(i - max_displayed_line);
                        return;
                }
        }
@@ -1601,16 +1618,22 @@ static void match_right_bracket(char bracket)
 
 static void match_left_bracket(char bracket)
 {
-       int i;
+       int i = cur_fline + max_displayed_line;
 
-       if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) {
+       if (i >= max_fline
+        || strchr(flines[i], bracket) == NULL
+       ) {
                print_statusline("No bracket in bottom line");
                return;
        }
 
        bracket = opp_bracket(bracket);
-       for (i = cur_fline + max_displayed_line; i >= 0; i--) {
+       for (; i >= 0; i--) {
                if (strchr(flines[i], bracket) != NULL) {
+                       /*
+                        * Line with matched left bracket becomes
+                        * first visible line
+                        */
                        buffer_line(i);
                        return;
                }
@@ -1778,9 +1801,10 @@ int less_main(int argc, char **argv)
        /* Some versions of less can survive w/o controlling tty,
         * try to do the same. This also allows to specify an alternative
         * tty via "less 1<>TTY".
-        * We don't try to use STDOUT_FILENO directly,
+        *
+        * We prefer not to use STDOUT_FILENO directly,
         * since we want to set this fd to non-blocking mode,
-        * and not bother with restoring it on exit.
+        * and not interfere with other processes which share stdout with us.
         */
        tty_name = xmalloc_ttyname(STDOUT_FILENO);
        if (tty_name) {
@@ -1792,10 +1816,12 @@ int less_main(int argc, char **argv)
                /* Try controlling tty */
  try_ctty:
                tty_fd = open(CURRENT_TTY, O_RDONLY);
-               if (tty_fd < 0)
-                       return bb_cat(argv);
+               if (tty_fd < 0) {
+                       /* If all else fails, less 481 uses stdout. Mimic that */
+                       tty_fd = STDOUT_FILENO;
+               }
        }
-       ndelay_on(tty_fd);
+       G.kbd_fd_orig_flags = ndelay_on(tty_fd);
        kbd_fd = tty_fd; /* save in a global */
 
        tcgetattr(kbd_fd, &term_orig);