Ignore:
Timestamp:
Nov 6, 2007, 11:01:53 AM (16 years ago)
Author:
Bruno Cornec
Message:
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/stable/mindi-busybox/miscutils/less.c

    r821 r1770  
    99
    1010/*
    11  *      This program needs a lot of development, so consider it in a beta stage
    12  *      at best.
     11 * TODO:
     12 * - Add more regular expression support - search modifiers, certain matches, etc.
     13 * - Add more complex bracket searching - currently, nested brackets are
     14 *   not considered.
     15 * - Add support for "F" as an input. This causes less to act in
     16 *   a similar way to tail -f.
     17 * - Allow horizontal scrolling.
    1318 *
    14  *      TODO:
    15  *      - Add more regular expression support - search modifiers, certain matches, etc.
    16  *      - Add more complex bracket searching - currently, nested brackets are
    17  *      not considered.
    18  *      - Add support for "F" as an input. This causes less to act in
    19  *      a similar way to tail -f.
    20  *      - Check for binary files, and prompt the user if a binary file
    21  *      is detected.
    22  *      - Allow horizontal scrolling. Currently, lines simply continue onto
    23  *      the next line, per the terminal's discretion
    24  *
    25  *      Notes:
    26  *      - filename is an array and not a pointer because that avoids all sorts
    27  *      of complications involving the fact that something that is pointed to
    28  *      will be changed if the pointer is changed.
    29  *      - the inp file pointer is used so that keyboard input works after
    30  *      redirected input has been read from stdin
    31 */
    32 
    33 #include "busybox.h"
    34 #include <stdio.h>
    35 #include <stdlib.h>
    36 #include <string.h>
    37 #include <termios.h>
    38 #include <unistd.h>
    39 #include <ctype.h>
    40 
    41 #ifdef CONFIG_FEATURE_LESS_REGEXP
     19 * Notes:
     20 * - the inp file pointer is used so that keyboard input works after
     21 *   redirected input has been read from stdin
     22 */
     23
     24#include <sched.h>  /* sched_yield() */
     25
     26#include "libbb.h"
     27#if ENABLE_FEATURE_LESS_REGEXP
    4228#include "xregex.h"
    4329#endif
    4430
    45 
    46 /* These are the escape sequences corresponding to special keys */
    47 #define REAL_KEY_UP 'A'
    48 #define REAL_KEY_DOWN 'B'
    49 #define REAL_KEY_RIGHT 'C'
    50 #define REAL_KEY_LEFT 'D'
    51 #define REAL_PAGE_UP '5'
    52 #define REAL_PAGE_DOWN '6'
    53 
    54 /* These are the special codes assigned by this program to the special keys */
    55 #define PAGE_UP 20
    56 #define PAGE_DOWN 21
    57 #define KEY_UP 22
    58 #define KEY_DOWN 23
    59 #define KEY_RIGHT 24
    60 #define KEY_LEFT 25
     31/* FIXME: currently doesn't work right */
     32#undef ENABLE_FEATURE_LESS_FLAGCS
     33#define ENABLE_FEATURE_LESS_FLAGCS 0
    6134
    6235/* The escape codes for highlighted and normal text */
    6336#define HIGHLIGHT "\033[7m"
    6437#define NORMAL "\033[0m"
    65 
    6638/* The escape code to clear the screen */
    6739#define CLEAR "\033[H\033[J"
    68 
    69 /* Maximum number of lines in a file */
    70 #define MAXLINES 10000
    71 
    72 static int height;
    73 static int width;
    74 static char **files;
    75 static char filename[256];
    76 static char **buffer;
    77 static char **flines;
    78 static int current_file = 1;
    79 static int line_pos;
    80 static int num_flines;
    81 static int num_files = 1;
    82 static int past_eof;
     40/* The escape code to clear to end of line */
     41#define CLEAR_2_EOL "\033[K"
     42
     43/* These are the escape sequences corresponding to special keys */
     44enum {
     45    REAL_KEY_UP = 'A',
     46    REAL_KEY_DOWN = 'B',
     47    REAL_KEY_RIGHT = 'C',
     48    REAL_KEY_LEFT = 'D',
     49    REAL_PAGE_UP = '5',
     50    REAL_PAGE_DOWN = '6',
     51    REAL_KEY_HOME = '7', // vt100? linux vt? or what?
     52    REAL_KEY_END = '8',
     53    REAL_KEY_HOME_ALT = '1', // ESC [1~ (vt100? linux vt? or what?)
     54    REAL_KEY_END_ALT = '4', // ESC [4~
     55    REAL_KEY_HOME_XTERM = 'H',
     56    REAL_KEY_END_XTERM = 'F',
     57
     58/* These are the special codes assigned by this program to the special keys */
     59    KEY_UP = 20,
     60    KEY_DOWN = 21,
     61    KEY_RIGHT = 22,
     62    KEY_LEFT = 23,
     63    PAGE_UP = 24,
     64    PAGE_DOWN = 25,
     65    KEY_HOME = 26,
     66    KEY_END = 27,
     67
     68/* Absolute max of lines eaten */
     69    MAXLINES = CONFIG_FEATURE_LESS_MAXLINES,
     70
     71/* This many "after the end" lines we will show (at max) */
     72    TILDES = 1,
     73};
    8374
    8475/* Command line options */
    85 static unsigned long flags;
    86 #define FLAG_E 1
    87 #define FLAG_M (1<<1)
    88 #define FLAG_m (1<<2)
    89 #define FLAG_N (1<<3)
    90 #define FLAG_TILDE (1<<4)
    91 
    92 /* This is needed so that program behaviour changes when input comes from
    93    stdin */
    94 static int inp_stdin;
    95 
    96 #ifdef CONFIG_FEATURE_LESS_MARKS
    97 static int mark_lines[15][2];
    98 static int num_marks;
    99 #endif
    100 
    101 #ifdef CONFIG_FEATURE_LESS_REGEXP
    102 static int match_found;
    103 static int *match_lines;
    104 static int match_pos;
    105 static int num_matches;
    106 static int match_backwards;
    107 static regex_t old_pattern;
    108 #endif
    109 
    110 /* Needed termios structures */
    111 static struct termios term_orig, term_vi;
    112 
    113 /* File pointer to get input from */
    114 static FILE *inp;
     76enum {
     77    FLAG_E = 1,
     78    FLAG_M = 1 << 1,
     79    FLAG_m = 1 << 2,
     80    FLAG_N = 1 << 3,
     81    FLAG_TILDE = 1 << 4,
     82/* hijack command line options variable for internal state vars */
     83    LESS_STATE_MATCH_BACKWARDS = 1 << 15,
     84};
     85
     86#if !ENABLE_FEATURE_LESS_REGEXP
     87enum { pattern_valid = 0 };
     88#endif
     89
     90struct globals {
     91    int cur_fline; /* signed */
     92    int kbd_fd;  /* fd to get input from */
     93/* last position in last line, taking into account tabs */
     94    size_t linepos;
     95    unsigned max_displayed_line;
     96    unsigned max_fline;
     97    unsigned max_lineno; /* this one tracks linewrap */
     98    unsigned width;
     99    ssize_t eof_error; /* eof if 0, error if < 0 */
     100    size_t readpos;
     101    size_t readeof;
     102    const char **buffer;
     103    const char **flines;
     104    const char *empty_line_marker;
     105    unsigned num_files;
     106    unsigned current_file;
     107    char *filename;
     108    char **files;
     109#if ENABLE_FEATURE_LESS_MARKS
     110    unsigned num_marks;
     111    unsigned mark_lines[15][2];
     112#endif
     113#if ENABLE_FEATURE_LESS_REGEXP
     114    unsigned *match_lines;
     115    int match_pos; /* signed! */
     116    unsigned num_matches;
     117    regex_t pattern;
     118    smallint pattern_valid;
     119#endif
     120    smallint terminated;
     121    struct termios term_orig, term_less;
     122};
     123#define G (*ptr_to_globals)
     124#define cur_fline           (G.cur_fline         )
     125#define kbd_fd              (G.kbd_fd            )
     126#define linepos             (G.linepos           )
     127#define max_displayed_line  (G.max_displayed_line)
     128#define max_fline           (G.max_fline         )
     129#define max_lineno          (G.max_lineno        )
     130#define width               (G.width             )
     131#define eof_error           (G.eof_error         )
     132#define readpos             (G.readpos           )
     133#define readeof             (G.readeof           )
     134#define buffer              (G.buffer            )
     135#define flines              (G.flines            )
     136#define empty_line_marker   (G.empty_line_marker )
     137#define num_files           (G.num_files         )
     138#define current_file        (G.current_file      )
     139#define filename            (G.filename          )
     140#define files               (G.files             )
     141#define num_marks           (G.num_marks         )
     142#define mark_lines          (G.mark_lines        )
     143#if ENABLE_FEATURE_LESS_REGEXP
     144#define match_lines         (G.match_lines       )
     145#define match_pos           (G.match_pos         )
     146#define num_matches         (G.num_matches       )
     147#define pattern             (G.pattern           )
     148#define pattern_valid       (G.pattern_valid     )
     149#endif
     150#define terminated          (G.terminated        )
     151#define term_orig           (G.term_orig         )
     152#define term_less           (G.term_less         )
     153#define INIT_G() do { \
     154        PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
     155        empty_line_marker = "~"; \
     156        num_files = 1; \
     157        current_file = 1; \
     158        eof_error = 1; \
     159        terminated = 1; \
     160    } while (0)
    115161
    116162/* Reset terminal input to normal */
     
    118164{
    119165    fflush(stdout);
    120     tcsetattr(fileno(inp), TCSANOW, &term_orig);
    121 }
    122 
    123 /* Set terminal input to raw mode  (taken from vi.c) */
    124 static void set_tty_raw(void)
    125 {
    126     tcsetattr(fileno(inp), TCSANOW, &term_vi);
     166    tcsetattr(kbd_fd, TCSANOW, &term_orig);
    127167}
    128168
    129169/* Exit the program gracefully */
    130 static void tless_exit(int code)
     170static void less_exit(int code)
    131171{
    132172    /* TODO: We really should save the terminal state when we start,
    133          and restore it when we exit. Less does this with the
    134          "ti" and "te" termcap commands; can this be done with
    135          only termios.h? */
    136 
     173     * and restore it when we exit. Less does this with the
     174     * "ti" and "te" termcap commands; can this be done with
     175     * only termios.h? */
    137176    putchar('\n');
    138     exit(code);
    139 }
    140 
    141 /* Grab a character from input without requiring the return key. If the
    142    character is ASCII \033, get more characters and assign certain sequences
    143    special return codes. Note that this function works best with raw input. */
    144 static int tless_getch(void)
    145 {
    146     int input;
    147 
    148     set_tty_raw();
    149 
    150     input = getc(inp);
    151     /* Detect escape sequences (i.e. arrow keys) and handle
    152        them accordingly */
    153 
    154     if (input == '\033' && getc(inp) == '[') {
    155         input = getc(inp);
    156         set_tty_cooked();
    157         if (input == REAL_KEY_UP)
    158             return KEY_UP;
    159         else if (input == REAL_KEY_DOWN)
    160             return KEY_DOWN;
    161         else if (input == REAL_KEY_RIGHT)
    162             return KEY_RIGHT;
    163         else if (input == REAL_KEY_LEFT)
    164             return KEY_LEFT;
    165         else if (input == REAL_PAGE_UP)
    166             return PAGE_UP;
    167         else if (input == REAL_PAGE_DOWN)
    168             return PAGE_DOWN;
    169     }
    170     /* The input is a normal ASCII value */
    171     else {
    172         set_tty_cooked();
    173         return input;
    174     }
    175     return 0;
     177    fflush_stdout_and_exit(code);
    176178}
    177179
    178180/* Move the cursor to a position (x,y), where (0,0) is the
    179181   top-left corner of the console */
    180 static void move_cursor(int x, int y)
    181 {
    182     printf("\033[%i;%iH", x, y);
     182static void move_cursor(int line, int row)
     183{
     184    printf("\033[%u;%uH", line, row);
    183185}
    184186
    185187static void clear_line(void)
    186188{
    187     move_cursor(height, 0);
    188     printf("\033[K");
    189 }
    190 
    191 /* This adds line numbers to every line, as the -N flag necessitates */
    192 static void add_linenumbers(void)
    193 {
    194     char current_line[256];
    195     int i;
    196 
    197     for (i = 0; i <= num_flines; i++) {
    198         safe_strncpy(current_line, flines[i], 256);
    199         flines[i] = bb_xasprintf("%5d %s", i + 1, current_line);
    200     }
    201 }
    202 
    203 static void data_readlines(void)
    204 {
    205     int i;
    206     char current_line[256];
    207     FILE *fp;
    208 
    209     fp = (inp_stdin) ? stdin : bb_xfopen(filename, "r");
    210     flines = NULL;
    211     for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) {
    212         strcpy(current_line, "");
    213         fgets(current_line, 256, fp);
    214         if (fp != stdin)
    215             bb_xferror(fp, filename);
    216         flines = xrealloc(flines, (i+1) * sizeof(char *));
    217         flines[i] = bb_xstrdup(current_line);
    218     }
    219     num_flines = i - 2;
    220 
    221     /* Reset variables for a new file */
    222 
    223     line_pos = 0;
    224     past_eof = 0;
    225 
    226     fclose(fp);
    227 
    228     if (inp == NULL)
    229         inp = (inp_stdin) ? bb_xfopen(CURRENT_TTY, "r") : stdin;
    230 
    231     if (flags & FLAG_N)
    232         add_linenumbers();
    233 }
    234 
    235 #ifdef CONFIG_FEATURE_LESS_FLAGS
    236 
    237 /* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
     189    printf("\033[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);
     190}
     191
     192static void print_hilite(const char *str)
     193{
     194    printf(HIGHLIGHT"%s"NORMAL, str);
     195}
     196
     197static void print_statusline(const char *str)
     198{
     199    clear_line();
     200    printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str);
     201}
     202
     203#if ENABLE_FEATURE_LESS_REGEXP
     204static void fill_match_lines(unsigned pos);
     205#else
     206#define fill_match_lines(pos) ((void)0)
     207#endif
     208
     209/* Devilishly complex routine.
     210 *
     211 * Has to deal with EOF and EPIPE on input,
     212 * with line wrapping, with last line not ending in '\n'
     213 * (possibly not ending YET!), with backspace and tabs.
     214 * It reads input again if last time we got an EOF (thus supporting
     215 * growing files) or EPIPE (watching output of slow process like make).
     216 *
     217 * Variables used:
     218 * flines[] - array of lines already read. Linewrap may cause
     219 *      one source file line to occupy several flines[n].
     220 * flines[max_fline] - last line, possibly incomplete.
     221 * terminated - 1 if flines[max_fline] is 'terminated'
     222 *      (if there was '\n' [which isn't stored itself, we just remember
     223 *      that it was seen])
     224 * max_lineno - last line's number, this one doesn't increment
     225 *      on line wrap, only on "real" new lines.
     226 * readbuf[0..readeof-1] - small preliminary buffer.
     227 * readbuf[readpos] - next character to add to current line.
     228 * linepos - screen line position of next char to be read
     229 *      (takes into account tabs and backspaces)
     230 * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error
     231 */
     232static void read_lines(void)
     233{
     234#define readbuf bb_common_bufsiz1
     235    char *current_line, *p;
     236    USE_FEATURE_LESS_REGEXP(unsigned old_max_fline = max_fline;)
     237    int w = width;
     238    char last_terminated = terminated;
     239
     240    if (option_mask32 & FLAG_N)
     241        w -= 8;
     242
     243    current_line = xmalloc(w);
     244    p = current_line;
     245    max_fline += last_terminated;
     246    if (!last_terminated) {
     247        const char *cp = flines[max_fline];
     248        if (option_mask32 & FLAG_N)
     249            cp += 8;
     250        strcpy(current_line, cp);
     251        p += strlen(current_line);
     252        /* linepos is still valid from previous read_lines() */
     253    } else {
     254        linepos = 0;
     255    }
     256
     257    while (1) {
     258 again:
     259        *p = '\0';
     260        terminated = 0;
     261        while (1) {
     262            char c;
     263            /* if no unprocessed chars left, eat more */
     264            if (readpos >= readeof) {
     265                smallint yielded = 0;
     266
     267                ndelay_on(0);
     268 read_again:
     269                eof_error = safe_read(0, readbuf, sizeof(readbuf));
     270                readpos = 0;
     271                readeof = eof_error;
     272                if (eof_error < 0) {
     273                    if (errno == EAGAIN && !yielded) {
     274            /* We can hit EAGAIN while searching for regexp match.
     275             * Yield is not 100% reliable solution in general,
     276             * but for less it should be good enough -
     277             * we give stdin supplier some CPU time to produce
     278             * more input. We do it just once.
     279             * Currently, we do not stop when we found the Nth
     280             * occurrence we were looking for. We read till end
     281             * (or double EAGAIN). TODO? */
     282                        sched_yield();
     283                        yielded = 1;
     284                        goto read_again;
     285                    }
     286                    readeof = 0;
     287                    if (errno != EAGAIN)
     288                        print_statusline("read error");
     289                }
     290                ndelay_off(0);
     291
     292                if (eof_error <= 0) {
     293                    goto reached_eof;
     294                }
     295            }
     296            c = readbuf[readpos];
     297            /* backspace? [needed for manpages] */
     298            /* <tab><bs> is (a) insane and */
     299            /* (b) harder to do correctly, so we refuse to do it */
     300            if (c == '\x8' && linepos && p[-1] != '\t') {
     301                readpos++; /* eat it */
     302                linepos--;
     303            /* was buggy (p could end up <= current_line)... */
     304                *--p = '\0';
     305                continue;
     306            }
     307            {
     308                size_t new_linepos = linepos + 1;
     309                if (c == '\t') {
     310                    new_linepos += 7;
     311                    new_linepos &= (~7);
     312                }
     313                if (new_linepos >= w)
     314                    break;
     315                linepos = new_linepos;
     316            }
     317            /* ok, we will eat this char */
     318            readpos++;
     319            if (c == '\n') {
     320                terminated = 1;
     321                linepos = 0;
     322                break;
     323            }
     324            /* NUL is substituted by '\n'! */
     325            if (c == '\0') c = '\n';
     326            *p++ = c;
     327            *p = '\0';
     328        }
     329        /* Corner case: linewrap with only "" wrapping to next line */
     330        /* Looks ugly on screen, so we do not store this empty line */
     331        if (!last_terminated && !current_line[0]) {
     332            last_terminated = 1;
     333            max_lineno++;
     334            goto again;
     335        }
     336 reached_eof:
     337        last_terminated = terminated;
     338        flines = xrealloc(flines, (max_fline+1) * sizeof(char *));
     339        if (option_mask32 & FLAG_N) {
     340            /* Width of 7 preserves tab spacing in the text */
     341            flines[max_fline] = xasprintf(
     342                (max_lineno <= 9999999) ? "%7u %s" : "%07u %s",
     343                max_lineno % 10000000, current_line);
     344            free(current_line);
     345            if (terminated)
     346                max_lineno++;
     347        } else {
     348            flines[max_fline] = xrealloc(current_line, strlen(current_line)+1);
     349        }
     350        if (max_fline >= MAXLINES) {
     351            eof_error = 0; /* Pretend we saw EOF */
     352            break;
     353        }
     354        if (max_fline > cur_fline + max_displayed_line)
     355            break;
     356        if (eof_error <= 0) {
     357            if (eof_error < 0 && errno == EAGAIN) {
     358                /* not yet eof or error, reset flag (or else
     359                 * we will hog CPU - select() will return
     360                 * immediately */
     361                eof_error = 1;
     362            }
     363            break;
     364        }
     365        max_fline++;
     366        current_line = xmalloc(w);
     367        p = current_line;
     368        linepos = 0;
     369    }
     370    fill_match_lines(old_max_fline);
     371#undef readbuf
     372}
     373
     374#if ENABLE_FEATURE_LESS_FLAGS
     375/* Interestingly, writing calc_percent as a function saves around 32 bytes
    238376 * on my build. */
    239377static int calc_percent(void)
    240378{
    241     return ((100 * (line_pos + height - 2) / num_flines) + 1);
     379    unsigned p = (100 * (cur_fline+max_displayed_line+1) + max_fline/2) / (max_fline+1);
     380    return p <= 100 ? p : 100;
    242381}
    243382
     
    247386    int percentage;
    248387
    249     if (!past_eof) {
    250         if (!line_pos) {
    251             if (num_files > 1)
    252                 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);
    253             else {
    254                 printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
    255             }
    256         }
    257         else {
    258             printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
    259         }
    260 
    261         if (line_pos == num_flines - height + 2) {
    262             printf("(END) %s", NORMAL);
    263             if ((num_files > 1) && (current_file != num_files))
    264                 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
    265         }
    266         else {
    267             percentage = calc_percent();
    268             printf("%i%% %s", percentage, NORMAL);
    269         }
    270     }
    271     else {
    272         printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
    273         if ((num_files > 1) && (current_file != num_files))
    274             printf("- Next: %s", files[current_file]);
    275         printf("%s", NORMAL);
    276     }
    277 }
    278 
    279 /* Print a status line if -m was specified */
    280 static void medium_status_print(void)
    281 {
    282     int percentage;
     388    clear_line();
     389    printf(HIGHLIGHT"%s", filename);
     390    if (num_files > 1)
     391        printf(" (file %i of %i)", current_file, num_files);
     392    printf(" lines %i-%i/%i ",
     393            cur_fline + 1, cur_fline + max_displayed_line + 1,
     394            max_fline + 1);
     395    if (cur_fline >= max_fline - max_displayed_line) {
     396        printf("(END)"NORMAL);
     397        if (num_files > 1 && current_file != num_files)
     398            printf(HIGHLIGHT" - next: %s"NORMAL, files[current_file]);
     399        return;
     400    }
    283401    percentage = calc_percent();
    284 
    285     if (!line_pos)
    286         printf("%s%s %i%%%s", HIGHLIGHT, filename, percentage, NORMAL);
    287     else if (line_pos == num_flines - height + 2)
    288         printf("%s(END)%s", HIGHLIGHT, NORMAL);
    289     else
    290         printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
     402    printf("%i%%"NORMAL, percentage);
    291403}
    292404#endif
     
    295407static void status_print(void)
    296408{
     409    const char *p;
     410
    297411    /* Change the status if flags have been set */
    298 #ifdef CONFIG_FEATURE_LESS_FLAGS
    299     if (flags & FLAG_M)
     412#if ENABLE_FEATURE_LESS_FLAGS
     413    if (option_mask32 & (FLAG_M|FLAG_m)) {
    300414        m_status_print();
    301     else if (flags & FLAG_m)
    302         medium_status_print();
     415        return;
     416    }
    303417    /* No flags set */
    304     else {
    305 #endif
    306         if (!line_pos) {
    307             printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
    308             if (num_files > 1)
    309                 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
     418#endif
     419
     420    clear_line();
     421    if (cur_fline && cur_fline < max_fline - max_displayed_line) {
     422        putchar(':');
     423        return;
     424    }
     425    p = "(END)";
     426    if (!cur_fline)
     427        p = filename;
     428    if (num_files > 1) {
     429        printf(HIGHLIGHT"%s (file %i of %i)"NORMAL,
     430                p, current_file, num_files);
     431        return;
     432    }
     433    print_hilite(p);
     434}
     435
     436static void cap_cur_fline(int nlines)
     437{
     438    int diff;
     439    if (cur_fline < 0)
     440        cur_fline = 0;
     441    if (cur_fline + max_displayed_line > max_fline + TILDES) {
     442        cur_fline -= nlines;
     443        if (cur_fline < 0)
     444            cur_fline = 0;
     445        diff = max_fline - (cur_fline + max_displayed_line) + TILDES;
     446        /* As the number of lines requested was too large, we just move
     447        to the end of the file */
     448        if (diff > 0)
     449            cur_fline += diff;
     450    }
     451}
     452
     453static const char controls[] ALIGN1 =
     454    /* NUL: never encountered; TAB: not converted */
     455    /**/"\x01\x02\x03\x04\x05\x06\x07\x08"  "\x0a\x0b\x0c\x0d\x0e\x0f"
     456    "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
     457    "\x7f\x9b"; /* DEL and infamous Meta-ESC :( */
     458static const char ctrlconv[] ALIGN1 =
     459    /* '\n': it's a former NUL - subst with '@', not 'J' */
     460    "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f"
     461    "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f";
     462
     463#if ENABLE_FEATURE_LESS_REGEXP
     464static void print_found(const char *line)
     465{
     466    int match_status;
     467    int eflags;
     468    char *growline;
     469    regmatch_t match_structs;
     470
     471    char buf[width];
     472    const char *str = line;
     473    char *p = buf;
     474    size_t n;
     475
     476    while (*str) {
     477        n = strcspn(str, controls);
     478        if (n) {
     479            if (!str[n]) break;
     480            memcpy(p, str, n);
     481            p += n;
     482            str += n;
    310483        }
    311         else if (line_pos == num_flines - height + 2) {
    312             printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
    313             if ((num_files > 1) && (current_file != num_files))
    314                 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
     484        n = strspn(str, controls);
     485        memset(p, '.', n);
     486        p += n;
     487        str += n;
     488    }
     489    strcpy(p, str);
     490
     491    /* buf[] holds quarantined version of str */
     492
     493    /* Each part of the line that matches has the HIGHLIGHT
     494       and NORMAL escape sequences placed around it.
     495       NB: we regex against line, but insert text
     496       from quarantined copy (buf[]) */
     497    str = buf;
     498    growline = NULL;
     499    eflags = 0;
     500    goto start;
     501
     502    while (match_status == 0) {
     503        char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL,
     504                growline ? : "",
     505                match_structs.rm_so, str,
     506                match_structs.rm_eo - match_structs.rm_so,
     507                        str + match_structs.rm_so);
     508        free(growline); growline = new;
     509        str += match_structs.rm_eo;
     510        line += match_structs.rm_eo;
     511        eflags = REG_NOTBOL;
     512 start:
     513        /* Most of the time doesn't find the regex, optimize for that */
     514        match_status = regexec(&pattern, line, 1, &match_structs, eflags);
     515    }
     516
     517    if (!growline) {
     518        printf(CLEAR_2_EOL"%s\n", str);
     519        return;
     520    }
     521    printf(CLEAR_2_EOL"%s%s\n", growline, str);
     522    free(growline);
     523}
     524#else
     525void print_found(const char *line);
     526#endif
     527
     528static void print_ascii(const char *str)
     529{
     530    char buf[width];
     531    char *p;
     532    size_t n;
     533
     534    printf(CLEAR_2_EOL);
     535    while (*str) {
     536        n = strcspn(str, controls);
     537        if (n) {
     538            if (!str[n]) break;
     539            printf("%.*s", (int) n, str);
     540            str += n;
    315541        }
    316         else {
    317             putchar(':');
    318         }
    319 #ifdef CONFIG_FEATURE_LESS_FLAGS
    320     }
    321 #endif
     542        n = strspn(str, controls);
     543        p = buf;
     544        do {
     545            if (*str == 0x7f)
     546                *p++ = '?';
     547            else if (*str == (char)0x9b)
     548            /* VT100's CSI, aka Meta-ESC. Who's inventor? */
     549            /* I want to know who committed this sin */
     550                *p++ = '{';
     551            else
     552                *p++ = ctrlconv[(unsigned char)*str];
     553            str++;
     554        } while (--n);
     555        *p = '\0';
     556        print_hilite(buf);
     557    }
     558    puts(str);
    322559}
    323560
     
    327564    int i;
    328565
    329     printf("%s", CLEAR);
    330     if (num_flines >= height - 2) {
    331         for (i = 0; i < height - 1; i++)
    332             printf("%s", buffer[i]);
    333     }
    334     else {
    335         for (i = 1; i < (height - 1 - num_flines); i++)
    336             putchar('\n');
    337         for (i = 0; i < height - 1; i++)
    338             printf("%s", buffer[i]);
    339     }
    340 
     566    move_cursor(0, 0);
     567    for (i = 0; i <= max_displayed_line; i++)
     568        if (pattern_valid)
     569            print_found(buffer[i]);
     570        else
     571            print_ascii(buffer[i]);
    341572    status_print();
    342573}
    343574
    344 /* Initialise the buffer */
    345 static void buffer_init(void)
     575static void buffer_fill_and_print(void)
    346576{
    347577    int i;
    348 
    349     if (buffer == NULL) {
    350         /* malloc the number of lines needed for the buffer */
    351         buffer = xrealloc(buffer, height * sizeof(char *));
    352     } else {
    353         for (i = 0; i < (height - 1); i++)
    354             free(buffer[i]);
    355     }
    356 
    357     /* Fill the buffer until the end of the file or the
    358        end of the buffer is reached */
    359     for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
    360         buffer[i] = bb_xstrdup(flines[i]);
    361     }
    362 
    363     /* If the buffer still isn't full, fill it with blank lines */
    364     for (; i < (height - 1); i++) {
    365         buffer[i] = bb_xstrdup("");
    366     }
     578    for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) {
     579        buffer[i] = flines[cur_fline + i];
     580    }
     581    for (; i <= max_displayed_line; i++) {
     582        buffer[i] = empty_line_marker;
     583    }
     584    buffer_print();
    367585}
    368586
     
    370588static void buffer_down(int nlines)
    371589{
     590    cur_fline += nlines;
     591    read_lines();
     592    cap_cur_fline(nlines);
     593    buffer_fill_and_print();
     594}
     595
     596static void buffer_up(int nlines)
     597{
     598    cur_fline -= nlines;
     599    if (cur_fline < 0) cur_fline = 0;
     600    read_lines();
     601    buffer_fill_and_print();
     602}
     603
     604static void buffer_line(int linenum)
     605{
     606    if (linenum < 0)
     607        linenum = 0;
     608    cur_fline = linenum;
     609    read_lines();
     610    if (linenum + max_displayed_line > max_fline)
     611        linenum = max_fline - max_displayed_line + TILDES;
     612    if (linenum < 0)
     613        linenum = 0;
     614    cur_fline = linenum;
     615    buffer_fill_and_print();
     616}
     617
     618static void open_file_and_read_lines(void)
     619{
     620    if (filename) {
     621        int fd = xopen(filename, O_RDONLY);
     622        dup2(fd, 0);
     623        if (fd) close(fd);
     624    } else {
     625        /* "less" with no arguments in argv[] */
     626        /* For status line only */
     627        filename = xstrdup(bb_msg_standard_input);
     628    }
     629    readpos = 0;
     630    readeof = 0;
     631    linepos = 0;
     632    terminated = 1;
     633    read_lines();
     634}
     635
     636/* Reinitialize everything for a new file - free the memory and start over */
     637static void reinitialize(void)
     638{
    372639    int i;
    373640
    374     if (!past_eof) {
    375         if (line_pos + (height - 3) + nlines < num_flines) {
    376             line_pos += nlines;
    377             for (i = 0; i < (height - 1); i++) {
    378                 free(buffer[i]);
    379                 buffer[i] = bb_xstrdup(flines[line_pos + i]);
    380             }
     641    if (flines) {
     642        for (i = 0; i <= max_fline; i++)
     643            free((void*)(flines[i]));
     644        free(flines);
     645        flines = NULL;
     646    }
     647
     648    max_fline = -1;
     649    cur_fline = 0;
     650    max_lineno = 0;
     651    open_file_and_read_lines();
     652    buffer_fill_and_print();
     653}
     654
     655static void getch_nowait(char* input, int sz)
     656{
     657    ssize_t rd;
     658    fd_set readfds;
     659 again:
     660    fflush(stdout);
     661
     662    /* NB: select returns whenever read will not block. Therefore:
     663     * (a) with O_NONBLOCK'ed fds select will return immediately
     664     * (b) if eof is reached, select will also return
     665     *     because read will immediately return 0 bytes.
     666     * Even if select says that input is available, read CAN block
     667     * (switch fd into O_NONBLOCK'ed mode to avoid it)
     668     */
     669    FD_ZERO(&readfds);
     670    if (max_fline <= cur_fline + max_displayed_line
     671     && eof_error > 0 /* did NOT reach eof yet */
     672    ) {
     673        /* We are interested in stdin */
     674        FD_SET(0, &readfds);
     675    }
     676    FD_SET(kbd_fd, &readfds);
     677    tcsetattr(kbd_fd, TCSANOW, &term_less);
     678    select(kbd_fd + 1, &readfds, NULL, NULL, NULL);
     679
     680    input[0] = '\0';
     681    ndelay_on(kbd_fd);
     682    rd = read(kbd_fd, input, sz);
     683    ndelay_off(kbd_fd);
     684    if (rd < 0) {
     685        /* No keyboard input, but we have input on stdin! */
     686        if (errno != EAGAIN) /* Huh?? */
     687            return;
     688        read_lines();
     689        buffer_fill_and_print();
     690        goto again;
     691    }
     692}
     693
     694/* Grab a character from input without requiring the return key. If the
     695 * character is ASCII \033, get more characters and assign certain sequences
     696 * special return codes. Note that this function works best with raw input. */
     697static int less_getch(void)
     698{
     699    char input[16];
     700    unsigned i;
     701 again:
     702    memset(input, 0, sizeof(input));
     703    getch_nowait(input, sizeof(input));
     704
     705    /* Detect escape sequences (i.e. arrow keys) and handle
     706     * them accordingly */
     707    if (input[0] == '\033' && input[1] == '[') {
     708        set_tty_cooked();
     709        i = input[2] - REAL_KEY_UP;
     710        if (i < 4)
     711            return 20 + i;
     712        i = input[2] - REAL_PAGE_UP;
     713        if (i < 4)
     714            return 24 + i;
     715        if (input[2] == REAL_KEY_HOME_XTERM)
     716            return KEY_HOME;
     717        if (input[2] == REAL_KEY_HOME_ALT)
     718            return KEY_HOME;
     719        if (input[2] == REAL_KEY_END_XTERM)
     720            return KEY_END;
     721        if (input[2] == REAL_KEY_END_ALT)
     722            return KEY_END;
     723        return 0;
     724    }
     725    /* Reject almost all control chars */
     726    i = input[0];
     727    if (i < ' ' && i != 0x0d && i != 8) goto again;
     728    set_tty_cooked();
     729    return i;
     730}
     731
     732static char* less_gets(int sz)
     733{
     734    char c;
     735    int i = 0;
     736    char *result = xzalloc(1);
     737    while (1) {
     738        fflush(stdout);
     739
     740        /* I be damned if I know why is it needed *repeatedly*,
     741         * but it is needed. Is it because of stdio? */
     742        tcsetattr(kbd_fd, TCSANOW, &term_less);
     743
     744        c = '\0';
     745        read(kbd_fd, &c, 1);
     746        if (c == 0x0d)
     747            return result;
     748        if (c == 0x7f)
     749            c = 8;
     750        if (c == 8 && i) {
     751            printf("\x8 \x8");
     752            i--;
    381753        }
    382         else {
    383             /* As the number of lines requested was too large, we just move
    384             to the end of the file */
    385             while (line_pos + (height - 3) + 1 < num_flines) {
    386                 line_pos += 1;
    387                 for (i = 0; i < (height - 1); i++) {
    388                     free(buffer[i]);
    389                     buffer[i] = bb_xstrdup(flines[line_pos + i]);
    390                 }
    391             }
    392         }
    393 
    394         /* We exit if the -E flag has been set */
    395         if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
    396             tless_exit(0);
    397     }
    398 }
    399 
    400 static void buffer_up(int nlines)
    401 {
    402     int i;
    403     int tilde_line;
    404 
    405     if (!past_eof) {
    406         if (line_pos - nlines >= 0) {
    407             line_pos -= nlines;
    408             for (i = 0; i < (height - 1); i++) {
    409                 free(buffer[i]);
    410                 buffer[i] = bb_xstrdup(flines[line_pos + i]);
    411             }
    412         }
    413         else {
    414         /* As the requested number of lines to move was too large, we
    415            move one line up at a time until we can't. */
    416             while (line_pos != 0) {
    417                 line_pos -= 1;
    418                 for (i = 0; i < (height - 1); i++) {
    419                     free(buffer[i]);
    420                     buffer[i] = bb_xstrdup(flines[line_pos + i]);
    421                 }
    422             }
    423         }
    424     }
    425     else {
    426         /* Work out where the tildes start */
    427         tilde_line = num_flines - line_pos + 3;
    428 
    429         line_pos -= nlines;
    430         /* Going backwards nlines lines has taken us to a point where
    431            nothing is past the EOF, so we revert to normal. */
    432         if (line_pos < num_flines - height + 3) {
    433             past_eof = 0;
    434             buffer_up(nlines);
    435         }
    436         else {
    437             /* We only move part of the buffer, as the rest
    438             is past the EOF */
    439             for (i = 0; i < (height - 1); i++) {
    440                 free(buffer[i]);
    441                 if (i < tilde_line - nlines + 1)
    442                     buffer[i] = bb_xstrdup(flines[line_pos + i]);
    443                 else {
    444                     if (line_pos >= num_flines - height + 2)
    445                         buffer[i] = bb_xstrdup("~\n");
    446                 }
    447             }
    448         }
    449     }
    450 }
    451 
    452 static void buffer_line(int linenum)
    453 {
    454     int i;
    455     past_eof = 0;
    456 
    457     if (linenum < 0 || linenum > num_flines) {
    458         clear_line();
    459         printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum + 1, NORMAL);
    460     }
    461     else if (linenum < (num_flines - height - 2)) {
    462         for (i = 0; i < (height - 1); i++) {
    463             free(buffer[i]);
    464             buffer[i] = bb_xstrdup(flines[linenum + i]);
    465         }
    466         line_pos = linenum;
    467         buffer_print();
    468     }
    469     else {
    470         for (i = 0; i < (height - 1); i++) {
    471             free(buffer[i]);
    472             if (linenum + i < num_flines + 2)
    473                 buffer[i] = bb_xstrdup(flines[linenum + i]);
    474             else
    475                 buffer[i] = bb_xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
    476         }
    477         line_pos = linenum;
    478         /* Set past_eof so buffer_down and buffer_up act differently */
    479         past_eof = 1;
    480         buffer_print();
    481     }
    482 }
    483 
    484 /* Reinitialise everything for a new file - free the memory and start over */
    485 static void reinitialise(void)
    486 {
    487     int i;
    488 
    489     for (i = 0; i <= num_flines; i++)
    490         free(flines[i]);
    491     free(flines);
    492 
    493     data_readlines();
    494     buffer_init();
    495     buffer_print();
     754        if (c < ' ')
     755            continue;
     756        if (i >= width - sz - 1)
     757            continue; /* len limit */
     758        putchar(c);
     759        result[i++] = c;
     760        result = xrealloc(result, i+1);
     761        result[i] = '\0';
     762    }
    496763}
    497764
    498765static void examine_file(void)
    499766{
    500     int newline_offset;
    501 
    502     clear_line();
    503     printf("Examine: ");
    504     fgets(filename, 256, inp);
    505 
    506     /* As fgets adds a newline to the end of an input string, we
    507        need to remove it */
    508     newline_offset = strlen(filename) - 1;
    509     filename[newline_offset] = '\0';
    510 
    511     files[num_files] = bb_xstrdup(filename);
     767    print_statusline("Examine: ");
     768    free(filename);
     769    filename = less_gets(sizeof("Examine: ")-1);
     770    /* files start by = argv. why we assume that argv is infinitely long??
     771    files[num_files] = filename;
    512772    current_file = num_files + 1;
    513     num_files++;
    514 
    515     inp_stdin = 0;
    516     reinitialise();
     773    num_files++; */
     774    files[0] = filename;
     775    num_files = current_file = 1;
     776    reinitialize();
    517777}
    518778
     
    520780 * -1: go back one file
    521781 *  0: go to the first file
    522  *  1: go forward one file
    523 */
     782 *  1: go forward one file */
    524783static void change_file(int direction)
    525784{
    526785    if (current_file != ((direction > 0) ? num_files : 1)) {
    527786        current_file = direction ? current_file + direction : 1;
    528         strcpy(filename, files[current_file - 1]);
    529         reinitialise();
    530     }
    531     else {
    532         clear_line();
    533         printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
     787        free(filename);
     788        filename = xstrdup(files[current_file - 1]);
     789        reinitialize();
     790    } else {
     791        print_statusline(direction > 0 ? "No next file" : "No previous file");
    534792    }
    535793}
     
    538796{
    539797    int i;
     798
     799    if (num_files < 2)
     800        return;
    540801
    541802    if (current_file != 1) {
     
    544805            files[i - 2] = files[i - 1];
    545806        num_files--;
    546         buffer_print();
    547     }
    548     else {
     807    } else {
    549808        change_file(1);
    550809        for (i = 2; i <= num_files; i++)
     
    552811        num_files--;
    553812        current_file--;
    554         buffer_print();
    555813    }
    556814}
     
    561819
    562820    /* Clear the current line and print a prompt */
    563     clear_line();
    564     printf(" :");
    565 
    566     keypress = tless_getch();
     821    print_statusline(" :");
     822
     823    keypress = less_getch();
    567824    switch (keypress) {
    568         case 'd':
    569             remove_current_file();
    570             break;
    571         case 'e':
    572             examine_file();
    573             break;
    574 #ifdef CONFIG_FEATURE_LESS_FLAGS
    575         case 'f':
    576             clear_line();
    577             m_status_print();
    578             break;
    579 #endif
    580         case 'n':
    581             change_file(1);
    582             break;
    583         case 'p':
    584             change_file(-1);
    585             break;
    586         case 'q':
    587             tless_exit(0);
    588             break;
    589         case 'x':
    590             change_file(0);
    591             break;
    592         default:
    593             break;
    594     }
    595 }
    596 
    597 #ifdef CONFIG_FEATURE_LESS_REGEXP
    598 /* The below two regular expression handler functions NEED development. */
    599 
    600 /* Get a regular expression from the user, and then go through the current
    601    file line by line, running a processing regex function on each one. */
    602 
    603 static char *process_regex_on_line(char *line, regex_t *pattern, int action)
    604 {
    605     /* This function takes the regex and applies it to the line.
    606        Each part of the line that matches has the HIGHLIGHT
    607        and NORMAL escape sequences placed around it by
    608        insert_highlights if action = 1, or has the escape sequences
    609        removed if action = 0, and then the line is returned. */
    610     int match_status;
    611     char *line2 = (char *) xmalloc((sizeof(char) * (strlen(line) + 1)) + 64);
    612     char *growline = "";
    613     regmatch_t match_structs;
    614 
    615     line2 = bb_xstrdup(line);
    616 
    617     match_found = 0;
    618     match_status = regexec(pattern, line2, 1, &match_structs, 0);
    619    
    620     while (match_status == 0) {
    621         if (match_found == 0)
    622             match_found = 1;
    623        
    624         if (action) {
    625             growline = bb_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);
     825    case 'd':
     826        remove_current_file();
     827        break;
     828    case 'e':
     829        examine_file();
     830        break;
     831#if ENABLE_FEATURE_LESS_FLAGS
     832    case 'f':
     833        m_status_print();
     834        break;
     835#endif
     836    case 'n':
     837        change_file(1);
     838        break;
     839    case 'p':
     840        change_file(-1);
     841        break;
     842    case 'q':
     843        less_exit(0);
     844        break;
     845    case 'x':
     846        change_file(0);
     847        break;
     848    }
     849}
     850
     851#if ENABLE_FEATURE_LESS_REGEXP
     852static void normalize_match_pos(int match)
     853{
     854    if (match >= num_matches)
     855        match = num_matches - 1;
     856    if (match < 0)
     857        match = 0;
     858    match_pos = match;
     859}
     860
     861static void goto_match(int match)
     862{
     863    int sv;
     864
     865    if (!pattern_valid)
     866        return;
     867    if (match < 0)
     868        match = 0;
     869    sv = cur_fline;
     870    /* Try to find next match if eof isn't reached yet */
     871    if (match >= num_matches && eof_error > 0) {
     872        cur_fline = MAXLINES; /* look as far as needed */
     873        read_lines();
     874    }
     875    if (num_matches) {
     876        cap_cur_fline(cur_fline);
     877        normalize_match_pos(match);
     878        buffer_line(match_lines[match_pos]);
     879    } else {
     880        cur_fline = sv;
     881        print_statusline("No matches found");
     882    }
     883}
     884
     885static void fill_match_lines(unsigned pos)
     886{
     887    if (!pattern_valid)
     888        return;
     889    /* Run the regex on each line of the current file */
     890    while (pos <= max_fline) {
     891        /* If this line matches */
     892        if (regexec(&pattern, flines[pos], 0, NULL, 0) == 0
     893        /* and we didn't match it last time */
     894         && !(num_matches && match_lines[num_matches-1] == pos)
     895        ) {
     896            match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int));
     897            match_lines[num_matches++] = pos;
    626898        }
    627         else {
    628             growline = bb_xasprintf("%s%.*s%.*s", growline, match_structs.rm_so - 4, line2, match_structs.rm_eo - match_structs.rm_so, line2 + match_structs.rm_so);
    629         }
    630        
    631         line2 += match_structs.rm_eo;
    632         match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL);
    633     }
    634    
    635     growline = bb_xasprintf("%s%s", growline, line2);
    636    
    637     return (match_found ? growline : line);
    638    
    639     free(growline);
    640     free(line2);
    641 }
    642 
    643 static void goto_match(int match)
    644 {
    645     /* This goes to a specific match - all line positions of matches are
    646        stored within the match_lines[] array. */
    647     if ((match < num_matches) && (match >= 0)) {
    648         buffer_line(match_lines[match]);
    649         match_pos = match;
     899        pos++;
    650900    }
    651901}
     
    653903static void regex_process(void)
    654904{
    655     char uncomp_regex[100];
    656     char *current_line;
    657     int i;
    658     int j = 0;
    659     regex_t pattern;
     905    char *uncomp_regex, *err;
     906
     907    /* Reset variables */
     908    free(match_lines);
     909    match_lines = NULL;
     910    match_pos = 0;
     911    num_matches = 0;
     912    if (pattern_valid) {
     913        regfree(&pattern);
     914        pattern_valid = 0;
     915    }
     916
    660917    /* Get the uncompiled regular expression from the user */
    661918    clear_line();
    662     putchar((match_backwards) ? '?' : '/');
    663     uncomp_regex[0] = 0;
    664     fgets(uncomp_regex, sizeof(uncomp_regex), inp);
    665    
    666     if (strlen(uncomp_regex) == 1) {
    667         if (num_matches)
    668             goto_match(match_backwards ? match_pos - 1 : match_pos + 1);
    669         else
    670             buffer_print();
     919    putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
     920    uncomp_regex = less_gets(1);
     921    if (!uncomp_regex[0]) {
     922        free(uncomp_regex);
     923        buffer_print();
    671924        return;
    672925    }
    673     uncomp_regex[strlen(uncomp_regex) - 1] = '\0';
    674    
     926
    675927    /* Compile the regex and check for errors */
    676     xregcomp(&pattern, uncomp_regex, 0);
    677 
    678     if (num_matches) {
    679         /* Get rid of all the highlights we added previously */
    680         for (i = 0; i <= num_flines; i++) {
    681             current_line = process_regex_on_line(flines[i], &old_pattern, 0);
    682             flines[i] = bb_xstrdup(current_line);
    683         }
    684     }
    685     old_pattern = pattern;
    686    
    687     /* Reset variables */
    688     match_lines = xrealloc(match_lines, sizeof(int));
    689     match_lines[0] = -1;
     928    err = regcomp_or_errmsg(&pattern, uncomp_regex, 0);
     929    free(uncomp_regex);
     930    if (err) {
     931        print_statusline(err);
     932        free(err);
     933        return;
     934    }
     935
     936    pattern_valid = 1;
    690937    match_pos = 0;
    691     num_matches = 0;
    692     match_found = 0;
    693     /* Run the regex on each line of the current file here */
    694     for (i = 0; i <= num_flines; i++) {
    695         current_line = process_regex_on_line(flines[i], &pattern, 1);
    696         flines[i] = bb_xstrdup(current_line);
    697         if (match_found) {
    698             match_lines = xrealloc(match_lines, (j + 1) * sizeof(int));
    699             match_lines[j] = i;
    700             j++;
    701         }
    702     }
    703    
    704     num_matches = j;
    705     if ((match_lines[0] != -1) && (num_flines > height - 2)) {
    706         if (match_backwards) {
    707             for (i = 0; i < num_matches; i++) {
    708                 if (match_lines[i] > line_pos) {
    709                     match_pos = i - 1;
    710                     buffer_line(match_lines[match_pos]);
    711                     break;
    712                 }
    713             }
    714         }
    715         else
    716             buffer_line(match_lines[0]);
    717     }
    718     else
    719         buffer_init();
     938    fill_match_lines(0);
     939    while (match_pos < num_matches) {
     940        if (match_lines[match_pos] > cur_fline)
     941            break;
     942        match_pos++;
     943    }
     944    if (option_mask32 & LESS_STATE_MATCH_BACKWARDS)
     945        match_pos--;
     946
     947    /* It's possible that no matches are found yet.
     948     * goto_match() will read input looking for match,
     949     * if needed */
     950    goto_match(match_pos);
    720951}
    721952#endif
     
    725956    int i = 1;
    726957    int num;
    727     char num_input[80];
     958    char num_input[sizeof(int)*4]; /* more than enough */
    728959    char keypress;
    729     char *endptr;
    730960
    731961    num_input[0] = first_digit;
     
    735965    printf(":%c", first_digit);
    736966
    737     /* Receive input until a letter is given (max 80 chars)*/
    738     while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
     967    /* Receive input until a letter is given */
     968    while (i < sizeof(num_input)-1) {
     969        num_input[i] = less_getch();
     970        if (!num_input[i] || !isdigit(num_input[i]))
     971            break;
    739972        putchar(num_input[i]);
    740973        i++;
     
    744977    keypress = num_input[i];
    745978    num_input[i] = '\0';
    746     num = strtol(num_input, &endptr, 10);
    747     if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES) {
     979    num = bb_strtou(num_input, NULL, 10);
     980    /* on format error, num == -1 */
     981    if (num < 1 || num > MAXLINES) {
    748982        buffer_print();
    749983        return;
     
    752986    /* We now know the number and the letter entered, so we process them */
    753987    switch (keypress) {
    754         case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
    755             buffer_down(num);
    756             break;
    757         case KEY_UP: case 'b': case 'w': case 'y': case 'u':
    758             buffer_up(num);
    759             break;
    760         case 'g': case '<': case 'G': case '>':
    761             if (num_flines >= height - 2)
    762                 buffer_line(num - 1);
    763             break;
    764         case 'p': case '%':
    765             buffer_line(((num / 100) * num_flines) - 1);
    766             break;
    767 #ifdef CONFIG_FEATURE_LESS_REGEXP
    768         case 'n':
    769             goto_match(match_pos + num);
    770             break;
    771         case '/':
    772             match_backwards = 0;
    773             regex_process();
    774             break;
    775         case '?':
    776             match_backwards = 1;
    777             regex_process();
    778             break;
    779 #endif
    780         default:
    781             break;
    782     }
    783 }
    784 
    785 #ifdef CONFIG_FEATURE_LESS_FLAGCS
     988    case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
     989        buffer_down(num);
     990        break;
     991    case KEY_UP: case 'b': case 'w': case 'y': case 'u':
     992        buffer_up(num);
     993        break;
     994    case 'g': case '<': case 'G': case '>':
     995        cur_fline = num + max_displayed_line;
     996        read_lines();
     997        buffer_line(num - 1);
     998        break;
     999    case 'p': case '%':
     1000        num = num * (max_fline / 100); /* + max_fline / 2; */
     1001        cur_fline = num + max_displayed_line;
     1002        read_lines();
     1003        buffer_line(num);
     1004        break;
     1005#if ENABLE_FEATURE_LESS_REGEXP
     1006    case 'n':
     1007        goto_match(match_pos + num);
     1008        break;
     1009    case '/':
     1010        option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
     1011        regex_process();
     1012        break;
     1013    case '?':
     1014        option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
     1015        regex_process();
     1016        break;
     1017#endif
     1018    }
     1019}
     1020
     1021#if ENABLE_FEATURE_LESS_FLAGCS
    7861022static void flag_change(void)
    7871023{
     
    7901026    clear_line();
    7911027    putchar('-');
    792     keypress = tless_getch();
     1028    keypress = less_getch();
    7931029
    7941030    switch (keypress) {
    795         case 'M':
    796             flags ^= FLAG_M;
    797             break;
    798         case 'm':
    799             flags ^= FLAG_m;
    800             break;
    801         case 'E':
    802             flags ^= FLAG_E;
    803             break;
    804         case '~':
    805             flags ^= FLAG_TILDE;
    806             break;
    807         default:
    808             break;
     1031    case 'M':
     1032        option_mask32 ^= FLAG_M;
     1033        break;
     1034    case 'm':
     1035        option_mask32 ^= FLAG_m;
     1036        break;
     1037    case 'E':
     1038        option_mask32 ^= FLAG_E;
     1039        break;
     1040    case '~':
     1041        option_mask32 ^= FLAG_TILDE;
     1042        break;
    8091043    }
    8101044}
     
    8171051    clear_line();
    8181052    putchar('_');
    819     keypress = tless_getch();
     1053    keypress = less_getch();
    8201054
    8211055    switch (keypress) {
    822         case 'M':
    823             flag_val = flags & FLAG_M;
    824             break;
    825         case 'm':
    826             flag_val = flags & FLAG_m;
    827             break;
    828         case '~':
    829             flag_val = flags & FLAG_TILDE;
    830             break;
    831         case 'N':
    832             flag_val = flags & FLAG_N;
    833             break;
    834         case 'E':
    835             flag_val = flags & FLAG_E;
    836             break;
    837         default:
    838             flag_val = 0;
    839             break;
     1056    case 'M':
     1057        flag_val = option_mask32 & FLAG_M;
     1058        break;
     1059    case 'm':
     1060        flag_val = option_mask32 & FLAG_m;
     1061        break;
     1062    case '~':
     1063        flag_val = option_mask32 & FLAG_TILDE;
     1064        break;
     1065    case 'N':
     1066        flag_val = option_mask32 & FLAG_N;
     1067        break;
     1068    case 'E':
     1069        flag_val = option_mask32 & FLAG_E;
     1070        break;
     1071    default:
     1072        flag_val = 0;
     1073        break;
    8401074    }
    8411075
    8421076    clear_line();
    843     printf("%s%s%i%s", HIGHLIGHT, "The status of the flag is: ", flag_val != 0, NORMAL);
    844 }
    845 #endif
    846 
    847 static void full_repaint(void)
    848 {
    849     int temp_line_pos = line_pos;
    850     data_readlines();
    851     buffer_init();
    852     buffer_line(temp_line_pos);
    853 }
    854 
     1077    printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0);
     1078}
     1079#endif
    8551080
    8561081static void save_input_to_file(void)
    8571082{
    858     char current_line[256];
     1083    const char *msg = "";
     1084    char *current_line;
    8591085    int i;
    8601086    FILE *fp;
    8611087
    862     clear_line();
    863     printf("Log file: ");
    864     fgets(current_line, 256, inp);
    865     current_line[strlen(current_line) - 1] = '\0';
    866     if (strlen(current_line) > 1) {
    867         fp = bb_xfopen(current_line, "w");
    868         for (i = 0; i < num_flines; i++)
    869             fprintf(fp, "%s", flines[i]);
     1088    print_statusline("Log file: ");
     1089    current_line = less_gets(sizeof("Log file: ")-1);
     1090    if (strlen(current_line) > 0) {
     1091        fp = fopen(current_line, "w");
     1092        if (!fp) {
     1093            msg = "Error opening log file";
     1094            goto ret;
     1095        }
     1096        for (i = 0; i <= max_fline; i++)
     1097            fprintf(fp, "%s\n", flines[i]);
    8701098        fclose(fp);
    871         buffer_print();
    872     }
    873     else
    874         printf("%sNo log file%s", HIGHLIGHT, NORMAL);
    875 }
    876 
    877 #ifdef CONFIG_FEATURE_LESS_MARKS
     1099        msg = "Done";
     1100    }
     1101 ret:
     1102    print_statusline(msg);
     1103    free(current_line);
     1104}
     1105
     1106#if ENABLE_FEATURE_LESS_MARKS
    8781107static void add_mark(void)
    8791108{
    8801109    int letter;
    881     int mark_line;
    882 
    883     clear_line();
    884     printf("Mark: ");
    885     letter = tless_getch();
     1110
     1111    print_statusline("Mark: ");
     1112    letter = less_getch();
    8861113
    8871114    if (isalpha(letter)) {
    888         mark_line = line_pos;
    889 
    8901115        /* If we exceed 15 marks, start overwriting previous ones */
    8911116        if (num_marks == 14)
     
    8931118
    8941119        mark_lines[num_marks][0] = letter;
    895         mark_lines[num_marks][1] = line_pos;
     1120        mark_lines[num_marks][1] = cur_fline;
    8961121        num_marks++;
    897     }
    898     else {
    899         clear_line();
    900         printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
     1122    } else {
     1123        print_statusline("Invalid mark letter");
    9011124    }
    9021125}
     
    9071130    int i;
    9081131
    909     clear_line();
    910     printf("Go to mark: ");
    911     letter = tless_getch();
     1132    print_statusline("Go to mark: ");
     1133    letter = less_getch();
    9121134    clear_line();
    9131135
     
    9181140                break;
    9191141            }
    920         if ((num_marks == 14) && (letter != mark_lines[14][0]))
    921             printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
    922     }
    923     else
    924         printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
    925 }
    926 #endif
    927 
    928 
    929 #ifdef CONFIG_FEATURE_LESS_BRACKETS
    930 
     1142        if (num_marks == 14 && letter != mark_lines[14][0])
     1143            print_statusline("Mark not set");
     1144    } else
     1145        print_statusline("Invalid mark letter");
     1146}
     1147#endif
     1148
     1149#if ENABLE_FEATURE_LESS_BRACKETS
    9311150static char opp_bracket(char bracket)
    9321151{
    9331152    switch (bracket) {
    934         case '{': case '[':
    935             return bracket + 2;
    936             break;
    937         case '(':
    938             return ')';
    939             break;
    940         case '}': case ']':
    941             return bracket - 2;
    942             break;
    943         case ')':
    944             return '(';
    945             break;
    946         default:
    947             return 0;
    948             break;
    949     }
     1153    case '{': case '[':
     1154        return bracket + 2;
     1155    case '(':
     1156        return ')';
     1157    case '}': case ']':
     1158        return bracket - 2;
     1159    case ')':
     1160        return '(';
     1161    }
     1162    return 0;
    9501163}
    9511164
     
    9551168    int i;
    9561169
    957     clear_line();
    958 
    959     if (strchr(flines[line_pos], bracket) == NULL)
    960         printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
    961     else {
    962         for (i = line_pos + 1; i < num_flines; i++) {
    963             if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
    964                 bracket_line = i;
    965                 break;
    966             }
     1170    if (strchr(flines[cur_fline], bracket) == NULL) {
     1171        print_statusline("No bracket in top line");
     1172        return;
     1173    }
     1174    for (i = cur_fline + 1; i < max_fline; i++) {
     1175        if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
     1176            bracket_line = i;
     1177            break;
    9671178        }
    968 
    969         if (bracket_line == -1)
    970             printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
    971 
    972         buffer_line(bracket_line - height + 2);
    973     }
     1179    }
     1180    if (bracket_line == -1)
     1181        print_statusline("No matching bracket found");
     1182    buffer_line(bracket_line - max_displayed_line);
    9741183}
    9751184
     
    9791188    int i;
    9801189
    981     clear_line();
    982 
    983     if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
    984         printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
    985         printf("%s", flines[line_pos + height]);
    986         sleep(4);
    987     }
    988     else {
    989         for (i = line_pos + height - 2; i >= 0; i--) {
    990             if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
    991                 bracket_line = i;
    992                 break;
    993             }
     1190    if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) {
     1191        print_statusline("No bracket in bottom line");
     1192        return;
     1193    }
     1194
     1195    for (i = cur_fline + max_displayed_line; i >= 0; i--) {
     1196        if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
     1197            bracket_line = i;
     1198            break;
    9941199        }
    995 
    996         if (bracket_line == -1)
    997             printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
    998 
    999         buffer_line(bracket_line);
    1000     }
    1001 }
    1002 
    1003 #endif  /* CONFIG_FEATURE_LESS_BRACKETS */
     1200    }
     1201    if (bracket_line == -1)
     1202        print_statusline("No matching bracket found");
     1203    buffer_line(bracket_line);
     1204}
     1205#endif  /* FEATURE_LESS_BRACKETS */
    10041206
    10051207static void keypress_process(int keypress)
    10061208{
    10071209    switch (keypress) {
    1008         case KEY_DOWN: case 'e': case 'j': case '\015':
    1009             buffer_down(1);
    1010             buffer_print();
    1011             break;
    1012         case KEY_UP: case 'y': case 'k':
    1013             buffer_up(1);
    1014             buffer_print();
    1015             break;
    1016         case PAGE_DOWN: case ' ': case 'z':
    1017             buffer_down(height - 1);
    1018             buffer_print();
    1019             break;
    1020         case PAGE_UP: case 'w': case 'b':
    1021             buffer_up(height - 1);
    1022             buffer_print();
    1023             break;
    1024         case 'd':
    1025             buffer_down((height - 1) / 2);
    1026             buffer_print();
    1027             break;
    1028         case 'u':
    1029             buffer_up((height - 1) / 2);
    1030             buffer_print();
    1031             break;
    1032         case 'g': case 'p': case '<': case '%':
    1033             buffer_line(0);
    1034             break;
    1035         case 'G': case '>':
    1036             buffer_line(num_flines - height + 2);
    1037             break;
    1038         case 'q': case 'Q':
    1039             tless_exit(0);
    1040             break;
    1041 #ifdef CONFIG_FEATURE_LESS_MARKS
    1042         case 'm':
    1043             add_mark();
    1044             buffer_print();
    1045             break;
    1046         case '\'':
    1047             goto_mark();
    1048             buffer_print();
    1049             break;
    1050 #endif
    1051         case 'r':
    1052             buffer_print();
    1053             break;
    1054         case 'R':
    1055             full_repaint();
    1056             break;
    1057         case 's':
    1058             if (inp_stdin)
    1059                 save_input_to_file();
    1060             break;
    1061         case 'E':
    1062             examine_file();
    1063             break;
    1064 #ifdef CONFIG_FEATURE_LESS_FLAGS
    1065         case '=':
    1066             clear_line();
    1067             m_status_print();
    1068             break;
    1069 #endif
    1070 #ifdef CONFIG_FEATURE_LESS_REGEXP
    1071         case '/':
    1072             match_backwards = 0;
    1073             regex_process();
    1074             break;
    1075         case 'n':
    1076             goto_match(match_pos + 1);
    1077             break;
    1078         case 'N':
    1079             goto_match(match_pos - 1);
    1080             break;
    1081         case '?':
    1082             match_backwards = 1;
    1083             regex_process();
    1084             break;
    1085 #endif
    1086 #ifdef CONFIG_FEATURE_LESS_FLAGCS
    1087         case '-':
    1088             flag_change();
    1089             buffer_print();
    1090             break;
    1091         case '_':
    1092             show_flag_status();
    1093             break;
    1094 #endif
    1095 #ifdef CONFIG_FEATURE_LESS_BRACKETS
    1096         case '{': case '(': case '[':
    1097             match_right_bracket(keypress);
    1098             break;
    1099         case '}': case ')': case ']':
    1100             match_left_bracket(keypress);
    1101             break;
    1102 #endif
    1103         case ':':
    1104             colon_process();
    1105             break;
    1106         default:
    1107             break;
     1210    case KEY_DOWN: case 'e': case 'j': case 0x0d:
     1211        buffer_down(1);
     1212        break;
     1213    case KEY_UP: case 'y': case 'k':
     1214        buffer_up(1);
     1215        break;
     1216    case PAGE_DOWN: case ' ': case 'z':
     1217        buffer_down(max_displayed_line + 1);
     1218        break;
     1219    case PAGE_UP: case 'w': case 'b':
     1220        buffer_up(max_displayed_line + 1);
     1221        break;
     1222    case 'd':
     1223        buffer_down((max_displayed_line + 1) / 2);
     1224        break;
     1225    case 'u':
     1226        buffer_up((max_displayed_line + 1) / 2);
     1227        break;
     1228    case KEY_HOME: case 'g': case 'p': case '<': case '%':
     1229        buffer_line(0);
     1230        break;
     1231    case KEY_END: case 'G': case '>':
     1232        cur_fline = MAXLINES;
     1233        read_lines();
     1234        buffer_line(cur_fline);
     1235        break;
     1236    case 'q': case 'Q':
     1237        less_exit(0);
     1238        break;
     1239#if ENABLE_FEATURE_LESS_MARKS
     1240    case 'm':
     1241        add_mark();
     1242        buffer_print();
     1243        break;
     1244    case '\'':
     1245        goto_mark();
     1246        buffer_print();
     1247        break;
     1248#endif
     1249    case 'r': case 'R':
     1250        buffer_print();
     1251        break;
     1252    /*case 'R':
     1253        full_repaint();
     1254        break;*/
     1255    case 's':
     1256        save_input_to_file();
     1257        break;
     1258    case 'E':
     1259        examine_file();
     1260        break;
     1261#if ENABLE_FEATURE_LESS_FLAGS
     1262    case '=':
     1263        m_status_print();
     1264        break;
     1265#endif
     1266#if ENABLE_FEATURE_LESS_REGEXP
     1267    case '/':
     1268        option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
     1269        regex_process();
     1270        break;
     1271    case 'n':
     1272        goto_match(match_pos + 1);
     1273        break;
     1274    case 'N':
     1275        goto_match(match_pos - 1);
     1276        break;
     1277    case '?':
     1278        option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
     1279        regex_process();
     1280        break;
     1281#endif
     1282#if ENABLE_FEATURE_LESS_FLAGCS
     1283    case '-':
     1284        flag_change();
     1285        buffer_print();
     1286        break;
     1287    case '_':
     1288        show_flag_status();
     1289        break;
     1290#endif
     1291#if ENABLE_FEATURE_LESS_BRACKETS
     1292    case '{': case '(': case '[':
     1293        match_right_bracket(keypress);
     1294        break;
     1295    case '}': case ')': case ']':
     1296        match_left_bracket(keypress);
     1297        break;
     1298#endif
     1299    case ':':
     1300        colon_process();
     1301        break;
    11081302    }
    11091303
     
    11121306}
    11131307
    1114 int less_main(int argc, char **argv) {
    1115 
     1308static void sig_catcher(int sig ATTRIBUTE_UNUSED)
     1309{
     1310    set_tty_cooked();
     1311    exit(1);
     1312}
     1313
     1314int less_main(int argc, char **argv);
     1315int less_main(int argc, char **argv)
     1316{
    11161317    int keypress;
    11171318
    1118     flags = bb_getopt_ulflags(argc, argv, "EMmN~");
    1119 
     1319    INIT_G();
     1320
     1321    /* TODO: -x: do not interpret backspace, -xx: tab also */
     1322    /* -xxx: newline also */
     1323    /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
     1324    getopt32(argv, "EMmN~");
    11201325    argc -= optind;
    11211326    argv += optind;
     1327    num_files = argc;
    11221328    files = argv;
    1123     num_files = argc;
     1329
     1330    /* Another popular pager, most, detects when stdout
     1331     * is not a tty and turns into cat. This makes sense. */
     1332    if (!isatty(STDOUT_FILENO))
     1333        return bb_cat(argv);
     1334    kbd_fd = open(CURRENT_TTY, O_RDONLY);
     1335    if (kbd_fd < 0)
     1336        return bb_cat(argv);
    11241337
    11251338    if (!num_files) {
    1126         if (ttyname(STDIN_FILENO) == NULL)
    1127             inp_stdin = 1;
    1128         else {
    1129             bb_error_msg("Missing filename");
     1339        if (isatty(STDIN_FILENO)) {
     1340            /* Just "less"? No args and no redirection? */
     1341            bb_error_msg("missing filename");
    11301342            bb_show_usage();
    11311343        }
    1132     }
    1133 
    1134     strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
    1135     get_terminal_width_height(0, &width, &height);
    1136     data_readlines();
    1137     tcgetattr(fileno(inp), &term_orig);
    1138     term_vi = term_orig;
    1139     term_vi.c_lflag &= (~ICANON & ~ECHO);
    1140     term_vi.c_iflag &= (~IXON & ~ICRNL);
    1141     term_vi.c_oflag &= (~ONLCR);
    1142     term_vi.c_cc[VMIN] = 1;
    1143     term_vi.c_cc[VTIME] = 0;
    1144     buffer_init();
    1145     buffer_print();
    1146 
     1344    } else
     1345        filename = xstrdup(files[0]);
     1346
     1347    get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
     1348    /* 20: two tabstops + 4 */
     1349    if (width < 20 || max_displayed_line < 3)
     1350        bb_error_msg_and_die("too narrow here");
     1351    max_displayed_line -= 2;
     1352
     1353    buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
     1354    if (option_mask32 & FLAG_TILDE)
     1355        empty_line_marker = "";
     1356
     1357    tcgetattr(kbd_fd, &term_orig);
     1358    signal(SIGTERM, sig_catcher);
     1359    signal(SIGINT, sig_catcher);
     1360    term_less = term_orig;
     1361    term_less.c_lflag &= ~(ICANON | ECHO);
     1362    term_less.c_iflag &= ~(IXON | ICRNL);
     1363    /*term_less.c_oflag &= ~ONLCR;*/
     1364    term_less.c_cc[VMIN] = 1;
     1365    term_less.c_cc[VTIME] = 0;
     1366
     1367    /* Want to do it just once, but it doesn't work, */
     1368    /* so we are redoing it (see code above). Mystery... */
     1369    /*tcsetattr(kbd_fd, TCSANOW, &term_less);*/
     1370
     1371    reinitialize();
    11471372    while (1) {
    1148         keypress = tless_getch();
     1373        keypress = less_getch();
    11491374        keypress_process(keypress);
    11501375    }
Note: See TracChangeset for help on using the changeset viewer.