blob: 63389dcd7ec0b42fe6fe2abe8c38bf9cf1629ef0 [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/*
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000011 * This program needs a lot of development, so consider it in a beta stage
12 * at best.
Rob Landley9200e792005-09-15 19:26:59 +000013 *
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000014 * TODO:
15 * - Add more regular expression support - search modifiers, certain matches, etc.
16 * - Add more complex bracket searching - currently, nested brackets are
17 * not considered.
18 * - Add support for "F" as an input. This causes less to act in
19 * a similar way to tail -f.
20 * - Check for binary files, and prompt the user if a binary file
21 * is detected.
22 * - Allow horizontal scrolling. Currently, lines simply continue onto
23 * the next line, per the terminal's discretion
Rob Landley9200e792005-09-15 19:26:59 +000024 *
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000025 * Notes:
26 * - filename is an array and not a pointer because that avoids all sorts
27 * of complications involving the fact that something that is pointed to
28 * will be changed if the pointer is changed.
29 * - the inp file pointer is used so that keyboard input works after
30 * redirected input has been read from stdin
Rob Landley9200e792005-09-15 19:26:59 +000031*/
32
Bernhard Reutner-Fischerc89982d2006-06-03 19:49:21 +000033#include "busybox.h"
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000034
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000035#ifdef CONFIG_FEATURE_LESS_REGEXP
36#include "xregex.h"
37#endif
38
39
Rob Landley9200e792005-09-15 19:26:59 +000040/* These are the escape sequences corresponding to special keys */
41#define REAL_KEY_UP 'A'
42#define REAL_KEY_DOWN 'B'
43#define REAL_KEY_RIGHT 'C'
44#define REAL_KEY_LEFT 'D'
45#define REAL_PAGE_UP '5'
46#define REAL_PAGE_DOWN '6'
47
48/* These are the special codes assigned by this program to the special keys */
49#define PAGE_UP 20
50#define PAGE_DOWN 21
51#define KEY_UP 22
52#define KEY_DOWN 23
53#define KEY_RIGHT 24
54#define KEY_LEFT 25
55
56/* The escape codes for highlighted and normal text */
57#define HIGHLIGHT "\033[7m"
58#define NORMAL "\033[0m"
59
60/* The escape code to clear the screen */
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000061#define CLEAR "\033[H\033[J"
Rob Landley9200e792005-09-15 19:26:59 +000062
63/* Maximum number of lines in a file */
64#define MAXLINES 10000
65
Rob Landley9200e792005-09-15 19:26:59 +000066static int height;
67static int width;
68static char **files;
69static char filename[256];
Rob Landleyd57ae8b2005-09-18 00:58:49 +000070static char **buffer;
71static char **flines;
Rob Landley9200e792005-09-15 19:26:59 +000072static int current_file = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000073static int line_pos;
Rob Landley9200e792005-09-15 19:26:59 +000074static int num_flines;
75static int num_files = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000076static int past_eof;
Rob Landley9200e792005-09-15 19:26:59 +000077
78/* Command line options */
Rob Landleyd57ae8b2005-09-18 00:58:49 +000079static unsigned long flags;
80#define FLAG_E 1
81#define FLAG_M (1<<1)
82#define FLAG_m (1<<2)
83#define FLAG_N (1<<3)
84#define FLAG_TILDE (1<<4)
Rob Landley9200e792005-09-15 19:26:59 +000085
86/* This is needed so that program behaviour changes when input comes from
87 stdin */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000088static int inp_stdin;
Rob Landley9200e792005-09-15 19:26:59 +000089
90#ifdef CONFIG_FEATURE_LESS_MARKS
91static int mark_lines[15][2];
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000092static int num_marks;
Rob Landley9200e792005-09-15 19:26:59 +000093#endif
94
95#ifdef CONFIG_FEATURE_LESS_REGEXP
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000096static int match_found;
Mike Frysingerf054be12006-04-29 04:21:10 +000097static int *match_lines;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000098static int match_pos;
99static int num_matches;
100static int match_backwards;
Mike Frysingerf054be12006-04-29 04:21:10 +0000101static regex_t old_pattern;
Rob Landley9200e792005-09-15 19:26:59 +0000102#endif
103
104/* Needed termios structures */
105static struct termios term_orig, term_vi;
106
107/* File pointer to get input from */
108static FILE *inp;
109
110/* Reset terminal input to normal */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000111static void set_tty_cooked(void)
112{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000113 fflush(stdout);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000114 tcsetattr(fileno(inp), TCSANOW, &term_orig);
Rob Landley9200e792005-09-15 19:26:59 +0000115}
116
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000117/* Set terminal input to raw mode (taken from vi.c) */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000118static void set_tty_raw(void)
119{
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000120 tcsetattr(fileno(inp), TCSANOW, &term_vi);
Rob Landley9200e792005-09-15 19:26:59 +0000121}
122
123/* Exit the program gracefully */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000124static void tless_exit(int code)
125{
Rob Landley9200e792005-09-15 19:26:59 +0000126 /* TODO: We really should save the terminal state when we start,
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000127 and restore it when we exit. Less does this with the
Rob Landley9200e792005-09-15 19:26:59 +0000128 "ti" and "te" termcap commands; can this be done with
129 only termios.h? */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000130
Rob Landley9200e792005-09-15 19:26:59 +0000131 putchar('\n');
132 exit(code);
133}
134
135/* Grab a character from input without requiring the return key. If the
136 character is ASCII \033, get more characters and assign certain sequences
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000137 special return codes. Note that this function works best with raw input. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000138static int tless_getch(void)
139{
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000140 int input;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000141
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000142 set_tty_raw();
143
144 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000145 /* Detect escape sequences (i.e. arrow keys) and handle
146 them accordingly */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000147
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000148 if (input == '\033' && getc(inp) == '[') {
149 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000150 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000151 if (input == REAL_KEY_UP)
152 return KEY_UP;
153 else if (input == REAL_KEY_DOWN)
154 return KEY_DOWN;
155 else if (input == REAL_KEY_RIGHT)
156 return KEY_RIGHT;
157 else if (input == REAL_KEY_LEFT)
158 return KEY_LEFT;
159 else if (input == REAL_PAGE_UP)
160 return PAGE_UP;
161 else if (input == REAL_PAGE_DOWN)
162 return PAGE_DOWN;
Rob Landley9200e792005-09-15 19:26:59 +0000163 }
164 /* The input is a normal ASCII value */
165 else {
166 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000167 return input;
Rob Landley9200e792005-09-15 19:26:59 +0000168 }
169 return 0;
170}
171
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000172/* Move the cursor to a position (x,y), where (0,0) is the
Rob Landley9200e792005-09-15 19:26:59 +0000173 top-left corner of the console */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000174static void move_cursor(int x, int y)
175{
Rob Landley9200e792005-09-15 19:26:59 +0000176 printf("\033[%i;%iH", x, y);
177}
178
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000179static void clear_line(void)
180{
Rob Landley9200e792005-09-15 19:26:59 +0000181 move_cursor(height, 0);
182 printf("\033[K");
183}
184
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000185/* This adds line numbers to every line, as the -N flag necessitates */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000186static void add_linenumbers(void)
187{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000188 char current_line[256];
189 int i;
190
191 for (i = 0; i <= num_flines; i++) {
192 safe_strncpy(current_line, flines[i], 256);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000193 flines[i] = xasprintf("%5d %s", i + 1, current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000194 }
195}
196
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000197static void data_readlines(void)
198{
Rob Landley9200e792005-09-15 19:26:59 +0000199 int i;
200 char current_line[256];
201 FILE *fp;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000202
Rob Landleyd921b2e2006-08-03 15:41:12 +0000203 fp = (inp_stdin) ? stdin : xfopen(filename, "r");
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000204 flines = NULL;
205 for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) {
Rob Landley9200e792005-09-15 19:26:59 +0000206 strcpy(current_line, "");
207 fgets(current_line, 256, fp);
Mike Frysinger00d10a92006-04-16 20:54:19 +0000208 if (fp != stdin)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000209 xferror(fp, filename);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000210 flines = xrealloc(flines, (i+1) * sizeof(char *));
Rob Landleyd921b2e2006-08-03 15:41:12 +0000211 flines[i] = xstrdup(current_line);
Rob Landley9200e792005-09-15 19:26:59 +0000212 }
213 num_flines = i - 2;
214
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000215 /* Reset variables for a new file */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000216
Rob Landley9200e792005-09-15 19:26:59 +0000217 line_pos = 0;
218 past_eof = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000219
Rob Landley9200e792005-09-15 19:26:59 +0000220 fclose(fp);
221
Mike Frysinger00d10a92006-04-16 20:54:19 +0000222 if (inp == NULL)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000223 inp = (inp_stdin) ? xfopen(CURRENT_TTY, "r") : stdin;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000224
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000225 if (flags & FLAG_N)
Rob Landley9200e792005-09-15 19:26:59 +0000226 add_linenumbers();
227}
228
Rob Landley9200e792005-09-15 19:26:59 +0000229#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000230
231/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
232 * on my build. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000233static int calc_percent(void)
234{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000235 return ((100 * (line_pos + height - 2) / num_flines) + 1);
236}
237
Rob Landley9200e792005-09-15 19:26:59 +0000238/* Print a status line if -M was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000239static void m_status_print(void)
240{
Rob Landley9200e792005-09-15 19:26:59 +0000241 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000242
Rob Landley9200e792005-09-15 19:26:59 +0000243 if (!past_eof) {
244 if (!line_pos) {
245 if (num_files > 1)
246 printf("%s%s %s%i%s%i%s%i-%i/%i ", HIGHLIGHT, filename, "(file ", current_file, " of ", num_files, ") lines ", line_pos + 1, line_pos + height - 1, num_flines + 1);
247 else {
248 printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
249 }
250 }
251 else {
252 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
253 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000254
Rob Landley9200e792005-09-15 19:26:59 +0000255 if (line_pos == num_flines - height + 2) {
256 printf("(END) %s", NORMAL);
257 if ((num_files > 1) && (current_file != num_files))
258 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
259 }
260 else {
261 percentage = calc_percent();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000262 printf("%i%% %s", percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000263 }
264 }
265 else {
266 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
267 if ((num_files > 1) && (current_file != num_files))
268 printf("- Next: %s", files[current_file]);
269 printf("%s", NORMAL);
270 }
271}
272
273/* Print a status line if -m was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000274static void medium_status_print(void)
275{
Rob Landley9200e792005-09-15 19:26:59 +0000276 int percentage;
277 percentage = calc_percent();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000278
Rob Landley9200e792005-09-15 19:26:59 +0000279 if (!line_pos)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000280 printf("%s%s %i%%%s", HIGHLIGHT, filename, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000281 else if (line_pos == num_flines - height + 2)
282 printf("%s(END)%s", HIGHLIGHT, NORMAL);
283 else
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000284 printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000285}
286#endif
287
288/* Print the status line */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000289static void status_print(void)
290{
Rob Landley9200e792005-09-15 19:26:59 +0000291 /* Change the status if flags have been set */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000292#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000293 if (flags & FLAG_M)
Rob Landley9200e792005-09-15 19:26:59 +0000294 m_status_print();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000295 else if (flags & FLAG_m)
Rob Landley9200e792005-09-15 19:26:59 +0000296 medium_status_print();
297 /* No flags set */
298 else {
299#endif
300 if (!line_pos) {
301 printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
302 if (num_files > 1)
303 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
304 }
305 else if (line_pos == num_flines - height + 2) {
306 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
307 if ((num_files > 1) && (current_file != num_files))
308 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
309 }
310 else {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000311 putchar(':');
Rob Landley9200e792005-09-15 19:26:59 +0000312 }
313#ifdef CONFIG_FEATURE_LESS_FLAGS
314 }
315#endif
316}
317
318/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000319static void buffer_print(void)
320{
Rob Landley9200e792005-09-15 19:26:59 +0000321 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000322
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000323 printf("%s", CLEAR);
Rob Landley9200e792005-09-15 19:26:59 +0000324 if (num_flines >= height - 2) {
Rob Landley9200e792005-09-15 19:26:59 +0000325 for (i = 0; i < height - 1; i++)
326 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000327 }
328 else {
Rob Landley9200e792005-09-15 19:26:59 +0000329 for (i = 1; i < (height - 1 - num_flines); i++)
330 putchar('\n');
331 for (i = 0; i < height - 1; i++)
332 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000333 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000334
335 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000336}
337
338/* Initialise the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000339static void buffer_init(void)
340{
Rob Landley9200e792005-09-15 19:26:59 +0000341 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000342
Mike Frysinger00d10a92006-04-16 20:54:19 +0000343 if (buffer == NULL) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000344 /* malloc the number of lines needed for the buffer */
345 buffer = xrealloc(buffer, height * sizeof(char *));
346 } else {
347 for (i = 0; i < (height - 1); i++)
348 free(buffer[i]);
349 }
350
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000351 /* Fill the buffer until the end of the file or the
Rob Landley9200e792005-09-15 19:26:59 +0000352 end of the buffer is reached */
353 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
Rob Landleyd921b2e2006-08-03 15:41:12 +0000354 buffer[i] = xstrdup(flines[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000355 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000356
Rob Landley9200e792005-09-15 19:26:59 +0000357 /* If the buffer still isn't full, fill it with blank lines */
358 for (; i < (height - 1); i++) {
Rob Landleyd921b2e2006-08-03 15:41:12 +0000359 buffer[i] = xstrdup("");
Rob Landley9200e792005-09-15 19:26:59 +0000360 }
361}
362
363/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000364static void buffer_down(int nlines)
365{
Rob Landley9200e792005-09-15 19:26:59 +0000366 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000367
Rob Landley9200e792005-09-15 19:26:59 +0000368 if (!past_eof) {
369 if (line_pos + (height - 3) + nlines < num_flines) {
370 line_pos += nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000371 for (i = 0; i < (height - 1); i++) {
372 free(buffer[i]);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000373 buffer[i] = xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000374 }
Rob Landley9200e792005-09-15 19:26:59 +0000375 }
376 else {
377 /* As the number of lines requested was too large, we just move
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000378 to the end of the file */
379 while (line_pos + (height - 3) + 1 < num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000380 line_pos += 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000381 for (i = 0; i < (height - 1); i++) {
382 free(buffer[i]);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000383 buffer[i] = xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000384 }
Rob Landley9200e792005-09-15 19:26:59 +0000385 }
386 }
387
388 /* We exit if the -E flag has been set */
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000389 if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
Rob Landley9200e792005-09-15 19:26:59 +0000390 tless_exit(0);
391 }
392}
393
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000394static void buffer_up(int nlines)
395{
Rob Landley9200e792005-09-15 19:26:59 +0000396 int i;
397 int tilde_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000398
Rob Landley9200e792005-09-15 19:26:59 +0000399 if (!past_eof) {
400 if (line_pos - nlines >= 0) {
401 line_pos -= nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000402 for (i = 0; i < (height - 1); i++) {
403 free(buffer[i]);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000404 buffer[i] = xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000405 }
Rob Landley9200e792005-09-15 19:26:59 +0000406 }
407 else {
408 /* As the requested number of lines to move was too large, we
409 move one line up at a time until we can't. */
410 while (line_pos != 0) {
411 line_pos -= 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000412 for (i = 0; i < (height - 1); i++) {
413 free(buffer[i]);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000414 buffer[i] = xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000415 }
Rob Landley9200e792005-09-15 19:26:59 +0000416 }
417 }
418 }
419 else {
420 /* Work out where the tildes start */
421 tilde_line = num_flines - line_pos + 3;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000422
Rob Landley9200e792005-09-15 19:26:59 +0000423 line_pos -= nlines;
424 /* Going backwards nlines lines has taken us to a point where
425 nothing is past the EOF, so we revert to normal. */
426 if (line_pos < num_flines - height + 3) {
427 past_eof = 0;
428 buffer_up(nlines);
429 }
430 else {
431 /* We only move part of the buffer, as the rest
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000432 is past the EOF */
Rob Landley9200e792005-09-15 19:26:59 +0000433 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000434 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000435 if (i < tilde_line - nlines + 1)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000436 buffer[i] = xstrdup(flines[line_pos + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000437 else {
438 if (line_pos >= num_flines - height + 2)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000439 buffer[i] = xstrdup("~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000440 }
441 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000442 }
Rob Landley9200e792005-09-15 19:26:59 +0000443 }
444}
445
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000446static void buffer_line(int linenum)
447{
Rob Landley9200e792005-09-15 19:26:59 +0000448 int i;
Rob Landley9200e792005-09-15 19:26:59 +0000449 past_eof = 0;
450
Mike Frysingerf054be12006-04-29 04:21:10 +0000451 if (linenum < 0 || linenum > num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000452 clear_line();
Mike Frysingerf054be12006-04-29 04:21:10 +0000453 printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum + 1, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000454 }
455 else if (linenum < (num_flines - height - 2)) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000456 for (i = 0; i < (height - 1); i++) {
457 free(buffer[i]);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000458 buffer[i] = xstrdup(flines[linenum + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000459 }
Rob Landley9200e792005-09-15 19:26:59 +0000460 line_pos = linenum;
Mike Frysingerf054be12006-04-29 04:21:10 +0000461 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000462 }
463 else {
464 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000465 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000466 if (linenum + i < num_flines + 2)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000467 buffer[i] = xstrdup(flines[linenum + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000468 else
Rob Landleyd921b2e2006-08-03 15:41:12 +0000469 buffer[i] = xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000470 }
471 line_pos = linenum;
472 /* Set past_eof so buffer_down and buffer_up act differently */
473 past_eof = 1;
Mike Frysingerf054be12006-04-29 04:21:10 +0000474 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000475 }
476}
477
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000478/* Reinitialise everything for a new file - free the memory and start over */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000479static void reinitialise(void)
480{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000481 int i;
482
483 for (i = 0; i <= num_flines; i++)
484 free(flines[i]);
485 free(flines);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000486
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000487 data_readlines();
488 buffer_init();
489 buffer_print();
490}
491
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000492static void examine_file(void)
493{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000494 int newline_offset;
495
496 clear_line();
497 printf("Examine: ");
498 fgets(filename, 256, inp);
499
500 /* As fgets adds a newline to the end of an input string, we
501 need to remove it */
502 newline_offset = strlen(filename) - 1;
503 filename[newline_offset] = '\0';
504
Rob Landleyd921b2e2006-08-03 15:41:12 +0000505 files[num_files] = xstrdup(filename);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000506 current_file = num_files + 1;
507 num_files++;
508
509 inp_stdin = 0;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000510 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000511}
512
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000513/* This function changes the file currently being paged. direction can be one of the following:
514 * -1: go back one file
515 * 0: go to the first file
516 * 1: go forward one file
517*/
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000518static void change_file(int direction)
519{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000520 if (current_file != ((direction > 0) ? num_files : 1)) {
521 current_file = direction ? current_file + direction : 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000522 strcpy(filename, files[current_file - 1]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000523 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000524 }
525 else {
526 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000527 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000528 }
529}
530
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000531static void remove_current_file(void)
532{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000533 int i;
534
535 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000536 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000537 for (i = 3; i <= num_files; i++)
538 files[i - 2] = files[i - 1];
539 num_files--;
540 buffer_print();
541 }
542 else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000543 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000544 for (i = 2; i <= num_files; i++)
545 files[i - 2] = files[i - 1];
546 num_files--;
547 current_file--;
548 buffer_print();
549 }
550}
551
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000552static void colon_process(void)
553{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000554 int keypress;
555
556 /* Clear the current line and print a prompt */
557 clear_line();
558 printf(" :");
559
560 keypress = tless_getch();
561 switch (keypress) {
562 case 'd':
563 remove_current_file();
564 break;
565 case 'e':
566 examine_file();
567 break;
568#ifdef CONFIG_FEATURE_LESS_FLAGS
569 case 'f':
570 clear_line();
571 m_status_print();
572 break;
573#endif
574 case 'n':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000575 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000576 break;
577 case 'p':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000578 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000579 break;
580 case 'q':
581 tless_exit(0);
582 break;
583 case 'x':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000584 change_file(0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000585 break;
586 default:
587 break;
588 }
589}
590
591#ifdef CONFIG_FEATURE_LESS_REGEXP
592/* The below two regular expression handler functions NEED development. */
593
594/* Get a regular expression from the user, and then go through the current
595 file line by line, running a processing regex function on each one. */
596
Mike Frysingerf054be12006-04-29 04:21:10 +0000597static char *process_regex_on_line(char *line, regex_t *pattern, int action)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000598{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000599 /* This function takes the regex and applies it to the line.
600 Each part of the line that matches has the HIGHLIGHT
601 and NORMAL escape sequences placed around it by
Mike Frysingerf054be12006-04-29 04:21:10 +0000602 insert_highlights if action = 1, or has the escape sequences
603 removed if action = 0, and then the line is returned. */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000604 int match_status;
Mike Frysingerbf2d9902006-04-16 21:30:47 +0000605 char *line2 = (char *) xmalloc((sizeof(char) * (strlen(line) + 1)) + 64);
Mike Frysingerf054be12006-04-29 04:21:10 +0000606 char *growline = "";
"Vladimir N. Oleynik"8d3c40d2005-09-16 13:16:01 +0000607 regmatch_t match_structs;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000608
Rob Landleyd921b2e2006-08-03 15:41:12 +0000609 line2 = xstrdup(line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000610
611 match_found = 0;
612 match_status = regexec(pattern, line2, 1, &match_structs, 0);
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000613
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000614 while (match_status == 0) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000615 if (match_found == 0)
616 match_found = 1;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000617
Mike Frysingerf054be12006-04-29 04:21:10 +0000618 if (action) {
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000619 growline = xasprintf("%s%.*s%s%.*s%s", growline, match_structs.rm_so, line2, HIGHLIGHT, match_structs.rm_eo - match_structs.rm_so, line2 + match_structs.rm_so, NORMAL);
Mike Frysingerf054be12006-04-29 04:21:10 +0000620 }
621 else {
Rob Landleyd921b2e2006-08-03 15:41:12 +0000622 growline = xasprintf("%s%.*s%.*s", growline, match_structs.rm_so - 4, line2, match_structs.rm_eo - match_structs.rm_so, line2 + match_structs.rm_so);
Mike Frysingerf054be12006-04-29 04:21:10 +0000623 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000624
Mike Frysingerf054be12006-04-29 04:21:10 +0000625 line2 += match_structs.rm_eo;
626 match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000627 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000628
Rob Landleyd921b2e2006-08-03 15:41:12 +0000629 growline = xasprintf("%s%s", growline, line2);
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000630
Mike Frysingerf054be12006-04-29 04:21:10 +0000631 return (match_found ? growline : line);
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000632
Mike Frysingerf054be12006-04-29 04:21:10 +0000633 free(growline);
634 free(line2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000635}
636
Rob Landleya2e98042006-04-18 01:53:41 +0000637static void goto_match(int match)
638{
639 /* This goes to a specific match - all line positions of matches are
640 stored within the match_lines[] array. */
641 if ((match < num_matches) && (match >= 0)) {
642 buffer_line(match_lines[match]);
643 match_pos = match;
644 }
645}
646
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000647static void regex_process(void)
648{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000649 char uncomp_regex[100];
Mike Frysingerf054be12006-04-29 04:21:10 +0000650 char *current_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000651 int i;
652 int j = 0;
Mike Frysinger20c22e02006-04-16 21:41:00 +0000653 regex_t pattern;
Rob Landleya2e98042006-04-18 01:53:41 +0000654 /* Get the uncompiled regular expression from the user */
655 clear_line();
656 putchar((match_backwards) ? '?' : '/');
657 uncomp_regex[0] = 0;
658 fgets(uncomp_regex, sizeof(uncomp_regex), inp);
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000659
Rob Landleya2e98042006-04-18 01:53:41 +0000660 if (strlen(uncomp_regex) == 1) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000661 if (num_matches)
662 goto_match(match_backwards ? match_pos - 1 : match_pos + 1);
663 else
664 buffer_print();
Rob Landleya2e98042006-04-18 01:53:41 +0000665 return;
666 }
Rob Landleya2e98042006-04-18 01:53:41 +0000667 uncomp_regex[strlen(uncomp_regex) - 1] = '\0';
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000668
Rob Landleya2e98042006-04-18 01:53:41 +0000669 /* Compile the regex and check for errors */
670 xregcomp(&pattern, uncomp_regex, 0);
671
Mike Frysingerf054be12006-04-29 04:21:10 +0000672 if (num_matches) {
673 /* Get rid of all the highlights we added previously */
674 for (i = 0; i <= num_flines; i++) {
675 current_line = process_regex_on_line(flines[i], &old_pattern, 0);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000676 flines[i] = xstrdup(current_line);
Mike Frysingerf054be12006-04-29 04:21:10 +0000677 }
678 }
679 old_pattern = pattern;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000680
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000681 /* Reset variables */
Mike Frysingerf054be12006-04-29 04:21:10 +0000682 match_lines = xrealloc(match_lines, sizeof(int));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000683 match_lines[0] = -1;
684 match_pos = 0;
685 num_matches = 0;
686 match_found = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000687 /* Run the regex on each line of the current file here */
688 for (i = 0; i <= num_flines; i++) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000689 current_line = process_regex_on_line(flines[i], &pattern, 1);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000690 flines[i] = xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000691 if (match_found) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000692 match_lines = xrealloc(match_lines, (j + 1) * sizeof(int));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000693 match_lines[j] = i;
694 j++;
695 }
696 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000697
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000698 num_matches = j;
Rob Landleya2e98042006-04-18 01:53:41 +0000699 if ((match_lines[0] != -1) && (num_flines > height - 2)) {
700 if (match_backwards) {
701 for (i = 0; i < num_matches; i++) {
702 if (match_lines[i] > line_pos) {
703 match_pos = i - 1;
704 buffer_line(match_lines[match_pos]);
705 break;
706 }
707 }
708 }
709 else
710 buffer_line(match_lines[0]);
711 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000712 else
713 buffer_init();
714}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000715#endif
716
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000717static void number_process(int first_digit)
718{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000719 int i = 1;
720 int num;
721 char num_input[80];
722 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000723 char *endptr;
724
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000725 num_input[0] = first_digit;
726
727 /* Clear the current line, print a prompt, and then print the digit */
728 clear_line();
729 printf(":%c", first_digit);
730
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000731 /* Receive input until a letter is given (max 80 chars)*/
732 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000733 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000734 i++;
735 }
736
737 /* Take the final letter out of the digits string */
738 keypress = num_input[i];
739 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000740 num = strtol(num_input, &endptr, 10);
Rob Landleya2e98042006-04-18 01:53:41 +0000741 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES) {
742 buffer_print();
743 return;
744 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000745
746 /* We now know the number and the letter entered, so we process them */
747 switch (keypress) {
748 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
749 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000750 break;
751 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
752 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000753 break;
754 case 'g': case '<': case 'G': case '>':
755 if (num_flines >= height - 2)
756 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000757 break;
758 case 'p': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +0000759 buffer_line(((num / 100) * num_flines) - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000760 break;
761#ifdef CONFIG_FEATURE_LESS_REGEXP
762 case 'n':
Rob Landleya2e98042006-04-18 01:53:41 +0000763 goto_match(match_pos + num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000764 break;
765 case '/':
Rob Landleya2e98042006-04-18 01:53:41 +0000766 match_backwards = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000767 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000768 break;
769 case '?':
Rob Landleya2e98042006-04-18 01:53:41 +0000770 match_backwards = 1;
771 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000772 break;
773#endif
774 default:
775 break;
776 }
777}
778
779#ifdef CONFIG_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000780static void flag_change(void)
781{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000782 int keypress;
783
784 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000785 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000786 keypress = tless_getch();
787
788 switch (keypress) {
789 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000790 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000791 break;
792 case 'm':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000793 flags ^= FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000794 break;
795 case 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000796 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000797 break;
798 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000799 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000800 break;
801 default:
802 break;
803 }
804}
805
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000806static void show_flag_status(void)
807{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000808 int keypress;
809 int flag_val;
810
811 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000812 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000813 keypress = tless_getch();
814
815 switch (keypress) {
816 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000817 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000818 break;
819 case 'm':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000820 flag_val = flags & FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000821 break;
822 case '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000823 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000824 break;
825 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000826 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000827 break;
828 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000829 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000830 break;
831 default:
832 flag_val = 0;
833 break;
834 }
835
836 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000837 printf("%s%s%i%s", HIGHLIGHT, "The status of the flag is: ", flag_val != 0, NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000838}
839#endif
840
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000841static void full_repaint(void)
842{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000843 int temp_line_pos = line_pos;
844 data_readlines();
845 buffer_init();
846 buffer_line(temp_line_pos);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000847}
848
849
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000850static void save_input_to_file(void)
851{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000852 char current_line[256];
853 int i;
854 FILE *fp;
855
856 clear_line();
857 printf("Log file: ");
858 fgets(current_line, 256, inp);
859 current_line[strlen(current_line) - 1] = '\0';
Mike Frysingerf054be12006-04-29 04:21:10 +0000860 if (strlen(current_line) > 1) {
Rob Landleyd921b2e2006-08-03 15:41:12 +0000861 fp = xfopen(current_line, "w");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000862 for (i = 0; i < num_flines; i++)
863 fprintf(fp, "%s", flines[i]);
864 fclose(fp);
865 buffer_print();
866 }
867 else
868 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
869}
870
871#ifdef CONFIG_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000872static void add_mark(void)
873{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000874 int letter;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000875
876 clear_line();
877 printf("Mark: ");
878 letter = tless_getch();
879
880 if (isalpha(letter)) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000881
882 /* If we exceed 15 marks, start overwriting previous ones */
883 if (num_marks == 14)
884 num_marks = 0;
885
886 mark_lines[num_marks][0] = letter;
887 mark_lines[num_marks][1] = line_pos;
888 num_marks++;
889 }
890 else {
891 clear_line();
892 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
893 }
894}
895
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000896static void goto_mark(void)
897{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000898 int letter;
899 int i;
900
901 clear_line();
902 printf("Go to mark: ");
903 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000904 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000905
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000906 if (isalpha(letter)) {
907 for (i = 0; i <= num_marks; i++)
908 if (letter == mark_lines[i][0]) {
909 buffer_line(mark_lines[i][1]);
910 break;
911 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000912 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000913 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000914 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000915 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000916 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000917}
918#endif
919
920
921#ifdef CONFIG_FEATURE_LESS_BRACKETS
922
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000923static char opp_bracket(char bracket)
924{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000925 switch (bracket) {
926 case '{': case '[':
927 return bracket + 2;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000928 case '(':
929 return ')';
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000930 case '}': case ']':
931 return bracket - 2;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000932 case ')':
933 return '(';
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000934 default:
935 return 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000936 }
937}
938
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000939static void match_right_bracket(char bracket)
940{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000941 int bracket_line = -1;
942 int i;
943
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000944 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000945
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000946 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000947 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000948 else {
949 for (i = line_pos + 1; i < num_flines; i++) {
950 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
951 bracket_line = i;
952 break;
953 }
954 }
955
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000956 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000957 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000958
959 buffer_line(bracket_line - height + 2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000960 }
961}
962
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000963static void match_left_bracket(char bracket)
964{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000965 int bracket_line = -1;
966 int i;
967
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000968 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000969
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000970 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000971 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
972 printf("%s", flines[line_pos + height]);
973 sleep(4);
974 }
975 else {
976 for (i = line_pos + height - 2; i >= 0; i--) {
977 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
978 bracket_line = i;
979 break;
980 }
981 }
982
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000983 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000984 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000985
986 buffer_line(bracket_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000987 }
988}
989
990#endif /* CONFIG_FEATURE_LESS_BRACKETS */
991
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000992static void keypress_process(int keypress)
993{
Rob Landley9200e792005-09-15 19:26:59 +0000994 switch (keypress) {
995 case KEY_DOWN: case 'e': case 'j': case '\015':
996 buffer_down(1);
997 buffer_print();
998 break;
999 case KEY_UP: case 'y': case 'k':
1000 buffer_up(1);
1001 buffer_print();
1002 break;
1003 case PAGE_DOWN: case ' ': case 'z':
1004 buffer_down(height - 1);
1005 buffer_print();
1006 break;
1007 case PAGE_UP: case 'w': case 'b':
1008 buffer_up(height - 1);
1009 buffer_print();
1010 break;
1011 case 'd':
1012 buffer_down((height - 1) / 2);
1013 buffer_print();
1014 break;
1015 case 'u':
1016 buffer_up((height - 1) / 2);
1017 buffer_print();
1018 break;
1019 case 'g': case 'p': case '<': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +00001020 buffer_line(0);
Rob Landley9200e792005-09-15 19:26:59 +00001021 break;
1022 case 'G': case '>':
Mike Frysingerf054be12006-04-29 04:21:10 +00001023 buffer_line(num_flines - height + 2);
Rob Landley9200e792005-09-15 19:26:59 +00001024 break;
1025 case 'q': case 'Q':
1026 tless_exit(0);
1027 break;
1028#ifdef CONFIG_FEATURE_LESS_MARKS
1029 case 'm':
1030 add_mark();
1031 buffer_print();
1032 break;
1033 case '\'':
1034 goto_mark();
1035 buffer_print();
1036 break;
1037#endif
1038 case 'r':
1039 buffer_print();
1040 break;
1041 case 'R':
1042 full_repaint();
1043 break;
1044 case 's':
1045 if (inp_stdin)
1046 save_input_to_file();
1047 break;
1048 case 'E':
1049 examine_file();
1050 break;
1051#ifdef CONFIG_FEATURE_LESS_FLAGS
1052 case '=':
1053 clear_line();
1054 m_status_print();
1055 break;
1056#endif
1057#ifdef CONFIG_FEATURE_LESS_REGEXP
1058 case '/':
Rob Landleya2e98042006-04-18 01:53:41 +00001059 match_backwards = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001060 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001061 break;
1062 case 'n':
1063 goto_match(match_pos + 1);
Rob Landley9200e792005-09-15 19:26:59 +00001064 break;
1065 case 'N':
1066 goto_match(match_pos - 1);
Rob Landley9200e792005-09-15 19:26:59 +00001067 break;
1068 case '?':
Rob Landleya2e98042006-04-18 01:53:41 +00001069 match_backwards = 1;
1070 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001071 break;
1072#endif
1073#ifdef CONFIG_FEATURE_LESS_FLAGCS
1074 case '-':
1075 flag_change();
1076 buffer_print();
1077 break;
1078 case '_':
1079 show_flag_status();
1080 break;
1081#endif
1082#ifdef CONFIG_FEATURE_LESS_BRACKETS
1083 case '{': case '(': case '[':
1084 match_right_bracket(keypress);
1085 break;
1086 case '}': case ')': case ']':
1087 match_left_bracket(keypress);
1088 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001089#endif
1090 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001091 colon_process();
1092 break;
1093 default:
1094 break;
1095 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001096
Rob Landley9200e792005-09-15 19:26:59 +00001097 if (isdigit(keypress))
1098 number_process(keypress);
1099}
1100
Rob Landley9200e792005-09-15 19:26:59 +00001101int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001102
Rob Landley9200e792005-09-15 19:26:59 +00001103 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001104
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001105 flags = getopt32(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001106
1107 argc -= optind;
1108 argv += optind;
1109 files = argv;
1110 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001111
Rob Landley9200e792005-09-15 19:26:59 +00001112 if (!num_files) {
1113 if (ttyname(STDIN_FILENO) == NULL)
1114 inp_stdin = 1;
1115 else {
1116 bb_error_msg("Missing filename");
1117 bb_show_usage();
1118 }
1119 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001120
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001121 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
Rob Landley69d863b2006-05-25 21:13:30 +00001122 get_terminal_width_height(0, &width, &height);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001123 data_readlines();
1124 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001125 term_vi = term_orig;
1126 term_vi.c_lflag &= (~ICANON & ~ECHO);
1127 term_vi.c_iflag &= (~IXON & ~ICRNL);
1128 term_vi.c_oflag &= (~ONLCR);
1129 term_vi.c_cc[VMIN] = 1;
1130 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001131 buffer_init();
1132 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001133
Rob Landley9200e792005-09-15 19:26:59 +00001134 while (1) {
1135 keypress = tless_getch();
1136 keypress_process(keypress);
1137 }
1138}