blob: 25b91c0fe89422d7124a9a1a91b09956bcb62e25 [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 }
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 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 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);
541 }
542
543 if (!growline) {
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000544 printf(CLEAR_2_EOL"%s\n", str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000545 return;
546 }
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000547 printf(CLEAR_2_EOL"%s%s\n", growline, str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000548 free(growline);
549}
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000550#else
551void print_found(const char *line);
552#endif
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000553
554static void print_ascii(const char *str)
555{
556 char buf[width];
557 char *p;
558 size_t n;
559
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000560 printf(CLEAR_2_EOL);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000561 while (*str) {
562 n = strcspn(str, controls);
563 if (n) {
564 if (!str[n]) break;
Denis Vlasenko806116b2006-12-31 12:14:16 +0000565 printf("%.*s", (int) n, str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000566 str += n;
567 }
568 n = strspn(str, controls);
569 p = buf;
570 do {
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000571 if (*str == 0x7f)
572 *p++ = '?';
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000573 else if (*str == (char)0x9b)
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000574 /* VT100's CSI, aka Meta-ESC. Who's inventor? */
575 /* I want to know who committed this sin */
576 *p++ = '{';
577 else
578 *p++ = ctrlconv[(unsigned char)*str];
579 str++;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000580 } while (--n);
581 *p = '\0';
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000582 print_hilite(buf);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000583 }
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000584 puts(str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000585}
586
Rob Landley9200e792005-09-15 19:26:59 +0000587/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000588static void buffer_print(void)
589{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000590 unsigned i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000591
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000592 move_cursor(0, 0);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000593 for (i = 0; i <= max_displayed_line; i++)
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000594 if (pattern_valid)
595 print_found(buffer[i]);
596 else
597 print_ascii(buffer[i]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000598 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000599}
600
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000601static void buffer_fill_and_print(void)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000602{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000603 unsigned i;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000604 for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) {
605 buffer[i] = flines[cur_fline + i];
Rob Landley9200e792005-09-15 19:26:59 +0000606 }
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000607 for (; i <= max_displayed_line; i++) {
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000608 buffer[i] = empty_line_marker;
Rob Landley9200e792005-09-15 19:26:59 +0000609 }
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000610 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000611}
612
613/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000614static void buffer_down(int nlines)
615{
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000616 cur_fline += nlines;
617 read_lines();
Denis Vlasenko5c1de362007-03-08 16:44:32 +0000618 cap_cur_fline(nlines);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000619 buffer_fill_and_print();
Rob Landley9200e792005-09-15 19:26:59 +0000620}
621
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000622static void buffer_up(int nlines)
623{
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000624 cur_fline -= nlines;
625 if (cur_fline < 0) cur_fline = 0;
626 read_lines();
627 buffer_fill_and_print();
Rob Landley9200e792005-09-15 19:26:59 +0000628}
629
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000630static void buffer_line(int linenum)
631{
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000632 if (linenum < 0)
633 linenum = 0;
634 cur_fline = linenum;
635 read_lines();
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000636 if (linenum + max_displayed_line > max_fline)
637 linenum = max_fline - max_displayed_line + TILDES;
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000638 if (linenum < 0)
639 linenum = 0;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000640 cur_fline = linenum;
641 buffer_fill_and_print();
Rob Landley9200e792005-09-15 19:26:59 +0000642}
643
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000644static void open_file_and_read_lines(void)
645{
646 if (filename) {
647 int fd = xopen(filename, O_RDONLY);
648 dup2(fd, 0);
649 if (fd) close(fd);
650 } else {
651 /* "less" with no arguments in argv[] */
652 /* For status line only */
653 filename = xstrdup(bb_msg_standard_input);
654 }
655 readpos = 0;
656 readeof = 0;
657 linepos = 0;
658 terminated = 1;
659 read_lines();
660}
661
662/* Reinitialize everything for a new file - free the memory and start over */
663static void reinitialize(void)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000664{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000665 unsigned i;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000666
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000667 if (flines) {
668 for (i = 0; i <= max_fline; i++)
669 free((void*)(flines[i]));
670 free(flines);
671 flines = NULL;
672 }
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000673
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000674 max_fline = -1;
675 cur_fline = 0;
676 max_lineno = 0;
677 open_file_and_read_lines();
678 buffer_fill_and_print();
679}
680
Denis Vlasenko33196372008-02-23 01:25:38 +0000681static ssize_t getch_nowait(char* input, int sz)
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000682{
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000683 ssize_t rd;
Denis Vlasenko33196372008-02-23 01:25:38 +0000684 struct pollfd pfd[2];
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000685
Denis Vlasenko33196372008-02-23 01:25:38 +0000686 pfd[0].fd = STDIN_FILENO;
687 pfd[0].events = POLLIN;
688 pfd[1].fd = kbd_fd;
689 pfd[1].events = POLLIN;
690 again:
691 tcsetattr(kbd_fd, TCSANOW, &term_less);
692 /* NB: select/poll returns whenever read will not block. Therefore:
693 * if eof is reached, select/poll will return immediately
694 * because read will immediately return 0 bytes.
695 * Even if select/poll says that input is available, read CAN block
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000696 * (switch fd into O_NONBLOCK'ed mode to avoid it)
697 */
Denis Vlasenko33196372008-02-23 01:25:38 +0000698 rd = 1;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000699 if (max_fline <= cur_fline + max_displayed_line
700 && eof_error > 0 /* did NOT reach eof yet */
701 ) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000702 /* We are interested in stdin */
Denis Vlasenko33196372008-02-23 01:25:38 +0000703 rd = 0;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000704 }
Denis Vlasenko33196372008-02-23 01:25:38 +0000705 /* position cursor if line input is done */
706 if (less_gets_pos >= 0)
707 move_cursor(max_displayed_line + 2, less_gets_pos + 1);
708 fflush(stdout);
709 safe_poll(pfd + rd, 2 - rd, -1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000710
711 input[0] = '\0';
Denis Vlasenko33196372008-02-23 01:25:38 +0000712 rd = safe_read(kbd_fd, input, sz); /* NB: kbd_fd is in O_NONBLOCK mode */
713 if (rd < 0 && errno == EAGAIN) {
714 /* No keyboard input -> we have input on stdin! */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000715 read_lines();
716 buffer_fill_and_print();
717 goto again;
718 }
Denis Vlasenko33196372008-02-23 01:25:38 +0000719 set_tty_cooked();
720 return rd;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000721}
722
723/* Grab a character from input without requiring the return key. If the
Denis Vlasenko7cea2622006-12-24 07:30:09 +0000724 * character is ASCII \033, get more characters and assign certain sequences
725 * special return codes. Note that this function works best with raw input. */
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000726static int less_getch(int pos)
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000727{
Denis Vlasenko33196372008-02-23 01:25:38 +0000728 unsigned char input[16];
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000729 unsigned i;
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000730
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000731 again:
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000732 less_gets_pos = pos;
Denis Vlasenko46340e32007-08-03 14:17:21 +0000733 memset(input, 0, sizeof(input));
Denis Vlasenko023dc672008-05-09 18:07:15 +0000734 getch_nowait((char *)input, sizeof(input));
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000735 less_gets_pos = -1;
Denis Vlasenko46340e32007-08-03 14:17:21 +0000736
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000737 /* Detect escape sequences (i.e. arrow keys) and handle
738 * them accordingly */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000739 if (input[0] == '\033' && input[1] == '[') {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000740 i = input[2] - REAL_KEY_UP;
741 if (i < 4)
742 return 20 + i;
743 i = input[2] - REAL_PAGE_UP;
744 if (i < 4)
745 return 24 + i;
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000746 if (input[2] == REAL_KEY_HOME_XTERM)
747 return KEY_HOME;
Denis Vlasenkof5a15762007-03-09 08:55:23 +0000748 if (input[2] == REAL_KEY_HOME_ALT)
749 return KEY_HOME;
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000750 if (input[2] == REAL_KEY_END_XTERM)
751 return KEY_END;
Denis Vlasenkof5a15762007-03-09 08:55:23 +0000752 if (input[2] == REAL_KEY_END_ALT)
753 return KEY_END;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000754 return 0;
755 }
756 /* Reject almost all control chars */
757 i = input[0];
Denis Vlasenko33196372008-02-23 01:25:38 +0000758 if (i < ' ' && i != 0x0d && i != 8)
759 goto again;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000760 return i;
761}
762
763static char* less_gets(int sz)
764{
765 char c;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000766 unsigned i = 0;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000767 char *result = xzalloc(1);
Denis Vlasenko33196372008-02-23 01:25:38 +0000768
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000769 while (1) {
Denis Vlasenko46340e32007-08-03 14:17:21 +0000770 c = '\0';
Denis Vlasenko33196372008-02-23 01:25:38 +0000771 less_gets_pos = sz + i;
772 getch_nowait(&c, 1);
773 if (c == 0x0d) {
Denis Vlasenkod553faf2008-02-23 12:22:17 +0000774 result[i] = '\0';
Denis Vlasenko33196372008-02-23 01:25:38 +0000775 less_gets_pos = -1;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000776 return result;
Denis Vlasenko33196372008-02-23 01:25:38 +0000777 }
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000778 if (c == 0x7f)
779 c = 8;
780 if (c == 8 && i) {
781 printf("\x8 \x8");
782 i--;
783 }
784 if (c < ' ')
785 continue;
786 if (i >= width - sz - 1)
787 continue; /* len limit */
Denis Vlasenko4daad902007-09-27 10:20:47 +0000788 bb_putchar(c);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000789 result[i++] = c;
790 result = xrealloc(result, i+1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000791 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000792}
793
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000794static void examine_file(void)
795{
Denis Vlasenko33196372008-02-23 01:25:38 +0000796 char *new_fname;
797
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000798 print_statusline("Examine: ");
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000799 new_fname = less_gets(sizeof("Examine: ") - 1);
Denis Vlasenko33196372008-02-23 01:25:38 +0000800 if (!new_fname[0]) {
Denis Vlasenko33196372008-02-23 01:25:38 +0000801 status_print();
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000802 err:
803 free(new_fname);
Denis Vlasenko33196372008-02-23 01:25:38 +0000804 return;
805 }
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000806 if (access(new_fname, R_OK) != 0) {
807 print_statusline("Cannot read this file");
808 goto err;
809 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000810 free(filename);
Denis Vlasenko33196372008-02-23 01:25:38 +0000811 filename = new_fname;
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000812 /* files start by = argv. why we assume that argv is infinitely long??
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000813 files[num_files] = filename;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000814 current_file = num_files + 1;
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000815 num_files++; */
816 files[0] = filename;
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000817 num_files = current_file = 1;
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000818 reinitialize();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000819}
820
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000821/* This function changes the file currently being paged. direction can be one of the following:
822 * -1: go back one file
823 * 0: go to the first file
Denis Vlasenko7cea2622006-12-24 07:30:09 +0000824 * 1: go forward one file */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000825static void change_file(int direction)
826{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000827 if (current_file != ((direction > 0) ? num_files : 1)) {
828 current_file = direction ? current_file + direction : 1;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000829 free(filename);
830 filename = xstrdup(files[current_file - 1]);
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000831 reinitialize();
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000832 } else {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000833 print_statusline(direction > 0 ? "No next file" : "No previous file");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000834 }
835}
836
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000837static void remove_current_file(void)
838{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000839 unsigned i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000840
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000841 if (num_files < 2)
842 return;
843
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000844 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000845 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000846 for (i = 3; i <= num_files; i++)
847 files[i - 2] = files[i - 1];
848 num_files--;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000849 } else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000850 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000851 for (i = 2; i <= num_files; i++)
852 files[i - 2] = files[i - 1];
853 num_files--;
854 current_file--;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000855 }
856}
857
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000858static void colon_process(void)
859{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000860 int keypress;
861
862 /* Clear the current line and print a prompt */
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000863 print_statusline(" :");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000864
Denis Vlasenkod2172c02008-02-23 11:54:37 +0000865 keypress = less_getch(2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000866 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000867 case 'd':
868 remove_current_file();
869 break;
870 case 'e':
871 examine_file();
872 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000873#if ENABLE_FEATURE_LESS_FLAGS
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000874 case 'f':
875 m_status_print();
876 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000877#endif
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000878 case 'n':
879 change_file(1);
880 break;
881 case 'p':
882 change_file(-1);
883 break;
884 case 'q':
885 less_exit(0);
886 break;
887 case 'x':
888 change_file(0);
889 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000890 }
891}
892
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000893#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000894static void normalize_match_pos(int match)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000895{
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000896 if (match >= num_matches)
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000897 match = num_matches - 1;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000898 if (match < 0)
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000899 match = 0;
900 match_pos = match;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000901}
902
Rob Landleya2e98042006-04-18 01:53:41 +0000903static void goto_match(int match)
904{
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000905 if (!pattern_valid)
906 return;
907 if (match < 0)
908 match = 0;
909 /* Try to find next match if eof isn't reached yet */
910 if (match >= num_matches && eof_error > 0) {
Denis Vlasenko24f824e2008-04-13 08:32:51 +0000911 wanted_match = match; /* "I want to read until I see N'th match" */
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000912 read_lines();
913 }
914 if (num_matches) {
915 normalize_match_pos(match);
916 buffer_line(match_lines[match_pos]);
Denis Vlasenko8465a992007-05-09 18:32:54 +0000917 } else {
Denis Vlasenko8465a992007-05-09 18:32:54 +0000918 print_statusline("No matches found");
Denis Vlasenkob30418a2007-02-14 20:49:14 +0000919 }
Rob Landleya2e98042006-04-18 01:53:41 +0000920}
921
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000922static void fill_match_lines(unsigned pos)
923{
924 if (!pattern_valid)
925 return;
926 /* Run the regex on each line of the current file */
927 while (pos <= max_fline) {
928 /* If this line matches */
929 if (regexec(&pattern, flines[pos], 0, NULL, 0) == 0
930 /* and we didn't match it last time */
931 && !(num_matches && match_lines[num_matches-1] == pos)
932 ) {
933 match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int));
934 match_lines[num_matches++] = pos;
935 }
936 pos++;
937 }
938}
939
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000940static void regex_process(void)
941{
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000942 char *uncomp_regex, *err;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000943
944 /* Reset variables */
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000945 free(match_lines);
946 match_lines = NULL;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000947 match_pos = 0;
948 num_matches = 0;
949 if (pattern_valid) {
950 regfree(&pattern);
951 pattern_valid = 0;
952 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000953
Rob Landleya2e98042006-04-18 01:53:41 +0000954 /* Get the uncompiled regular expression from the user */
955 clear_line();
Denis Vlasenko4daad902007-09-27 10:20:47 +0000956 bb_putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
Denis Vlasenkof4dff772006-12-24 07:14:17 +0000957 uncomp_regex = less_gets(1);
958 if (!uncomp_regex[0]) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000959 free(uncomp_regex);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000960 buffer_print();
Rob Landleya2e98042006-04-18 01:53:41 +0000961 return;
962 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000963
Rob Landleya2e98042006-04-18 01:53:41 +0000964 /* Compile the regex and check for errors */
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000965 err = regcomp_or_errmsg(&pattern, uncomp_regex, 0);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000966 free(uncomp_regex);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000967 if (err) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000968 print_statusline(err);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000969 free(err);
970 return;
971 }
Denis Vlasenkoa1c63122007-03-08 18:12:01 +0000972
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000973 pattern_valid = 1;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000974 match_pos = 0;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000975 fill_match_lines(0);
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000976 while (match_pos < num_matches) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000977 if ((int)match_lines[match_pos] > cur_fline)
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000978 break;
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000979 match_pos++;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000980 }
Denis Vlasenko5a4f0992006-12-25 01:23:02 +0000981 if (option_mask32 & LESS_STATE_MATCH_BACKWARDS)
982 match_pos--;
Denis Vlasenkoa1c63122007-03-08 18:12:01 +0000983
984 /* It's possible that no matches are found yet.
985 * goto_match() will read input looking for match,
986 * if needed */
987 goto_match(match_pos);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000988}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000989#endif
990
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000991static void number_process(int first_digit)
992{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000993 unsigned i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000994 int num;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000995 char num_input[sizeof(int)*4]; /* more than enough */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000996 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000997
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000998 num_input[0] = first_digit;
999
1000 /* Clear the current line, print a prompt, and then print the digit */
1001 clear_line();
1002 printf(":%c", first_digit);
1003
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001004 /* Receive input until a letter is given */
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001005 i = 1;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001006 while (i < sizeof(num_input)-1) {
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001007 num_input[i] = less_getch(i + 1);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001008 if (!num_input[i] || !isdigit(num_input[i]))
1009 break;
Denis Vlasenko4daad902007-09-27 10:20:47 +00001010 bb_putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001011 i++;
1012 }
1013
1014 /* Take the final letter out of the digits string */
1015 keypress = num_input[i];
1016 num_input[i] = '\0';
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001017 num = bb_strtou(num_input, NULL, 10);
1018 /* on format error, num == -1 */
1019 if (num < 1 || num > MAXLINES) {
Rob Landleya2e98042006-04-18 01:53:41 +00001020 buffer_print();
1021 return;
1022 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001023
1024 /* We now know the number and the letter entered, so we process them */
1025 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001026 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
1027 buffer_down(num);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001028 break;
1029 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
1030 buffer_up(num);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001031 break;
1032 case 'g': case '<': case 'G': case '>':
1033 cur_fline = num + max_displayed_line;
Denis Vlasenko8e858e22007-03-07 09:35:43 +00001034 read_lines();
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001035 buffer_line(num - 1);
1036 break;
1037 case 'p': case '%':
1038 num = num * (max_fline / 100); /* + max_fline / 2; */
1039 cur_fline = num + max_displayed_line;
Denis Vlasenko8e858e22007-03-07 09:35:43 +00001040 read_lines();
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001041 buffer_line(num);
1042 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001043#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001044 case 'n':
1045 goto_match(match_pos + num);
1046 break;
1047 case '/':
1048 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
1049 regex_process();
1050 break;
1051 case '?':
1052 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
1053 regex_process();
1054 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001055#endif
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001056 }
1057}
1058
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001059#if ENABLE_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001060static void flag_change(void)
1061{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001062 int keypress;
1063
1064 clear_line();
Denis Vlasenko4daad902007-09-27 10:20:47 +00001065 bb_putchar('-');
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001066 keypress = less_getch(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001067
1068 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001069 case 'M':
1070 option_mask32 ^= FLAG_M;
1071 break;
1072 case 'm':
1073 option_mask32 ^= FLAG_m;
1074 break;
1075 case 'E':
1076 option_mask32 ^= FLAG_E;
1077 break;
1078 case '~':
1079 option_mask32 ^= FLAG_TILDE;
1080 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001081 }
1082}
1083
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001084static void show_flag_status(void)
1085{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001086 int keypress;
1087 int flag_val;
1088
1089 clear_line();
Denis Vlasenko4daad902007-09-27 10:20:47 +00001090 bb_putchar('_');
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001091 keypress = less_getch(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001092
1093 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001094 case 'M':
1095 flag_val = option_mask32 & FLAG_M;
1096 break;
1097 case 'm':
1098 flag_val = option_mask32 & FLAG_m;
1099 break;
1100 case '~':
1101 flag_val = option_mask32 & FLAG_TILDE;
1102 break;
1103 case 'N':
1104 flag_val = option_mask32 & FLAG_N;
1105 break;
1106 case 'E':
1107 flag_val = option_mask32 & FLAG_E;
1108 break;
1109 default:
1110 flag_val = 0;
1111 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001112 }
1113
1114 clear_line();
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001115 printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001116}
1117#endif
1118
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001119static void save_input_to_file(void)
1120{
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001121 const char *msg = "";
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001122 char *current_line;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001123 unsigned i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001124 FILE *fp;
1125
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001126 print_statusline("Log file: ");
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001127 current_line = less_gets(sizeof("Log file: ")-1);
Denis Vlasenko33196372008-02-23 01:25:38 +00001128 if (current_line[0]) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001129 fp = fopen(current_line, "w");
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001130 if (!fp) {
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001131 msg = "Error opening log file";
1132 goto ret;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001133 }
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001134 for (i = 0; i <= max_fline; i++)
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001135 fprintf(fp, "%s\n", flines[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001136 fclose(fp);
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001137 msg = "Done";
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001138 }
Denis Vlasenko5a4f0992006-12-25 01:23:02 +00001139 ret:
1140 print_statusline(msg);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001141 free(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001142}
1143
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001144#if ENABLE_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001145static void add_mark(void)
1146{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001147 int letter;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001148
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001149 print_statusline("Mark: ");
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001150 letter = less_getch(sizeof("Mark: ") - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001151
1152 if (isalpha(letter)) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001153 /* If we exceed 15 marks, start overwriting previous ones */
1154 if (num_marks == 14)
1155 num_marks = 0;
1156
1157 mark_lines[num_marks][0] = letter;
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001158 mark_lines[num_marks][1] = cur_fline;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001159 num_marks++;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001160 } else {
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001161 print_statusline("Invalid mark letter");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001162 }
1163}
1164
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001165static void goto_mark(void)
1166{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001167 int letter;
1168 int i;
1169
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001170 print_statusline("Go to mark: ");
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001171 letter = less_getch(sizeof("Go to mark: ") - 1);
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001172 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001173
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001174 if (isalpha(letter)) {
1175 for (i = 0; i <= num_marks; i++)
1176 if (letter == mark_lines[i][0]) {
1177 buffer_line(mark_lines[i][1]);
1178 break;
1179 }
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001180 if (num_marks == 14 && letter != mark_lines[14][0])
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001181 print_statusline("Mark not set");
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001182 } else
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001183 print_statusline("Invalid mark letter");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001184}
1185#endif
1186
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001187#if ENABLE_FEATURE_LESS_BRACKETS
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001188static char opp_bracket(char bracket)
1189{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001190 switch (bracket) {
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001191 case '{': case '[': /* '}' == '{' + 2. Same for '[' */
1192 bracket++;
1193 case '(': /* ')' == '(' + 1 */
1194 bracket++;
1195 break;
1196 case '}': case ']':
1197 bracket--;
1198 case ')':
1199 bracket--;
1200 break;
1201 };
1202 return bracket;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001203}
1204
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001205static void match_right_bracket(char bracket)
1206{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001207 unsigned i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001208
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001209 if (strchr(flines[cur_fline], bracket) == NULL) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001210 print_statusline("No bracket in top line");
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001211 return;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001212 }
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001213 bracket = opp_bracket(bracket);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001214 for (i = cur_fline + 1; i < max_fline; i++) {
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001215 if (strchr(flines[i], bracket) != NULL) {
1216 buffer_line(i);
1217 return;
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001218 }
1219 }
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001220 print_statusline("No matching bracket found");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001221}
1222
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001223static void match_left_bracket(char bracket)
1224{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001225 int i;
1226
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001227 if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +00001228 print_statusline("No bracket in bottom line");
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001229 return;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001230 }
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001231
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001232 bracket = opp_bracket(bracket);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001233 for (i = cur_fline + max_displayed_line; i >= 0; i--) {
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001234 if (strchr(flines[i], bracket) != NULL) {
1235 buffer_line(i);
1236 return;
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001237 }
1238 }
Denis Vlasenkob78d1c02008-02-24 19:18:18 +00001239 print_statusline("No matching bracket found");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001240}
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001241#endif /* FEATURE_LESS_BRACKETS */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001242
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001243static void keypress_process(int keypress)
1244{
Rob Landley9200e792005-09-15 19:26:59 +00001245 switch (keypress) {
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001246 case KEY_DOWN: case 'e': case 'j': case 0x0d:
1247 buffer_down(1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001248 break;
1249 case KEY_UP: case 'y': case 'k':
1250 buffer_up(1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001251 break;
Denis Vlasenko78e2d4e2007-09-27 17:11:48 +00001252 case PAGE_DOWN: case ' ': case 'z': case 'f':
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001253 buffer_down(max_displayed_line + 1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001254 break;
1255 case PAGE_UP: case 'w': case 'b':
1256 buffer_up(max_displayed_line + 1);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001257 break;
1258 case 'd':
1259 buffer_down((max_displayed_line + 1) / 2);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001260 break;
1261 case 'u':
1262 buffer_up((max_displayed_line + 1) / 2);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001263 break;
1264 case KEY_HOME: case 'g': case 'p': case '<': case '%':
1265 buffer_line(0);
1266 break;
1267 case KEY_END: case 'G': case '>':
1268 cur_fline = MAXLINES;
1269 read_lines();
1270 buffer_line(cur_fline);
1271 break;
1272 case 'q': case 'Q':
1273 less_exit(0);
1274 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001275#if ENABLE_FEATURE_LESS_MARKS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001276 case 'm':
1277 add_mark();
1278 buffer_print();
1279 break;
1280 case '\'':
1281 goto_mark();
1282 buffer_print();
1283 break;
Rob Landley9200e792005-09-15 19:26:59 +00001284#endif
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001285 case 'r': case 'R':
1286 buffer_print();
1287 break;
1288 /*case 'R':
1289 full_repaint();
1290 break;*/
1291 case 's':
1292 save_input_to_file();
1293 break;
1294 case 'E':
1295 examine_file();
1296 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001297#if ENABLE_FEATURE_LESS_FLAGS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001298 case '=':
1299 m_status_print();
1300 break;
Rob Landley9200e792005-09-15 19:26:59 +00001301#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001302#if ENABLE_FEATURE_LESS_REGEXP
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001303 case '/':
1304 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
1305 regex_process();
1306 break;
1307 case 'n':
1308 goto_match(match_pos + 1);
1309 break;
1310 case 'N':
1311 goto_match(match_pos - 1);
1312 break;
1313 case '?':
1314 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
1315 regex_process();
1316 break;
Rob Landley9200e792005-09-15 19:26:59 +00001317#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001318#if ENABLE_FEATURE_LESS_FLAGCS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001319 case '-':
1320 flag_change();
1321 buffer_print();
1322 break;
1323 case '_':
1324 show_flag_status();
1325 break;
Rob Landley9200e792005-09-15 19:26:59 +00001326#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001327#if ENABLE_FEATURE_LESS_BRACKETS
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001328 case '{': case '(': case '[':
1329 match_right_bracket(keypress);
1330 break;
1331 case '}': case ')': case ']':
1332 match_left_bracket(keypress);
1333 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001334#endif
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001335 case ':':
1336 colon_process();
1337 break;
Rob Landley9200e792005-09-15 19:26:59 +00001338 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001339
Rob Landley9200e792005-09-15 19:26:59 +00001340 if (isdigit(keypress))
1341 number_process(keypress);
1342}
1343
Denis Vlasenko400d8bb2008-02-24 13:36:01 +00001344static void sig_catcher(int sig)
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001345{
Denis Vlasenko400d8bb2008-02-24 13:36:01 +00001346 less_exit(- sig);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001347}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001348
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001349int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001350int less_main(int argc, char **argv)
1351{
Rob Landley9200e792005-09-15 19:26:59 +00001352 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001353
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +00001354 INIT_G();
1355
Denis Vlasenko95b30712006-12-31 19:23:31 +00001356 /* TODO: -x: do not interpret backspace, -xx: tab also */
1357 /* -xxx: newline also */
1358 /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
Denis Vlasenkofe7cd642007-08-18 15:32:12 +00001359 getopt32(argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001360 argc -= optind;
1361 argv += optind;
Rob Landley9200e792005-09-15 19:26:59 +00001362 num_files = argc;
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001363 files = argv;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001364
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001365 /* Another popular pager, most, detects when stdout
1366 * is not a tty and turns into cat. This makes sense. */
1367 if (!isatty(STDOUT_FILENO))
1368 return bb_cat(argv);
Denis Vlasenko4eb8b932007-03-10 16:32:14 +00001369 kbd_fd = open(CURRENT_TTY, O_RDONLY);
1370 if (kbd_fd < 0)
1371 return bb_cat(argv);
Denis Vlasenko33196372008-02-23 01:25:38 +00001372 ndelay_on(kbd_fd);
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001373
Rob Landley9200e792005-09-15 19:26:59 +00001374 if (!num_files) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001375 if (isatty(STDIN_FILENO)) {
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001376 /* Just "less"? No args and no redirection? */
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00001377 bb_error_msg("missing filename");
Rob Landley9200e792005-09-15 19:26:59 +00001378 bb_show_usage();
1379 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001380 } else
1381 filename = xstrdup(files[0]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001382
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001383 get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
1384 /* 20: two tabstops + 4 */
1385 if (width < 20 || max_displayed_line < 3)
Denis Vlasenko107fe7c2008-03-17 08:38:45 +00001386 return bb_cat(argv);
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001387 max_displayed_line -= 2;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +00001388
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001389 buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001390 if (option_mask32 & FLAG_TILDE)
1391 empty_line_marker = "";
Denis Vlasenko3f3190e2006-12-21 00:22:03 +00001392
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001393 tcgetattr(kbd_fd, &term_orig);
Denis Vlasenkoa1d24a02007-05-31 15:55:03 +00001394 term_less = term_orig;
1395 term_less.c_lflag &= ~(ICANON | ECHO);
1396 term_less.c_iflag &= ~(IXON | ICRNL);
1397 /*term_less.c_oflag &= ~ONLCR;*/
1398 term_less.c_cc[VMIN] = 1;
1399 term_less.c_cc[VTIME] = 0;
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001400
Denis Vlasenkod553faf2008-02-23 12:22:17 +00001401 /* We want to restore term_orig on exit */
Denis Vlasenkocf7cf622008-03-19 19:38:46 +00001402 bb_signals(BB_FATAL_SIGS, sig_catcher);
Denis Vlasenkod553faf2008-02-23 12:22:17 +00001403
Denis Vlasenkof4dff772006-12-24 07:14:17 +00001404 reinitialize();
Rob Landley9200e792005-09-15 19:26:59 +00001405 while (1) {
Denis Vlasenkod2172c02008-02-23 11:54:37 +00001406 keypress = less_getch(-1); /* -1: do not position cursor */
Rob Landley9200e792005-09-15 19:26:59 +00001407 keypress_process(keypress);
1408 }
1409}