Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

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

    r1765 r2725  
    55 * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
    66 *
    7  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
     7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    88 */
    99
     
    2222 */
    2323
    24 #include <sched.h>  /* sched_yield() */
     24#include <sched.h>  /* sched_yield() */
    2525
    2626#include "libbb.h"
     
    2929#endif
    3030
    31 /* FIXME: currently doesn't work right */
    32 #undef ENABLE_FEATURE_LESS_FLAGCS
    33 #define ENABLE_FEATURE_LESS_FLAGCS 0
    34 
     31
     32#define ESC "\033"
    3533/* The escape codes for highlighted and normal text */
    36 #define HIGHLIGHT "\033[7m"
    37 #define NORMAL "\033[0m"
    38 /* The escape code to clear the screen */
    39 #define CLEAR "\033[H\033[J"
    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 */
     34#define HIGHLIGHT   ESC"[7m"
     35#define NORMAL      ESC"[0m"
     36/* The escape code to home and clear to the end of screen */
     37#define CLEAR       ESC"[H\033[J"
     38/* The escape code to clear to the end of line */
     39#define CLEAR_2_EOL ESC"[K"
     40
    4441enum {
    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 
    6842/* Absolute max of lines eaten */
    6943    MAXLINES = CONFIG_FEATURE_LESS_MAXLINES,
    70 
    7144/* This many "after the end" lines we will show (at max) */
    7245    TILDES = 1,
     
    7548/* Command line options */
    7649enum {
    77     FLAG_E = 1,
     50    FLAG_E = 1 << 0,
    7851    FLAG_M = 1 << 1,
    7952    FLAG_m = 1 << 2,
    8053    FLAG_N = 1 << 3,
    8154    FLAG_TILDE = 1 << 4,
     55    FLAG_I = 1 << 5,
     56    FLAG_S = (1 << 6) * ENABLE_FEATURE_LESS_DASHCMD,
    8257/* hijack command line options variable for internal state vars */
    8358    LESS_STATE_MATCH_BACKWARDS = 1 << 15,
     
    9166    int cur_fline; /* signed */
    9267    int kbd_fd;  /* fd to get input from */
     68    int less_gets_pos;
    9369/* last position in last line, taking into account tabs */
    94     size_t linepos;
    95     unsigned max_displayed_line;
     70    size_t last_line_pos;
    9671    unsigned max_fline;
    9772    unsigned max_lineno; /* this one tracks linewrap */
     73    unsigned max_displayed_line;
    9874    unsigned width;
     75#if ENABLE_FEATURE_LESS_WINCH
     76    unsigned winch_counter;
     77#endif
    9978    ssize_t eof_error; /* eof if 0, error if < 0 */
    100     size_t readpos;
    101     size_t readeof;
     79    ssize_t readpos;
     80    ssize_t readeof; /* must be signed */
    10281    const char **buffer;
    10382    const char **flines;
     
    11493    unsigned *match_lines;
    11594    int match_pos; /* signed! */
    116     unsigned num_matches;
     95    int wanted_match; /* signed! */
     96    int num_matches;
    11797    regex_t pattern;
    11898    smallint pattern_valid;
     
    120100    smallint terminated;
    121101    struct termios term_orig, term_less;
     102    char kbd_input[KEYCODE_BUFFER_SIZE];
    122103};
    123104#define G (*ptr_to_globals)
    124105#define cur_fline           (G.cur_fline         )
    125106#define kbd_fd              (G.kbd_fd            )
    126 #define linepos             (G.linepos           )
    127 #define max_displayed_line  (G.max_displayed_line)
     107#define less_gets_pos       (G.less_gets_pos     )
     108#define last_line_pos       (G.last_line_pos     )
    128109#define max_fline           (G.max_fline         )
    129110#define max_lineno          (G.max_lineno        )
     111#define max_displayed_line  (G.max_displayed_line)
    130112#define width               (G.width             )
     113#define winch_counter       (G.winch_counter     )
     114/* This one is 100% not cached by compiler on read access */
     115#define WINCH_COUNTER (*(volatile unsigned *)&winch_counter)
    131116#define eof_error           (G.eof_error         )
    132117#define readpos             (G.readpos           )
     
    145130#define match_pos           (G.match_pos         )
    146131#define num_matches         (G.num_matches       )
     132#define wanted_match        (G.wanted_match      )
    147133#define pattern             (G.pattern           )
    148134#define pattern_valid       (G.pattern_valid     )
     
    151137#define term_orig           (G.term_orig         )
    152138#define term_less           (G.term_less         )
     139#define kbd_input           (G.kbd_input         )
    153140#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)
     141    SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
     142    less_gets_pos = -1; \
     143    empty_line_marker = "~"; \
     144    num_files = 1; \
     145    current_file = 1; \
     146    eof_error = 1; \
     147    terminated = 1; \
     148    IF_FEATURE_LESS_REGEXP(wanted_match = -1;) \
     149} while (0)
     150
     151/* flines[] are lines read from stdin, each in malloc'ed buffer.
     152 * Line numbers are stored as uint32_t prepended to each line.
     153 * Pointer is adjusted so that flines[i] points directly past
     154 * line number. Accesor: */
     155#define MEMPTR(p) ((char*)(p) - 4)
     156#define LINENO(p) (*(uint32_t*)((p) - 4))
     157
    161158
    162159/* Reset terminal input to normal */
    163160static void set_tty_cooked(void)
    164161{
    165     fflush(stdout);
     162    fflush_all();
    166163    tcsetattr(kbd_fd, TCSANOW, &term_orig);
    167 }
    168 
    169 /* Exit the program gracefully */
    170 static void less_exit(int code)
    171 {
    172     /* TODO: We really should save the terminal state when we start,
    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? */
    176     putchar('\n');
    177     fflush_stdout_and_exit(code);
    178164}
    179165
     
    182168static void move_cursor(int line, int row)
    183169{
    184     printf("\033[%u;%uH", line, row);
     170    printf(ESC"[%u;%uH", line, row);
    185171}
    186172
    187173static void clear_line(void)
    188174{
    189     printf("\033[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);
     175    printf(ESC"[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);
    190176}
    191177
     
    200186    printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str);
    201187}
     188
     189/* Exit the program gracefully */
     190static void less_exit(int code)
     191{
     192    set_tty_cooked();
     193    clear_line();
     194    if (code < 0)
     195        kill_myself_with_sig(- code); /* does not return */
     196    exit(code);
     197}
     198
     199#if (ENABLE_FEATURE_LESS_DASHCMD && ENABLE_FEATURE_LESS_LINENUMS) \
     200 || ENABLE_FEATURE_LESS_WINCH
     201static void re_wrap(void)
     202{
     203    int w = width;
     204    int new_line_pos;
     205    int src_idx;
     206    int dst_idx;
     207    int new_cur_fline = 0;
     208    uint32_t lineno;
     209    char linebuf[w + 1];
     210    const char **old_flines = flines;
     211    const char *s;
     212    char **new_flines = NULL;
     213    char *d;
     214
     215    if (option_mask32 & FLAG_N)
     216        w -= 8;
     217
     218    src_idx = 0;
     219    dst_idx = 0;
     220    s = old_flines[0];
     221    lineno = LINENO(s);
     222    d = linebuf;
     223    new_line_pos = 0;
     224    while (1) {
     225        *d = *s;
     226        if (*d != '\0') {
     227            new_line_pos++;
     228            if (*d == '\t') /* tab */
     229                new_line_pos += 7;
     230            s++;
     231            d++;
     232            if (new_line_pos >= w) {
     233                int sz;
     234                /* new line is full, create next one */
     235                *d = '\0';
     236 next_new:
     237                sz = (d - linebuf) + 1; /* + 1: NUL */
     238                d = ((char*)xmalloc(sz + 4)) + 4;
     239                LINENO(d) = lineno;
     240                memcpy(d, linebuf, sz);
     241                new_flines = xrealloc_vector(new_flines, 8, dst_idx);
     242                new_flines[dst_idx] = d;
     243                dst_idx++;
     244                if (new_line_pos < w) {
     245                    /* if we came here thru "goto next_new" */
     246                    if (src_idx > max_fline)
     247                        break;
     248                    lineno = LINENO(s);
     249                }
     250                d = linebuf;
     251                new_line_pos = 0;
     252            }
     253            continue;
     254        }
     255        /* *d == NUL: old line ended, go to next old one */
     256        free(MEMPTR(old_flines[src_idx]));
     257        /* btw, convert cur_fline... */
     258        if (cur_fline == src_idx)
     259            new_cur_fline = dst_idx;
     260        src_idx++;
     261        /* no more lines? finish last new line (and exit the loop) */
     262        if (src_idx > max_fline)
     263            goto next_new;
     264        s = old_flines[src_idx];
     265        if (lineno != LINENO(s)) {
     266            /* this is not a continuation line!
     267             * create next _new_ line too */
     268            goto next_new;
     269        }
     270    }
     271
     272    free(old_flines);
     273    flines = (const char **)new_flines;
     274
     275    max_fline = dst_idx - 1;
     276    last_line_pos = new_line_pos;
     277    cur_fline = new_cur_fline;
     278    /* max_lineno is screen-size independent */
     279#if ENABLE_FEATURE_LESS_REGEXP
     280    pattern_valid = 0;
     281#endif
     282}
     283#endif
    202284
    203285#if ENABLE_FEATURE_LESS_REGEXP
     
    226308 * readbuf[0..readeof-1] - small preliminary buffer.
    227309 * readbuf[readpos] - next character to add to current line.
    228  * linepos - screen line position of next char to be read
     310 * last_line_pos - screen line position of next char to be read
    229311 *      (takes into account tabs and backspaces)
    230312 * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error
     
    234316#define readbuf bb_common_bufsiz1
    235317    char *current_line, *p;
    236     USE_FEATURE_LESS_REGEXP(unsigned old_max_fline = max_fline;)
    237318    int w = width;
    238319    char last_terminated = terminated;
     320#if ENABLE_FEATURE_LESS_REGEXP
     321    unsigned old_max_fline = max_fline;
     322    time_t last_time = 0;
     323    unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */
     324#endif
    239325
    240326    if (option_mask32 & FLAG_N)
    241327        w -= 8;
    242328
    243     current_line = xmalloc(w);
    244     p = current_line;
     329 IF_FEATURE_LESS_REGEXP(again0:)
     330
     331    p = current_line = ((char*)xmalloc(w + 4)) + 4;
    245332    max_fline += last_terminated;
    246333    if (!last_terminated) {
    247334        const char *cp = flines[max_fline];
    248         if (option_mask32 & FLAG_N)
    249             cp += 8;
    250         strcpy(current_line, cp);
     335        strcpy(p, cp);
    251336        p += strlen(current_line);
    252         /* linepos is still valid from previous read_lines() */
     337        free(MEMPTR(flines[max_fline]));
     338        /* last_line_pos is still valid from previous read_lines() */
    253339    } else {
    254         linepos = 0;
    255     }
    256 
    257     while (1) {
    258  again:
     340        last_line_pos = 0;
     341    }
     342
     343    while (1) { /* read lines until we reach cur_fline or wanted_match */
    259344        *p = '\0';
    260345        terminated = 0;
    261         while (1) {
     346        while (1) { /* read chars until we have a line */
    262347            char c;
    263348            /* if no unprocessed chars left, eat more */
    264349            if (readpos >= readeof) {
    265                 smallint yielded = 0;
    266 
    267350                ndelay_on(0);
    268  read_again:
    269                 eof_error = safe_read(0, readbuf, sizeof(readbuf));
     351                eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf));
     352                ndelay_off(0);
    270353                readpos = 0;
    271354                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) {
     355                if (eof_error <= 0)
    293356                    goto reached_eof;
    294                 }
    295357            }
    296358            c = readbuf[readpos];
     
    298360            /* <tab><bs> is (a) insane and */
    299361            /* (b) harder to do correctly, so we refuse to do it */
    300             if (c == '\x8' && linepos && p[-1] != '\t') {
     362            if (c == '\x8' && last_line_pos && p[-1] != '\t') {
    301363                readpos++; /* eat it */
    302                 linepos--;
     364                last_line_pos--;
    303365            /* was buggy (p could end up <= current_line)... */
    304366                *--p = '\0';
     
    306368            }
    307369            {
    308                 size_t new_linepos = linepos + 1;
     370                size_t new_last_line_pos = last_line_pos + 1;
    309371                if (c == '\t') {
    310                     new_linepos += 7;
    311                     new_linepos &= (~7);
     372                    new_last_line_pos += 7;
     373                    new_last_line_pos &= (~7);
    312374                }
    313                 if (new_linepos >= w)
     375                if ((int)new_last_line_pos >= w)
    314376                    break;
    315                 linepos = new_linepos;
     377                last_line_pos = new_last_line_pos;
    316378            }
    317379            /* ok, we will eat this char */
     
    319381            if (c == '\n') {
    320382                terminated = 1;
    321                 linepos = 0;
     383                last_line_pos = 0;
    322384                break;
    323385            }
     
    326388            *p++ = c;
    327389            *p = '\0';
    328         }
     390        } /* end of "read chars until we have a line" loop */
    329391        /* Corner case: linewrap with only "" wrapping to next line */
    330392        /* Looks ugly on screen, so we do not store this empty line */
     
    332394            last_terminated = 1;
    333395            max_lineno++;
    334             goto again;
     396            continue;
    335397        }
    336398 reached_eof:
    337399        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         }
     400        flines = xrealloc_vector(flines, 8, max_fline);
     401
     402        flines[max_fline] = (char*)xrealloc(MEMPTR(current_line), strlen(current_line) + 1 + 4) + 4;
     403        LINENO(flines[max_fline]) = max_lineno;
     404        if (terminated)
     405            max_lineno++;
     406
    350407        if (max_fline >= MAXLINES) {
    351408            eof_error = 0; /* Pretend we saw EOF */
    352409            break;
    353410        }
    354         if (max_fline > cur_fline + max_displayed_line)
     411        if (!(option_mask32 & FLAG_S)
     412          ? (max_fline > cur_fline + max_displayed_line)
     413          : (max_fline >= cur_fline
     414             && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line)
     415        ) {
     416#if !ENABLE_FEATURE_LESS_REGEXP
    355417            break;
     418#else
     419            if (wanted_match >= num_matches) { /* goto_match called us */
     420                fill_match_lines(old_max_fline);
     421                old_max_fline = max_fline;
     422            }
     423            if (wanted_match < num_matches)
     424                break;
     425#endif
     426        }
    356427        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;
     428            if (eof_error < 0) {
     429                if (errno == EAGAIN) {
     430                    /* not yet eof or error, reset flag (or else
     431                     * we will hog CPU - select() will return
     432                     * immediately */
     433                    eof_error = 1;
     434                } else {
     435                    print_statusline(bb_msg_read_error);
     436                }
    362437            }
     438#if !ENABLE_FEATURE_LESS_REGEXP
    363439            break;
     440#else
     441            if (wanted_match < num_matches) {
     442                break;
     443            } else { /* goto_match called us */
     444                time_t t = time(NULL);
     445                if (t != last_time) {
     446                    last_time = t;
     447                    if (--seconds_p1 == 0)
     448                        break;
     449                }
     450                sched_yield();
     451                goto again0; /* go loop again (max 2 seconds) */
     452            }
     453#endif
    364454        }
    365455        max_fline++;
    366         current_line = xmalloc(w);
     456        current_line = ((char*)xmalloc(w + 4)) + 4;
    367457        p = current_line;
    368         linepos = 0;
    369     }
     458        last_line_pos = 0;
     459    } /* end of "read lines until we reach cur_fline" loop */
    370460    fill_match_lines(old_max_fline);
     461#if ENABLE_FEATURE_LESS_REGEXP
     462    /* prevent us from being stuck in search for a match */
     463    wanted_match = -1;
     464#endif
    371465#undef readbuf
    372466}
     
    385479{
    386480    int percentage;
     481
     482    if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
     483        return;
    387484
    388485    clear_line();
     
    393490            cur_fline + 1, cur_fline + max_displayed_line + 1,
    394491            max_fline + 1);
    395     if (cur_fline >= max_fline - max_displayed_line) {
     492    if (cur_fline >= (int)(max_fline - max_displayed_line)) {
    396493        printf("(END)"NORMAL);
    397494        if (num_files > 1 && current_file != num_files)
     
    408505{
    409506    const char *p;
     507
     508    if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
     509        return;
    410510
    411511    /* Change the status if flags have been set */
     
    419519
    420520    clear_line();
    421     if (cur_fline && cur_fline < max_fline - max_displayed_line) {
    422         putchar(':');
     521    if (cur_fline && cur_fline < (int)(max_fline - max_displayed_line)) {
     522        bb_putchar(':');
    423523        return;
    424524    }
     
    445545        diff = max_fline - (cur_fline + max_displayed_line) + TILDES;
    446546        /* As the number of lines requested was too large, we just move
    447         to the end of the file */
     547         * to the end of the file */
    448548        if (diff > 0)
    449549            cur_fline += diff;
     
    457557    "\x7f\x9b"; /* DEL and infamous Meta-ESC :( */
    458558static const char ctrlconv[] ALIGN1 =
    459     /* '\n': it's a former NUL - subst with '@', not 'J' */
     559    /* why 40 instead of 4a below? - it is a replacement for '\n'.
     560     * '\n' is a former NUL - we subst it with @, not J */
    460561    "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f"
    461562    "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f";
     563
     564static void lineno_str(char *nbuf9, const char *line)
     565{
     566    nbuf9[0] = '\0';
     567    if (option_mask32 & FLAG_N) {
     568        const char *fmt;
     569        unsigned n;
     570
     571        if (line == empty_line_marker) {
     572            memset(nbuf9, ' ', 8);
     573            nbuf9[8] = '\0';
     574            return;
     575        }
     576        /* Width of 7 preserves tab spacing in the text */
     577        fmt = "%7u ";
     578        n = LINENO(line) + 1;
     579        if (n > 9999999) {
     580            n %= 10000000;
     581            fmt = "%07u ";
     582        }
     583        sprintf(nbuf9, fmt, n);
     584    }
     585}
     586
    462587
    463588#if ENABLE_FEATURE_LESS_REGEXP
     
    470595
    471596    char buf[width];
     597    char nbuf9[9];
    472598    const char *str = line;
    473599    char *p = buf;
     
    502628    while (match_status == 0) {
    503629        char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL,
    504                 growline ? : "",
     630                growline ? growline : "",
    505631                match_structs.rm_so, str,
    506632                match_structs.rm_eo - match_structs.rm_so,
    507633                        str + match_structs.rm_so);
    508         free(growline); growline = new;
     634        free(growline);
     635        growline = new;
    509636        str += match_structs.rm_eo;
    510637        line += match_structs.rm_eo;
     
    513640        /* Most of the time doesn't find the regex, optimize for that */
    514641        match_status = regexec(&pattern, line, 1, &match_structs, eflags);
    515     }
    516 
     642        /* if even "" matches, treat it as "not a match" */
     643        if (match_structs.rm_so >= match_structs.rm_eo)
     644            match_status = 1;
     645    }
     646
     647    lineno_str(nbuf9, line);
    517648    if (!growline) {
    518         printf(CLEAR_2_EOL"%s\n", str);
     649        printf(CLEAR_2_EOL"%s%s\n", nbuf9, str);
    519650        return;
    520651    }
    521     printf(CLEAR_2_EOL"%s%s\n", growline, str);
     652    printf(CLEAR_2_EOL"%s%s%s\n", nbuf9, growline, str);
    522653    free(growline);
    523654}
     
    529660{
    530661    char buf[width];
     662    char nbuf9[9];
    531663    char *p;
    532664    size_t n;
    533665
    534     printf(CLEAR_2_EOL);
     666    lineno_str(nbuf9, str);
     667    printf(CLEAR_2_EOL"%s", nbuf9);
     668
    535669    while (*str) {
    536670        n = strcspn(str, controls);
     
    562696static void buffer_print(void)
    563697{
    564     int i;
     698    unsigned i;
    565699
    566700    move_cursor(0, 0);
     
    575709static void buffer_fill_and_print(void)
    576710{
    577     int i;
     711    unsigned i;
     712#if ENABLE_FEATURE_LESS_DASHCMD
     713    int fpos = cur_fline;
     714
     715    if (option_mask32 & FLAG_S) {
     716        /* Go back to the beginning of this line */
     717        while (fpos && LINENO(flines[fpos]) == LINENO(flines[fpos-1]))
     718            fpos--;
     719    }
     720
     721    i = 0;
     722    while (i <= max_displayed_line && fpos <= max_fline) {
     723        int lineno = LINENO(flines[fpos]);
     724        buffer[i] = flines[fpos];
     725        i++;
     726        do {
     727            fpos++;
     728        } while ((fpos <= max_fline)
     729              && (option_mask32 & FLAG_S)
     730              && lineno == LINENO(flines[fpos])
     731        );
     732    }
     733#else
    578734    for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) {
    579735        buffer[i] = flines[cur_fline + i];
    580736    }
     737#endif
    581738    for (; i <= max_displayed_line; i++) {
    582739        buffer[i] = empty_line_marker;
     
    619776{
    620777    if (filename) {
    621         int fd = xopen(filename, O_RDONLY);
    622         dup2(fd, 0);
    623         if (fd) close(fd);
     778        xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO);
    624779    } else {
    625780        /* "less" with no arguments in argv[] */
     
    629784    readpos = 0;
    630785    readeof = 0;
    631     linepos = 0;
     786    last_line_pos = 0;
    632787    terminated = 1;
    633788    read_lines();
     
    637792static void reinitialize(void)
    638793{
    639     int i;
     794    unsigned i;
    640795
    641796    if (flines) {
    642797        for (i = 0; i <= max_fline; i++)
    643             free((void*)(flines[i]));
     798            free(MEMPTR(flines[i]));
    644799        free(flines);
    645800        flines = NULL;
     
    653808}
    654809
    655 static void getch_nowait(char* input, int sz)
    656 {
    657     ssize_t rd;
    658     fd_set readfds;
     810static int getch_nowait(void)
     811{
     812    int rd;
     813    struct pollfd pfd[2];
     814
     815    pfd[0].fd = STDIN_FILENO;
     816    pfd[0].events = POLLIN;
     817    pfd[1].fd = kbd_fd;
     818    pfd[1].events = POLLIN;
    659819 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
     820    tcsetattr(kbd_fd, TCSANOW, &term_less);
     821    /* NB: select/poll returns whenever read will not block. Therefore:
     822     * if eof is reached, select/poll will return immediately
     823     * because read will immediately return 0 bytes.
     824     * Even if select/poll says that input is available, read CAN block
    667825     * (switch fd into O_NONBLOCK'ed mode to avoid it)
    668826     */
    669     FD_ZERO(&readfds);
    670     if (max_fline <= cur_fline + max_displayed_line
    671      && eof_error > 0 /* did NOT reach eof yet */
     827    rd = 1;
     828    /* Are we interested in stdin? */
     829//TODO: reuse code for determining this
     830    if (!(option_mask32 & FLAG_S)
     831       ? !(max_fline > cur_fline + max_displayed_line)
     832       : !(max_fline >= cur_fline
     833           && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line)
    672834    ) {
    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();
     835        if (eof_error > 0) /* did NOT reach eof yet */
     836            rd = 0; /* yes, we are interested in stdin */
     837    }
     838    /* Position cursor if line input is done */
     839    if (less_gets_pos >= 0)
     840        move_cursor(max_displayed_line + 2, less_gets_pos + 1);
     841    fflush_all();
     842
     843    if (kbd_input[0] == 0) { /* if nothing is buffered */
     844#if ENABLE_FEATURE_LESS_WINCH
     845        while (1) {
     846            int r;
     847            /* NB: SIGWINCH interrupts poll() */
     848            r = poll(pfd + rd, 2 - rd, -1);
     849            if (/*r < 0 && errno == EINTR &&*/ winch_counter)
     850                return '\\'; /* anything which has no defined function */
     851            if (r) break;
     852        }
     853#else
     854        safe_poll(pfd + rd, 2 - rd, -1);
     855#endif
     856    }
     857
     858    /* We have kbd_fd in O_NONBLOCK mode, read inside read_key()
     859     * would not block even if there is no input available */
     860    rd = read_key(kbd_fd, kbd_input, /*timeout off:*/ -2);
     861    if (rd == -1) {
     862        if (errno == EAGAIN) {
     863            /* No keyboard input available. Since poll() did return,
     864             * we should have input on stdin */
     865            read_lines();
     866            buffer_fill_and_print();
     867            goto again;
     868        }
     869        /* EOF/error (ssh session got killed etc) */
     870        less_exit(0);
     871    }
     872    set_tty_cooked();
     873    return rd;
     874}
     875
     876/* Grab a character from input without requiring the return key.
     877 * May return KEYCODE_xxx values.
     878 * Note that this function works best with raw input. */
     879static int less_getch(int pos)
     880{
     881    int i;
     882
     883 again:
     884    less_gets_pos = pos;
     885    i = getch_nowait();
     886    less_gets_pos = -1;
     887
     888    /* Discard Ctrl-something chars */
     889    if (i >= 0 && i < ' ' && i != 0x0d && i != 8)
    690890        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. */
    697 static 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();
    729891    return i;
    730892}
     
    732894static char* less_gets(int sz)
    733895{
    734     char c;
    735     int i = 0;
     896    int c;
     897    unsigned i = 0;
    736898    char *result = xzalloc(1);
     899
    737900    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 
    744901        c = '\0';
    745         read(kbd_fd, &c, 1);
    746         if (c == 0x0d)
     902        less_gets_pos = sz + i;
     903        c = getch_nowait();
     904        if (c == 0x0d) {
     905            result[i] = '\0';
     906            less_gets_pos = -1;
    747907            return result;
     908        }
    748909        if (c == 0x7f)
    749910            c = 8;
     
    752913            i--;
    753914        }
    754         if (c < ' ')
     915        if (c < ' ') /* filters out KEYCODE_xxx too (<0) */
    755916            continue;
    756917        if (i >= width - sz - 1)
    757918            continue; /* len limit */
    758         putchar(c);
     919        bb_putchar(c);
    759920        result[i++] = c;
    760921        result = xrealloc(result, i+1);
    761         result[i] = '\0';
    762922    }
    763923}
     
    765925static void examine_file(void)
    766926{
     927    char *new_fname;
     928
    767929    print_statusline("Examine: ");
     930    new_fname = less_gets(sizeof("Examine: ") - 1);
     931    if (!new_fname[0]) {
     932        status_print();
     933 err:
     934        free(new_fname);
     935        return;
     936    }
     937    if (access(new_fname, R_OK) != 0) {
     938        print_statusline("Cannot read this file");
     939        goto err;
     940    }
    768941    free(filename);
    769     filename = less_gets(sizeof("Examine: ")-1);
     942    filename = new_fname;
    770943    /* files start by = argv. why we assume that argv is infinitely long??
    771944    files[num_files] = filename;
     
    795968static void remove_current_file(void)
    796969{
    797     int i;
     970    unsigned i;
    798971
    799972    if (num_files < 2)
     
    821994    print_statusline(" :");
    822995
    823     keypress = less_getch();
     996    keypress = less_getch(2);
    824997    switch (keypress) {
    825998    case 'd':
     
    8411014        break;
    8421015    case 'q':
    843         less_exit(0);
     1016        less_exit(EXIT_SUCCESS);
    8441017        break;
    8451018    case 'x':
     
    8611034static void goto_match(int match)
    8621035{
    863     int sv;
    864 
    8651036    if (!pattern_valid)
    8661037        return;
    8671038    if (match < 0)
    8681039        match = 0;
    869     sv = cur_fline;
    8701040    /* Try to find next match if eof isn't reached yet */
    8711041    if (match >= num_matches && eof_error > 0) {
    872         cur_fline = MAXLINES; /* look as far as needed */
     1042        wanted_match = match; /* "I want to read until I see N'th match" */
    8731043        read_lines();
    8741044    }
    8751045    if (num_matches) {
    876         cap_cur_fline(cur_fline);
    8771046        normalize_match_pos(match);
    8781047        buffer_line(match_lines[match_pos]);
    8791048    } else {
    880         cur_fline = sv;
    8811049        print_statusline("No matches found");
    8821050    }
     
    8941062         && !(num_matches && match_lines[num_matches-1] == pos)
    8951063        ) {
    896             match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int));
     1064            match_lines = xrealloc_vector(match_lines, 4, num_matches);
    8971065            match_lines[num_matches++] = pos;
    8981066        }
     
    9171085    /* Get the uncompiled regular expression from the user */
    9181086    clear_line();
    919     putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
     1087    bb_putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
    9201088    uncomp_regex = less_gets(1);
    9211089    if (!uncomp_regex[0]) {
     
    9261094
    9271095    /* Compile the regex and check for errors */
    928     err = regcomp_or_errmsg(&pattern, uncomp_regex, 0);
     1096    err = regcomp_or_errmsg(&pattern, uncomp_regex,
     1097                (option_mask32 & FLAG_I) ? REG_ICASE : 0);
    9291098    free(uncomp_regex);
    9301099    if (err) {
     
    9381107    fill_match_lines(0);
    9391108    while (match_pos < num_matches) {
    940         if (match_lines[match_pos] > cur_fline)
     1109        if ((int)match_lines[match_pos] > cur_fline)
    9411110            break;
    9421111        match_pos++;
     
    9541123static void number_process(int first_digit)
    9551124{
    956     int i = 1;
     1125    unsigned i;
    9571126    int num;
     1127    int keypress;
    9581128    char num_input[sizeof(int)*4]; /* more than enough */
    959     char keypress;
    9601129
    9611130    num_input[0] = first_digit;
     
    9661135
    9671136    /* Receive input until a letter is given */
     1137    i = 1;
    9681138    while (i < sizeof(num_input)-1) {
    969         num_input[i] = less_getch();
    970         if (!num_input[i] || !isdigit(num_input[i]))
     1139        keypress = less_getch(i + 1);
     1140        if ((unsigned)keypress > 255 || !isdigit(num_input[i]))
    9711141            break;
    972         putchar(num_input[i]);
     1142        num_input[i] = keypress;
     1143        bb_putchar(keypress);
    9731144        i++;
    9741145    }
    9751146
    976     /* Take the final letter out of the digits string */
    977     keypress = num_input[i];
    9781147    num_input[i] = '\0';
    9791148    num = bb_strtou(num_input, NULL, 10);
     
    9861155    /* We now know the number and the letter entered, so we process them */
    9871156    switch (keypress) {
    988     case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
     1157    case KEYCODE_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
    9891158        buffer_down(num);
    9901159        break;
    991     case KEY_UP: case 'b': case 'w': case 'y': case 'u':
     1160    case KEYCODE_UP: case 'b': case 'w': case 'y': case 'u':
    9921161        buffer_up(num);
    9931162        break;
     
    10191188}
    10201189
    1021 #if ENABLE_FEATURE_LESS_FLAGCS
     1190#if ENABLE_FEATURE_LESS_DASHCMD
    10221191static void flag_change(void)
    10231192{
     
    10251194
    10261195    clear_line();
    1027     putchar('-');
    1028     keypress = less_getch();
     1196    bb_putchar('-');
     1197    keypress = less_getch(1);
    10291198
    10301199    switch (keypress) {
     
    10411210        option_mask32 ^= FLAG_TILDE;
    10421211        break;
    1043     }
    1044 }
    1045 
     1212    case 'S':
     1213        option_mask32 ^= FLAG_S;
     1214        buffer_fill_and_print();
     1215        break;
     1216#if ENABLE_FEATURE_LESS_LINENUMS
     1217    case 'N':
     1218        option_mask32 ^= FLAG_N;
     1219        re_wrap();
     1220        buffer_fill_and_print();
     1221        break;
     1222#endif
     1223    }
     1224}
     1225
     1226#ifdef BLOAT
    10461227static void show_flag_status(void)
    10471228{
     
    10501231
    10511232    clear_line();
    1052     putchar('_');
    1053     keypress = less_getch();
     1233    bb_putchar('_');
     1234    keypress = less_getch(1);
    10541235
    10551236    switch (keypress) {
     
    10791260#endif
    10801261
     1262#endif /* ENABLE_FEATURE_LESS_DASHCMD */
     1263
    10811264static void save_input_to_file(void)
    10821265{
    10831266    const char *msg = "";
    10841267    char *current_line;
    1085     int i;
     1268    unsigned i;
    10861269    FILE *fp;
    10871270
    10881271    print_statusline("Log file: ");
    10891272    current_line = less_gets(sizeof("Log file: ")-1);
    1090     if (strlen(current_line) > 0) {
    1091         fp = fopen(current_line, "w");
     1273    if (current_line[0]) {
     1274        fp = fopen_for_write(current_line);
    10921275        if (!fp) {
    10931276            msg = "Error opening log file";
     
    11101293
    11111294    print_statusline("Mark: ");
    1112     letter = less_getch();
     1295    letter = less_getch(sizeof("Mark: ") - 1);
    11131296
    11141297    if (isalpha(letter)) {
     
    11311314
    11321315    print_statusline("Go to mark: ");
    1133     letter = less_getch();
     1316    letter = less_getch(sizeof("Go to mark: ") - 1);
    11341317    clear_line();
    11351318
     
    11511334{
    11521335    switch (bracket) {
    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;
     1336        case '{': case '[': /* '}' == '{' + 2. Same for '[' */
     1337            bracket++;
     1338        case '(':           /* ')' == '(' + 1 */
     1339            bracket++;
     1340            break;
     1341        case '}': case ']':
     1342            bracket--;
     1343        case ')':
     1344            bracket--;
     1345            break;
     1346    };
     1347    return bracket;
    11631348}
    11641349
    11651350static void match_right_bracket(char bracket)
    11661351{
    1167     int bracket_line = -1;
    1168     int i;
     1352    unsigned i;
    11691353
    11701354    if (strchr(flines[cur_fline], bracket) == NULL) {
     
    11721356        return;
    11731357    }
     1358    bracket = opp_bracket(bracket);
    11741359    for (i = cur_fline + 1; i < max_fline; i++) {
    1175         if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
    1176             bracket_line = i;
    1177             break;
    1178         }
    1179     }
    1180     if (bracket_line == -1)
    1181         print_statusline("No matching bracket found");
    1182     buffer_line(bracket_line - max_displayed_line);
     1360        if (strchr(flines[i], bracket) != NULL) {
     1361            buffer_line(i);
     1362            return;
     1363        }
     1364    }
     1365    print_statusline("No matching bracket found");
    11831366}
    11841367
    11851368static void match_left_bracket(char bracket)
    11861369{
    1187     int bracket_line = -1;
    11881370    int i;
    11891371
     
    11931375    }
    11941376
     1377    bracket = opp_bracket(bracket);
    11951378    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;
    1199         }
    1200     }
    1201     if (bracket_line == -1)
    1202         print_statusline("No matching bracket found");
    1203     buffer_line(bracket_line);
     1379        if (strchr(flines[i], bracket) != NULL) {
     1380            buffer_line(i);
     1381            return;
     1382        }
     1383    }
     1384    print_statusline("No matching bracket found");
    12041385}
    12051386#endif  /* FEATURE_LESS_BRACKETS */
     
    12081389{
    12091390    switch (keypress) {
    1210     case KEY_DOWN: case 'e': case 'j': case 0x0d:
     1391    case KEYCODE_DOWN: case 'e': case 'j': case 0x0d:
    12111392        buffer_down(1);
    12121393        break;
    1213     case KEY_UP: case 'y': case 'k':
     1394    case KEYCODE_UP: case 'y': case 'k':
    12141395        buffer_up(1);
    12151396        break;
    1216     case PAGE_DOWN: case ' ': case 'z':
     1397    case KEYCODE_PAGEDOWN: case ' ': case 'z': case 'f':
    12171398        buffer_down(max_displayed_line + 1);
    12181399        break;
    1219     case PAGE_UP: case 'w': case 'b':
     1400    case KEYCODE_PAGEUP: case 'w': case 'b':
    12201401        buffer_up(max_displayed_line + 1);
    12211402        break;
     
    12261407        buffer_up((max_displayed_line + 1) / 2);
    12271408        break;
    1228     case KEY_HOME: case 'g': case 'p': case '<': case '%':
     1409    case KEYCODE_HOME: case 'g': case 'p': case '<': case '%':
    12291410        buffer_line(0);
    12301411        break;
    1231     case KEY_END: case 'G': case '>':
     1412    case KEYCODE_END: case 'G': case '>':
    12321413        cur_fline = MAXLINES;
    12331414        read_lines();
     
    12351416        break;
    12361417    case 'q': case 'Q':
    1237         less_exit(0);
     1418        less_exit(EXIT_SUCCESS);
    12381419        break;
    12391420#if ENABLE_FEATURE_LESS_MARKS
     
    12801461        break;
    12811462#endif
    1282 #if ENABLE_FEATURE_LESS_FLAGCS
     1463#if ENABLE_FEATURE_LESS_DASHCMD
    12831464    case '-':
    12841465        flag_change();
    12851466        buffer_print();
    12861467        break;
     1468#ifdef BLOAT
    12871469    case '_':
    12881470        show_flag_status();
    12891471        break;
     1472#endif
    12901473#endif
    12911474#if ENABLE_FEATURE_LESS_BRACKETS
     
    13061489}
    13071490
    1308 static void sig_catcher(int sig ATTRIBUTE_UNUSED)
    1309 {
    1310     set_tty_cooked();
    1311     exit(1);
    1312 }
    1313 
    1314 int less_main(int argc, char **argv);
     1491static void sig_catcher(int sig)
     1492{
     1493    less_exit(- sig);
     1494}
     1495
     1496#if ENABLE_FEATURE_LESS_WINCH
     1497static void sigwinch_handler(int sig UNUSED_PARAM)
     1498{
     1499    winch_counter++;
     1500}
     1501#endif
     1502
     1503int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
    13151504int less_main(int argc, char **argv)
    13161505{
     
    13221511    /* -xxx: newline also */
    13231512    /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
    1324     getopt32(argv, "EMmN~");
     1513    getopt32(argv, "EMmN~I" IF_FEATURE_LESS_DASHCMD("S"));
    13251514    argc -= optind;
    13261515    argv += optind;
     
    13311520     * is not a tty and turns into cat. This makes sense. */
    13321521    if (!isatty(STDOUT_FILENO))
    1333         return bb_cat(argv);
    1334     kbd_fd = open(CURRENT_TTY, O_RDONLY);
    1335     if (kbd_fd < 0)
    13361522        return bb_cat(argv);
    13371523
     
    13421528            bb_show_usage();
    13431529        }
    1344     } else
     1530    } else {
    13451531        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 *));
     1532    }
     1533
    13541534    if (option_mask32 & FLAG_TILDE)
    13551535        empty_line_marker = "";
    13561536
     1537    kbd_fd = open(CURRENT_TTY, O_RDONLY);
     1538    if (kbd_fd < 0)
     1539        return bb_cat(argv);
     1540    ndelay_on(kbd_fd);
     1541
    13571542    tcgetattr(kbd_fd, &term_orig);
    1358     signal(SIGTERM, sig_catcher);
    1359     signal(SIGINT, sig_catcher);
    13601543    term_less = term_orig;
    13611544    term_less.c_lflag &= ~(ICANON | ECHO);
     
    13651548    term_less.c_cc[VTIME] = 0;
    13661549
    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 
     1550    get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
     1551    /* 20: two tabstops + 4 */
     1552    if (width < 20 || max_displayed_line < 3)
     1553        return bb_cat(argv);
     1554    max_displayed_line -= 2;
     1555
     1556    /* We want to restore term_orig on exit */
     1557    bb_signals(BB_FATAL_SIGS, sig_catcher);
     1558#if ENABLE_FEATURE_LESS_WINCH
     1559    signal(SIGWINCH, sigwinch_handler);
     1560#endif
     1561
     1562    buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
    13711563    reinitialize();
    13721564    while (1) {
    1373         keypress = less_getch();
     1565#if ENABLE_FEATURE_LESS_WINCH
     1566        while (WINCH_COUNTER) {
     1567 again:
     1568            winch_counter--;
     1569            get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
     1570            /* 20: two tabstops + 4 */
     1571            if (width < 20)
     1572                width = 20;
     1573            if (max_displayed_line < 3)
     1574                max_displayed_line = 3;
     1575            max_displayed_line -= 2;
     1576            free(buffer);
     1577            buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
     1578            /* Avoid re-wrap and/or redraw if we already know
     1579             * we need to do it again. These ops are expensive */
     1580            if (WINCH_COUNTER)
     1581                goto again;
     1582            re_wrap();
     1583            if (WINCH_COUNTER)
     1584                goto again;
     1585            buffer_fill_and_print();
     1586            /* This took some time. Loop back and check,
     1587             * were there another SIGWINCH? */
     1588        }
     1589#endif
     1590        keypress = less_getch(-1); /* -1: do not position cursor */
    13741591        keypress_process(keypress);
    13751592    }
    13761593}
     1594
     1595/*
     1596Help text of less version 418 is below.
     1597If you are implementing something, keeping
     1598key and/or command line switch compatibility is a good idea:
     1599
     1600
     1601                   SUMMARY OF LESS COMMANDS
     1602
     1603      Commands marked with * may be preceded by a number, N.
     1604      Notes in parentheses indicate the behavior if N is given.
     1605  h  H                 Display this help.
     1606  q  :q  Q  :Q  ZZ     Exit.
     1607 ---------------------------------------------------------------------------
     1608                           MOVING
     1609  e  ^E  j  ^N  CR  *  Forward  one line   (or N lines).
     1610  y  ^Y  k  ^K  ^P  *  Backward one line   (or N lines).
     1611  f  ^F  ^V  SPACE  *  Forward  one window (or N lines).
     1612  b  ^B  ESC-v      *  Backward one window (or N lines).
     1613  z                 *  Forward  one window (and set window to N).
     1614  w                 *  Backward one window (and set window to N).
     1615  ESC-SPACE         *  Forward  one window, but don't stop at end-of-file.
     1616  d  ^D             *  Forward  one half-window (and set half-window to N).
     1617  u  ^U             *  Backward one half-window (and set half-window to N).
     1618  ESC-)  RightArrow *  Left  one half screen width (or N positions).
     1619  ESC-(  LeftArrow  *  Right one half screen width (or N positions).
     1620  F                    Forward forever; like "tail -f".
     1621  r  ^R  ^L            Repaint screen.
     1622  R                    Repaint screen, discarding buffered input.
     1623        ---------------------------------------------------
     1624        Default "window" is the screen height.
     1625        Default "half-window" is half of the screen height.
     1626 ---------------------------------------------------------------------------
     1627                          SEARCHING
     1628  /pattern          *  Search forward for (N-th) matching line.
     1629  ?pattern          *  Search backward for (N-th) matching line.
     1630  n                 *  Repeat previous search (for N-th occurrence).
     1631  N                 *  Repeat previous search in reverse direction.
     1632  ESC-n             *  Repeat previous search, spanning files.
     1633  ESC-N             *  Repeat previous search, reverse dir. & spanning files.
     1634  ESC-u                Undo (toggle) search highlighting.
     1635        ---------------------------------------------------
     1636        Search patterns may be modified by one or more of:
     1637        ^N or !  Search for NON-matching lines.
     1638        ^E or *  Search multiple files (pass thru END OF FILE).
     1639        ^F or @  Start search at FIRST file (for /) or last file (for ?).
     1640        ^K       Highlight matches, but don't move (KEEP position).
     1641        ^R       Don't use REGULAR EXPRESSIONS.
     1642 ---------------------------------------------------------------------------
     1643                           JUMPING
     1644  g  <  ESC-<       *  Go to first line in file (or line N).
     1645  G  >  ESC->       *  Go to last line in file (or line N).
     1646  p  %              *  Go to beginning of file (or N percent into file).
     1647  t                 *  Go to the (N-th) next tag.
     1648  T                 *  Go to the (N-th) previous tag.
     1649  {  (  [           *  Find close bracket } ) ].
     1650  }  )  ]           *  Find open bracket { ( [.
     1651  ESC-^F <c1> <c2>  *  Find close bracket <c2>.
     1652  ESC-^B <c1> <c2>  *  Find open bracket <c1>
     1653        ---------------------------------------------------
     1654        Each "find close bracket" command goes forward to the close bracket
     1655          matching the (N-th) open bracket in the top line.
     1656        Each "find open bracket" command goes backward to the open bracket
     1657          matching the (N-th) close bracket in the bottom line.
     1658  m<letter>            Mark the current position with <letter>.
     1659  '<letter>            Go to a previously marked position.
     1660  ''                   Go to the previous position.
     1661  ^X^X                 Same as '.
     1662        ---------------------------------------------------
     1663        A mark is any upper-case or lower-case letter.
     1664        Certain marks are predefined:
     1665             ^  means  beginning of the file
     1666             $  means  end of the file
     1667 ---------------------------------------------------------------------------
     1668                        CHANGING FILES
     1669  :e [file]            Examine a new file.
     1670  ^X^V                 Same as :e.
     1671  :n                *  Examine the (N-th) next file from the command line.
     1672  :p                *  Examine the (N-th) previous file from the command line.
     1673  :x                *  Examine the first (or N-th) file from the command line.
     1674  :d                   Delete the current file from the command line list.
     1675  =  ^G  :f            Print current file name.
     1676 ---------------------------------------------------------------------------
     1677                    MISCELLANEOUS COMMANDS
     1678  -<flag>              Toggle a command line option [see OPTIONS below].
     1679  --<name>             Toggle a command line option, by name.
     1680  _<flag>              Display the setting of a command line option.
     1681  __<name>             Display the setting of an option, by name.
     1682  +cmd                 Execute the less cmd each time a new file is examined.
     1683  !command             Execute the shell command with $SHELL.
     1684  |Xcommand            Pipe file between current pos & mark X to shell command.
     1685  v                    Edit the current file with $VISUAL or $EDITOR.
     1686  V                    Print version number of "less".
     1687 ---------------------------------------------------------------------------
     1688                           OPTIONS
     1689        Most options may be changed either on the command line,
     1690        or from within less by using the - or -- command.
     1691        Options may be given in one of two forms: either a single
     1692        character preceded by a -, or a name preceeded by --.
     1693  -?  ........  --help
     1694                  Display help (from command line).
     1695  -a  ........  --search-skip-screen
     1696                  Forward search skips current screen.
     1697  -b [N]  ....  --buffers=[N]
     1698                  Number of buffers.
     1699  -B  ........  --auto-buffers
     1700                  Don't automatically allocate buffers for pipes.
     1701  -c  ........  --clear-screen
     1702                  Repaint by clearing rather than scrolling.
     1703  -d  ........  --dumb
     1704                  Dumb terminal.
     1705  -D [xn.n]  .  --color=xn.n
     1706                  Set screen colors. (MS-DOS only)
     1707  -e  -E  ....  --quit-at-eof  --QUIT-AT-EOF
     1708                  Quit at end of file.
     1709  -f  ........  --force
     1710                  Force open non-regular files.
     1711  -F  ........  --quit-if-one-screen
     1712                  Quit if entire file fits on first screen.
     1713  -g  ........  --hilite-search
     1714                  Highlight only last match for searches.
     1715  -G  ........  --HILITE-SEARCH
     1716                  Don't highlight any matches for searches.
     1717  -h [N]  ....  --max-back-scroll=[N]
     1718                  Backward scroll limit.
     1719  -i  ........  --ignore-case
     1720                  Ignore case in searches that do not contain uppercase.
     1721  -I  ........  --IGNORE-CASE
     1722                  Ignore case in all searches.
     1723  -j [N]  ....  --jump-target=[N]
     1724                  Screen position of target lines.
     1725  -J  ........  --status-column
     1726                  Display a status column at left edge of screen.
     1727  -k [file]  .  --lesskey-file=[file]
     1728                  Use a lesskey file.
     1729  -L  ........  --no-lessopen
     1730                  Ignore the LESSOPEN environment variable.
     1731  -m  -M  ....  --long-prompt  --LONG-PROMPT
     1732                  Set prompt style.
     1733  -n  -N  ....  --line-numbers  --LINE-NUMBERS
     1734                  Don't use line numbers.
     1735  -o [file]  .  --log-file=[file]
     1736                  Copy to log file (standard input only).
     1737  -O [file]  .  --LOG-FILE=[file]
     1738                  Copy to log file (unconditionally overwrite).
     1739  -p [pattern]  --pattern=[pattern]
     1740                  Start at pattern (from command line).
     1741  -P [prompt]   --prompt=[prompt]
     1742                  Define new prompt.
     1743  -q  -Q  ....  --quiet  --QUIET  --silent --SILENT
     1744                  Quiet the terminal bell.
     1745  -r  -R  ....  --raw-control-chars  --RAW-CONTROL-CHARS
     1746                  Output "raw" control characters.
     1747  -s  ........  --squeeze-blank-lines
     1748                  Squeeze multiple blank lines.
     1749  -S  ........  --chop-long-lines
     1750                  Chop long lines.
     1751  -t [tag]  ..  --tag=[tag]
     1752                  Find a tag.
     1753  -T [tagsfile] --tag-file=[tagsfile]
     1754                  Use an alternate tags file.
     1755  -u  -U  ....  --underline-special  --UNDERLINE-SPECIAL
     1756                  Change handling of backspaces.
     1757  -V  ........  --version
     1758                  Display the version number of "less".
     1759  -w  ........  --hilite-unread
     1760                  Highlight first new line after forward-screen.
     1761  -W  ........  --HILITE-UNREAD
     1762                  Highlight first new line after any forward movement.
     1763  -x [N[,...]]  --tabs=[N[,...]]
     1764                  Set tab stops.
     1765  -X  ........  --no-init
     1766                  Don't use termcap init/deinit strings.
     1767                --no-keypad
     1768                  Don't use termcap keypad init/deinit strings.
     1769  -y [N]  ....  --max-forw-scroll=[N]
     1770                  Forward scroll limit.
     1771  -z [N]  ....  --window=[N]
     1772                  Set size of window.
     1773  -" [c[c]]  .  --quotes=[c[c]]
     1774                  Set shell quote characters.
     1775  -~  ........  --tilde
     1776                  Don't display tildes after end of file.
     1777  -# [N]  ....  --shift=[N]
     1778                  Horizontal scroll amount (0 = one half screen width)
     1779
     1780 ---------------------------------------------------------------------------
     1781                          LINE EDITING
     1782        These keys can be used to edit text being entered
     1783        on the "command line" at the bottom of the screen.
     1784 RightArrow                       ESC-l     Move cursor right one character.
     1785 LeftArrow                        ESC-h     Move cursor left one character.
     1786 CNTL-RightArrow  ESC-RightArrow  ESC-w     Move cursor right one word.
     1787 CNTL-LeftArrow   ESC-LeftArrow   ESC-b     Move cursor left one word.
     1788 HOME                             ESC-0     Move cursor to start of line.
     1789 END                              ESC-$     Move cursor to end of line.
     1790 BACKSPACE                                  Delete char to left of cursor.
     1791 DELETE                           ESC-x     Delete char under cursor.
     1792 CNTL-BACKSPACE   ESC-BACKSPACE             Delete word to left of cursor.
     1793 CNTL-DELETE      ESC-DELETE      ESC-X     Delete word under cursor.
     1794 CNTL-U           ESC (MS-DOS only)         Delete entire line.
     1795 UpArrow                          ESC-k     Retrieve previous command line.
     1796 DownArrow                        ESC-j     Retrieve next command line.
     1797 TAB                                        Complete filename & cycle.
     1798 SHIFT-TAB                        ESC-TAB   Complete filename & reverse cycle.
     1799 CNTL-L                                     Complete filename, list all.
     1800*/
Note: See TracChangeset for help on using the changeset viewer.