blob: 03b93973f5c4ecea8b54592ebc9d6f6e37391ffb [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
14 * not considered.
15 * - Add support for "F" as an input. This causes less to act in
16 * a similar way to tail -f.
17 * - Allow horizontal scrolling.
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
21 * redirected input has been read from stdin
22 */
Rob Landley9200e792005-09-15 19:26:59 +000023
Bernhard Reutner-Fischerc89982d2006-06-03 19:49:21 +000024#include "busybox.h"
Denis Vlasenko9a7cef92006-12-20 02:46:48 +000025#if ENABLE_FEATURE_LESS_REGEXP
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000026#include "xregex.h"
27#endif
28
29
Rob Landley9200e792005-09-15 19:26:59 +000030/* These are the escape sequences corresponding to special keys */
31#define REAL_KEY_UP 'A'
32#define REAL_KEY_DOWN 'B'
33#define REAL_KEY_RIGHT 'C'
34#define REAL_KEY_LEFT 'D'
35#define REAL_PAGE_UP '5'
36#define REAL_PAGE_DOWN '6'
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +000037#define REAL_KEY_HOME '7'
38#define REAL_KEY_END '8'
Rob Landley9200e792005-09-15 19:26:59 +000039
40/* These are the special codes assigned by this program to the special keys */
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +000041#define KEY_UP 20
42#define KEY_DOWN 21
43#define KEY_RIGHT 22
44#define KEY_LEFT 23
45#define PAGE_UP 24
46#define PAGE_DOWN 25
47#define KEY_HOME 26
48#define KEY_END 27
Rob Landley9200e792005-09-15 19:26:59 +000049
50/* The escape codes for highlighted and normal text */
51#define HIGHLIGHT "\033[7m"
52#define NORMAL "\033[0m"
Rob Landley9200e792005-09-15 19:26:59 +000053/* The escape code to clear the screen */
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000054#define CLEAR "\033[H\033[J"
Denis Vlasenkoe865e812006-12-21 13:24:58 +000055/* The escape code to clear to end of line */
56#define CLEAR_2_EOL "\033[K"
Rob Landley9200e792005-09-15 19:26:59 +000057
Denis Vlasenko9a7cef92006-12-20 02:46:48 +000058#define MAXLINES CONFIG_FEATURE_LESS_MAXLINES
Rob Landley9200e792005-09-15 19:26:59 +000059
Rob Landley9200e792005-09-15 19:26:59 +000060static int height;
61static int width;
62static char **files;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +000063static char *filename;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +000064static const char **buffer;
Rob Landleyd57ae8b2005-09-18 00:58:49 +000065static char **flines;
Rob Landley9200e792005-09-15 19:26:59 +000066static int current_file = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000067static int line_pos;
Rob Landley9200e792005-09-15 19:26:59 +000068static int num_flines;
69static int num_files = 1;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +000070static const char *empty_line_marker = "~";
Rob Landley9200e792005-09-15 19:26:59 +000071
72/* Command line options */
Rob Landleyd57ae8b2005-09-18 00:58:49 +000073#define FLAG_E 1
74#define FLAG_M (1<<1)
75#define FLAG_m (1<<2)
76#define FLAG_N (1<<3)
77#define FLAG_TILDE (1<<4)
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +000078/* hijack command line options variable for internal state vars */
Denis Vlasenko9a7cef92006-12-20 02:46:48 +000079#define LESS_STATE_MATCH_BACKWARDS (1<<6)
Rob Landley9200e792005-09-15 19:26:59 +000080
Denis Vlasenko9a7cef92006-12-20 02:46:48 +000081#if ENABLE_FEATURE_LESS_MARKS
Rob Landley9200e792005-09-15 19:26:59 +000082static int mark_lines[15][2];
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000083static int num_marks;
Rob Landley9200e792005-09-15 19:26:59 +000084#endif
85
Denis Vlasenko9a7cef92006-12-20 02:46:48 +000086#if ENABLE_FEATURE_LESS_REGEXP
Mike Frysingerf054be12006-04-29 04:21:10 +000087static int *match_lines;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000088static int match_pos;
89static int num_matches;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +000090static regex_t pattern;
91static int pattern_valid;
Rob Landley9200e792005-09-15 19:26:59 +000092#endif
93
94/* Needed termios structures */
95static struct termios term_orig, term_vi;
96
97/* File pointer to get input from */
98static FILE *inp;
99
100/* Reset terminal input to normal */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000101static void set_tty_cooked(void)
102{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000103 fflush(stdout);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000104 tcsetattr(fileno(inp), TCSANOW, &term_orig);
Rob Landley9200e792005-09-15 19:26:59 +0000105}
106
Rob Landley9200e792005-09-15 19:26:59 +0000107/* Exit the program gracefully */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000108static void tless_exit(int code)
109{
Rob Landley9200e792005-09-15 19:26:59 +0000110 /* TODO: We really should save the terminal state when we start,
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000111 and restore it when we exit. Less does this with the
Rob Landley9200e792005-09-15 19:26:59 +0000112 "ti" and "te" termcap commands; can this be done with
113 only termios.h? */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000114
Rob Landley9200e792005-09-15 19:26:59 +0000115 putchar('\n');
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000116 fflush_stdout_and_exit(code);
Rob Landley9200e792005-09-15 19:26:59 +0000117}
118
119/* Grab a character from input without requiring the return key. If the
120 character is ASCII \033, get more characters and assign certain sequences
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000121 special return codes. Note that this function works best with raw input. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000122static int tless_getch(void)
123{
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000124 int input;
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000125 /* Set terminal input to raw mode (taken from vi.c) */
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000126 tcsetattr(fileno(inp), TCSANOW, &term_vi);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000127 again:
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000128 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000129 /* Detect escape sequences (i.e. arrow keys) and handle
130 them accordingly */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000131
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000132 if (input == '\033' && getc(inp) == '[') {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000133 unsigned i;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000134 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000135 set_tty_cooked();
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000136
137 i = input - REAL_KEY_UP;
138 if (i < 4)
139 return 20 + i;
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000140 i = input - REAL_PAGE_UP;
141 if (i < 4)
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000142 return 24 + i;
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000143 return 0; /* ?? */
Rob Landley9200e792005-09-15 19:26:59 +0000144 }
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000145 /* Reject almost all control chars */
146 if (input < ' ' && input != 0x0d && input != 8) goto again;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000147 set_tty_cooked();
148 return input;
Rob Landley9200e792005-09-15 19:26:59 +0000149}
150
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000151static char* tless_gets(int sz)
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000152{
153 int c;
154 int i = 0;
155 char *result = xzalloc(1);
156 while (1) {
157 c = tless_getch();
158 if (c == 0x0d)
159 return result;
160 if (c == 0x7f) c = 8;
161 if (c == 8 && i) {
162 printf("\x8 \x8");
163 i--;
164 }
165 if (c < ' ')
166 continue;
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000167 if (i >= width - sz - 1)
168 continue; /* len limit */
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000169 putchar(c);
170 result[i++] = c;
171 result = xrealloc(result, i+1);
172 result[i] = '\0';
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000173 }
174}
175
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000176/* Move the cursor to a position (x,y), where (0,0) is the
Rob Landley9200e792005-09-15 19:26:59 +0000177 top-left corner of the console */
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000178static void move_cursor(int line, int row)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000179{
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000180 printf("\033[%u;%uH", line, row);
Rob Landley9200e792005-09-15 19:26:59 +0000181}
182
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000183static void clear_line(void)
184{
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000185 printf("\033[%u;0H" CLEAR_2_EOL, height);
186}
187
188static void print_hilite(const char *str)
189{
190 printf(HIGHLIGHT"%s"NORMAL, str);
Rob Landley9200e792005-09-15 19:26:59 +0000191}
192
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000193static void print_statusline(const char *str)
194{
195 clear_line();
196 printf(HIGHLIGHT"%.*s"NORMAL, width-1, str);
197}
198
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000199static void data_readlines(void)
200{
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000201 unsigned i, pos;
202 unsigned lineno = 1;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000203 int w = width;
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000204 char terminated, last_terminated = 1;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000205 char *current_line, *p;
Rob Landley9200e792005-09-15 19:26:59 +0000206 FILE *fp;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000207
Denis Vlasenkod51d14e2006-12-21 13:57:37 +0000208 if (filename)
209 fp = xfopen(filename, "r");
210 else {
211 /* "less" with no arguments in argv[] */
212 fp = stdin;
213 /* For status line only */
214 filename = xstrdup(bb_msg_standard_input);
215 }
216
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000217 flines = NULL;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000218 if (option_mask32 & FLAG_N) {
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000219 w -= 8;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000220 }
221 for (i = 0; !feof(fp) && i <= MAXLINES; i++) {
222 flines = xrealloc(flines, (i+1) * sizeof(char *));
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000223 current_line = xmalloc(w);
224 again:
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000225 p = current_line;
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000226 pos = 0;
227 terminated = 0;
228 while (1) {
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000229 int c = getc(fp);
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000230 if (c == '\t') pos += (pos^7) & 7;
231 pos++;
232 if (pos >= w) { ungetc(c, fp); break; }
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000233 if (c == EOF) break;
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000234 if (c == '\n') { terminated = 1; break; }
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000235 /* NUL is substituted by '\n'! */
236 if (c == '\0') c = '\n';
237 *p++ = c;
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000238 }
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000239 *p = '\0';
Mike Frysinger00d10a92006-04-16 20:54:19 +0000240 if (fp != stdin)
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000241 die_if_ferror(fp, filename);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000242
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000243 /* Corner case: linewrap with only "" wrapping to next line */
244 /* Looks ugly on screen, so we do not store this empty line */
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000245 if (!last_terminated && !current_line[0]) {
246 last_terminated = 1;
247 lineno++;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000248 goto again;
249 }
Denis Vlasenkod51d14e2006-12-21 13:57:37 +0000250
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000251 last_terminated = terminated;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000252 if (option_mask32 & FLAG_N) {
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000253 /* Width of 7 preserves tab spacing in the text */
254 flines[i] = xasprintf(
255 (lineno <= 9999999) ? "%7u %s" : "%07u %s",
256 lineno % 10000000, current_line);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000257 free(current_line);
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000258 if (terminated)
259 lineno++;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000260 } else {
261 flines[i] = xrealloc(current_line, strlen(current_line)+1);
262 }
Rob Landley9200e792005-09-15 19:26:59 +0000263 }
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000264 num_flines = i - 1; /* buggie: 'num_flines' must be 'max_fline' */
Rob Landley9200e792005-09-15 19:26:59 +0000265
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000266 /* Reset variables for a new file */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000267
Rob Landley9200e792005-09-15 19:26:59 +0000268 line_pos = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000269
Rob Landley9200e792005-09-15 19:26:59 +0000270 fclose(fp);
Rob Landley9200e792005-09-15 19:26:59 +0000271}
272
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000273#if ENABLE_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000274
275/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
276 * on my build. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000277static int calc_percent(void)
278{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000279 return ((100 * (line_pos + height - 2) / num_flines) + 1);
280}
281
Rob Landley9200e792005-09-15 19:26:59 +0000282/* Print a status line if -M was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000283static void m_status_print(void)
284{
Rob Landley9200e792005-09-15 19:26:59 +0000285 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000286
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000287 clear_line();
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000288 printf(HIGHLIGHT"%s", filename);
289 if (num_files > 1)
290 printf(" (file %i of %i)", current_file, num_files);
291 printf(" lines %i-%i/%i ",
292 line_pos + 1, line_pos + height - 1,
293 num_flines + 1);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000294 if (line_pos >= num_flines - height + 2) {
Denis Vlasenkod51d14e2006-12-21 13:57:37 +0000295 printf("(END)"NORMAL);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000296 if (num_files > 1 && current_file != num_files)
Denis Vlasenkod51d14e2006-12-21 13:57:37 +0000297 printf(HIGHLIGHT" - next: %s "NORMAL, files[current_file]);
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000298 return;
Rob Landley9200e792005-09-15 19:26:59 +0000299 }
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000300 percentage = calc_percent();
301 printf("%i%% "NORMAL, percentage);
Rob Landley9200e792005-09-15 19:26:59 +0000302}
303
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000304#endif
305
Rob Landley9200e792005-09-15 19:26:59 +0000306/* Print the status line */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000307static void status_print(void)
308{
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000309 const char *p;
310
Rob Landley9200e792005-09-15 19:26:59 +0000311 /* Change the status if flags have been set */
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000312#if ENABLE_FEATURE_LESS_FLAGS
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000313 if (option_mask32 & (FLAG_M|FLAG_m)) {
Rob Landley9200e792005-09-15 19:26:59 +0000314 m_status_print();
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000315 return;
Rob Landley9200e792005-09-15 19:26:59 +0000316 }
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000317 /* No flags set */
Rob Landley9200e792005-09-15 19:26:59 +0000318#endif
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000319
320 clear_line();
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000321 if (line_pos && line_pos < num_flines - height + 2) {
322 putchar(':');
323 return;
324 }
Denis Vlasenkod51d14e2006-12-21 13:57:37 +0000325 p = "(END)";
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000326 if (!line_pos)
327 p = filename;
328 if (num_files > 1) {
329 printf(HIGHLIGHT"%s (file %i of %i) "NORMAL,
330 p, current_file, num_files);
331 return;
332 }
333 print_hilite(p);
Rob Landley9200e792005-09-15 19:26:59 +0000334}
335
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000336static char controls[] =
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000337 /* NUL: never encountered; TAB: not converted */
338 /**/"\x01\x02\x03\x04\x05\x06\x07\x08" "\x0a\x0b\x0c\x0d\x0e\x0f"
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000339 "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
340 "\x7f\x9b"; /* DEL and infamous Meta-ESC :( */
341static char ctrlconv[] =
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000342 /* '\n': it's a former NUL - subst with '@', not 'J' */
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000343 "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f"
344 "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f";
345
346static void print_found(const char *line)
347{
348 int match_status;
349 int eflags;
350 char *growline;
351 regmatch_t match_structs;
352
353 char buf[width];
354 const char *str = line;
355 char *p = buf;
356 size_t n;
357
358 while (*str) {
359 n = strcspn(str, controls);
360 if (n) {
361 if (!str[n]) break;
362 memcpy(p, str, n);
363 p += n;
364 str += n;
365 }
366 n = strspn(str, controls);
367 memset(p, '.', n);
368 p += n;
369 str += n;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000370 }
371 strcpy(p, str);
372
373 /* buf[] holds quarantined version of str */
374
375 /* Each part of the line that matches has the HIGHLIGHT
376 and NORMAL escape sequences placed around it.
377 NB: we regex against line, but insert text
378 from quarantined copy (buf[]) */
379 str = buf;
380 growline = NULL;
381 eflags = 0;
382 goto start;
383
384 while (match_status == 0) {
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000385 char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL,
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000386 growline ? : "",
387 match_structs.rm_so, str,
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000388 match_structs.rm_eo - match_structs.rm_so,
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000389 str + match_structs.rm_so);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000390 free(growline); growline = new;
391 str += match_structs.rm_eo;
392 line += match_structs.rm_eo;
393 eflags = REG_NOTBOL;
394 start:
395 /* Most of the time doesn't find the regex, optimize for that */
396 match_status = regexec(&pattern, line, 1, &match_structs, eflags);
397 }
398
399 if (!growline) {
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000400 printf(CLEAR_2_EOL"%s\n", str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000401 return;
402 }
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000403 printf(CLEAR_2_EOL"%s%s\n", growline, str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000404 free(growline);
405}
406
407static void print_ascii(const char *str)
408{
409 char buf[width];
410 char *p;
411 size_t n;
412
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000413 printf(CLEAR_2_EOL);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000414 while (*str) {
415 n = strcspn(str, controls);
416 if (n) {
417 if (!str[n]) break;
418 printf("%.*s", n, str);
419 str += n;
420 }
421 n = strspn(str, controls);
422 p = buf;
423 do {
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000424 if (*str == 0x7f)
425 *p++ = '?';
426 else if (*str == 0x9b)
427 /* VT100's CSI, aka Meta-ESC. Who's inventor? */
428 /* I want to know who committed this sin */
429 *p++ = '{';
430 else
431 *p++ = ctrlconv[(unsigned char)*str];
432 str++;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000433 } while (--n);
434 *p = '\0';
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000435 print_hilite(buf);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000436 }
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000437 puts(str);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000438}
439
Rob Landley9200e792005-09-15 19:26:59 +0000440/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000441static void buffer_print(void)
442{
Rob Landley9200e792005-09-15 19:26:59 +0000443 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000444
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000445 move_cursor(0, 0);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000446 for (i = 0; i < height - 1; i++)
447 if (pattern_valid)
448 print_found(buffer[i]);
449 else
450 print_ascii(buffer[i]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000451 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000452}
453
454/* Initialise the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000455static void buffer_init(void)
456{
Rob Landley9200e792005-09-15 19:26:59 +0000457 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000458
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000459 /* Fill the buffer until the end of the file or the
Rob Landley9200e792005-09-15 19:26:59 +0000460 end of the buffer is reached */
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000461 for (i = 0; i < height - 1 && i <= num_flines; i++) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000462 buffer[i] = flines[i];
Rob Landley9200e792005-09-15 19:26:59 +0000463 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000464
Rob Landley9200e792005-09-15 19:26:59 +0000465 /* If the buffer still isn't full, fill it with blank lines */
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000466 for (; i < height - 1; i++) {
467 buffer[i] = empty_line_marker;
Rob Landley9200e792005-09-15 19:26:59 +0000468 }
469}
470
471/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000472static void buffer_down(int nlines)
473{
Rob Landley9200e792005-09-15 19:26:59 +0000474 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000475
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000476 if (line_pos + (height - 3) + nlines < num_flines) {
477 line_pos += nlines;
478 for (i = 0; i < (height - 1); i++) {
479 buffer[i] = flines[line_pos + i];
480 }
481 } else {
482 /* As the number of lines requested was too large, we just move
483 to the end of the file */
484 while (line_pos + (height - 3) + 1 < num_flines) {
485 line_pos += 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000486 for (i = 0; i < (height - 1); i++) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000487 buffer[i] = flines[line_pos + i];
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000488 }
Rob Landley9200e792005-09-15 19:26:59 +0000489 }
Rob Landley9200e792005-09-15 19:26:59 +0000490 }
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000491
492 /* We exit if the -E flag has been set */
493 if ((option_mask32 & FLAG_E) && (line_pos + (height - 2) == num_flines))
494 tless_exit(0);
Rob Landley9200e792005-09-15 19:26:59 +0000495}
496
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000497static void buffer_up(int nlines)
498{
Rob Landley9200e792005-09-15 19:26:59 +0000499 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000500
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000501 line_pos -= nlines;
502 if (line_pos < 0) line_pos = 0;
503 for (i = 0; i < height - 1; i++) {
504 if (line_pos + i <= num_flines) {
505 buffer[i] = flines[line_pos + i];
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000506 } else {
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000507 buffer[i] = empty_line_marker;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000508 }
Rob Landley9200e792005-09-15 19:26:59 +0000509 }
510}
511
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000512static void buffer_line(int linenum)
513{
Rob Landley9200e792005-09-15 19:26:59 +0000514 int i;
Rob Landley9200e792005-09-15 19:26:59 +0000515
Mike Frysingerf054be12006-04-29 04:21:10 +0000516 if (linenum < 0 || linenum > num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000517 clear_line();
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000518 printf(HIGHLIGHT"%s%u"NORMAL, "Cannot seek to line ", linenum + 1);
519 return;
Rob Landley9200e792005-09-15 19:26:59 +0000520 }
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000521
522 for (i = 0; i < height - 1; i++) {
523 if (linenum + i <= num_flines)
524 buffer[i] = flines[linenum + i];
525 else {
526 buffer[i] = empty_line_marker;
527 }
528 }
529 line_pos = linenum;
530 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000531}
532
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000533/* Reinitialise everything for a new file - free the memory and start over */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000534static void reinitialise(void)
535{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000536 int i;
537
538 for (i = 0; i <= num_flines; i++)
539 free(flines[i]);
540 free(flines);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000541
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000542 data_readlines();
543 buffer_init();
544 buffer_print();
545}
546
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000547static void examine_file(void)
548{
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000549 print_statusline("Examine: ");
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000550 free(filename);
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000551 filename = tless_gets(sizeof("Examine: ")-1);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000552 /* files start by = argv. why we assume that argv is infinitely long??
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000553 files[num_files] = filename;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000554 current_file = num_files + 1;
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000555 num_files++; */
556 files[0] = filename;
Denis Vlasenkoe147a722006-12-21 13:26:54 +0000557 num_files = current_file = 1;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000558 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000559}
560
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000561/* This function changes the file currently being paged. direction can be one of the following:
562 * -1: go back one file
563 * 0: go to the first file
564 * 1: go forward one file
565*/
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000566static void change_file(int direction)
567{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000568 if (current_file != ((direction > 0) ? num_files : 1)) {
569 current_file = direction ? current_file + direction : 1;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000570 free(filename);
571 filename = xstrdup(files[current_file - 1]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000572 reinitialise();
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000573 } else {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000574 print_statusline(direction > 0 ? "No next file" : "No previous file");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000575 }
576}
577
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000578static void remove_current_file(void)
579{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000580 int i;
581
582 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000583 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000584 for (i = 3; i <= num_files; i++)
585 files[i - 2] = files[i - 1];
586 num_files--;
587 buffer_print();
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000588 } else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000589 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000590 for (i = 2; i <= num_files; i++)
591 files[i - 2] = files[i - 1];
592 num_files--;
593 current_file--;
594 buffer_print();
595 }
596}
597
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000598static void colon_process(void)
599{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000600 int keypress;
601
602 /* Clear the current line and print a prompt */
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000603 print_statusline(" :");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000604
605 keypress = tless_getch();
606 switch (keypress) {
607 case 'd':
608 remove_current_file();
609 break;
610 case 'e':
611 examine_file();
612 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000613#if ENABLE_FEATURE_LESS_FLAGS
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000614 case 'f':
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000615 m_status_print();
616 break;
617#endif
618 case 'n':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000619 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000620 break;
621 case 'p':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000622 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000623 break;
624 case 'q':
625 tless_exit(0);
626 break;
627 case 'x':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000628 change_file(0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000629 break;
630 default:
631 break;
632 }
633}
634
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000635static int normalize_match_pos(int match)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000636{
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000637 match_pos = match;
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000638 if (match >= num_matches)
639 match_pos = num_matches - 1;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000640 if (match < 0)
641 return (match_pos = 0);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000642 return match_pos;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000643}
644
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000645#if ENABLE_FEATURE_LESS_REGEXP
Rob Landleya2e98042006-04-18 01:53:41 +0000646static void goto_match(int match)
647{
Denis Vlasenkof65d1332006-12-21 15:23:45 +0000648 if (num_matches)
649 buffer_line(match_lines[normalize_match_pos(match)]);
Rob Landleya2e98042006-04-18 01:53:41 +0000650}
651
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000652static void regex_process(void)
653{
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000654 char *uncomp_regex, *err;
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000655
656 /* Reset variables */
657 match_lines = xrealloc(match_lines, sizeof(int));
658 match_lines[0] = -1;
659 match_pos = 0;
660 num_matches = 0;
661 if (pattern_valid) {
662 regfree(&pattern);
663 pattern_valid = 0;
664 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000665
Rob Landleya2e98042006-04-18 01:53:41 +0000666 /* Get the uncompiled regular expression from the user */
667 clear_line();
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000668 putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000669 uncomp_regex = tless_gets(1);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000670 if (/*!uncomp_regex ||*/ !uncomp_regex[0]) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000671 free(uncomp_regex);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000672 buffer_print();
Rob Landleya2e98042006-04-18 01:53:41 +0000673 return;
674 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000675
Rob Landleya2e98042006-04-18 01:53:41 +0000676 /* Compile the regex and check for errors */
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000677 err = regcomp_or_errmsg(&pattern, uncomp_regex, 0);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000678 free(uncomp_regex);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000679 if (err) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000680 print_statusline(err);
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000681 free(err);
682 return;
683 }
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000684 pattern_valid = 1;
Rob Landleya2e98042006-04-18 01:53:41 +0000685
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000686 /* Run the regex on each line of the current file */
687 for (match_pos = 0; match_pos <= num_flines; match_pos++) {
688 if (regexec(&pattern, flines[match_pos], 0, NULL, 0) == 0) {
689 match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int));
690 match_lines[num_matches++] = match_pos;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000691 }
692 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000693
Denis Vlasenko3f3190e2006-12-21 00:22:03 +0000694 if (num_matches == 0 || num_flines <= height - 2) {
695 buffer_print();
696 return;
697 }
698 for (match_pos = 0; match_pos < num_matches; match_pos++) {
699 if (match_lines[match_pos] > line_pos)
700 break;
701 }
702 if (option_mask32 & LESS_STATE_MATCH_BACKWARDS) match_pos--;
703 buffer_line(match_lines[normalize_match_pos(match_pos)]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000704}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000705#endif
706
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000707static void number_process(int first_digit)
708{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000709 int i = 1;
710 int num;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000711 char num_input[sizeof(int)*4]; /* more than enough */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000712 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000713
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000714 num_input[0] = first_digit;
715
716 /* Clear the current line, print a prompt, and then print the digit */
717 clear_line();
718 printf(":%c", first_digit);
719
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000720 /* Receive input until a letter is given */
721 while (i < sizeof(num_input)-1) {
722 num_input[i] = tless_getch();
723 if (!num_input[i] || !isdigit(num_input[i]))
724 break;
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000725 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000726 i++;
727 }
728
729 /* Take the final letter out of the digits string */
730 keypress = num_input[i];
731 num_input[i] = '\0';
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000732 num = bb_strtou(num_input, NULL, 10);
733 /* on format error, num == -1 */
734 if (num < 1 || num > MAXLINES) {
Rob Landleya2e98042006-04-18 01:53:41 +0000735 buffer_print();
736 return;
737 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000738
739 /* We now know the number and the letter entered, so we process them */
740 switch (keypress) {
741 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
742 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000743 break;
744 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
745 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000746 break;
747 case 'g': case '<': case 'G': case '>':
748 if (num_flines >= height - 2)
749 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000750 break;
751 case 'p': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +0000752 buffer_line(((num / 100) * num_flines) - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000753 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000754#if ENABLE_FEATURE_LESS_REGEXP
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000755 case 'n':
Rob Landleya2e98042006-04-18 01:53:41 +0000756 goto_match(match_pos + num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000757 break;
758 case '/':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000759 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000760 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000761 break;
762 case '?':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000763 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
Rob Landleya2e98042006-04-18 01:53:41 +0000764 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000765 break;
766#endif
767 default:
768 break;
769 }
770}
771
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000772#if ENABLE_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000773static void flag_change(void)
774{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000775 int keypress;
776
777 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000778 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000779 keypress = tless_getch();
780
781 switch (keypress) {
782 case 'M':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000783 option_mask32 ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000784 break;
785 case 'm':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000786 option_mask32 ^= FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000787 break;
788 case 'E':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000789 option_mask32 ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000790 break;
791 case '~':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000792 option_mask32 ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000793 break;
794 default:
795 break;
796 }
797}
798
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000799static void show_flag_status(void)
800{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000801 int keypress;
802 int flag_val;
803
804 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000805 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000806 keypress = tless_getch();
807
808 switch (keypress) {
809 case 'M':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000810 flag_val = option_mask32 & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000811 break;
812 case 'm':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000813 flag_val = option_mask32 & FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000814 break;
815 case '~':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000816 flag_val = option_mask32 & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000817 break;
818 case 'N':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000819 flag_val = option_mask32 & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000820 break;
821 case 'E':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000822 flag_val = option_mask32 & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000823 break;
824 default:
825 flag_val = 0;
826 break;
827 }
828
829 clear_line();
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000830 printf(HIGHLIGHT"%s%u"NORMAL, "The status of the flag is: ", flag_val != 0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000831}
832#endif
833
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000834static void full_repaint(void)
835{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000836 int temp_line_pos = line_pos;
837 data_readlines();
838 buffer_init();
839 buffer_line(temp_line_pos);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000840}
841
842
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000843static void save_input_to_file(void)
844{
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000845 char *current_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000846 int i;
847 FILE *fp;
848
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000849 print_statusline("Log file: ");
850 current_line = tless_gets(sizeof("Log file: ")-1);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000851 if (strlen(current_line) > 0) {
852 fp = fopen(current_line, "w");
853 free(current_line);
854 if (!fp) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000855 print_statusline("Error opening log file");
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000856 return;
857 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000858 for (i = 0; i < num_flines; i++)
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000859 fprintf(fp, "%s\n", flines[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000860 fclose(fp);
861 buffer_print();
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000862 return;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000863 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000864 free(current_line);
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000865 print_statusline("No log file");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000866}
867
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000868#if ENABLE_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000869static void add_mark(void)
870{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000871 int letter;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000872
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000873 print_statusline("Mark: ");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000874 letter = tless_getch();
875
876 if (isalpha(letter)) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000877
878 /* If we exceed 15 marks, start overwriting previous ones */
879 if (num_marks == 14)
880 num_marks = 0;
881
882 mark_lines[num_marks][0] = letter;
883 mark_lines[num_marks][1] = line_pos;
884 num_marks++;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000885 } else {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000886 print_statusline("Invalid mark letter");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000887 }
888}
889
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000890static void goto_mark(void)
891{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000892 int letter;
893 int i;
894
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000895 print_statusline("Go to mark: ");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000896 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000897 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000898
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000899 if (isalpha(letter)) {
900 for (i = 0; i <= num_marks; i++)
901 if (letter == mark_lines[i][0]) {
902 buffer_line(mark_lines[i][1]);
903 break;
904 }
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000905 if (num_marks == 14 && letter != mark_lines[14][0])
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000906 print_statusline("Mark not set");
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000907 } else
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000908 print_statusline("Invalid mark letter");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000909}
910#endif
911
912
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000913#if ENABLE_FEATURE_LESS_BRACKETS
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000914
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000915static char opp_bracket(char bracket)
916{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000917 switch (bracket) {
918 case '{': case '[':
919 return bracket + 2;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000920 case '(':
921 return ')';
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000922 case '}': case ']':
923 return bracket - 2;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000924 case ')':
925 return '(';
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000926 default:
927 return 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000928 }
929}
930
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000931static void match_right_bracket(char bracket)
932{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000933 int bracket_line = -1;
934 int i;
935
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000936 if (strchr(flines[line_pos], bracket) == NULL) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000937 print_statusline("No bracket in top line");
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000938 return;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000939 }
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000940 for (i = line_pos + 1; i < num_flines; i++) {
941 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
942 bracket_line = i;
943 break;
944 }
945 }
946 if (bracket_line == -1)
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000947 print_statusline("No matching bracket found");
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000948 buffer_line(bracket_line - height + 2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000949}
950
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000951static void match_left_bracket(char bracket)
952{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000953 int bracket_line = -1;
954 int i;
955
956 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000957 print_statusline("No bracket in bottom line");
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000958 return;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000959 }
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000960
961 for (i = line_pos + height - 2; i >= 0; i--) {
962 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
963 bracket_line = i;
964 break;
965 }
966 }
967 if (bracket_line == -1)
Denis Vlasenkof1282a82006-12-21 17:03:20 +0000968 print_statusline("No matching bracket found");
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000969 buffer_line(bracket_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000970}
971
Denis Vlasenko9a7cef92006-12-20 02:46:48 +0000972#endif /* FEATURE_LESS_BRACKETS */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000973
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000974static void keypress_process(int keypress)
975{
Rob Landley9200e792005-09-15 19:26:59 +0000976 switch (keypress) {
Denis Vlasenkoe865e812006-12-21 13:24:58 +0000977 case KEY_DOWN: case 'e': case 'j': case 0x0d:
Rob Landley9200e792005-09-15 19:26:59 +0000978 buffer_down(1);
979 buffer_print();
980 break;
981 case KEY_UP: case 'y': case 'k':
982 buffer_up(1);
983 buffer_print();
984 break;
985 case PAGE_DOWN: case ' ': case 'z':
986 buffer_down(height - 1);
987 buffer_print();
988 break;
989 case PAGE_UP: case 'w': case 'b':
990 buffer_up(height - 1);
991 buffer_print();
992 break;
993 case 'd':
994 buffer_down((height - 1) / 2);
995 buffer_print();
996 break;
997 case 'u':
998 buffer_up((height - 1) / 2);
999 buffer_print();
1000 break;
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +00001001 case KEY_HOME: case 'g': case 'p': case '<': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +00001002 buffer_line(0);
Rob Landley9200e792005-09-15 19:26:59 +00001003 break;
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +00001004 case KEY_END: case 'G': case '>':
Mike Frysingerf054be12006-04-29 04:21:10 +00001005 buffer_line(num_flines - height + 2);
Rob Landley9200e792005-09-15 19:26:59 +00001006 break;
1007 case 'q': case 'Q':
1008 tless_exit(0);
1009 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001010#if ENABLE_FEATURE_LESS_MARKS
Rob Landley9200e792005-09-15 19:26:59 +00001011 case 'm':
1012 add_mark();
1013 buffer_print();
1014 break;
1015 case '\'':
1016 goto_mark();
1017 buffer_print();
1018 break;
1019#endif
1020 case 'r':
1021 buffer_print();
1022 break;
1023 case 'R':
1024 full_repaint();
1025 break;
1026 case 's':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001027 save_input_to_file();
Rob Landley9200e792005-09-15 19:26:59 +00001028 break;
1029 case 'E':
1030 examine_file();
1031 break;
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001032#if ENABLE_FEATURE_LESS_FLAGS
Rob Landley9200e792005-09-15 19:26:59 +00001033 case '=':
Rob Landley9200e792005-09-15 19:26:59 +00001034 m_status_print();
1035 break;
1036#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001037#if ENABLE_FEATURE_LESS_REGEXP
Rob Landley9200e792005-09-15 19:26:59 +00001038 case '/':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001039 option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
Rob Landley9200e792005-09-15 19:26:59 +00001040 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001041 break;
1042 case 'n':
Denis Vlasenkof65d1332006-12-21 15:23:45 +00001043//printf("HERE 3\n");sleep(1);
Rob Landley9200e792005-09-15 19:26:59 +00001044 goto_match(match_pos + 1);
Rob Landley9200e792005-09-15 19:26:59 +00001045 break;
1046 case 'N':
1047 goto_match(match_pos - 1);
Rob Landley9200e792005-09-15 19:26:59 +00001048 break;
1049 case '?':
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001050 option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
Rob Landleya2e98042006-04-18 01:53:41 +00001051 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001052 break;
1053#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001054#if ENABLE_FEATURE_LESS_FLAGCS
Rob Landley9200e792005-09-15 19:26:59 +00001055 case '-':
1056 flag_change();
1057 buffer_print();
1058 break;
1059 case '_':
1060 show_flag_status();
1061 break;
1062#endif
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001063#if ENABLE_FEATURE_LESS_BRACKETS
Rob Landley9200e792005-09-15 19:26:59 +00001064 case '{': case '(': case '[':
1065 match_right_bracket(keypress);
1066 break;
1067 case '}': case ')': case ']':
1068 match_left_bracket(keypress);
1069 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001070#endif
1071 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001072 colon_process();
1073 break;
1074 default:
1075 break;
1076 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001077
Rob Landley9200e792005-09-15 19:26:59 +00001078 if (isdigit(keypress))
1079 number_process(keypress);
1080}
1081
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001082static void sig_catcher(int sig ATTRIBUTE_UNUSED)
1083{
1084 set_tty_cooked();
1085 exit(1);
1086}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001087
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001088int less_main(int argc, char **argv)
1089{
Rob Landley9200e792005-09-15 19:26:59 +00001090 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001091
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001092 getopt32(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001093 argc -= optind;
1094 argv += optind;
1095 files = argv;
1096 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001097
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001098 /* Another popular pager, most, detects when stdout
1099 * is not a tty and turns into cat. This makes sense. */
1100 if (!isatty(STDOUT_FILENO))
1101 return bb_cat(argv);
1102
Rob Landley9200e792005-09-15 19:26:59 +00001103 if (!num_files) {
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001104 if (isatty(STDIN_FILENO)) {
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001105 /* Just "less"? No args and no redirection? */
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00001106 bb_error_msg("missing filename");
Rob Landley9200e792005-09-15 19:26:59 +00001107 bb_show_usage();
1108 }
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001109 } else
1110 filename = xstrdup(files[0]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001111
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001112 inp = xfopen(CURRENT_TTY, "r");
1113
1114 get_terminal_width_height(fileno(inp), &width, &height);
Denis Vlasenko3f3190e2006-12-21 00:22:03 +00001115 if (width < 10 || height < 3)
1116 bb_error_msg_and_die("too narrow here");
1117
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001118 buffer = xmalloc(height * sizeof(char *));
1119 if (option_mask32 & FLAG_TILDE)
1120 empty_line_marker = "";
Denis Vlasenko3f3190e2006-12-21 00:22:03 +00001121
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001122 data_readlines();
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001123
Denis Vlasenko3f3190e2006-12-21 00:22:03 +00001124 tcgetattr(fileno(inp), &term_orig);
Denis Vlasenko9a7cef92006-12-20 02:46:48 +00001125 signal(SIGTERM, sig_catcher);
1126 signal(SIGINT, sig_catcher);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001127 term_vi = term_orig;
1128 term_vi.c_lflag &= (~ICANON & ~ECHO);
1129 term_vi.c_iflag &= (~IXON & ~ICRNL);
1130 term_vi.c_oflag &= (~ONLCR);
1131 term_vi.c_cc[VMIN] = 1;
1132 term_vi.c_cc[VTIME] = 0;
Denis Vlasenkoe865e812006-12-21 13:24:58 +00001133
Rob Landley9200e792005-09-15 19:26:59 +00001134 buffer_init();
1135 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001136
Rob Landley9200e792005-09-15 19:26:59 +00001137 while (1) {
1138 keypress = tless_getch();
1139 keypress_process(keypress);
1140 }
1141}