blob: eba916a80efbfdf83e2101f3418364ea25cf943d [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;
662 regex_t *pattern;
663
664 /* Reset variables */
665 match_lines[0] = -1;
666 match_pos = 0;
667 num_matches = 0;
668 match_found = 0;
669
670 pattern = (regex_t *) malloc(sizeof(regex_t));
671 memset(pattern, 0, sizeof(regex_t));
672
673 /* Get the uncompiled regular expression from the user */
674 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000675 putchar((match_backwards) ? '?' : '/');
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000676 uncomp_regex[0] = 0;
677 fgets(uncomp_regex, sizeof(uncomp_regex), stdin);
678 i = strlen(uncomp_regex);
Mike Frysinger00d10a92006-04-16 20:54:19 +0000679 if (i > 0) {
680 if (uncomp_regex[i-1] == '\n')
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000681 uncomp_regex[i-1] = '\0';
682 else
683 while((i = getchar()) != '\n' && i != EOF);
684 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000685
686 /* Compile the regex and check for errors */
687 xregcomp(pattern, uncomp_regex, 0);
688
689 /* Run the regex on each line of the current file here */
690 for (i = 0; i <= num_flines; i++) {
691 strcpy(current_line, process_regex_on_line(flines[i], pattern));
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000692 flines[i] = bb_xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000693 if (match_found) {
694 match_lines[j] = i;
695 j++;
696 }
697 }
698
699 num_matches = j;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000700 if ((match_lines[0] != -1) && (num_flines > height - 2))
701 buffer_line(match_lines[0]);
702 else
703 buffer_init();
704}
705
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000706static void goto_match(int match)
707{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000708 /* This goes to a specific match - all line positions of matches are
709 stored within the match_lines[] array. */
710 if ((match < num_matches) && (match >= 0)) {
711 buffer_line(match_lines[match]);
712 match_pos = match;
713 }
714}
715
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000716static void search_backwards(void)
717{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000718 int current_linepos = line_pos;
719 int i;
720
721 match_backwards = 1;
722 regex_process();
723
724 for (i = 0; i < num_matches; i++) {
725 if (match_lines[i] > current_linepos) {
726 buffer_line(match_lines[i - num_back_match]);
727 break;
728 }
729 }
730
731 /* Reset variables */
732 match_backwards = 0;
733 num_back_match = 1;
734
735}
736#endif
737
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000738static void number_process(int first_digit)
739{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000740 int i = 1;
741 int num;
742 char num_input[80];
743 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000744 char *endptr;
745
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000746 num_input[0] = first_digit;
747
748 /* Clear the current line, print a prompt, and then print the digit */
749 clear_line();
750 printf(":%c", first_digit);
751
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000752 /* Receive input until a letter is given (max 80 chars)*/
753 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000754 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000755 i++;
756 }
757
758 /* Take the final letter out of the digits string */
759 keypress = num_input[i];
760 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000761 num = strtol(num_input, &endptr, 10);
762 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES)
763 goto END;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000764
765 /* We now know the number and the letter entered, so we process them */
766 switch (keypress) {
767 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
768 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000769 break;
770 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
771 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000772 break;
773 case 'g': case '<': case 'G': case '>':
774 if (num_flines >= height - 2)
775 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000776 break;
777 case 'p': case '%':
778 buffer_line(reverse_percent(num));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000779 break;
780#ifdef CONFIG_FEATURE_LESS_REGEXP
781 case 'n':
782 goto_match(match_pos + num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000783 break;
784 case '/':
785 regex_process();
786 goto_match(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000787 break;
788 case '?':
789 num_back_match = num;
790 search_backwards();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000791 break;
792#endif
793 default:
794 break;
795 }
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000796END:
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000797 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000798}
799
800#ifdef CONFIG_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000801static void flag_change(void)
802{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000803 int keypress;
804
805 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000806 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000807 keypress = tless_getch();
808
809 switch (keypress) {
810 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000811 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000812 break;
813 case 'm':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000814 flags ^= FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000815 break;
816 case 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000817 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000818 break;
819 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000820 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000821 break;
822 default:
823 break;
824 }
825}
826
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000827static void show_flag_status(void)
828{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000829 int keypress;
830 int flag_val;
831
832 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000833 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000834 keypress = tless_getch();
835
836 switch (keypress) {
837 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000838 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000839 break;
840 case 'm':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000841 flag_val = flags & FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000842 break;
843 case '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000844 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000845 break;
846 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000847 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000848 break;
849 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000850 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000851 break;
852 default:
853 flag_val = 0;
854 break;
855 }
856
857 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000858 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 +0000859}
860#endif
861
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000862static void full_repaint(void)
863{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000864 int temp_line_pos = line_pos;
865 data_readlines();
866 buffer_init();
867 buffer_line(temp_line_pos);
868 buffer_print();
869}
870
871
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000872static void save_input_to_file(void)
873{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000874 char current_line[256];
875 int i;
876 FILE *fp;
877
878 clear_line();
879 printf("Log file: ");
880 fgets(current_line, 256, inp);
881 current_line[strlen(current_line) - 1] = '\0';
882 if (strlen(current_line)) {
883 fp = bb_xfopen(current_line, "w");
884 for (i = 0; i < num_flines; i++)
885 fprintf(fp, "%s", flines[i]);
886 fclose(fp);
887 buffer_print();
888 }
889 else
890 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
891}
892
893#ifdef CONFIG_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000894static void add_mark(void)
895{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000896 int letter;
897 int mark_line;
898
899 clear_line();
900 printf("Mark: ");
901 letter = tless_getch();
902
903 if (isalpha(letter)) {
904 mark_line = line_pos;
905
906 /* If we exceed 15 marks, start overwriting previous ones */
907 if (num_marks == 14)
908 num_marks = 0;
909
910 mark_lines[num_marks][0] = letter;
911 mark_lines[num_marks][1] = line_pos;
912 num_marks++;
913 }
914 else {
915 clear_line();
916 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
917 }
918}
919
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000920static void goto_mark(void)
921{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000922 int letter;
923 int i;
924
925 clear_line();
926 printf("Go to mark: ");
927 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000928 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000929
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000930 if (isalpha(letter)) {
931 for (i = 0; i <= num_marks; i++)
932 if (letter == mark_lines[i][0]) {
933 buffer_line(mark_lines[i][1]);
934 break;
935 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000936 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000937 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000938 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000939 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000940 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000941}
942#endif
943
944
945#ifdef CONFIG_FEATURE_LESS_BRACKETS
946
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000947static char opp_bracket(char bracket)
948{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000949 switch (bracket) {
950 case '{': case '[':
951 return bracket + 2;
952 break;
953 case '(':
954 return ')';
955 break;
956 case '}': case ']':
957 return bracket - 2;
958 break;
959 case ')':
960 return '(';
961 break;
962 default:
963 return 0;
964 break;
965 }
966}
967
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000968static void match_right_bracket(char bracket)
969{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000970 int bracket_line = -1;
971 int i;
972
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000973 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000974
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000975 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000976 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000977 else {
978 for (i = line_pos + 1; i < num_flines; i++) {
979 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
980 bracket_line = i;
981 break;
982 }
983 }
984
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000985 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000986 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000987
988 buffer_line(bracket_line - height + 2);
989 buffer_print();
990 }
991}
992
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000993static void match_left_bracket(char bracket)
994{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000995 int bracket_line = -1;
996 int i;
997
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000998 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000999
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001000 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001001 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
1002 printf("%s", flines[line_pos + height]);
1003 sleep(4);
1004 }
1005 else {
1006 for (i = line_pos + height - 2; i >= 0; i--) {
1007 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
1008 bracket_line = i;
1009 break;
1010 }
1011 }
1012
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001013 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001014 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001015
1016 buffer_line(bracket_line);
1017 buffer_print();
1018 }
1019}
1020
1021#endif /* CONFIG_FEATURE_LESS_BRACKETS */
1022
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001023static void keypress_process(int keypress)
1024{
Rob Landley9200e792005-09-15 19:26:59 +00001025 switch (keypress) {
1026 case KEY_DOWN: case 'e': case 'j': case '\015':
1027 buffer_down(1);
1028 buffer_print();
1029 break;
1030 case KEY_UP: case 'y': case 'k':
1031 buffer_up(1);
1032 buffer_print();
1033 break;
1034 case PAGE_DOWN: case ' ': case 'z':
1035 buffer_down(height - 1);
1036 buffer_print();
1037 break;
1038 case PAGE_UP: case 'w': case 'b':
1039 buffer_up(height - 1);
1040 buffer_print();
1041 break;
1042 case 'd':
1043 buffer_down((height - 1) / 2);
1044 buffer_print();
1045 break;
1046 case 'u':
1047 buffer_up((height - 1) / 2);
1048 buffer_print();
1049 break;
1050 case 'g': case 'p': case '<': case '%':
1051 buffer_up(num_flines + 1);
1052 buffer_print();
1053 break;
1054 case 'G': case '>':
1055 buffer_down(num_flines + 1);
1056 buffer_print();
1057 break;
1058 case 'q': case 'Q':
1059 tless_exit(0);
1060 break;
1061#ifdef CONFIG_FEATURE_LESS_MARKS
1062 case 'm':
1063 add_mark();
1064 buffer_print();
1065 break;
1066 case '\'':
1067 goto_mark();
1068 buffer_print();
1069 break;
1070#endif
1071 case 'r':
1072 buffer_print();
1073 break;
1074 case 'R':
1075 full_repaint();
1076 break;
1077 case 's':
1078 if (inp_stdin)
1079 save_input_to_file();
1080 break;
1081 case 'E':
1082 examine_file();
1083 break;
1084#ifdef CONFIG_FEATURE_LESS_FLAGS
1085 case '=':
1086 clear_line();
1087 m_status_print();
1088 break;
1089#endif
1090#ifdef CONFIG_FEATURE_LESS_REGEXP
1091 case '/':
1092 regex_process();
1093 buffer_print();
1094 break;
1095 case 'n':
1096 goto_match(match_pos + 1);
1097 buffer_print();
1098 break;
1099 case 'N':
1100 goto_match(match_pos - 1);
1101 buffer_print();
1102 break;
1103 case '?':
1104 search_backwards();
1105 buffer_print();
1106 break;
1107#endif
1108#ifdef CONFIG_FEATURE_LESS_FLAGCS
1109 case '-':
1110 flag_change();
1111 buffer_print();
1112 break;
1113 case '_':
1114 show_flag_status();
1115 break;
1116#endif
1117#ifdef CONFIG_FEATURE_LESS_BRACKETS
1118 case '{': case '(': case '[':
1119 match_right_bracket(keypress);
1120 break;
1121 case '}': case ')': case ']':
1122 match_left_bracket(keypress);
1123 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001124#endif
1125 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001126 colon_process();
1127 break;
1128 default:
1129 break;
1130 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001131
Rob Landley9200e792005-09-15 19:26:59 +00001132 if (isdigit(keypress))
1133 number_process(keypress);
1134}
1135
Rob Landley9200e792005-09-15 19:26:59 +00001136int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001137
Rob Landley9200e792005-09-15 19:26:59 +00001138 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001139
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001140 flags = bb_getopt_ulflags(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001141
1142 argc -= optind;
1143 argv += optind;
1144 files = argv;
1145 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001146
Rob Landley9200e792005-09-15 19:26:59 +00001147 if (!num_files) {
1148 if (ttyname(STDIN_FILENO) == NULL)
1149 inp_stdin = 1;
1150 else {
1151 bb_error_msg("Missing filename");
1152 bb_show_usage();
1153 }
1154 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001155
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001156 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
Rob Landley9200e792005-09-15 19:26:59 +00001157 tty_width_height();
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001158 data_readlines();
1159 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001160 term_vi = term_orig;
1161 term_vi.c_lflag &= (~ICANON & ~ECHO);
1162 term_vi.c_iflag &= (~IXON & ~ICRNL);
1163 term_vi.c_oflag &= (~ONLCR);
1164 term_vi.c_cc[VMIN] = 1;
1165 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001166 buffer_init();
1167 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001168
Rob Landley9200e792005-09-15 19:26:59 +00001169 while (1) {
1170 keypress = tless_getch();
1171 keypress_process(keypress);
1172 }
1173}