blob: 9d42748dca2fbd34e08feecacf90a49049a01ee8 [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;
Mike Frysingerbf2d9902006-04-16 21:30:47 +0000628 char *line2 = (char *) xmalloc((sizeof(char) * (strlen(line) + 1)) + 64);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000629 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 strcpy(line2, line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000634
635 match_found = 0;
636 match_status = regexec(pattern, line2, 1, &match_structs, 0);
637
638 while (match_status == 0) {
639
Mike Frysingerbf2d9902006-04-16 21:30:47 +0000640 memset(sub_line, 0, sizeof(sub_line));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000641
642 if (match_found == 0)
643 match_found = 1;
644
645 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 +0000646 if ((size_t)match_structs.rm_eo + 11 + prev_eo < strlen(line2))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000647 strcat(sub_line, line2 + match_structs.rm_eo + 11 + prev_eo);
648
649 prev_eo += match_structs.rm_eo + 11;
650 match_status = regexec(pattern, sub_line, 1, &match_structs, REG_NOTBOL);
651 }
652
653 return line2;
654}
655
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000656static void regex_process(void)
657{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000658 char uncomp_regex[100];
659 char current_line[256];
660 int i;
661 int j = 0;
Mike Frysinger20c22e02006-04-16 21:41:00 +0000662 regex_t pattern;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000663
664 /* Reset variables */
665 match_lines[0] = -1;
666 match_pos = 0;
667 num_matches = 0;
668 match_found = 0;
669
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000670 /* Get the uncompiled regular expression from the user */
671 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000672 putchar((match_backwards) ? '?' : '/');
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000673 uncomp_regex[0] = 0;
674 fgets(uncomp_regex, sizeof(uncomp_regex), stdin);
675 i = strlen(uncomp_regex);
Mike Frysinger00d10a92006-04-16 20:54:19 +0000676 if (i > 0) {
677 if (uncomp_regex[i-1] == '\n')
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000678 uncomp_regex[i-1] = '\0';
679 else
680 while((i = getchar()) != '\n' && i != EOF);
Mike Frysinger20c22e02006-04-16 21:41:00 +0000681 } else
682 return;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000683
684 /* Compile the regex and check for errors */
Mike Frysinger20c22e02006-04-16 21:41:00 +0000685 xregcomp(&pattern, uncomp_regex, 0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000686
687 /* Run the regex on each line of the current file here */
688 for (i = 0; i <= num_flines; i++) {
Mike Frysinger20c22e02006-04-16 21:41:00 +0000689 strcpy(current_line, process_regex_on_line(flines[i], &pattern));
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000690 flines[i] = bb_xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000691 if (match_found) {
692 match_lines[j] = i;
693 j++;
694 }
695 }
696
697 num_matches = j;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000698 if ((match_lines[0] != -1) && (num_flines > height - 2))
699 buffer_line(match_lines[0]);
700 else
701 buffer_init();
702}
703
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000704static void goto_match(int match)
705{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000706 /* This goes to a specific match - all line positions of matches are
707 stored within the match_lines[] array. */
708 if ((match < num_matches) && (match >= 0)) {
709 buffer_line(match_lines[match]);
710 match_pos = match;
711 }
712}
713
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000714static void search_backwards(void)
715{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000716 int current_linepos = line_pos;
717 int i;
718
719 match_backwards = 1;
720 regex_process();
721
722 for (i = 0; i < num_matches; i++) {
723 if (match_lines[i] > current_linepos) {
724 buffer_line(match_lines[i - num_back_match]);
725 break;
726 }
727 }
728
729 /* Reset variables */
730 match_backwards = 0;
731 num_back_match = 1;
732
733}
734#endif
735
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000736static void number_process(int first_digit)
737{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000738 int i = 1;
739 int num;
740 char num_input[80];
741 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000742 char *endptr;
743
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000744 num_input[0] = first_digit;
745
746 /* Clear the current line, print a prompt, and then print the digit */
747 clear_line();
748 printf(":%c", first_digit);
749
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000750 /* Receive input until a letter is given (max 80 chars)*/
751 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000752 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000753 i++;
754 }
755
756 /* Take the final letter out of the digits string */
757 keypress = num_input[i];
758 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000759 num = strtol(num_input, &endptr, 10);
760 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES)
761 goto END;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000762
763 /* We now know the number and the letter entered, so we process them */
764 switch (keypress) {
765 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
766 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000767 break;
768 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
769 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000770 break;
771 case 'g': case '<': case 'G': case '>':
772 if (num_flines >= height - 2)
773 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000774 break;
775 case 'p': case '%':
776 buffer_line(reverse_percent(num));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000777 break;
778#ifdef CONFIG_FEATURE_LESS_REGEXP
779 case 'n':
780 goto_match(match_pos + num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000781 break;
782 case '/':
783 regex_process();
784 goto_match(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000785 break;
786 case '?':
787 num_back_match = num;
788 search_backwards();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000789 break;
790#endif
791 default:
792 break;
793 }
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000794END:
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000795 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000796}
797
798#ifdef CONFIG_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000799static void flag_change(void)
800{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000801 int keypress;
802
803 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000804 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000805 keypress = tless_getch();
806
807 switch (keypress) {
808 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000809 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000810 break;
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 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000815 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000816 break;
817 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000818 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000819 break;
820 default:
821 break;
822 }
823}
824
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000825static void show_flag_status(void)
826{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000827 int keypress;
828 int flag_val;
829
830 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000831 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000832 keypress = tless_getch();
833
834 switch (keypress) {
835 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000836 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000837 break;
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 '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000842 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000843 break;
844 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000845 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000846 break;
847 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000848 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000849 break;
850 default:
851 flag_val = 0;
852 break;
853 }
854
855 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000856 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 +0000857}
858#endif
859
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000860static void full_repaint(void)
861{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000862 int temp_line_pos = line_pos;
863 data_readlines();
864 buffer_init();
865 buffer_line(temp_line_pos);
866 buffer_print();
867}
868
869
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000870static void save_input_to_file(void)
871{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000872 char current_line[256];
873 int i;
874 FILE *fp;
875
876 clear_line();
877 printf("Log file: ");
878 fgets(current_line, 256, inp);
879 current_line[strlen(current_line) - 1] = '\0';
880 if (strlen(current_line)) {
881 fp = bb_xfopen(current_line, "w");
882 for (i = 0; i < num_flines; i++)
883 fprintf(fp, "%s", flines[i]);
884 fclose(fp);
885 buffer_print();
886 }
887 else
888 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
889}
890
891#ifdef CONFIG_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000892static void add_mark(void)
893{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000894 int letter;
895 int mark_line;
896
897 clear_line();
898 printf("Mark: ");
899 letter = tless_getch();
900
901 if (isalpha(letter)) {
902 mark_line = line_pos;
903
904 /* If we exceed 15 marks, start overwriting previous ones */
905 if (num_marks == 14)
906 num_marks = 0;
907
908 mark_lines[num_marks][0] = letter;
909 mark_lines[num_marks][1] = line_pos;
910 num_marks++;
911 }
912 else {
913 clear_line();
914 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
915 }
916}
917
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000918static void goto_mark(void)
919{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000920 int letter;
921 int i;
922
923 clear_line();
924 printf("Go to mark: ");
925 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000926 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000927
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000928 if (isalpha(letter)) {
929 for (i = 0; i <= num_marks; i++)
930 if (letter == mark_lines[i][0]) {
931 buffer_line(mark_lines[i][1]);
932 break;
933 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000934 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000935 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000936 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000937 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000938 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000939}
940#endif
941
942
943#ifdef CONFIG_FEATURE_LESS_BRACKETS
944
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000945static char opp_bracket(char bracket)
946{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000947 switch (bracket) {
948 case '{': case '[':
949 return bracket + 2;
950 break;
951 case '(':
952 return ')';
953 break;
954 case '}': case ']':
955 return bracket - 2;
956 break;
957 case ')':
958 return '(';
959 break;
960 default:
961 return 0;
962 break;
963 }
964}
965
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000966static void match_right_bracket(char bracket)
967{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000968 int bracket_line = -1;
969 int i;
970
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000971 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000972
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000973 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000974 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000975 else {
976 for (i = line_pos + 1; i < num_flines; 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 - height + 2);
987 buffer_print();
988 }
989}
990
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000991static void match_left_bracket(char bracket)
992{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000993 int bracket_line = -1;
994 int i;
995
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000996 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000997
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000998 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000999 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
1000 printf("%s", flines[line_pos + height]);
1001 sleep(4);
1002 }
1003 else {
1004 for (i = line_pos + height - 2; i >= 0; i--) {
1005 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
1006 bracket_line = i;
1007 break;
1008 }
1009 }
1010
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001011 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001012 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001013
1014 buffer_line(bracket_line);
1015 buffer_print();
1016 }
1017}
1018
1019#endif /* CONFIG_FEATURE_LESS_BRACKETS */
1020
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001021static void keypress_process(int keypress)
1022{
Rob Landley9200e792005-09-15 19:26:59 +00001023 switch (keypress) {
1024 case KEY_DOWN: case 'e': case 'j': case '\015':
1025 buffer_down(1);
1026 buffer_print();
1027 break;
1028 case KEY_UP: case 'y': case 'k':
1029 buffer_up(1);
1030 buffer_print();
1031 break;
1032 case PAGE_DOWN: case ' ': case 'z':
1033 buffer_down(height - 1);
1034 buffer_print();
1035 break;
1036 case PAGE_UP: case 'w': case 'b':
1037 buffer_up(height - 1);
1038 buffer_print();
1039 break;
1040 case 'd':
1041 buffer_down((height - 1) / 2);
1042 buffer_print();
1043 break;
1044 case 'u':
1045 buffer_up((height - 1) / 2);
1046 buffer_print();
1047 break;
1048 case 'g': case 'p': case '<': case '%':
1049 buffer_up(num_flines + 1);
1050 buffer_print();
1051 break;
1052 case 'G': case '>':
1053 buffer_down(num_flines + 1);
1054 buffer_print();
1055 break;
1056 case 'q': case 'Q':
1057 tless_exit(0);
1058 break;
1059#ifdef CONFIG_FEATURE_LESS_MARKS
1060 case 'm':
1061 add_mark();
1062 buffer_print();
1063 break;
1064 case '\'':
1065 goto_mark();
1066 buffer_print();
1067 break;
1068#endif
1069 case 'r':
1070 buffer_print();
1071 break;
1072 case 'R':
1073 full_repaint();
1074 break;
1075 case 's':
1076 if (inp_stdin)
1077 save_input_to_file();
1078 break;
1079 case 'E':
1080 examine_file();
1081 break;
1082#ifdef CONFIG_FEATURE_LESS_FLAGS
1083 case '=':
1084 clear_line();
1085 m_status_print();
1086 break;
1087#endif
1088#ifdef CONFIG_FEATURE_LESS_REGEXP
1089 case '/':
1090 regex_process();
1091 buffer_print();
1092 break;
1093 case 'n':
1094 goto_match(match_pos + 1);
1095 buffer_print();
1096 break;
1097 case 'N':
1098 goto_match(match_pos - 1);
1099 buffer_print();
1100 break;
1101 case '?':
1102 search_backwards();
1103 buffer_print();
1104 break;
1105#endif
1106#ifdef CONFIG_FEATURE_LESS_FLAGCS
1107 case '-':
1108 flag_change();
1109 buffer_print();
1110 break;
1111 case '_':
1112 show_flag_status();
1113 break;
1114#endif
1115#ifdef CONFIG_FEATURE_LESS_BRACKETS
1116 case '{': case '(': case '[':
1117 match_right_bracket(keypress);
1118 break;
1119 case '}': case ')': case ']':
1120 match_left_bracket(keypress);
1121 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001122#endif
1123 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001124 colon_process();
1125 break;
1126 default:
1127 break;
1128 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001129
Rob Landley9200e792005-09-15 19:26:59 +00001130 if (isdigit(keypress))
1131 number_process(keypress);
1132}
1133
Rob Landley9200e792005-09-15 19:26:59 +00001134int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001135
Rob Landley9200e792005-09-15 19:26:59 +00001136 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001137
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001138 flags = bb_getopt_ulflags(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001139
1140 argc -= optind;
1141 argv += optind;
1142 files = argv;
1143 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001144
Rob Landley9200e792005-09-15 19:26:59 +00001145 if (!num_files) {
1146 if (ttyname(STDIN_FILENO) == NULL)
1147 inp_stdin = 1;
1148 else {
1149 bb_error_msg("Missing filename");
1150 bb_show_usage();
1151 }
1152 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001153
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001154 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
Rob Landley9200e792005-09-15 19:26:59 +00001155 tty_width_height();
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001156 data_readlines();
1157 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001158 term_vi = term_orig;
1159 term_vi.c_lflag &= (~ICANON & ~ECHO);
1160 term_vi.c_iflag &= (~IXON & ~ICRNL);
1161 term_vi.c_oflag &= (~ONLCR);
1162 term_vi.c_cc[VMIN] = 1;
1163 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001164 buffer_init();
1165 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001166
Rob Landley9200e792005-09-15 19:26:59 +00001167 while (1) {
1168 keypress = tless_getch();
1169 keypress_process(keypress);
1170 }
1171}