blob: 6b0cab526da8fae68f080a96de11604c2d44b874 [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
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <termios.h>
37#include <unistd.h>
Rob Landley9200e792005-09-15 19:26:59 +000038#include <ctype.h>
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000039
Rob Landley9200e792005-09-15 19:26:59 +000040#include "busybox.h"
41
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000042#ifdef CONFIG_FEATURE_LESS_REGEXP
43#include "xregex.h"
44#endif
45
46
Rob Landley9200e792005-09-15 19:26:59 +000047/* These are the escape sequences corresponding to special keys */
48#define REAL_KEY_UP 'A'
49#define REAL_KEY_DOWN 'B'
50#define REAL_KEY_RIGHT 'C'
51#define REAL_KEY_LEFT 'D'
52#define REAL_PAGE_UP '5'
53#define REAL_PAGE_DOWN '6'
54
55/* These are the special codes assigned by this program to the special keys */
56#define PAGE_UP 20
57#define PAGE_DOWN 21
58#define KEY_UP 22
59#define KEY_DOWN 23
60#define KEY_RIGHT 24
61#define KEY_LEFT 25
62
63/* The escape codes for highlighted and normal text */
64#define HIGHLIGHT "\033[7m"
65#define NORMAL "\033[0m"
66
67/* The escape code to clear the screen */
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000068#define CLEAR "\033[H\033[J"
Rob Landley9200e792005-09-15 19:26:59 +000069
70/* Maximum number of lines in a file */
71#define MAXLINES 10000
72
73/* Get height and width of terminal */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000074#define tty_width_height() get_terminal_width_height(0, &width, &height)
Rob Landley9200e792005-09-15 19:26:59 +000075
76static int height;
77static int width;
78static char **files;
79static char filename[256];
Rob Landleyd57ae8b2005-09-18 00:58:49 +000080static char **buffer;
81static char **flines;
Rob Landley9200e792005-09-15 19:26:59 +000082static int current_file = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000083static int line_pos;
Rob Landley9200e792005-09-15 19:26:59 +000084static int num_flines;
85static int num_files = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000086static int past_eof;
Rob Landley9200e792005-09-15 19:26:59 +000087
88/* Command line options */
Rob Landleyd57ae8b2005-09-18 00:58:49 +000089static unsigned long flags;
90#define FLAG_E 1
91#define FLAG_M (1<<1)
92#define FLAG_m (1<<2)
93#define FLAG_N (1<<3)
94#define FLAG_TILDE (1<<4)
Rob Landley9200e792005-09-15 19:26:59 +000095
96/* This is needed so that program behaviour changes when input comes from
97 stdin */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000098static int inp_stdin;
Rob Landley9200e792005-09-15 19:26:59 +000099
100#ifdef CONFIG_FEATURE_LESS_MARKS
101static int mark_lines[15][2];
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000102static int num_marks;
Rob Landley9200e792005-09-15 19:26:59 +0000103#endif
104
105#ifdef CONFIG_FEATURE_LESS_REGEXP
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000106static int match_found;
Rob Landley9200e792005-09-15 19:26:59 +0000107static int match_lines[100];
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000108static int match_pos;
109static int num_matches;
110static int match_backwards;
Rob Landley9200e792005-09-15 19:26:59 +0000111static int num_back_match = 1;
112#endif
113
114/* Needed termios structures */
115static struct termios term_orig, term_vi;
116
117/* File pointer to get input from */
118static FILE *inp;
119
120/* Reset terminal input to normal */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000121static void set_tty_cooked(void)
122{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000123 fflush(stdout);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000124 tcsetattr(fileno(inp), TCSANOW, &term_orig);
Rob Landley9200e792005-09-15 19:26:59 +0000125}
126
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000127/* Set terminal input to raw mode (taken from vi.c) */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000128static void set_tty_raw(void)
129{
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000130 tcsetattr(fileno(inp), TCSANOW, &term_vi);
Rob Landley9200e792005-09-15 19:26:59 +0000131}
132
133/* Exit the program gracefully */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000134static void tless_exit(int code)
135{
Rob Landley9200e792005-09-15 19:26:59 +0000136 /* TODO: We really should save the terminal state when we start,
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000137 and restore it when we exit. Less does this with the
Rob Landley9200e792005-09-15 19:26:59 +0000138 "ti" and "te" termcap commands; can this be done with
139 only termios.h? */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000140
Rob Landley9200e792005-09-15 19:26:59 +0000141 putchar('\n');
142 exit(code);
143}
144
145/* Grab a character from input without requiring the return key. If the
146 character is ASCII \033, get more characters and assign certain sequences
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000147 special return codes. Note that this function works best with raw input. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000148static int tless_getch(void)
149{
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000150 int input;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000151
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000152 set_tty_raw();
153
154 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000155 /* Detect escape sequences (i.e. arrow keys) and handle
156 them accordingly */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000157
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000158 if (input == '\033' && getc(inp) == '[') {
159 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000160 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000161 if (input == REAL_KEY_UP)
162 return KEY_UP;
163 else if (input == REAL_KEY_DOWN)
164 return KEY_DOWN;
165 else if (input == REAL_KEY_RIGHT)
166 return KEY_RIGHT;
167 else if (input == REAL_KEY_LEFT)
168 return KEY_LEFT;
169 else if (input == REAL_PAGE_UP)
170 return PAGE_UP;
171 else if (input == REAL_PAGE_DOWN)
172 return PAGE_DOWN;
Rob Landley9200e792005-09-15 19:26:59 +0000173 }
174 /* The input is a normal ASCII value */
175 else {
176 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000177 return input;
Rob Landley9200e792005-09-15 19:26:59 +0000178 }
179 return 0;
180}
181
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000182/* Move the cursor to a position (x,y), where (0,0) is the
Rob Landley9200e792005-09-15 19:26:59 +0000183 top-left corner of the console */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000184static void move_cursor(int x, int y)
185{
Rob Landley9200e792005-09-15 19:26:59 +0000186 printf("\033[%i;%iH", x, y);
187}
188
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000189static void clear_line(void)
190{
Rob Landley9200e792005-09-15 19:26:59 +0000191 move_cursor(height, 0);
192 printf("\033[K");
193}
194
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000195/* This adds line numbers to every line, as the -N flag necessitates */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000196static void add_linenumbers(void)
197{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000198 char current_line[256];
199 int i;
200
201 for (i = 0; i <= num_flines; i++) {
202 safe_strncpy(current_line, flines[i], 256);
"Vladimir N. Oleynik"39a841c2005-09-29 16:18:57 +0000203 flines[i] = bb_xasprintf("%5d %s", i + 1, current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000204 }
205}
206
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000207static void data_readlines(void)
208{
Rob Landley9200e792005-09-15 19:26:59 +0000209 int i;
210 char current_line[256];
211 FILE *fp;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000212
Rob Landley9200e792005-09-15 19:26:59 +0000213 fp = (inp_stdin) ? stdin : bb_xfopen(filename, "rt");
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000214 flines = NULL;
215 for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) {
Rob Landley9200e792005-09-15 19:26:59 +0000216 strcpy(current_line, "");
217 fgets(current_line, 256, fp);
Mike Frysinger00d10a92006-04-16 20:54:19 +0000218 if (fp != stdin)
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000219 bb_xferror(fp, filename);
220 flines = xrealloc(flines, (i+1) * sizeof(char *));
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000221 flines[i] = bb_xstrdup(current_line);
Rob Landley9200e792005-09-15 19:26:59 +0000222 }
223 num_flines = i - 2;
224
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000225 /* Reset variables for a new file */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000226
Rob Landley9200e792005-09-15 19:26:59 +0000227 line_pos = 0;
228 past_eof = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000229
Rob Landley9200e792005-09-15 19:26:59 +0000230 fclose(fp);
231
Mike Frysinger00d10a92006-04-16 20:54:19 +0000232 if (inp == NULL)
"Vladimir N. Oleynik"8315cd52005-12-15 11:53:22 +0000233 inp = (inp_stdin) ? bb_xfopen(CURRENT_TTY, "r") : stdin;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000234
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000235 if (flags & FLAG_N)
Rob Landley9200e792005-09-15 19:26:59 +0000236 add_linenumbers();
237}
238
Rob Landley9200e792005-09-15 19:26:59 +0000239/* Turn a percentage into a line number */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000240static int reverse_percent(int percentage)
241{
Rob Landley9200e792005-09-15 19:26:59 +0000242 double linenum = percentage;
243 linenum = ((linenum / 100) * num_flines) - 1;
244 return(linenum);
245}
246
247#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000248
249/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
250 * on my build. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000251static int calc_percent(void)
252{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000253 return ((100 * (line_pos + height - 2) / num_flines) + 1);
254}
255
Rob Landley9200e792005-09-15 19:26:59 +0000256/* Print a status line if -M was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000257static void m_status_print(void)
258{
Rob Landley9200e792005-09-15 19:26:59 +0000259 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000260
Rob Landley9200e792005-09-15 19:26:59 +0000261 if (!past_eof) {
262 if (!line_pos) {
263 if (num_files > 1)
264 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);
265 else {
266 printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
267 }
268 }
269 else {
270 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
271 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000272
Rob Landley9200e792005-09-15 19:26:59 +0000273 if (line_pos == num_flines - height + 2) {
274 printf("(END) %s", NORMAL);
275 if ((num_files > 1) && (current_file != num_files))
276 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
277 }
278 else {
279 percentage = calc_percent();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000280 printf("%i%% %s", percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000281 }
282 }
283 else {
284 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
285 if ((num_files > 1) && (current_file != num_files))
286 printf("- Next: %s", files[current_file]);
287 printf("%s", NORMAL);
288 }
289}
290
291/* Print a status line if -m was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000292static void medium_status_print(void)
293{
Rob Landley9200e792005-09-15 19:26:59 +0000294 int percentage;
295 percentage = calc_percent();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000296
Rob Landley9200e792005-09-15 19:26:59 +0000297 if (!line_pos)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000298 printf("%s%s %i%%%s", HIGHLIGHT, filename, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000299 else if (line_pos == num_flines - height + 2)
300 printf("%s(END)%s", HIGHLIGHT, NORMAL);
301 else
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000302 printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000303}
304#endif
305
306/* Print the status line */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000307static void status_print(void)
308{
Rob Landley9200e792005-09-15 19:26:59 +0000309 /* Change the status if flags have been set */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000310#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000311 if (flags & FLAG_M)
Rob Landley9200e792005-09-15 19:26:59 +0000312 m_status_print();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000313 else if (flags & FLAG_m)
Rob Landley9200e792005-09-15 19:26:59 +0000314 medium_status_print();
315 /* No flags set */
316 else {
317#endif
318 if (!line_pos) {
319 printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
320 if (num_files > 1)
321 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
322 }
323 else if (line_pos == num_flines - height + 2) {
324 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
325 if ((num_files > 1) && (current_file != num_files))
326 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
327 }
328 else {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000329 putchar(':');
Rob Landley9200e792005-09-15 19:26:59 +0000330 }
331#ifdef CONFIG_FEATURE_LESS_FLAGS
332 }
333#endif
334}
335
336/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000337static void buffer_print(void)
338{
Rob Landley9200e792005-09-15 19:26:59 +0000339 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000340
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000341 printf("%s", CLEAR);
Rob Landley9200e792005-09-15 19:26:59 +0000342 if (num_flines >= height - 2) {
Rob Landley9200e792005-09-15 19:26:59 +0000343 for (i = 0; i < height - 1; i++)
344 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000345 }
346 else {
Rob Landley9200e792005-09-15 19:26:59 +0000347 for (i = 1; i < (height - 1 - num_flines); i++)
348 putchar('\n');
349 for (i = 0; i < height - 1; i++)
350 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000351 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000352
353 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000354}
355
356/* Initialise the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000357static void buffer_init(void)
358{
Rob Landley9200e792005-09-15 19:26:59 +0000359 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000360
Mike Frysinger00d10a92006-04-16 20:54:19 +0000361 if (buffer == NULL) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000362 /* malloc the number of lines needed for the buffer */
363 buffer = xrealloc(buffer, height * sizeof(char *));
364 } else {
365 for (i = 0; i < (height - 1); i++)
366 free(buffer[i]);
367 }
368
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000369 /* Fill the buffer until the end of the file or the
Rob Landley9200e792005-09-15 19:26:59 +0000370 end of the buffer is reached */
371 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000372 buffer[i] = bb_xstrdup(flines[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000373 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000374
Rob Landley9200e792005-09-15 19:26:59 +0000375 /* If the buffer still isn't full, fill it with blank lines */
376 for (; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000377 buffer[i] = bb_xstrdup("");
Rob Landley9200e792005-09-15 19:26:59 +0000378 }
379}
380
381/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000382static void buffer_down(int nlines)
383{
Rob Landley9200e792005-09-15 19:26:59 +0000384 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000385
Rob Landley9200e792005-09-15 19:26:59 +0000386 if (!past_eof) {
387 if (line_pos + (height - 3) + nlines < num_flines) {
388 line_pos += nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000389 for (i = 0; i < (height - 1); i++) {
390 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000391 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000392 }
Rob Landley9200e792005-09-15 19:26:59 +0000393 }
394 else {
395 /* As the number of lines requested was too large, we just move
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000396 to the end of the file */
397 while (line_pos + (height - 3) + 1 < num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000398 line_pos += 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000399 for (i = 0; i < (height - 1); i++) {
400 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000401 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000402 }
Rob Landley9200e792005-09-15 19:26:59 +0000403 }
404 }
405
406 /* We exit if the -E flag has been set */
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000407 if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
Rob Landley9200e792005-09-15 19:26:59 +0000408 tless_exit(0);
409 }
410}
411
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000412static void buffer_up(int nlines)
413{
Rob Landley9200e792005-09-15 19:26:59 +0000414 int i;
415 int tilde_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000416
Rob Landley9200e792005-09-15 19:26:59 +0000417 if (!past_eof) {
418 if (line_pos - nlines >= 0) {
419 line_pos -= nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000420 for (i = 0; i < (height - 1); i++) {
421 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000422 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000423 }
Rob Landley9200e792005-09-15 19:26:59 +0000424 }
425 else {
426 /* As the requested number of lines to move was too large, we
427 move one line up at a time until we can't. */
428 while (line_pos != 0) {
429 line_pos -= 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000430 for (i = 0; i < (height - 1); i++) {
431 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000432 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000433 }
Rob Landley9200e792005-09-15 19:26:59 +0000434 }
435 }
436 }
437 else {
438 /* Work out where the tildes start */
439 tilde_line = num_flines - line_pos + 3;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000440
Rob Landley9200e792005-09-15 19:26:59 +0000441 line_pos -= nlines;
442 /* Going backwards nlines lines has taken us to a point where
443 nothing is past the EOF, so we revert to normal. */
444 if (line_pos < num_flines - height + 3) {
445 past_eof = 0;
446 buffer_up(nlines);
447 }
448 else {
449 /* We only move part of the buffer, as the rest
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000450 is past the EOF */
Rob Landley9200e792005-09-15 19:26:59 +0000451 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000452 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000453 if (i < tilde_line - nlines + 1)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000454 buffer[i] = bb_xstrdup(flines[line_pos + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000455 else {
456 if (line_pos >= num_flines - height + 2)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000457 buffer[i] = bb_xstrdup("~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000458 }
459 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000460 }
Rob Landley9200e792005-09-15 19:26:59 +0000461 }
462}
463
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000464static void buffer_line(int linenum)
465{
Rob Landley9200e792005-09-15 19:26:59 +0000466 int i;
467
468 past_eof = 0;
469
470 if (linenum < 1 || linenum > num_flines) {
471 clear_line();
472 printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum, NORMAL);
473 }
474 else if (linenum < (num_flines - height - 2)) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000475 for (i = 0; i < (height - 1); i++) {
476 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000477 buffer[i] = bb_xstrdup(flines[linenum + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000478 }
Rob Landley9200e792005-09-15 19:26:59 +0000479 line_pos = linenum;
480 }
481 else {
482 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000483 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000484 if (linenum + i < num_flines + 2)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000485 buffer[i] = bb_xstrdup(flines[linenum + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000486 else
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000487 buffer[i] = bb_xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000488 }
489 line_pos = linenum;
490 /* Set past_eof so buffer_down and buffer_up act differently */
491 past_eof = 1;
492 }
493}
494
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000495/* Reinitialise everything for a new file - free the memory and start over */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000496static void reinitialise(void)
497{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000498 int i;
499
500 for (i = 0; i <= num_flines; i++)
501 free(flines[i]);
502 free(flines);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000503
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000504 data_readlines();
505 buffer_init();
506 buffer_print();
507}
508
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000509static void examine_file(void)
510{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000511 int newline_offset;
512
513 clear_line();
514 printf("Examine: ");
515 fgets(filename, 256, inp);
516
517 /* As fgets adds a newline to the end of an input string, we
518 need to remove it */
519 newline_offset = strlen(filename) - 1;
520 filename[newline_offset] = '\0';
521
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000522 files[num_files] = bb_xstrdup(filename);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000523 current_file = num_files + 1;
524 num_files++;
525
526 inp_stdin = 0;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000527 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000528}
529
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000530/* This function changes the file currently being paged. direction can be one of the following:
531 * -1: go back one file
532 * 0: go to the first file
533 * 1: go forward one file
534*/
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000535static void change_file(int direction)
536{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000537 if (current_file != ((direction > 0) ? num_files : 1)) {
538 current_file = direction ? current_file + direction : 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000539 strcpy(filename, files[current_file - 1]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000540 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000541 }
542 else {
543 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000544 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000545 }
546}
547
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000548static void remove_current_file(void)
549{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000550 int i;
551
552 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000553 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000554 for (i = 3; i <= num_files; i++)
555 files[i - 2] = files[i - 1];
556 num_files--;
557 buffer_print();
558 }
559 else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000560 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000561 for (i = 2; i <= num_files; i++)
562 files[i - 2] = files[i - 1];
563 num_files--;
564 current_file--;
565 buffer_print();
566 }
567}
568
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000569static void colon_process(void)
570{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000571 int keypress;
572
573 /* Clear the current line and print a prompt */
574 clear_line();
575 printf(" :");
576
577 keypress = tless_getch();
578 switch (keypress) {
579 case 'd':
580 remove_current_file();
581 break;
582 case 'e':
583 examine_file();
584 break;
585#ifdef CONFIG_FEATURE_LESS_FLAGS
586 case 'f':
587 clear_line();
588 m_status_print();
589 break;
590#endif
591 case 'n':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000592 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000593 break;
594 case 'p':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000595 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000596 break;
597 case 'q':
598 tless_exit(0);
599 break;
600 case 'x':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000601 change_file(0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000602 break;
603 default:
604 break;
605 }
606}
607
608#ifdef CONFIG_FEATURE_LESS_REGEXP
609/* The below two regular expression handler functions NEED development. */
610
611/* Get a regular expression from the user, and then go through the current
612 file line by line, running a processing regex function on each one. */
613
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000614static char *insert_highlights(char *line, int start, int end)
615{
"Vladimir N. Oleynik"39a841c2005-09-29 16:18:57 +0000616 return bb_xasprintf("%.*s%s%.*s%s%s", start, line, HIGHLIGHT,
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000617 end - start, line + start, NORMAL, line + end);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000618}
619
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000620static char *process_regex_on_line(char *line, regex_t *pattern)
621{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000622 /* This function takes the regex and applies it to the line.
623 Each part of the line that matches has the HIGHLIGHT
624 and NORMAL escape sequences placed around it by
625 insert_highlights, and then the line is returned. */
626
627 int match_status;
628 char *line2 = (char *) malloc((sizeof(char) * (strlen(line) + 1)) + 64);
629 char sub_line[256];
630 int prev_eo = 0;
"Vladimir N. Oleynik"8d3c40d2005-09-16 13:16:01 +0000631 regmatch_t match_structs;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000632
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000633 memset(sub_line, 0, 256);
634 strcpy(line2, line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000635
636 match_found = 0;
637 match_status = regexec(pattern, line2, 1, &match_structs, 0);
638
639 while (match_status == 0) {
640
641 memset(sub_line, 0, 256);
642
643 if (match_found == 0)
644 match_found = 1;
645
646 line2 = insert_highlights(line2, match_structs.rm_so + prev_eo, match_structs.rm_eo + prev_eo);
"Vladimir N. Oleynik"73ffd762006-02-01 12:56:19 +0000647 if ((size_t)match_structs.rm_eo + 11 + prev_eo < strlen(line2))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000648 strcat(sub_line, line2 + match_structs.rm_eo + 11 + prev_eo);
649
650 prev_eo += match_structs.rm_eo + 11;
651 match_status = regexec(pattern, sub_line, 1, &match_structs, REG_NOTBOL);
652 }
653
654 return line2;
655}
656
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000657static void regex_process(void)
658{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000659 char uncomp_regex[100];
660 char current_line[256];
661 int i;
662 int j = 0;
663 regex_t *pattern;
664
665 /* Reset variables */
666 match_lines[0] = -1;
667 match_pos = 0;
668 num_matches = 0;
669 match_found = 0;
670
671 pattern = (regex_t *) malloc(sizeof(regex_t));
672 memset(pattern, 0, sizeof(regex_t));
673
674 /* Get the uncompiled regular expression from the user */
675 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000676 putchar((match_backwards) ? '?' : '/');
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000677 uncomp_regex[0] = 0;
678 fgets(uncomp_regex, sizeof(uncomp_regex), stdin);
679 i = strlen(uncomp_regex);
Mike Frysinger00d10a92006-04-16 20:54:19 +0000680 if (i > 0) {
681 if (uncomp_regex[i-1] == '\n')
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000682 uncomp_regex[i-1] = '\0';
683 else
684 while((i = getchar()) != '\n' && i != EOF);
685 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000686
687 /* Compile the regex and check for errors */
688 xregcomp(pattern, uncomp_regex, 0);
689
690 /* Run the regex on each line of the current file here */
691 for (i = 0; i <= num_flines; i++) {
692 strcpy(current_line, process_regex_on_line(flines[i], pattern));
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000693 flines[i] = bb_xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000694 if (match_found) {
695 match_lines[j] = i;
696 j++;
697 }
698 }
699
700 num_matches = j;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000701 if ((match_lines[0] != -1) && (num_flines > height - 2))
702 buffer_line(match_lines[0]);
703 else
704 buffer_init();
705}
706
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000707static void goto_match(int match)
708{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000709 /* This goes to a specific match - all line positions of matches are
710 stored within the match_lines[] array. */
711 if ((match < num_matches) && (match >= 0)) {
712 buffer_line(match_lines[match]);
713 match_pos = match;
714 }
715}
716
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000717static void search_backwards(void)
718{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000719 int current_linepos = line_pos;
720 int i;
721
722 match_backwards = 1;
723 regex_process();
724
725 for (i = 0; i < num_matches; i++) {
726 if (match_lines[i] > current_linepos) {
727 buffer_line(match_lines[i - num_back_match]);
728 break;
729 }
730 }
731
732 /* Reset variables */
733 match_backwards = 0;
734 num_back_match = 1;
735
736}
737#endif
738
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000739static void number_process(int first_digit)
740{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000741 int i = 1;
742 int num;
743 char num_input[80];
744 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000745 char *endptr;
746
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000747 num_input[0] = first_digit;
748
749 /* Clear the current line, print a prompt, and then print the digit */
750 clear_line();
751 printf(":%c", first_digit);
752
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000753 /* Receive input until a letter is given (max 80 chars)*/
754 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000755 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000756 i++;
757 }
758
759 /* Take the final letter out of the digits string */
760 keypress = num_input[i];
761 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000762 num = strtol(num_input, &endptr, 10);
763 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES)
764 goto END;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000765
766 /* We now know the number and the letter entered, so we process them */
767 switch (keypress) {
768 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
769 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000770 break;
771 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
772 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000773 break;
774 case 'g': case '<': case 'G': case '>':
775 if (num_flines >= height - 2)
776 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000777 break;
778 case 'p': case '%':
779 buffer_line(reverse_percent(num));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000780 break;
781#ifdef CONFIG_FEATURE_LESS_REGEXP
782 case 'n':
783 goto_match(match_pos + num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000784 break;
785 case '/':
786 regex_process();
787 goto_match(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000788 break;
789 case '?':
790 num_back_match = num;
791 search_backwards();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000792 break;
793#endif
794 default:
795 break;
796 }
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000797END:
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000798 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000799}
800
801#ifdef CONFIG_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000802static void flag_change(void)
803{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000804 int keypress;
805
806 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000807 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000808 keypress = tless_getch();
809
810 switch (keypress) {
811 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000812 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000813 break;
814 case 'm':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000815 flags ^= FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000816 break;
817 case 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000818 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000819 break;
820 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000821 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000822 break;
823 default:
824 break;
825 }
826}
827
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000828static void show_flag_status(void)
829{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000830 int keypress;
831 int flag_val;
832
833 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000834 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000835 keypress = tless_getch();
836
837 switch (keypress) {
838 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000839 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000840 break;
841 case 'm':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000842 flag_val = flags & FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000843 break;
844 case '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000845 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000846 break;
847 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000848 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000849 break;
850 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000851 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000852 break;
853 default:
854 flag_val = 0;
855 break;
856 }
857
858 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000859 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 +0000860}
861#endif
862
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000863static void full_repaint(void)
864{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000865 int temp_line_pos = line_pos;
866 data_readlines();
867 buffer_init();
868 buffer_line(temp_line_pos);
869 buffer_print();
870}
871
872
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000873static void save_input_to_file(void)
874{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000875 char current_line[256];
876 int i;
877 FILE *fp;
878
879 clear_line();
880 printf("Log file: ");
881 fgets(current_line, 256, inp);
882 current_line[strlen(current_line) - 1] = '\0';
883 if (strlen(current_line)) {
884 fp = bb_xfopen(current_line, "w");
885 for (i = 0; i < num_flines; i++)
886 fprintf(fp, "%s", flines[i]);
887 fclose(fp);
888 buffer_print();
889 }
890 else
891 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
892}
893
894#ifdef CONFIG_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000895static void add_mark(void)
896{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000897 int letter;
898 int mark_line;
899
900 clear_line();
901 printf("Mark: ");
902 letter = tless_getch();
903
904 if (isalpha(letter)) {
905 mark_line = line_pos;
906
907 /* If we exceed 15 marks, start overwriting previous ones */
908 if (num_marks == 14)
909 num_marks = 0;
910
911 mark_lines[num_marks][0] = letter;
912 mark_lines[num_marks][1] = line_pos;
913 num_marks++;
914 }
915 else {
916 clear_line();
917 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
918 }
919}
920
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000921static void goto_mark(void)
922{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000923 int letter;
924 int i;
925
926 clear_line();
927 printf("Go to mark: ");
928 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000929 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000930
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000931 if (isalpha(letter)) {
932 for (i = 0; i <= num_marks; i++)
933 if (letter == mark_lines[i][0]) {
934 buffer_line(mark_lines[i][1]);
935 break;
936 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000937 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000938 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000939 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000940 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000941 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000942}
943#endif
944
945
946#ifdef CONFIG_FEATURE_LESS_BRACKETS
947
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000948static char opp_bracket(char bracket)
949{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000950 switch (bracket) {
951 case '{': case '[':
952 return bracket + 2;
953 break;
954 case '(':
955 return ')';
956 break;
957 case '}': case ']':
958 return bracket - 2;
959 break;
960 case ')':
961 return '(';
962 break;
963 default:
964 return 0;
965 break;
966 }
967}
968
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000969static void match_right_bracket(char bracket)
970{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000971 int bracket_line = -1;
972 int i;
973
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000974 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000975
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000976 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000977 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000978 else {
979 for (i = line_pos + 1; i < num_flines; i++) {
980 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
981 bracket_line = i;
982 break;
983 }
984 }
985
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000986 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000987 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000988
989 buffer_line(bracket_line - height + 2);
990 buffer_print();
991 }
992}
993
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000994static void match_left_bracket(char bracket)
995{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000996 int bracket_line = -1;
997 int i;
998
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000999 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001000
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001001 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001002 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
1003 printf("%s", flines[line_pos + height]);
1004 sleep(4);
1005 }
1006 else {
1007 for (i = line_pos + height - 2; i >= 0; i--) {
1008 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
1009 bracket_line = i;
1010 break;
1011 }
1012 }
1013
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001014 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001015 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001016
1017 buffer_line(bracket_line);
1018 buffer_print();
1019 }
1020}
1021
1022#endif /* CONFIG_FEATURE_LESS_BRACKETS */
1023
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001024static void keypress_process(int keypress)
1025{
Rob Landley9200e792005-09-15 19:26:59 +00001026 switch (keypress) {
1027 case KEY_DOWN: case 'e': case 'j': case '\015':
1028 buffer_down(1);
1029 buffer_print();
1030 break;
1031 case KEY_UP: case 'y': case 'k':
1032 buffer_up(1);
1033 buffer_print();
1034 break;
1035 case PAGE_DOWN: case ' ': case 'z':
1036 buffer_down(height - 1);
1037 buffer_print();
1038 break;
1039 case PAGE_UP: case 'w': case 'b':
1040 buffer_up(height - 1);
1041 buffer_print();
1042 break;
1043 case 'd':
1044 buffer_down((height - 1) / 2);
1045 buffer_print();
1046 break;
1047 case 'u':
1048 buffer_up((height - 1) / 2);
1049 buffer_print();
1050 break;
1051 case 'g': case 'p': case '<': case '%':
1052 buffer_up(num_flines + 1);
1053 buffer_print();
1054 break;
1055 case 'G': case '>':
1056 buffer_down(num_flines + 1);
1057 buffer_print();
1058 break;
1059 case 'q': case 'Q':
1060 tless_exit(0);
1061 break;
1062#ifdef CONFIG_FEATURE_LESS_MARKS
1063 case 'm':
1064 add_mark();
1065 buffer_print();
1066 break;
1067 case '\'':
1068 goto_mark();
1069 buffer_print();
1070 break;
1071#endif
1072 case 'r':
1073 buffer_print();
1074 break;
1075 case 'R':
1076 full_repaint();
1077 break;
1078 case 's':
1079 if (inp_stdin)
1080 save_input_to_file();
1081 break;
1082 case 'E':
1083 examine_file();
1084 break;
1085#ifdef CONFIG_FEATURE_LESS_FLAGS
1086 case '=':
1087 clear_line();
1088 m_status_print();
1089 break;
1090#endif
1091#ifdef CONFIG_FEATURE_LESS_REGEXP
1092 case '/':
1093 regex_process();
1094 buffer_print();
1095 break;
1096 case 'n':
1097 goto_match(match_pos + 1);
1098 buffer_print();
1099 break;
1100 case 'N':
1101 goto_match(match_pos - 1);
1102 buffer_print();
1103 break;
1104 case '?':
1105 search_backwards();
1106 buffer_print();
1107 break;
1108#endif
1109#ifdef CONFIG_FEATURE_LESS_FLAGCS
1110 case '-':
1111 flag_change();
1112 buffer_print();
1113 break;
1114 case '_':
1115 show_flag_status();
1116 break;
1117#endif
1118#ifdef CONFIG_FEATURE_LESS_BRACKETS
1119 case '{': case '(': case '[':
1120 match_right_bracket(keypress);
1121 break;
1122 case '}': case ')': case ']':
1123 match_left_bracket(keypress);
1124 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001125#endif
1126 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001127 colon_process();
1128 break;
1129 default:
1130 break;
1131 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001132
Rob Landley9200e792005-09-15 19:26:59 +00001133 if (isdigit(keypress))
1134 number_process(keypress);
1135}
1136
Rob Landley9200e792005-09-15 19:26:59 +00001137int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001138
Rob Landley9200e792005-09-15 19:26:59 +00001139 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001140
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001141 flags = bb_getopt_ulflags(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001142
1143 argc -= optind;
1144 argv += optind;
1145 files = argv;
1146 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001147
Rob Landley9200e792005-09-15 19:26:59 +00001148 if (!num_files) {
1149 if (ttyname(STDIN_FILENO) == NULL)
1150 inp_stdin = 1;
1151 else {
1152 bb_error_msg("Missing filename");
1153 bb_show_usage();
1154 }
1155 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001156
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001157 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
Rob Landley9200e792005-09-15 19:26:59 +00001158 tty_width_height();
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001159 data_readlines();
1160 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001161 term_vi = term_orig;
1162 term_vi.c_lflag &= (~ICANON & ~ECHO);
1163 term_vi.c_iflag &= (~IXON & ~ICRNL);
1164 term_vi.c_oflag &= (~ONLCR);
1165 term_vi.c_cc[VMIN] = 1;
1166 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001167 buffer_init();
1168 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001169
Rob Landley9200e792005-09-15 19:26:59 +00001170 while (1) {
1171 keypress = tless_getch();
1172 keypress_process(keypress);
1173 }
1174}