blob: 1a67ca7ce31017c7154750eab21932a73e5a68ee [file] [log] [blame]
Rob Landley9200e792005-09-15 19:26:59 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Mini less implementation for busybox
4 *
Rob Landley9200e792005-09-15 19:26:59 +00005 * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
6 *
Mike Frysingerf284c762006-04-16 20:38:26 +00007 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8 */
9
10/*
Denis Vlasenko3f3190e2006-12-21 00:22:03 +000011 * TODO:
12 * - Add more regular expression support - search modifiers, certain matches, etc.
13 * - Add more complex bracket searching - currently, nested brackets are
Denis Vlasenko5a4f0992006-12-25 01:23:02 +000014 * not considered.
Denis Vlasenko3f3190e2006-12-21 00:22:03 +000015 * - Add support for "F" as an input. This causes less to act in
Denis Vlasenko5a4f0992006-12-25 01:23:02 +000016 * a similar way to tail -f.
Denis Vlasenko3f3190e2006-12-21 00:22:03 +000017 * - Allow horizontal scrolling.
Rob Landley9200e792005-09-15 19:26:59 +000018 *
Denis Vlasenko3f3190e2006-12-21 00:22:03 +000019 * Notes:
20 * - the inp file pointer is used so that keyboard input works after
Denis Vlasenko5a4f0992006-12-25 01:23:02 +000021 * redirected input has been read from stdin
Denis Vlasenko3f3190e2006-12-21 00:22:03 +000022 */
Rob Landley9200e792005-09-15 19:26:59 +000023
Denis Vlasenko18d6fc12007-03-08 17:52:36 +000024#include <sched.h> /* sched_yield() */
25
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000026#include "libbb.h"
Denis Vlasenko9a7cef92006-12-20 02:46:48 +000027#if ENABLE_FEATURE_LESS_REGEXP
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000028#include "xregex.h"
29#endif
30
Denis Vlasenkof4dff772006-12-24 07:14:17 +000031/* FIXME: currently doesn't work right */
32#undef ENABLE_FEATURE_LESS_FLAGCS
33#define ENABLE_FEATURE_LESS_FLAGCS 0
Rob Landley9200e792005-09-15 19:26:59 +000034
35/* The escape codes for highlighted and normal text */
36#define HIGHLIGHT "\033[7m"
37#define NORMAL "\033[0m"
Rob Landley9200e792005-09-15 19:26:59 +000038/* The escape code to clear the screen */
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000039#define CLEAR "\033[H\033[J"
Denis Vlasenkoe865e812006-12-21 13:24:58 +000040/* The escape code to clear to end of line */
41#define CLEAR_2_EOL "\033[K"
Rob Landley9200e792005-09-15 19:26:59 +000042
Denis Vlasenkof4dff772006-12-24 07:14:17 +000043/* 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',
Denis Vlasenkof5a15762007-03-09 08:55:23 +000051 REAL_KEY_HOME = '7', // vt100? linux vt? or what?
Denis Vlasenkof4dff772006-12-24 07:14:17 +000052 REAL_KEY_END = '8',
Denis Vlasenkof5a15762007-03-09 08:55:23 +000053 REAL_KEY_HOME_ALT = '1', // ESC [1~ (vt100? linux vt? or what?)
Denis Vlasenkoc86e0522007-03-20 11:30:28 +000054 REAL_KEY_END_ALT = '4', // ESC [4~
Denis Vlasenkob30418a2007-02-14 20:49:14 +000055 REAL_KEY_HOME_XTERM = 'H',
56 REAL_KEY_END_XTERM = 'F',
Rob Landley9200e792005-09-15 19:26:59 +000057
Denis Vlasenkof4dff772006-12-24 07:14:17 +000058/* 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};
74
Rob Landley9200e792005-09-15 19:26:59 +000075/* Command line options */
Denis Vlasenkof4dff772006-12-24 07:14:17 +000076enum {
77 FLAG_E = 1,
78 FLAG_M = 1 << 1,
79 FLAG_m = 1 << 2,
80 FLAG_N = 1 << 3,
81 FLAG_TILDE = 1 << 4,
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +000082/* hijack command line options variable for internal state vars */
Denis Vlasenkof4dff772006-12-24 07:14:17 +000083 LESS_STATE_MATCH_BACKWARDS = 1 << 15,
84};
Rob Landley9200e792005-09-15 19:26:59 +000085
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +000086#if !ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenko3bba5452006-12-30 17:57:03 +000087enum { pattern_valid = 0 };
Rob Landley9200e792005-09-15 19:26:59 +000088#endif
89
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +000090struct globals {
91 int cur_fline; /* signed */
92 int kbd_fd; /* fd to get input from */
Denis Vlasenko33196372008-02-23 01:25:38 +000093 int less_gets_pos;
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +000094/* last position in last line, taking into account tabs */
95 size_t linepos;
96 unsigned max_displayed_line;
97 unsigned max_fline;
98 unsigned max_lineno; /* this one tracks linewrap */
99 unsigned width;
100 ssize_t eof_error; /* eof if 0, error if < 0 */
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000101 ssize_t readpos;
102 ssize_t readeof; /* must be signed */
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +0000103 const char **buffer;
104 const char **flines;
105 const char *empty_line_marker;
106 unsigned num_files;
107 unsigned current_file;
108 char *filename;
109 char **files;
110#if ENABLE_FEATURE_LESS_MARKS
111 unsigned num_marks;
112 unsigned mark_lines[15][2];
113#endif
114#if ENABLE_FEATURE_LESS_REGEXP
115 unsigned *match_lines;
116 int match_pos; /* signed! */
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000117 int wanted_match; /* signed! */
118 int num_matches;
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +0000119 regex_t pattern;
120 smallint pattern_valid;
121#endif
122 smallint terminated;
123 struct termios term_orig, term_less;
124};
125#define G (*ptr_to_globals)
126#define cur_fline (G.cur_fline )
127#define kbd_fd (G.kbd_fd )
Denis Vlasenko33196372008-02-23 01:25:38 +0000128#define less_gets_pos (G.less_gets_pos )
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +0000129#define linepos (G.linepos )
130#define max_displayed_line (G.max_displayed_line)
131#define max_fline (G.max_fline )
132#define max_lineno (G.max_lineno )
133#define width (G.width )
134#define eof_error (G.eof_error )
135#define readpos (G.readpos )
136#define readeof (G.readeof )
137#define buffer (G.buffer )
138#define flines (G.flines )
139#define empty_line_marker (G.empty_line_marker )
140#define num_files (G.num_files )
141#define current_file (G.current_file )
142#define filename (G.filename )
143#define files (G.files )
144#define num_marks (G.num_marks )
145#define mark_lines (G.mark_lines )
Denis Vlasenko342b0ab2007-05-31 23:06:18 +0000146#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +0000147#define match_lines (G.match_lines )
148#define match_pos (G.match_pos )
149#define num_matches (G.num_matches )
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000150#define wanted_match (G.wanted_match )
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +0000151#define pattern (G.pattern )
152#define pattern_valid (G.pattern_valid )
Denis Vlasenko342b0ab2007-05-31 23:06:18 +0000153#endif
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +0000154#define terminated (G.terminated )
155#define term_orig (G.term_orig )
156#define term_less (G.term_less )
157#define INIT_G() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000158 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
159 less_gets_pos = -1; \
160 empty_line_marker = "~"; \
161 num_files = 1; \
162 current_file = 1; \
163 eof_error = 1; \
164 terminated = 1; \
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000165 USE_FEATURE_LESS_REGEXP(wanted_match = -1;) \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000166} while (0)
Rob Landley9200e792005-09-15 19:26:59 +0000167
168/* Reset terminal input to normal */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000169static void set_tty_cooked(void)
170{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000171 fflush(stdout);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000172 tcsetattr(kbd_fd, TCSANOW, &term_orig);
Rob Landley9200e792005-09-15 19:26:59 +0000173}
174
Rob Landley9200e792005-09-15 19:26:59 +0000175/* Exit the program gracefully */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000176static void less_exit(int code)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000177{
Denis Vlasenko4daad902007-09-27 10:20:47 +0000178 bb_putchar('\n');
Denis Vlasenkod553faf2008-02-23 12:22:17 +0000179 set_tty_cooked();
Denis Vlasenko400d8bb2008-02-24 13:36:01 +0000180 if (code < 0)
181 kill_myself_with_sig(- code); /* does not return */
182 exit(code);
Rob Landley9200e792005-09-15 19:26:59 +0000183}
184
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000185/* Move the cursor to a position (x,y), where (0,0) is the
Rob Landley9200e792005-09-15 19:26:59 +0000186 top-left corner of the console */
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000187static void move_cursor(int line, int row)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000188{
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000189 printf("\033[%u;%uH", line, row);
Rob Landley9200e792005-09-15 19:26:59 +0000190}
191
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000192static void clear_line(void)
193{
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000194 printf("\033[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000195}
196
197static void print_hilite(const char *str)
198{
199 printf(HIGHLIGHT"%s"NORMAL, str);
Rob Landley9200e792005-09-15 19:26:59 +0000200}
201
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000202static void print_statusline(const char *str)
203{
204 clear_line();
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000205 printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str);
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000206}
207
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000208#if ENABLE_FEATURE_LESS_REGEXP
209static void fill_match_lines(unsigned pos);
210#else
211#define fill_match_lines(pos) ((void)0)
212#endif
213
Denis Vlasenko22a9a3c2007-05-31 15:56:10 +0000214/* Devilishly complex routine.
215 *
216 * Has to deal with EOF and EPIPE on input,
217 * with line wrapping, with last line not ending in '\n'
218 * (possibly not ending YET!), with backspace and tabs.
Denis Vlasenkoc2f011a2007-05-31 21:31:56 +0000219 * It reads input again if last time we got an EOF (thus supporting
220 * growing files) or EPIPE (watching output of slow process like make).
Denis Vlasenko22a9a3c2007-05-31 15:56:10 +0000221 *
222 * Variables used:
223 * flines[] - array of lines already read. Linewrap may cause
224 * one source file line to occupy several flines[n].
225 * flines[max_fline] - last line, possibly incomplete.
226 * terminated - 1 if flines[max_fline] is 'terminated'
227 * (if there was '\n' [which isn't stored itself, we just remember
228 * that it was seen])
229 * max_lineno - last line's number, this one doesn't increment
230 * on line wrap, only on "real" new lines.
231 * readbuf[0..readeof-1] - small preliminary buffer.
232 * readbuf[readpos] - next character to add to current line.
233 * linepos - screen line position of next char to be read
234 * (takes into account tabs and backspaces)
235 * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error
236 */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000237static void read_lines(void)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000238{
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000239#define readbuf bb_common_bufsiz1
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000240 char *current_line, *p;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000241 int w = width;
242 char last_terminated = terminated;
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000243#if ENABLE_FEATURE_LESS_REGEXP
244 unsigned old_max_fline = max_fline;
245 time_t last_time = 0;
246 unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */
247#endif
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000248
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000249 if (option_mask32 & FLAG_N)
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000250 w -= 8;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000251
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000252 USE_FEATURE_LESS_REGEXP(again0:)
253
254 p = current_line = xmalloc(w);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000255 max_fline += last_terminated;
256 if (!last_terminated) {
257 const char *cp = flines[max_fline];
258 if (option_mask32 & FLAG_N)
259 cp += 8;
260 strcpy(current_line, cp);
261 p += strlen(current_line);
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000262 free((char*)flines[max_fline]);
Denis Vlasenko22a9a3c2007-05-31 15:56:10 +0000263 /* linepos is still valid from previous read_lines() */
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000264 } else {
265 linepos = 0;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000266 }
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000267
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000268 while (1) { /* read lines until we reach cur_fline or wanted_match */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000269 *p = '\0';
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000270 terminated = 0;
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000271 while (1) { /* read chars until we have a line */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000272 char c;
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000273 /* if no unprocessed chars left, eat more */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000274 if (readpos >= readeof) {
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000275 ndelay_on(0);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000276 eof_error = safe_read(0, readbuf, sizeof(readbuf));
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000277 ndelay_off(0);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000278 readpos = 0;
279 readeof = eof_error;
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000280 if (eof_error <= 0)
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000281 goto reached_eof;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000282 }
283 c = readbuf[readpos];
Denis Vlasenko50ddabc2006-12-31 19:36:01 +0000284 /* backspace? [needed for manpages] */
285 /* <tab><bs> is (a) insane and */
286 /* (b) harder to do correctly, so we refuse to do it */
287 if (c == '\x8' && linepos && p[-1] != '\t') {
Denis Vlasenko95b30712006-12-31 19:23:31 +0000288 readpos++; /* eat it */
Denis Vlasenko95b30712006-12-31 19:23:31 +0000289 linepos--;
Denis Vlasenko22a9a3c2007-05-31 15:56:10 +0000290 /* was buggy (p could end up <= current_line)... */
Denis Vlasenko50ddabc2006-12-31 19:36:01 +0000291 *--p = '\0';
Denis Vlasenko95b30712006-12-31 19:23:31 +0000292 continue;
293 }
Denis Vlasenko22a9a3c2007-05-31 15:56:10 +0000294 {
295 size_t new_linepos = linepos + 1;
296 if (c == '\t') {
297 new_linepos += 7;
298 new_linepos &= (~7);
299 }
300 if (new_linepos >= w)
301 break;
302 linepos = new_linepos;
303 }
Denis Vlasenko95b30712006-12-31 19:23:31 +0000304 /* ok, we will eat this char */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000305 readpos++;
Denis Vlasenko22a9a3c2007-05-31 15:56:10 +0000306 if (c == '\n') {
307 terminated = 1;
308 linepos = 0;
309 break;
310 }
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000311 /* NUL is substituted by '\n'! */
312 if (c == '\0') c = '\n';
313 *p++ = c;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000314 *p = '\0';
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000315 } /* end of "read chars until we have a line" loop */
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000316 /* Corner case: linewrap with only "" wrapping to next line */
317 /* Looks ugly on screen, so we do not store this empty line */
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000318 if (!last_terminated && !current_line[0]) {
319 last_terminated = 1;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000320 max_lineno++;
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000321 continue;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000322 }
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000323 reached_eof:
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000324 last_terminated = terminated;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000325 flines = xrealloc(flines, (max_fline+1) * sizeof(char *));
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000326 if (option_mask32 & FLAG_N) {
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000327 /* Width of 7 preserves tab spacing in the text */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000328 flines[max_fline] = xasprintf(
329 (max_lineno <= 9999999) ? "%7u %s" : "%07u %s",
330 max_lineno % 10000000, current_line);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000331 free(current_line);
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000332 if (terminated)
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000333 max_lineno++;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000334 } else {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000335 flines[max_fline] = xrealloc(current_line, strlen(current_line)+1);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000336 }
Denis Vlasenkofdcfa2a2007-05-31 23:55:39 +0000337 if (max_fline >= MAXLINES) {
338 eof_error = 0; /* Pretend we saw EOF */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000339 break;
Denis Vlasenkofdcfa2a2007-05-31 23:55:39 +0000340 }
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000341 if (max_fline > cur_fline + max_displayed_line) {
342#if !ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000343 break;
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000344#else
345 if (wanted_match >= num_matches) { /* goto_match called us */
346 fill_match_lines(old_max_fline);
347 old_max_fline = max_fline;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000348 }
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000349 if (wanted_match < num_matches)
350 break;
351#endif
352 }
353 if (eof_error <= 0) {
354 if (eof_error < 0) {
355 if (errno == EAGAIN) {
356 /* not yet eof or error, reset flag (or else
357 * we will hog CPU - select() will return
358 * immediately */
359 eof_error = 1;
360 } else {
361 print_statusline("read error");
362 }
363 }
364#if !ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000365 break;
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000366#else
367 if (wanted_match < num_matches) {
368 break;
369 } else { /* goto_match called us */
370 time_t t = time(NULL);
371 if (t != last_time) {
372 last_time = t;
373 if (--seconds_p1 == 0)
374 break;
375 }
376 sched_yield();
377 goto again0; /* go loop again (max 2 seconds) */
378 }
379#endif
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000380 }
381 max_fline++;
382 current_line = xmalloc(w);
383 p = current_line;
384 linepos = 0;
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000385 } /* end of "read lines until we reach cur_fline" loop */
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000386 fill_match_lines(old_max_fline);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000387#undef readbuf
Rob Landley9200e792005-09-15 19:26:59 +0000388}
389
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000390#if ENABLE_FEATURE_LESS_FLAGS
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000391/* Interestingly, writing calc_percent as a function saves around 32 bytes
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000392 * on my build. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000393static int calc_percent(void)
394{
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000395 unsigned p = (100 * (cur_fline+max_displayed_line+1) + max_fline/2) / (max_fline+1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000396 return p <= 100 ? p : 100;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000397}
398
Rob Landley9200e792005-09-15 19:26:59 +0000399/* Print a status line if -M was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000400static void m_status_print(void)
401{
Rob Landley9200e792005-09-15 19:26:59 +0000402 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000403
Denis Vlasenko33196372008-02-23 01:25:38 +0000404 if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
405 return;
406
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000407 clear_line();
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000408 printf(HIGHLIGHT"%s", filename);
409 if (num_files > 1)
410 printf(" (file %i of %i)", current_file, num_files);
411 printf(" lines %i-%i/%i ",
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000412 cur_fline + 1, cur_fline + max_displayed_line + 1,
413 max_fline + 1);
414 if (cur_fline >= max_fline - max_displayed_line) {
Denis Vlasenkod51d14e2006-12-21 13:57:37 +0000415 printf("(END)"NORMAL);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000416 if (num_files > 1 && current_file != num_files)
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000417 printf(HIGHLIGHT" - next: %s"NORMAL, files[current_file]);
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000418 return;
Rob Landley9200e792005-09-15 19:26:59 +0000419 }
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000420 percentage = calc_percent();
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000421 printf("%i%%"NORMAL, percentage);
Rob Landley9200e792005-09-15 19:26:59 +0000422}
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000423#endif
424
Rob Landley9200e792005-09-15 19:26:59 +0000425/* Print the status line */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000426static void status_print(void)
427{
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000428 const char *p;
429
Denis Vlasenko33196372008-02-23 01:25:38 +0000430 if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
431 return;
432
Rob Landley9200e792005-09-15 19:26:59 +0000433 /* Change the status if flags have been set */
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000434#if ENABLE_FEATURE_LESS_FLAGS
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000435 if (option_mask32 & (FLAG_M|FLAG_m)) {
Rob Landley9200e792005-09-15 19:26:59 +0000436 m_status_print();
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000437 return;
Rob Landley9200e792005-09-15 19:26:59 +0000438 }
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000439 /* No flags set */
Rob Landley9200e792005-09-15 19:26:59 +0000440#endif
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000441
442 clear_line();
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000443 if (cur_fline && cur_fline < max_fline - max_displayed_line) {
Denis Vlasenko4daad902007-09-27 10:20:47 +0000444 bb_putchar(':');
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000445 return;
446 }
Denis Vlasenkod51d14e2006-12-21 13:57:37 +0000447 p = "(END)";
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000448 if (!cur_fline)
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000449 p = filename;
450 if (num_files > 1) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000451 printf(HIGHLIGHT"%s (file %i of %i)"NORMAL,
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000452 p, current_file, num_files);
453 return;
454 }
455 print_hilite(p);
Rob Landley9200e792005-09-15 19:26:59 +0000456}
457
Denis Vlasenko5c1de362007-03-08 16:44:32 +0000458static void cap_cur_fline(int nlines)
459{
460 int diff;
461 if (cur_fline < 0)
462 cur_fline = 0;
463 if (cur_fline + max_displayed_line > max_fline + TILDES) {
464 cur_fline -= nlines;
465 if (cur_fline < 0)
466 cur_fline = 0;
467 diff = max_fline - (cur_fline + max_displayed_line) + TILDES;
468 /* As the number of lines requested was too large, we just move
469 to the end of the file */
470 if (diff > 0)
471 cur_fline += diff;
472 }
473}
474
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000475static const char controls[] ALIGN1 =
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000476 /* NUL: never encountered; TAB: not converted */
477 /**/"\x01\x02\x03\x04\x05\x06\x07\x08" "\x0a\x0b\x0c\x0d\x0e\x0f"
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000478 "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
479 "\x7f\x9b"; /* DEL and infamous Meta-ESC :( */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000480static const char ctrlconv[] ALIGN1 =
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000481 /* '\n': it's a former NUL - subst with '@', not 'J' */
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000482 "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f"
483 "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f";
484
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000485#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000486static void print_found(const char *line)
487{
488 int match_status;
489 int eflags;
490 char *growline;
491 regmatch_t match_structs;
492
493 char buf[width];
494 const char *str = line;
495 char *p = buf;
496 size_t n;
497
498 while (*str) {
499 n = strcspn(str, controls);
500 if (n) {
501 if (!str[n]) break;
502 memcpy(p, str, n);
503 p += n;
504 str += n;
505 }
506 n = strspn(str, controls);
507 memset(p, '.', n);
508 p += n;
509 str += n;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000510 }
511 strcpy(p, str);
512
513 /* buf[] holds quarantined version of str */
514
515 /* Each part of the line that matches has the HIGHLIGHT
516 and NORMAL escape sequences placed around it.
517 NB: we regex against line, but insert text
518 from quarantined copy (buf[]) */
519 str = buf;
520 growline = NULL;
521 eflags = 0;
522 goto start;
523
524 while (match_status == 0) {
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000525 char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL,
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000526 growline ? : "",
527 match_structs.rm_so, str,
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000528 match_structs.rm_eo - match_structs.rm_so,
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000529 str + match_structs.rm_so);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000530 free(growline); growline = new;
531 str += match_structs.rm_eo;
532 line += match_structs.rm_eo;
533 eflags = REG_NOTBOL;
534 start:
535 /* Most of the time doesn't find the regex, optimize for that */
536 match_status = regexec(&pattern, line, 1, &match_structs, eflags);
537 }
538
539 if (!growline) {
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000540 printf(CLEAR_2_EOL"%s\n", str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000541 return;
542 }
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000543 printf(CLEAR_2_EOL"%s%s\n", growline, str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000544 free(growline);
545}
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000546#else
547void print_found(const char *line);
548#endif
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000549
550static void print_ascii(const char *str)
551{
552 char buf[width];
553 char *p;
554 size_t n;
555
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000556 printf(CLEAR_2_EOL);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000557 while (*str) {
558 n = strcspn(str, controls);
559 if (n) {
560 if (!str[n]) break;
Denis Vlasenko806116b2006-12-31 12:14:16 +0000561 printf("%.*s", (int) n, str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000562 str += n;
563 }
564 n = strspn(str, controls);
565 p = buf;
566 do {
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000567 if (*str == 0x7f)
568 *p++ = '?';
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000569 else if (*str == (char)0x9b)
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000570 /* VT100's CSI, aka Meta-ESC. Who's inventor? */
571 /* I want to know who committed this sin */
572 *p++ = '{';
573 else
574 *p++ = ctrlconv[(unsigned char)*str];
575 str++;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000576 } while (--n);
577 *p = '\0';
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000578 print_hilite(buf);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000579 }
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000580 puts(str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000581}
582
Rob Landley9200e792005-09-15 19:26:59 +0000583/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000584static void buffer_print(void)
585{
Rob Landley9200e792005-09-15 19:26:59 +0000586 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000587
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000588 move_cursor(0, 0);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000589 for (i = 0; i <= max_displayed_line; i++)
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000590 if (pattern_valid)
591 print_found(buffer[i]);
592 else
593 print_ascii(buffer[i]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000594 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000595}
596
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000597static void buffer_fill_and_print(void)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000598{
Rob Landley9200e792005-09-15 19:26:59 +0000599 int i;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000600 for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) {
601 buffer[i] = flines[cur_fline + i];
Rob Landley9200e792005-09-15 19:26:59 +0000602 }
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000603 for (; i <= max_displayed_line; i++) {
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000604 buffer[i] = empty_line_marker;
Rob Landley9200e792005-09-15 19:26:59 +0000605 }
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000606 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000607}
608
609/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000610static void buffer_down(int nlines)
611{
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000612 cur_fline += nlines;
613 read_lines();
Denis Vlasenko5c1de362007-03-08 16:44:32 +0000614 cap_cur_fline(nlines);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000615 buffer_fill_and_print();
Rob Landley9200e792005-09-15 19:26:59 +0000616}
617
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000618static void buffer_up(int nlines)
619{
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000620 cur_fline -= nlines;
621 if (cur_fline < 0) cur_fline = 0;
622 read_lines();
623 buffer_fill_and_print();
Rob Landley9200e792005-09-15 19:26:59 +0000624}
625
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000626static void buffer_line(int linenum)
627{
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000628 if (linenum < 0)
629 linenum = 0;
630 cur_fline = linenum;
631 read_lines();
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000632 if (linenum + max_displayed_line > max_fline)
633 linenum = max_fline - max_displayed_line + TILDES;
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000634 if (linenum < 0)
635 linenum = 0;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000636 cur_fline = linenum;
637 buffer_fill_and_print();
Rob Landley9200e792005-09-15 19:26:59 +0000638}
639
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000640static void open_file_and_read_lines(void)
641{
642 if (filename) {
643 int fd = xopen(filename, O_RDONLY);
644 dup2(fd, 0);
645 if (fd) close(fd);
646 } else {
647 /* "less" with no arguments in argv[] */
648 /* For status line only */
649 filename = xstrdup(bb_msg_standard_input);
650 }
651 readpos = 0;
652 readeof = 0;
653 linepos = 0;
654 terminated = 1;
655 read_lines();
656}
657
658/* Reinitialize everything for a new file - free the memory and start over */
659static void reinitialize(void)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000660{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000661 int i;
662
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000663 if (flines) {
664 for (i = 0; i <= max_fline; i++)
665 free((void*)(flines[i]));
666 free(flines);
667 flines = NULL;
668 }
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000669
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000670 max_fline = -1;
671 cur_fline = 0;
672 max_lineno = 0;
673 open_file_and_read_lines();
674 buffer_fill_and_print();
675}
676
Denis Vlasenko33196372008-02-23 01:25:38 +0000677static ssize_t getch_nowait(char* input, int sz)
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000678{
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000679 ssize_t rd;
Denis Vlasenko33196372008-02-23 01:25:38 +0000680 struct pollfd pfd[2];
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000681
Denis Vlasenko33196372008-02-23 01:25:38 +0000682 pfd[0].fd = STDIN_FILENO;
683 pfd[0].events = POLLIN;
684 pfd[1].fd = kbd_fd;
685 pfd[1].events = POLLIN;
686 again:
687 tcsetattr(kbd_fd, TCSANOW, &term_less);
688 /* NB: select/poll returns whenever read will not block. Therefore:
689 * if eof is reached, select/poll will return immediately
690 * because read will immediately return 0 bytes.
691 * Even if select/poll says that input is available, read CAN block
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000692 * (switch fd into O_NONBLOCK'ed mode to avoid it)
693 */
Denis Vlasenko33196372008-02-23 01:25:38 +0000694 rd = 1;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000695 if (max_fline <= cur_fline + max_displayed_line
696 && eof_error > 0 /* did NOT reach eof yet */
697 ) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000698 /* We are interested in stdin */
Denis Vlasenko33196372008-02-23 01:25:38 +0000699 rd = 0;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000700 }
Denis Vlasenko33196372008-02-23 01:25:38 +0000701 /* position cursor if line input is done */
702 if (less_gets_pos >= 0)
703 move_cursor(max_displayed_line + 2, less_gets_pos + 1);
704 fflush(stdout);
705 safe_poll(pfd + rd, 2 - rd, -1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000706
707 input[0] = '\0';
Denis Vlasenko33196372008-02-23 01:25:38 +0000708 rd = safe_read(kbd_fd, input, sz); /* NB: kbd_fd is in O_NONBLOCK mode */
709 if (rd < 0 && errno == EAGAIN) {
710 /* No keyboard input -> we have input on stdin! */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000711 read_lines();
712 buffer_fill_and_print();
713 goto again;
714 }
Denis Vlasenko33196372008-02-23 01:25:38 +0000715 set_tty_cooked();
716 return rd;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000717}
718
719/* Grab a character from input without requiring the return key. If the
Denis Vlasenko7cea2622006-12-24 07:30:09 +0000720 * character is ASCII \033, get more characters and assign certain sequences
721 * special return codes. Note that this function works best with raw input. */
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000722static int less_getch(int pos)
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000723{
Denis Vlasenko33196372008-02-23 01:25:38 +0000724 unsigned char input[16];
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000725 unsigned i;
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000726
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000727 again:
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000728 less_gets_pos = pos;
Denis Vlasenko46340e32007-08-03 14:17:21 +0000729 memset(input, 0, sizeof(input));
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000730 getch_nowait(input, sizeof(input));
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000731 less_gets_pos = -1;
Denis Vlasenko46340e32007-08-03 14:17:21 +0000732
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000733 /* Detect escape sequences (i.e. arrow keys) and handle
734 * them accordingly */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000735 if (input[0] == '\033' && input[1] == '[') {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000736 i = input[2] - REAL_KEY_UP;
737 if (i < 4)
738 return 20 + i;
739 i = input[2] - REAL_PAGE_UP;
740 if (i < 4)
741 return 24 + i;
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000742 if (input[2] == REAL_KEY_HOME_XTERM)
743 return KEY_HOME;
Denis Vlasenkof5a15762007-03-09 08:55:23 +0000744 if (input[2] == REAL_KEY_HOME_ALT)
745 return KEY_HOME;
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000746 if (input[2] == REAL_KEY_END_XTERM)
747 return KEY_END;
Denis Vlasenkof5a15762007-03-09 08:55:23 +0000748 if (input[2] == REAL_KEY_END_ALT)
749 return KEY_END;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000750 return 0;
751 }
752 /* Reject almost all control chars */
753 i = input[0];
Denis Vlasenko33196372008-02-23 01:25:38 +0000754 if (i < ' ' && i != 0x0d && i != 8)
755 goto again;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000756 return i;
757}
758
759static char* less_gets(int sz)
760{
761 char c;
762 int i = 0;
763 char *result = xzalloc(1);
Denis Vlasenko33196372008-02-23 01:25:38 +0000764
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000765 while (1) {
Denis Vlasenko46340e32007-08-03 14:17:21 +0000766 c = '\0';
Denis Vlasenko33196372008-02-23 01:25:38 +0000767 less_gets_pos = sz + i;
768 getch_nowait(&c, 1);
769 if (c == 0x0d) {
Denis Vlasenkod553faf2008-02-23 12:22:17 +0000770 result[i] = '\0';
Denis Vlasenko33196372008-02-23 01:25:38 +0000771 less_gets_pos = -1;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000772 return result;
Denis Vlasenko33196372008-02-23 01:25:38 +0000773 }
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000774 if (c == 0x7f)
775 c = 8;
776 if (c == 8 && i) {
777 printf("\x8 \x8");
778 i--;
779 }
780 if (c < ' ')
781 continue;
782 if (i >= width - sz - 1)
783 continue; /* len limit */
Denis Vlasenko4daad902007-09-27 10:20:47 +0000784 bb_putchar(c);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000785 result[i++] = c;
786 result = xrealloc(result, i+1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000787 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000788}
789
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000790static void examine_file(void)
791{
Denis Vlasenko33196372008-02-23 01:25:38 +0000792 char *new_fname;
793
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000794 print_statusline("Examine: ");
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000795 new_fname = less_gets(sizeof("Examine: ") - 1);
Denis Vlasenko33196372008-02-23 01:25:38 +0000796 if (!new_fname[0]) {
Denis Vlasenko33196372008-02-23 01:25:38 +0000797 status_print();
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000798 err:
799 free(new_fname);
Denis Vlasenko33196372008-02-23 01:25:38 +0000800 return;
801 }
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000802 if (access(new_fname, R_OK) != 0) {
803 print_statusline("Cannot read this file");
804 goto err;
805 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000806 free(filename);
Denis Vlasenko33196372008-02-23 01:25:38 +0000807 filename = new_fname;
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000808 /* files start by = argv. why we assume that argv is infinitely long??
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000809 files[num_files] = filename;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000810 current_file = num_files + 1;
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000811 num_files++; */
812 files[0] = filename;
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000813 num_files = current_file = 1;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000814 reinitialize();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000815}
816
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000817/* This function changes the file currently being paged. direction can be one of the following:
818 * -1: go back one file
819 * 0: go to the first file
Denis Vlasenko7cea2622006-12-24 07:30:09 +0000820 * 1: go forward one file */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000821static void change_file(int direction)
822{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000823 if (current_file != ((direction > 0) ? num_files : 1)) {
824 current_file = direction ? current_file + direction : 1;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000825 free(filename);
826 filename = xstrdup(files[current_file - 1]);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000827 reinitialize();
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000828 } else {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000829 print_statusline(direction > 0 ? "No next file" : "No previous file");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000830 }
831}
832
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000833static void remove_current_file(void)
834{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000835 int i;
836
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000837 if (num_files < 2)
838 return;
839
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000840 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000841 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000842 for (i = 3; i <= num_files; i++)
843 files[i - 2] = files[i - 1];
844 num_files--;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000845 } else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000846 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000847 for (i = 2; i <= num_files; i++)
848 files[i - 2] = files[i - 1];
849 num_files--;
850 current_file--;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000851 }
852}
853
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000854static void colon_process(void)
855{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000856 int keypress;
857
858 /* Clear the current line and print a prompt */
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000859 print_statusline(" :");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000860
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000861 keypress = less_getch(2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000862 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000863 case 'd':
864 remove_current_file();
865 break;
866 case 'e':
867 examine_file();
868 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000869#if ENABLE_FEATURE_LESS_FLAGS
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000870 case 'f':
871 m_status_print();
872 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000873#endif
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000874 case 'n':
875 change_file(1);
876 break;
877 case 'p':
878 change_file(-1);
879 break;
880 case 'q':
881 less_exit(0);
882 break;
883 case 'x':
884 change_file(0);
885 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000886 }
887}
888
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000889#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000890static void normalize_match_pos(int match)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000891{
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000892 if (match >= num_matches)
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000893 match = num_matches - 1;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000894 if (match < 0)
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000895 match = 0;
896 match_pos = match;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000897}
898
Rob Landleya2e98042006-04-18 01:53:41 +0000899static void goto_match(int match)
900{
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000901 if (!pattern_valid)
902 return;
903 if (match < 0)
904 match = 0;
905 /* Try to find next match if eof isn't reached yet */
906 if (match >= num_matches && eof_error > 0) {
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000907 wanted_match = match;
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000908 read_lines();
Denis Vlasenko107fe7c2008-03-17 08:38:45 +0000909 if (wanted_match >= num_matches) {
910 /* We still failed to find it. Prevent future
911 * read_lines() from trying... */
912 wanted_match = num_matches - 1;
913 }
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000914 }
915 if (num_matches) {
916 normalize_match_pos(match);
917 buffer_line(match_lines[match_pos]);
Denis Vlasenko8465a992007-05-09 18:32:54 +0000918 } else {
Denis Vlasenko8465a992007-05-09 18:32:54 +0000919 print_statusline("No matches found");
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000920 }
Rob Landleya2e98042006-04-18 01:53:41 +0000921}
922
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000923static void fill_match_lines(unsigned pos)
924{
925 if (!pattern_valid)
926 return;
927 /* Run the regex on each line of the current file */
928 while (pos <= max_fline) {
929 /* If this line matches */
930 if (regexec(&pattern, flines[pos], 0, NULL, 0) == 0
931 /* and we didn't match it last time */
932 && !(num_matches && match_lines[num_matches-1] == pos)
933 ) {
934 match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int));
935 match_lines[num_matches++] = pos;
936 }
937 pos++;
938 }
939}
940
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000941static void regex_process(void)
942{
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000943 char *uncomp_regex, *err;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000944
945 /* Reset variables */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000946 free(match_lines);
947 match_lines = NULL;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000948 match_pos = 0;
949 num_matches = 0;
950 if (pattern_valid) {
951 regfree(&pattern);
952 pattern_valid = 0;
953 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000954
Rob Landleya2e98042006-04-18 01:53:41 +0000955 /* Get the uncompiled regular expression from the user */
956 clear_line();
Denis Vlasenko4daad902007-09-27 10:20:47 +0000957 bb_putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000958 uncomp_regex = less_gets(1);
959 if (!uncomp_regex[0]) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000960 free(uncomp_regex);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000961 buffer_print();
Rob Landleya2e98042006-04-18 01:53:41 +0000962 return;
963 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000964
Rob Landleya2e98042006-04-18 01:53:41 +0000965 /* Compile the regex and check for errors */
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000966 err = regcomp_or_errmsg(&pattern, uncomp_regex, 0);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000967 free(uncomp_regex);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000968 if (err) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000969 print_statusline(err);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000970 free(err);
971 return;
972 }
Denis Vlasenkoa1c63122007-03-08 18:12:01 +0000973
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000974 pattern_valid = 1;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000975 match_pos = 0;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000976 fill_match_lines(0);
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000977 while (match_pos < num_matches) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000978 if (match_lines[match_pos] > cur_fline)
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000979 break;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000980 match_pos++;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000981 }
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000982 if (option_mask32 & LESS_STATE_MATCH_BACKWARDS)
983 match_pos--;
Denis Vlasenkoa1c63122007-03-08 18:12:01 +0000984
985 /* It's possible that no matches are found yet.
986 * goto_match() will read input looking for match,
987 * if needed */
988 goto_match(match_pos);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000989}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000990#endif
991
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000992static void number_process(int first_digit)
993{
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000994 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000995 int num;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000996 char num_input[sizeof(int)*4]; /* more than enough */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000997 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000998
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000999 num_input[0] = first_digit;
1000
1001 /* Clear the current line, print a prompt, and then print the digit */
1002 clear_line();
1003 printf(":%c", first_digit);
1004
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001005 /* Receive input until a letter is given */
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001006 i = 1;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001007 while (i < sizeof(num_input)-1) {
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001008 num_input[i] = less_getch(i + 1);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001009 if (!num_input[i] || !isdigit(num_input[i]))
1010 break;
Denis Vlasenko4daad902007-09-27 10:20:47 +00001011 bb_putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001012 i++;
1013 }
1014
1015 /* Take the final letter out of the digits string */
1016 keypress = num_input[i];
1017 num_input[i] = '\0';
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001018 num = bb_strtou(num_input, NULL, 10);
1019 /* on format error, num == -1 */
1020 if (num < 1 || num > MAXLINES) {
Rob Landleya2e98042006-04-18 01:53:41 +00001021 buffer_print();
1022 return;
1023 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001024
1025 /* We now know the number and the letter entered, so we process them */
1026 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001027 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
1028 buffer_down(num);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001029 break;
1030 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
1031 buffer_up(num);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001032 break;
1033 case 'g': case '<': case 'G': case '>':
1034 cur_fline = num + max_displayed_line;
Denis Vlasenko8e858e22007-03-07 09:35:43 +00001035 read_lines();
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001036 buffer_line(num - 1);
1037 break;
1038 case 'p': case '%':
1039 num = num * (max_fline / 100); /* + max_fline / 2; */
1040 cur_fline = num + max_displayed_line;
Denis Vlasenko8e858e22007-03-07 09:35:43 +00001041 read_lines();
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001042 buffer_line(num);
1043 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001044#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001045 case 'n':
1046 goto_match(match_pos + num);
1047 break;
1048 case '/':
1049 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
1050 regex_process();
1051 break;
1052 case '?':
1053 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
1054 regex_process();
1055 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001056#endif
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001057 }
1058}
1059
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001060#if ENABLE_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001061static void flag_change(void)
1062{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001063 int keypress;
1064
1065 clear_line();
Denis Vlasenko4daad902007-09-27 10:20:47 +00001066 bb_putchar('-');
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001067 keypress = less_getch(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001068
1069 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001070 case 'M':
1071 option_mask32 ^= FLAG_M;
1072 break;
1073 case 'm':
1074 option_mask32 ^= FLAG_m;
1075 break;
1076 case 'E':
1077 option_mask32 ^= FLAG_E;
1078 break;
1079 case '~':
1080 option_mask32 ^= FLAG_TILDE;
1081 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001082 }
1083}
1084
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001085static void show_flag_status(void)
1086{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001087 int keypress;
1088 int flag_val;
1089
1090 clear_line();
Denis Vlasenko4daad902007-09-27 10:20:47 +00001091 bb_putchar('_');
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001092 keypress = less_getch(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001093
1094 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001095 case 'M':
1096 flag_val = option_mask32 & FLAG_M;
1097 break;
1098 case 'm':
1099 flag_val = option_mask32 & FLAG_m;
1100 break;
1101 case '~':
1102 flag_val = option_mask32 & FLAG_TILDE;
1103 break;
1104 case 'N':
1105 flag_val = option_mask32 & FLAG_N;
1106 break;
1107 case 'E':
1108 flag_val = option_mask32 & FLAG_E;
1109 break;
1110 default:
1111 flag_val = 0;
1112 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001113 }
1114
1115 clear_line();
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001116 printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001117}
1118#endif
1119
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001120static void save_input_to_file(void)
1121{
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001122 const char *msg = "";
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001123 char *current_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001124 int i;
1125 FILE *fp;
1126
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001127 print_statusline("Log file: ");
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001128 current_line = less_gets(sizeof("Log file: ")-1);
Denis Vlasenko33196372008-02-23 01:25:38 +00001129 if (current_line[0]) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001130 fp = fopen(current_line, "w");
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001131 if (!fp) {
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001132 msg = "Error opening log file";
1133 goto ret;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001134 }
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001135 for (i = 0; i <= max_fline; i++)
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001136 fprintf(fp, "%s\n", flines[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001137 fclose(fp);
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001138 msg = "Done";
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001139 }
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001140 ret:
1141 print_statusline(msg);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001142 free(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001143}
1144
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001145#if ENABLE_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001146static void add_mark(void)
1147{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001148 int letter;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001149
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001150 print_statusline("Mark: ");
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001151 letter = less_getch(sizeof("Mark: ") - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001152
1153 if (isalpha(letter)) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001154 /* If we exceed 15 marks, start overwriting previous ones */
1155 if (num_marks == 14)
1156 num_marks = 0;
1157
1158 mark_lines[num_marks][0] = letter;
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001159 mark_lines[num_marks][1] = cur_fline;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001160 num_marks++;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001161 } else {
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001162 print_statusline("Invalid mark letter");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001163 }
1164}
1165
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001166static void goto_mark(void)
1167{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001168 int letter;
1169 int i;
1170
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001171 print_statusline("Go to mark: ");
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001172 letter = less_getch(sizeof("Go to mark: ") - 1);
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001173 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001174
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001175 if (isalpha(letter)) {
1176 for (i = 0; i <= num_marks; i++)
1177 if (letter == mark_lines[i][0]) {
1178 buffer_line(mark_lines[i][1]);
1179 break;
1180 }
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001181 if (num_marks == 14 && letter != mark_lines[14][0])
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001182 print_statusline("Mark not set");
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001183 } else
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001184 print_statusline("Invalid mark letter");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001185}
1186#endif
1187
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001188#if ENABLE_FEATURE_LESS_BRACKETS
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001189static char opp_bracket(char bracket)
1190{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001191 switch (bracket) {
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001192 case '{': case '[': /* '}' == '{' + 2. Same for '[' */
1193 bracket++;
1194 case '(': /* ')' == '(' + 1 */
1195 bracket++;
1196 break;
1197 case '}': case ']':
1198 bracket--;
1199 case ')':
1200 bracket--;
1201 break;
1202 };
1203 return bracket;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001204}
1205
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001206static void match_right_bracket(char bracket)
1207{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001208 int i;
1209
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001210 if (strchr(flines[cur_fline], bracket) == NULL) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001211 print_statusline("No bracket in top line");
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001212 return;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001213 }
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001214 bracket = opp_bracket(bracket);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001215 for (i = cur_fline + 1; i < max_fline; i++) {
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001216 if (strchr(flines[i], bracket) != NULL) {
1217 buffer_line(i);
1218 return;
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001219 }
1220 }
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001221 print_statusline("No matching bracket found");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001222}
1223
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001224static void match_left_bracket(char bracket)
1225{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001226 int i;
1227
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001228 if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001229 print_statusline("No bracket in bottom line");
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001230 return;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001231 }
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001232
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001233 bracket = opp_bracket(bracket);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001234 for (i = cur_fline + max_displayed_line; i >= 0; i--) {
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001235 if (strchr(flines[i], bracket) != NULL) {
1236 buffer_line(i);
1237 return;
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001238 }
1239 }
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001240 print_statusline("No matching bracket found");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001241}
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001242#endif /* FEATURE_LESS_BRACKETS */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001243
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001244static void keypress_process(int keypress)
1245{
Rob Landley9200e792005-09-15 19:26:59 +00001246 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001247 case KEY_DOWN: case 'e': case 'j': case 0x0d:
1248 buffer_down(1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001249 break;
1250 case KEY_UP: case 'y': case 'k':
1251 buffer_up(1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001252 break;
Denis Vlasenko78e2d4e2007-09-27 17:11:48 +00001253 case PAGE_DOWN: case ' ': case 'z': case 'f':
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001254 buffer_down(max_displayed_line + 1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001255 break;
1256 case PAGE_UP: case 'w': case 'b':
1257 buffer_up(max_displayed_line + 1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001258 break;
1259 case 'd':
1260 buffer_down((max_displayed_line + 1) / 2);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001261 break;
1262 case 'u':
1263 buffer_up((max_displayed_line + 1) / 2);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001264 break;
1265 case KEY_HOME: case 'g': case 'p': case '<': case '%':
1266 buffer_line(0);
1267 break;
1268 case KEY_END: case 'G': case '>':
1269 cur_fline = MAXLINES;
1270 read_lines();
1271 buffer_line(cur_fline);
1272 break;
1273 case 'q': case 'Q':
1274 less_exit(0);
1275 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001276#if ENABLE_FEATURE_LESS_MARKS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001277 case 'm':
1278 add_mark();
1279 buffer_print();
1280 break;
1281 case '\'':
1282 goto_mark();
1283 buffer_print();
1284 break;
Rob Landley9200e792005-09-15 19:26:59 +00001285#endif
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001286 case 'r': case 'R':
1287 buffer_print();
1288 break;
1289 /*case 'R':
1290 full_repaint();
1291 break;*/
1292 case 's':
1293 save_input_to_file();
1294 break;
1295 case 'E':
1296 examine_file();
1297 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001298#if ENABLE_FEATURE_LESS_FLAGS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001299 case '=':
1300 m_status_print();
1301 break;
Rob Landley9200e792005-09-15 19:26:59 +00001302#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001303#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001304 case '/':
1305 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
1306 regex_process();
1307 break;
1308 case 'n':
1309 goto_match(match_pos + 1);
1310 break;
1311 case 'N':
1312 goto_match(match_pos - 1);
1313 break;
1314 case '?':
1315 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
1316 regex_process();
1317 break;
Rob Landley9200e792005-09-15 19:26:59 +00001318#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001319#if ENABLE_FEATURE_LESS_FLAGCS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001320 case '-':
1321 flag_change();
1322 buffer_print();
1323 break;
1324 case '_':
1325 show_flag_status();
1326 break;
Rob Landley9200e792005-09-15 19:26:59 +00001327#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001328#if ENABLE_FEATURE_LESS_BRACKETS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001329 case '{': case '(': case '[':
1330 match_right_bracket(keypress);
1331 break;
1332 case '}': case ')': case ']':
1333 match_left_bracket(keypress);
1334 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001335#endif
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001336 case ':':
1337 colon_process();
1338 break;
Rob Landley9200e792005-09-15 19:26:59 +00001339 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001340
Rob Landley9200e792005-09-15 19:26:59 +00001341 if (isdigit(keypress))
1342 number_process(keypress);
1343}
1344
Denis Vlasenko400d8bb2008-02-24 13:36:01 +00001345static void sig_catcher(int sig)
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001346{
Denis Vlasenko400d8bb2008-02-24 13:36:01 +00001347 less_exit(- sig);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001348}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001349
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001350int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001351int less_main(int argc, char **argv)
1352{
Rob Landley9200e792005-09-15 19:26:59 +00001353 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001354
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +00001355 INIT_G();
1356
Denis Vlasenko95b30712006-12-31 19:23:31 +00001357 /* TODO: -x: do not interpret backspace, -xx: tab also */
1358 /* -xxx: newline also */
1359 /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
Denis Vlasenkofe7cd642007-08-18 15:32:12 +00001360 getopt32(argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001361 argc -= optind;
1362 argv += optind;
Rob Landley9200e792005-09-15 19:26:59 +00001363 num_files = argc;
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001364 files = argv;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001365
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001366 /* Another popular pager, most, detects when stdout
1367 * is not a tty and turns into cat. This makes sense. */
1368 if (!isatty(STDOUT_FILENO))
1369 return bb_cat(argv);
Denis Vlasenko4eb8b932007-03-10 16:32:14 +00001370 kbd_fd = open(CURRENT_TTY, O_RDONLY);
1371 if (kbd_fd < 0)
1372 return bb_cat(argv);
Denis Vlasenko33196372008-02-23 01:25:38 +00001373 ndelay_on(kbd_fd);
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001374
Rob Landley9200e792005-09-15 19:26:59 +00001375 if (!num_files) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001376 if (isatty(STDIN_FILENO)) {
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001377 /* Just "less"? No args and no redirection? */
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00001378 bb_error_msg("missing filename");
Rob Landley9200e792005-09-15 19:26:59 +00001379 bb_show_usage();
1380 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001381 } else
1382 filename = xstrdup(files[0]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001383
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001384 get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
1385 /* 20: two tabstops + 4 */
1386 if (width < 20 || max_displayed_line < 3)
Denis Vlasenko107fe7c2008-03-17 08:38:45 +00001387 return bb_cat(argv);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001388 max_displayed_line -= 2;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +00001389
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001390 buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001391 if (option_mask32 & FLAG_TILDE)
1392 empty_line_marker = "";
Denis Vlasenko3f3190e2006-12-21 00:22:03 +00001393
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001394 tcgetattr(kbd_fd, &term_orig);
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +00001395 term_less = term_orig;
1396 term_less.c_lflag &= ~(ICANON | ECHO);
1397 term_less.c_iflag &= ~(IXON | ICRNL);
1398 /*term_less.c_oflag &= ~ONLCR;*/
1399 term_less.c_cc[VMIN] = 1;
1400 term_less.c_cc[VTIME] = 0;
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001401
Denis Vlasenkod553faf2008-02-23 12:22:17 +00001402 /* We want to restore term_orig on exit */
Denis Vlasenkocf7cf622008-03-19 19:38:46 +00001403 bb_signals(BB_FATAL_SIGS, sig_catcher);
Denis Vlasenkod553faf2008-02-23 12:22:17 +00001404
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001405 reinitialize();
Rob Landley9200e792005-09-15 19:26:59 +00001406 while (1) {
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001407 keypress = less_getch(-1); /* -1: do not position cursor */
Rob Landley9200e792005-09-15 19:26:59 +00001408 keypress_process(keypress);
1409 }
1410}