blob: 530a40a8c8314203a5b527f115a24a6eb236641b [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
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000175/* Move the cursor to a position (x,y), where (0,0) is the
Rob Landley9200e792005-09-15 19:26:59 +0000176 top-left corner of the console */
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000177static void move_cursor(int line, int row)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000178{
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000179 printf("\033[%u;%uH", line, row);
Rob Landley9200e792005-09-15 19:26:59 +0000180}
181
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000182static void clear_line(void)
183{
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000184 printf("\033[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000185}
186
187static void print_hilite(const char *str)
188{
189 printf(HIGHLIGHT"%s"NORMAL, str);
Rob Landley9200e792005-09-15 19:26:59 +0000190}
191
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000192static void print_statusline(const char *str)
193{
194 clear_line();
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000195 printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str);
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000196}
197
Denis Vlasenko3fe4f982008-06-09 16:02:39 +0000198/* Exit the program gracefully */
199static void less_exit(int code)
200{
201 set_tty_cooked();
202 clear_line();
203 if (code < 0)
204 kill_myself_with_sig(- code); /* does not return */
205 exit(code);
206}
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);
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000276 eof_error = safe_read(STDIN_FILENO, 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 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000300 if ((int)new_linepos >= w)
Denis Vlasenko22a9a3c2007-05-31 15:56:10 +0000301 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 Vlasenkodeeed592008-07-08 05:14:36 +0000325 flines = xrealloc_vector(flines, 8, max_fline);
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 Vlasenkodeeed592008-07-08 05:14:36 +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 Vlasenko24f824e2008-04-13 08:32:51 +0000387#if ENABLE_FEATURE_LESS_REGEXP
388 /* prevent us from being stuck in search for a match */
389 wanted_match = -1;
390#endif
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000391#undef readbuf
Rob Landley9200e792005-09-15 19:26:59 +0000392}
393
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000394#if ENABLE_FEATURE_LESS_FLAGS
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000395/* Interestingly, writing calc_percent as a function saves around 32 bytes
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000396 * on my build. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000397static int calc_percent(void)
398{
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000399 unsigned p = (100 * (cur_fline+max_displayed_line+1) + max_fline/2) / (max_fline+1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000400 return p <= 100 ? p : 100;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000401}
402
Rob Landley9200e792005-09-15 19:26:59 +0000403/* Print a status line if -M was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000404static void m_status_print(void)
405{
Rob Landley9200e792005-09-15 19:26:59 +0000406 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000407
Denis Vlasenko33196372008-02-23 01:25:38 +0000408 if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
409 return;
410
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000411 clear_line();
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000412 printf(HIGHLIGHT"%s", filename);
413 if (num_files > 1)
414 printf(" (file %i of %i)", current_file, num_files);
415 printf(" lines %i-%i/%i ",
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000416 cur_fline + 1, cur_fline + max_displayed_line + 1,
417 max_fline + 1);
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000418 if (cur_fline >= (int)(max_fline - max_displayed_line)) {
Denis Vlasenkod51d14e2006-12-21 13:57:37 +0000419 printf("(END)"NORMAL);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000420 if (num_files > 1 && current_file != num_files)
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000421 printf(HIGHLIGHT" - next: %s"NORMAL, files[current_file]);
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000422 return;
Rob Landley9200e792005-09-15 19:26:59 +0000423 }
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000424 percentage = calc_percent();
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000425 printf("%i%%"NORMAL, percentage);
Rob Landley9200e792005-09-15 19:26:59 +0000426}
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000427#endif
428
Rob Landley9200e792005-09-15 19:26:59 +0000429/* Print the status line */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000430static void status_print(void)
431{
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000432 const char *p;
433
Denis Vlasenko33196372008-02-23 01:25:38 +0000434 if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
435 return;
436
Rob Landley9200e792005-09-15 19:26:59 +0000437 /* Change the status if flags have been set */
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000438#if ENABLE_FEATURE_LESS_FLAGS
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000439 if (option_mask32 & (FLAG_M|FLAG_m)) {
Rob Landley9200e792005-09-15 19:26:59 +0000440 m_status_print();
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000441 return;
Rob Landley9200e792005-09-15 19:26:59 +0000442 }
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000443 /* No flags set */
Rob Landley9200e792005-09-15 19:26:59 +0000444#endif
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000445
446 clear_line();
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000447 if (cur_fline && cur_fline < (int)(max_fline - max_displayed_line)) {
Denis Vlasenko4daad902007-09-27 10:20:47 +0000448 bb_putchar(':');
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000449 return;
450 }
Denis Vlasenkod51d14e2006-12-21 13:57:37 +0000451 p = "(END)";
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000452 if (!cur_fline)
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000453 p = filename;
454 if (num_files > 1) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000455 printf(HIGHLIGHT"%s (file %i of %i)"NORMAL,
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000456 p, current_file, num_files);
457 return;
458 }
459 print_hilite(p);
Rob Landley9200e792005-09-15 19:26:59 +0000460}
461
Denis Vlasenko5c1de362007-03-08 16:44:32 +0000462static void cap_cur_fline(int nlines)
463{
464 int diff;
465 if (cur_fline < 0)
466 cur_fline = 0;
467 if (cur_fline + max_displayed_line > max_fline + TILDES) {
468 cur_fline -= nlines;
469 if (cur_fline < 0)
470 cur_fline = 0;
471 diff = max_fline - (cur_fline + max_displayed_line) + TILDES;
472 /* As the number of lines requested was too large, we just move
473 to the end of the file */
474 if (diff > 0)
475 cur_fline += diff;
476 }
477}
478
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000479static const char controls[] ALIGN1 =
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000480 /* NUL: never encountered; TAB: not converted */
481 /**/"\x01\x02\x03\x04\x05\x06\x07\x08" "\x0a\x0b\x0c\x0d\x0e\x0f"
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000482 "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
483 "\x7f\x9b"; /* DEL and infamous Meta-ESC :( */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000484static const char ctrlconv[] ALIGN1 =
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000485 /* '\n': it's a former NUL - subst with '@', not 'J' */
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000486 "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f"
487 "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f";
488
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000489#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000490static void print_found(const char *line)
491{
492 int match_status;
493 int eflags;
494 char *growline;
495 regmatch_t match_structs;
496
497 char buf[width];
498 const char *str = line;
499 char *p = buf;
500 size_t n;
501
502 while (*str) {
503 n = strcspn(str, controls);
504 if (n) {
505 if (!str[n]) break;
506 memcpy(p, str, n);
507 p += n;
508 str += n;
509 }
510 n = strspn(str, controls);
511 memset(p, '.', n);
512 p += n;
513 str += n;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000514 }
515 strcpy(p, str);
516
517 /* buf[] holds quarantined version of str */
518
519 /* Each part of the line that matches has the HIGHLIGHT
520 and NORMAL escape sequences placed around it.
521 NB: we regex against line, but insert text
522 from quarantined copy (buf[]) */
523 str = buf;
524 growline = NULL;
525 eflags = 0;
526 goto start;
527
528 while (match_status == 0) {
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000529 char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL,
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000530 growline ? : "",
531 match_structs.rm_so, str,
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000532 match_structs.rm_eo - match_structs.rm_so,
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000533 str + match_structs.rm_so);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000534 free(growline); growline = new;
535 str += match_structs.rm_eo;
536 line += match_structs.rm_eo;
537 eflags = REG_NOTBOL;
538 start:
539 /* Most of the time doesn't find the regex, optimize for that */
540 match_status = regexec(&pattern, line, 1, &match_structs, eflags);
Denis Vlasenko3fe4f982008-06-09 16:02:39 +0000541 /* if even "" matches, treat it as "not a match" */
542 if (match_structs.rm_so >= match_structs.rm_eo)
543 match_status = 1;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000544 }
545
546 if (!growline) {
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000547 printf(CLEAR_2_EOL"%s\n", str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000548 return;
549 }
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000550 printf(CLEAR_2_EOL"%s%s\n", growline, str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000551 free(growline);
552}
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000553#else
554void print_found(const char *line);
555#endif
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000556
557static void print_ascii(const char *str)
558{
559 char buf[width];
560 char *p;
561 size_t n;
562
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000563 printf(CLEAR_2_EOL);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000564 while (*str) {
565 n = strcspn(str, controls);
566 if (n) {
567 if (!str[n]) break;
Denis Vlasenko806116b2006-12-31 12:14:16 +0000568 printf("%.*s", (int) n, str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000569 str += n;
570 }
571 n = strspn(str, controls);
572 p = buf;
573 do {
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000574 if (*str == 0x7f)
575 *p++ = '?';
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000576 else if (*str == (char)0x9b)
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000577 /* VT100's CSI, aka Meta-ESC. Who's inventor? */
578 /* I want to know who committed this sin */
579 *p++ = '{';
580 else
581 *p++ = ctrlconv[(unsigned char)*str];
582 str++;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000583 } while (--n);
584 *p = '\0';
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000585 print_hilite(buf);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000586 }
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000587 puts(str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000588}
589
Rob Landley9200e792005-09-15 19:26:59 +0000590/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000591static void buffer_print(void)
592{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000593 unsigned i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000594
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000595 move_cursor(0, 0);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000596 for (i = 0; i <= max_displayed_line; i++)
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000597 if (pattern_valid)
598 print_found(buffer[i]);
599 else
600 print_ascii(buffer[i]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000601 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000602}
603
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000604static void buffer_fill_and_print(void)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000605{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000606 unsigned i;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000607 for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) {
608 buffer[i] = flines[cur_fline + i];
Rob Landley9200e792005-09-15 19:26:59 +0000609 }
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000610 for (; i <= max_displayed_line; i++) {
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000611 buffer[i] = empty_line_marker;
Rob Landley9200e792005-09-15 19:26:59 +0000612 }
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000613 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000614}
615
616/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000617static void buffer_down(int nlines)
618{
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000619 cur_fline += nlines;
620 read_lines();
Denis Vlasenko5c1de362007-03-08 16:44:32 +0000621 cap_cur_fline(nlines);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000622 buffer_fill_and_print();
Rob Landley9200e792005-09-15 19:26:59 +0000623}
624
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000625static void buffer_up(int nlines)
626{
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000627 cur_fline -= nlines;
628 if (cur_fline < 0) cur_fline = 0;
629 read_lines();
630 buffer_fill_and_print();
Rob Landley9200e792005-09-15 19:26:59 +0000631}
632
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000633static void buffer_line(int linenum)
634{
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000635 if (linenum < 0)
636 linenum = 0;
637 cur_fline = linenum;
638 read_lines();
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000639 if (linenum + max_displayed_line > max_fline)
640 linenum = max_fline - max_displayed_line + TILDES;
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000641 if (linenum < 0)
642 linenum = 0;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000643 cur_fline = linenum;
644 buffer_fill_and_print();
Rob Landley9200e792005-09-15 19:26:59 +0000645}
646
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000647static void open_file_and_read_lines(void)
648{
649 if (filename) {
650 int fd = xopen(filename, O_RDONLY);
651 dup2(fd, 0);
652 if (fd) close(fd);
653 } else {
654 /* "less" with no arguments in argv[] */
655 /* For status line only */
656 filename = xstrdup(bb_msg_standard_input);
657 }
658 readpos = 0;
659 readeof = 0;
660 linepos = 0;
661 terminated = 1;
662 read_lines();
663}
664
665/* Reinitialize everything for a new file - free the memory and start over */
666static void reinitialize(void)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000667{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000668 unsigned i;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000669
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000670 if (flines) {
671 for (i = 0; i <= max_fline; i++)
672 free((void*)(flines[i]));
673 free(flines);
674 flines = NULL;
675 }
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000676
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000677 max_fline = -1;
678 cur_fline = 0;
679 max_lineno = 0;
680 open_file_and_read_lines();
681 buffer_fill_and_print();
682}
683
Denis Vlasenko33196372008-02-23 01:25:38 +0000684static ssize_t getch_nowait(char* input, int sz)
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000685{
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000686 ssize_t rd;
Denis Vlasenko33196372008-02-23 01:25:38 +0000687 struct pollfd pfd[2];
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000688
Denis Vlasenko33196372008-02-23 01:25:38 +0000689 pfd[0].fd = STDIN_FILENO;
690 pfd[0].events = POLLIN;
691 pfd[1].fd = kbd_fd;
692 pfd[1].events = POLLIN;
693 again:
694 tcsetattr(kbd_fd, TCSANOW, &term_less);
695 /* NB: select/poll returns whenever read will not block. Therefore:
696 * if eof is reached, select/poll will return immediately
697 * because read will immediately return 0 bytes.
698 * Even if select/poll says that input is available, read CAN block
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000699 * (switch fd into O_NONBLOCK'ed mode to avoid it)
700 */
Denis Vlasenko33196372008-02-23 01:25:38 +0000701 rd = 1;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000702 if (max_fline <= cur_fline + max_displayed_line
703 && eof_error > 0 /* did NOT reach eof yet */
704 ) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000705 /* We are interested in stdin */
Denis Vlasenko33196372008-02-23 01:25:38 +0000706 rd = 0;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000707 }
Denis Vlasenko33196372008-02-23 01:25:38 +0000708 /* position cursor if line input is done */
709 if (less_gets_pos >= 0)
710 move_cursor(max_displayed_line + 2, less_gets_pos + 1);
711 fflush(stdout);
712 safe_poll(pfd + rd, 2 - rd, -1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000713
714 input[0] = '\0';
Denis Vlasenko33196372008-02-23 01:25:38 +0000715 rd = safe_read(kbd_fd, input, sz); /* NB: kbd_fd is in O_NONBLOCK mode */
716 if (rd < 0 && errno == EAGAIN) {
717 /* No keyboard input -> we have input on stdin! */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000718 read_lines();
719 buffer_fill_and_print();
720 goto again;
721 }
Denis Vlasenko33196372008-02-23 01:25:38 +0000722 set_tty_cooked();
723 return rd;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000724}
725
726/* Grab a character from input without requiring the return key. If the
Denis Vlasenko7cea2622006-12-24 07:30:09 +0000727 * character is ASCII \033, get more characters and assign certain sequences
728 * special return codes. Note that this function works best with raw input. */
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000729static int less_getch(int pos)
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000730{
Denis Vlasenko33196372008-02-23 01:25:38 +0000731 unsigned char input[16];
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000732 unsigned i;
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000733
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000734 again:
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000735 less_gets_pos = pos;
Denis Vlasenko46340e32007-08-03 14:17:21 +0000736 memset(input, 0, sizeof(input));
Denis Vlasenko023dc672008-05-09 18:07:15 +0000737 getch_nowait((char *)input, sizeof(input));
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000738 less_gets_pos = -1;
Denis Vlasenko46340e32007-08-03 14:17:21 +0000739
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000740 /* Detect escape sequences (i.e. arrow keys) and handle
741 * them accordingly */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000742 if (input[0] == '\033' && input[1] == '[') {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000743 i = input[2] - REAL_KEY_UP;
744 if (i < 4)
745 return 20 + i;
746 i = input[2] - REAL_PAGE_UP;
747 if (i < 4)
748 return 24 + i;
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000749 if (input[2] == REAL_KEY_HOME_XTERM)
750 return KEY_HOME;
Denis Vlasenkof5a15762007-03-09 08:55:23 +0000751 if (input[2] == REAL_KEY_HOME_ALT)
752 return KEY_HOME;
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000753 if (input[2] == REAL_KEY_END_XTERM)
754 return KEY_END;
Denis Vlasenkof5a15762007-03-09 08:55:23 +0000755 if (input[2] == REAL_KEY_END_ALT)
756 return KEY_END;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000757 return 0;
758 }
759 /* Reject almost all control chars */
760 i = input[0];
Denis Vlasenko33196372008-02-23 01:25:38 +0000761 if (i < ' ' && i != 0x0d && i != 8)
762 goto again;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000763 return i;
764}
765
766static char* less_gets(int sz)
767{
768 char c;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000769 unsigned i = 0;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000770 char *result = xzalloc(1);
Denis Vlasenko33196372008-02-23 01:25:38 +0000771
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000772 while (1) {
Denis Vlasenko46340e32007-08-03 14:17:21 +0000773 c = '\0';
Denis Vlasenko33196372008-02-23 01:25:38 +0000774 less_gets_pos = sz + i;
775 getch_nowait(&c, 1);
776 if (c == 0x0d) {
Denis Vlasenkod553faf2008-02-23 12:22:17 +0000777 result[i] = '\0';
Denis Vlasenko33196372008-02-23 01:25:38 +0000778 less_gets_pos = -1;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000779 return result;
Denis Vlasenko33196372008-02-23 01:25:38 +0000780 }
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000781 if (c == 0x7f)
782 c = 8;
783 if (c == 8 && i) {
784 printf("\x8 \x8");
785 i--;
786 }
787 if (c < ' ')
788 continue;
789 if (i >= width - sz - 1)
790 continue; /* len limit */
Denis Vlasenko4daad902007-09-27 10:20:47 +0000791 bb_putchar(c);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000792 result[i++] = c;
793 result = xrealloc(result, i+1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000794 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000795}
796
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000797static void examine_file(void)
798{
Denis Vlasenko33196372008-02-23 01:25:38 +0000799 char *new_fname;
800
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000801 print_statusline("Examine: ");
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000802 new_fname = less_gets(sizeof("Examine: ") - 1);
Denis Vlasenko33196372008-02-23 01:25:38 +0000803 if (!new_fname[0]) {
Denis Vlasenko33196372008-02-23 01:25:38 +0000804 status_print();
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000805 err:
806 free(new_fname);
Denis Vlasenko33196372008-02-23 01:25:38 +0000807 return;
808 }
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000809 if (access(new_fname, R_OK) != 0) {
810 print_statusline("Cannot read this file");
811 goto err;
812 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000813 free(filename);
Denis Vlasenko33196372008-02-23 01:25:38 +0000814 filename = new_fname;
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000815 /* files start by = argv. why we assume that argv is infinitely long??
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000816 files[num_files] = filename;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000817 current_file = num_files + 1;
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000818 num_files++; */
819 files[0] = filename;
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000820 num_files = current_file = 1;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000821 reinitialize();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000822}
823
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000824/* This function changes the file currently being paged. direction can be one of the following:
825 * -1: go back one file
826 * 0: go to the first file
Denis Vlasenko7cea2622006-12-24 07:30:09 +0000827 * 1: go forward one file */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000828static void change_file(int direction)
829{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000830 if (current_file != ((direction > 0) ? num_files : 1)) {
831 current_file = direction ? current_file + direction : 1;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000832 free(filename);
833 filename = xstrdup(files[current_file - 1]);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000834 reinitialize();
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000835 } else {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000836 print_statusline(direction > 0 ? "No next file" : "No previous file");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000837 }
838}
839
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000840static void remove_current_file(void)
841{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000842 unsigned i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000843
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000844 if (num_files < 2)
845 return;
846
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000847 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000848 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000849 for (i = 3; i <= num_files; i++)
850 files[i - 2] = files[i - 1];
851 num_files--;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000852 } else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000853 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000854 for (i = 2; i <= num_files; i++)
855 files[i - 2] = files[i - 1];
856 num_files--;
857 current_file--;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000858 }
859}
860
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000861static void colon_process(void)
862{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000863 int keypress;
864
865 /* Clear the current line and print a prompt */
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000866 print_statusline(" :");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000867
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000868 keypress = less_getch(2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000869 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000870 case 'd':
871 remove_current_file();
872 break;
873 case 'e':
874 examine_file();
875 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000876#if ENABLE_FEATURE_LESS_FLAGS
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000877 case 'f':
878 m_status_print();
879 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000880#endif
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000881 case 'n':
882 change_file(1);
883 break;
884 case 'p':
885 change_file(-1);
886 break;
887 case 'q':
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000888 less_exit(EXIT_SUCCESS);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000889 break;
890 case 'x':
891 change_file(0);
892 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000893 }
894}
895
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000896#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000897static void normalize_match_pos(int match)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000898{
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000899 if (match >= num_matches)
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000900 match = num_matches - 1;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000901 if (match < 0)
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000902 match = 0;
903 match_pos = match;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000904}
905
Rob Landleya2e98042006-04-18 01:53:41 +0000906static void goto_match(int match)
907{
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000908 if (!pattern_valid)
909 return;
910 if (match < 0)
911 match = 0;
912 /* Try to find next match if eof isn't reached yet */
913 if (match >= num_matches && eof_error > 0) {
Denis Vlasenko24f824e2008-04-13 08:32:51 +0000914 wanted_match = match; /* "I want to read until I see N'th match" */
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000915 read_lines();
916 }
917 if (num_matches) {
918 normalize_match_pos(match);
919 buffer_line(match_lines[match_pos]);
Denis Vlasenko8465a992007-05-09 18:32:54 +0000920 } else {
Denis Vlasenko8465a992007-05-09 18:32:54 +0000921 print_statusline("No matches found");
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000922 }
Rob Landleya2e98042006-04-18 01:53:41 +0000923}
924
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000925static void fill_match_lines(unsigned pos)
926{
927 if (!pattern_valid)
928 return;
929 /* Run the regex on each line of the current file */
930 while (pos <= max_fline) {
931 /* If this line matches */
932 if (regexec(&pattern, flines[pos], 0, NULL, 0) == 0
933 /* and we didn't match it last time */
934 && !(num_matches && match_lines[num_matches-1] == pos)
935 ) {
Denis Vlasenkodeeed592008-07-08 05:14:36 +0000936 match_lines = xrealloc_vector(match_lines, 4, num_matches);
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000937 match_lines[num_matches++] = pos;
938 }
939 pos++;
940 }
941}
942
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000943static void regex_process(void)
944{
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000945 char *uncomp_regex, *err;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000946
947 /* Reset variables */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000948 free(match_lines);
949 match_lines = NULL;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000950 match_pos = 0;
951 num_matches = 0;
952 if (pattern_valid) {
953 regfree(&pattern);
954 pattern_valid = 0;
955 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000956
Rob Landleya2e98042006-04-18 01:53:41 +0000957 /* Get the uncompiled regular expression from the user */
958 clear_line();
Denis Vlasenko4daad902007-09-27 10:20:47 +0000959 bb_putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000960 uncomp_regex = less_gets(1);
961 if (!uncomp_regex[0]) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000962 free(uncomp_regex);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000963 buffer_print();
Rob Landleya2e98042006-04-18 01:53:41 +0000964 return;
965 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000966
Rob Landleya2e98042006-04-18 01:53:41 +0000967 /* Compile the regex and check for errors */
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000968 err = regcomp_or_errmsg(&pattern, uncomp_regex, 0);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000969 free(uncomp_regex);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000970 if (err) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000971 print_statusline(err);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000972 free(err);
973 return;
974 }
Denis Vlasenkoa1c63122007-03-08 18:12:01 +0000975
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000976 pattern_valid = 1;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000977 match_pos = 0;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000978 fill_match_lines(0);
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000979 while (match_pos < num_matches) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000980 if ((int)match_lines[match_pos] > cur_fline)
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000981 break;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000982 match_pos++;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000983 }
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000984 if (option_mask32 & LESS_STATE_MATCH_BACKWARDS)
985 match_pos--;
Denis Vlasenkoa1c63122007-03-08 18:12:01 +0000986
987 /* It's possible that no matches are found yet.
988 * goto_match() will read input looking for match,
989 * if needed */
990 goto_match(match_pos);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000991}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000992#endif
993
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000994static void number_process(int first_digit)
995{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000996 unsigned i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000997 int num;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000998 char num_input[sizeof(int)*4]; /* more than enough */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000999 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001000
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001001 num_input[0] = first_digit;
1002
1003 /* Clear the current line, print a prompt, and then print the digit */
1004 clear_line();
1005 printf(":%c", first_digit);
1006
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001007 /* Receive input until a letter is given */
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001008 i = 1;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001009 while (i < sizeof(num_input)-1) {
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001010 num_input[i] = less_getch(i + 1);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001011 if (!num_input[i] || !isdigit(num_input[i]))
1012 break;
Denis Vlasenko4daad902007-09-27 10:20:47 +00001013 bb_putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001014 i++;
1015 }
1016
1017 /* Take the final letter out of the digits string */
1018 keypress = num_input[i];
1019 num_input[i] = '\0';
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001020 num = bb_strtou(num_input, NULL, 10);
1021 /* on format error, num == -1 */
1022 if (num < 1 || num > MAXLINES) {
Rob Landleya2e98042006-04-18 01:53:41 +00001023 buffer_print();
1024 return;
1025 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001026
1027 /* We now know the number and the letter entered, so we process them */
1028 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001029 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
1030 buffer_down(num);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001031 break;
1032 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
1033 buffer_up(num);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001034 break;
1035 case 'g': case '<': case 'G': case '>':
1036 cur_fline = num + max_displayed_line;
Denis Vlasenko8e858e22007-03-07 09:35:43 +00001037 read_lines();
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001038 buffer_line(num - 1);
1039 break;
1040 case 'p': case '%':
1041 num = num * (max_fline / 100); /* + max_fline / 2; */
1042 cur_fline = num + max_displayed_line;
Denis Vlasenko8e858e22007-03-07 09:35:43 +00001043 read_lines();
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001044 buffer_line(num);
1045 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001046#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001047 case 'n':
1048 goto_match(match_pos + num);
1049 break;
1050 case '/':
1051 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
1052 regex_process();
1053 break;
1054 case '?':
1055 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
1056 regex_process();
1057 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001058#endif
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001059 }
1060}
1061
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001062#if ENABLE_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001063static void flag_change(void)
1064{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001065 int keypress;
1066
1067 clear_line();
Denis Vlasenko4daad902007-09-27 10:20:47 +00001068 bb_putchar('-');
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001069 keypress = less_getch(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001070
1071 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001072 case 'M':
1073 option_mask32 ^= FLAG_M;
1074 break;
1075 case 'm':
1076 option_mask32 ^= FLAG_m;
1077 break;
1078 case 'E':
1079 option_mask32 ^= FLAG_E;
1080 break;
1081 case '~':
1082 option_mask32 ^= FLAG_TILDE;
1083 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001084 }
1085}
1086
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001087static void show_flag_status(void)
1088{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001089 int keypress;
1090 int flag_val;
1091
1092 clear_line();
Denis Vlasenko4daad902007-09-27 10:20:47 +00001093 bb_putchar('_');
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001094 keypress = less_getch(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001095
1096 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001097 case 'M':
1098 flag_val = option_mask32 & FLAG_M;
1099 break;
1100 case 'm':
1101 flag_val = option_mask32 & FLAG_m;
1102 break;
1103 case '~':
1104 flag_val = option_mask32 & FLAG_TILDE;
1105 break;
1106 case 'N':
1107 flag_val = option_mask32 & FLAG_N;
1108 break;
1109 case 'E':
1110 flag_val = option_mask32 & FLAG_E;
1111 break;
1112 default:
1113 flag_val = 0;
1114 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001115 }
1116
1117 clear_line();
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001118 printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001119}
1120#endif
1121
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001122static void save_input_to_file(void)
1123{
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001124 const char *msg = "";
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001125 char *current_line;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001126 unsigned i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001127 FILE *fp;
1128
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001129 print_statusline("Log file: ");
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001130 current_line = less_gets(sizeof("Log file: ")-1);
Denis Vlasenko33196372008-02-23 01:25:38 +00001131 if (current_line[0]) {
Denis Vlasenko5415c852008-07-21 23:05:26 +00001132 fp = fopen_for_write(current_line);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001133 if (!fp) {
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001134 msg = "Error opening log file";
1135 goto ret;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001136 }
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001137 for (i = 0; i <= max_fline; i++)
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001138 fprintf(fp, "%s\n", flines[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001139 fclose(fp);
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001140 msg = "Done";
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001141 }
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001142 ret:
1143 print_statusline(msg);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001144 free(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001145}
1146
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001147#if ENABLE_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001148static void add_mark(void)
1149{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001150 int letter;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001151
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001152 print_statusline("Mark: ");
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001153 letter = less_getch(sizeof("Mark: ") - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001154
1155 if (isalpha(letter)) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001156 /* If we exceed 15 marks, start overwriting previous ones */
1157 if (num_marks == 14)
1158 num_marks = 0;
1159
1160 mark_lines[num_marks][0] = letter;
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001161 mark_lines[num_marks][1] = cur_fline;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001162 num_marks++;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001163 } else {
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001164 print_statusline("Invalid mark letter");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001165 }
1166}
1167
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001168static void goto_mark(void)
1169{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001170 int letter;
1171 int i;
1172
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001173 print_statusline("Go to mark: ");
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001174 letter = less_getch(sizeof("Go to mark: ") - 1);
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001175 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001176
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001177 if (isalpha(letter)) {
1178 for (i = 0; i <= num_marks; i++)
1179 if (letter == mark_lines[i][0]) {
1180 buffer_line(mark_lines[i][1]);
1181 break;
1182 }
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001183 if (num_marks == 14 && letter != mark_lines[14][0])
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001184 print_statusline("Mark not set");
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001185 } else
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001186 print_statusline("Invalid mark letter");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001187}
1188#endif
1189
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001190#if ENABLE_FEATURE_LESS_BRACKETS
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001191static char opp_bracket(char bracket)
1192{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001193 switch (bracket) {
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001194 case '{': case '[': /* '}' == '{' + 2. Same for '[' */
1195 bracket++;
1196 case '(': /* ')' == '(' + 1 */
1197 bracket++;
1198 break;
1199 case '}': case ']':
1200 bracket--;
1201 case ')':
1202 bracket--;
1203 break;
1204 };
1205 return bracket;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001206}
1207
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001208static void match_right_bracket(char bracket)
1209{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001210 unsigned i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001211
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001212 if (strchr(flines[cur_fline], bracket) == NULL) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001213 print_statusline("No bracket in top line");
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001214 return;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001215 }
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001216 bracket = opp_bracket(bracket);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001217 for (i = cur_fline + 1; i < max_fline; i++) {
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001218 if (strchr(flines[i], bracket) != NULL) {
1219 buffer_line(i);
1220 return;
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001221 }
1222 }
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001223 print_statusline("No matching bracket found");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001224}
1225
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001226static void match_left_bracket(char bracket)
1227{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001228 int i;
1229
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001230 if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001231 print_statusline("No bracket in bottom line");
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001232 return;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001233 }
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001234
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001235 bracket = opp_bracket(bracket);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001236 for (i = cur_fline + max_displayed_line; i >= 0; i--) {
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001237 if (strchr(flines[i], bracket) != NULL) {
1238 buffer_line(i);
1239 return;
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001240 }
1241 }
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001242 print_statusline("No matching bracket found");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001243}
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001244#endif /* FEATURE_LESS_BRACKETS */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001245
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001246static void keypress_process(int keypress)
1247{
Rob Landley9200e792005-09-15 19:26:59 +00001248 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001249 case KEY_DOWN: case 'e': case 'j': case 0x0d:
1250 buffer_down(1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001251 break;
1252 case KEY_UP: case 'y': case 'k':
1253 buffer_up(1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001254 break;
Denis Vlasenko78e2d4e2007-09-27 17:11:48 +00001255 case PAGE_DOWN: case ' ': case 'z': case 'f':
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001256 buffer_down(max_displayed_line + 1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001257 break;
1258 case PAGE_UP: case 'w': case 'b':
1259 buffer_up(max_displayed_line + 1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001260 break;
1261 case 'd':
1262 buffer_down((max_displayed_line + 1) / 2);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001263 break;
1264 case 'u':
1265 buffer_up((max_displayed_line + 1) / 2);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001266 break;
1267 case KEY_HOME: case 'g': case 'p': case '<': case '%':
1268 buffer_line(0);
1269 break;
1270 case KEY_END: case 'G': case '>':
1271 cur_fline = MAXLINES;
1272 read_lines();
1273 buffer_line(cur_fline);
1274 break;
1275 case 'q': case 'Q':
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00001276 less_exit(EXIT_SUCCESS);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001277 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001278#if ENABLE_FEATURE_LESS_MARKS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001279 case 'm':
1280 add_mark();
1281 buffer_print();
1282 break;
1283 case '\'':
1284 goto_mark();
1285 buffer_print();
1286 break;
Rob Landley9200e792005-09-15 19:26:59 +00001287#endif
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001288 case 'r': case 'R':
1289 buffer_print();
1290 break;
1291 /*case 'R':
1292 full_repaint();
1293 break;*/
1294 case 's':
1295 save_input_to_file();
1296 break;
1297 case 'E':
1298 examine_file();
1299 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001300#if ENABLE_FEATURE_LESS_FLAGS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001301 case '=':
1302 m_status_print();
1303 break;
Rob Landley9200e792005-09-15 19:26:59 +00001304#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001305#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001306 case '/':
1307 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
1308 regex_process();
1309 break;
1310 case 'n':
1311 goto_match(match_pos + 1);
1312 break;
1313 case 'N':
1314 goto_match(match_pos - 1);
1315 break;
1316 case '?':
1317 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
1318 regex_process();
1319 break;
Rob Landley9200e792005-09-15 19:26:59 +00001320#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001321#if ENABLE_FEATURE_LESS_FLAGCS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001322 case '-':
1323 flag_change();
1324 buffer_print();
1325 break;
1326 case '_':
1327 show_flag_status();
1328 break;
Rob Landley9200e792005-09-15 19:26:59 +00001329#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001330#if ENABLE_FEATURE_LESS_BRACKETS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001331 case '{': case '(': case '[':
1332 match_right_bracket(keypress);
1333 break;
1334 case '}': case ')': case ']':
1335 match_left_bracket(keypress);
1336 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001337#endif
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001338 case ':':
1339 colon_process();
1340 break;
Rob Landley9200e792005-09-15 19:26:59 +00001341 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001342
Rob Landley9200e792005-09-15 19:26:59 +00001343 if (isdigit(keypress))
1344 number_process(keypress);
1345}
1346
Denis Vlasenko400d8bb2008-02-24 13:36:01 +00001347static void sig_catcher(int sig)
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001348{
Denis Vlasenko400d8bb2008-02-24 13:36:01 +00001349 less_exit(- sig);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001350}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001351
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001352int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001353int less_main(int argc, char **argv)
1354{
Rob Landley9200e792005-09-15 19:26:59 +00001355 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001356
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +00001357 INIT_G();
1358
Denis Vlasenko95b30712006-12-31 19:23:31 +00001359 /* TODO: -x: do not interpret backspace, -xx: tab also */
1360 /* -xxx: newline also */
1361 /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
Denis Vlasenkofe7cd642007-08-18 15:32:12 +00001362 getopt32(argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001363 argc -= optind;
1364 argv += optind;
Rob Landley9200e792005-09-15 19:26:59 +00001365 num_files = argc;
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001366 files = argv;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001367
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001368 /* Another popular pager, most, detects when stdout
1369 * is not a tty and turns into cat. This makes sense. */
1370 if (!isatty(STDOUT_FILENO))
1371 return bb_cat(argv);
Denis Vlasenko4eb8b932007-03-10 16:32:14 +00001372 kbd_fd = open(CURRENT_TTY, O_RDONLY);
1373 if (kbd_fd < 0)
1374 return bb_cat(argv);
Denis Vlasenko33196372008-02-23 01:25:38 +00001375 ndelay_on(kbd_fd);
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001376
Rob Landley9200e792005-09-15 19:26:59 +00001377 if (!num_files) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001378 if (isatty(STDIN_FILENO)) {
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001379 /* Just "less"? No args and no redirection? */
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00001380 bb_error_msg("missing filename");
Rob Landley9200e792005-09-15 19:26:59 +00001381 bb_show_usage();
1382 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001383 } else
1384 filename = xstrdup(files[0]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001385
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001386 get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
1387 /* 20: two tabstops + 4 */
1388 if (width < 20 || max_displayed_line < 3)
Denis Vlasenko107fe7c2008-03-17 08:38:45 +00001389 return bb_cat(argv);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001390 max_displayed_line -= 2;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +00001391
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001392 buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001393 if (option_mask32 & FLAG_TILDE)
1394 empty_line_marker = "";
Denis Vlasenko3f3190e2006-12-21 00:22:03 +00001395
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001396 tcgetattr(kbd_fd, &term_orig);
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +00001397 term_less = term_orig;
1398 term_less.c_lflag &= ~(ICANON | ECHO);
1399 term_less.c_iflag &= ~(IXON | ICRNL);
1400 /*term_less.c_oflag &= ~ONLCR;*/
1401 term_less.c_cc[VMIN] = 1;
1402 term_less.c_cc[VTIME] = 0;
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001403
Denis Vlasenkod553faf2008-02-23 12:22:17 +00001404 /* We want to restore term_orig on exit */
Denis Vlasenkocf7cf622008-03-19 19:38:46 +00001405 bb_signals(BB_FATAL_SIGS, sig_catcher);
Denis Vlasenkod553faf2008-02-23 12:22:17 +00001406
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001407 reinitialize();
Rob Landley9200e792005-09-15 19:26:59 +00001408 while (1) {
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001409 keypress = less_getch(-1); /* -1: do not position cursor */
Rob Landley9200e792005-09-15 19:26:59 +00001410 keypress_process(keypress);
1411 }
1412}