blob: 84e4b612dc49141eb837a5a36b6ead210bb54206 [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;
Mike Frysingerf054be12006-04-29 04:21:10 +0000107static int *match_lines;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000108static int match_pos;
109static int num_matches;
110static int match_backwards;
Mike Frysingerf054be12006-04-29 04:21:10 +0000111static regex_t old_pattern;
Rob Landley9200e792005-09-15 19:26:59 +0000112#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#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000240
241/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
242 * on my build. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000243static int calc_percent(void)
244{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000245 return ((100 * (line_pos + height - 2) / num_flines) + 1);
246}
247
Rob Landley9200e792005-09-15 19:26:59 +0000248/* Print a status line if -M was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000249static void m_status_print(void)
250{
Rob Landley9200e792005-09-15 19:26:59 +0000251 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000252
Rob Landley9200e792005-09-15 19:26:59 +0000253 if (!past_eof) {
254 if (!line_pos) {
255 if (num_files > 1)
256 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);
257 else {
258 printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
259 }
260 }
261 else {
262 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
263 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000264
Rob Landley9200e792005-09-15 19:26:59 +0000265 if (line_pos == num_flines - height + 2) {
266 printf("(END) %s", NORMAL);
267 if ((num_files > 1) && (current_file != num_files))
268 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
269 }
270 else {
271 percentage = calc_percent();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000272 printf("%i%% %s", percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000273 }
274 }
275 else {
276 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
277 if ((num_files > 1) && (current_file != num_files))
278 printf("- Next: %s", files[current_file]);
279 printf("%s", NORMAL);
280 }
281}
282
283/* Print a status line if -m was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000284static void medium_status_print(void)
285{
Rob Landley9200e792005-09-15 19:26:59 +0000286 int percentage;
287 percentage = calc_percent();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000288
Rob Landley9200e792005-09-15 19:26:59 +0000289 if (!line_pos)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000290 printf("%s%s %i%%%s", HIGHLIGHT, filename, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000291 else if (line_pos == num_flines - height + 2)
292 printf("%s(END)%s", HIGHLIGHT, NORMAL);
293 else
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000294 printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000295}
296#endif
297
298/* Print the status line */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000299static void status_print(void)
300{
Rob Landley9200e792005-09-15 19:26:59 +0000301 /* Change the status if flags have been set */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000302#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000303 if (flags & FLAG_M)
Rob Landley9200e792005-09-15 19:26:59 +0000304 m_status_print();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000305 else if (flags & FLAG_m)
Rob Landley9200e792005-09-15 19:26:59 +0000306 medium_status_print();
307 /* No flags set */
308 else {
309#endif
310 if (!line_pos) {
311 printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
312 if (num_files > 1)
313 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
314 }
315 else if (line_pos == num_flines - height + 2) {
316 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
317 if ((num_files > 1) && (current_file != num_files))
318 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
319 }
320 else {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000321 putchar(':');
Rob Landley9200e792005-09-15 19:26:59 +0000322 }
323#ifdef CONFIG_FEATURE_LESS_FLAGS
324 }
325#endif
326}
327
328/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000329static void buffer_print(void)
330{
Rob Landley9200e792005-09-15 19:26:59 +0000331 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000332
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000333 printf("%s", CLEAR);
Rob Landley9200e792005-09-15 19:26:59 +0000334 if (num_flines >= height - 2) {
Rob Landley9200e792005-09-15 19:26:59 +0000335 for (i = 0; i < height - 1; i++)
336 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000337 }
338 else {
Rob Landley9200e792005-09-15 19:26:59 +0000339 for (i = 1; i < (height - 1 - num_flines); i++)
340 putchar('\n');
341 for (i = 0; i < height - 1; i++)
342 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000343 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000344
345 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000346}
347
348/* Initialise the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000349static void buffer_init(void)
350{
Rob Landley9200e792005-09-15 19:26:59 +0000351 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000352
Mike Frysinger00d10a92006-04-16 20:54:19 +0000353 if (buffer == NULL) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000354 /* malloc the number of lines needed for the buffer */
355 buffer = xrealloc(buffer, height * sizeof(char *));
356 } else {
357 for (i = 0; i < (height - 1); i++)
358 free(buffer[i]);
359 }
360
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000361 /* Fill the buffer until the end of the file or the
Rob Landley9200e792005-09-15 19:26:59 +0000362 end of the buffer is reached */
363 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000364 buffer[i] = bb_xstrdup(flines[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000365 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000366
Rob Landley9200e792005-09-15 19:26:59 +0000367 /* If the buffer still isn't full, fill it with blank lines */
368 for (; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000369 buffer[i] = bb_xstrdup("");
Rob Landley9200e792005-09-15 19:26:59 +0000370 }
371}
372
373/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000374static void buffer_down(int nlines)
375{
Rob Landley9200e792005-09-15 19:26:59 +0000376 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000377
Rob Landley9200e792005-09-15 19:26:59 +0000378 if (!past_eof) {
379 if (line_pos + (height - 3) + nlines < num_flines) {
380 line_pos += nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000381 for (i = 0; i < (height - 1); i++) {
382 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000383 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000384 }
Rob Landley9200e792005-09-15 19:26:59 +0000385 }
386 else {
387 /* As the number of lines requested was too large, we just move
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000388 to the end of the file */
389 while (line_pos + (height - 3) + 1 < num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000390 line_pos += 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000391 for (i = 0; i < (height - 1); i++) {
392 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000393 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000394 }
Rob Landley9200e792005-09-15 19:26:59 +0000395 }
396 }
397
398 /* We exit if the -E flag has been set */
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000399 if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
Rob Landley9200e792005-09-15 19:26:59 +0000400 tless_exit(0);
401 }
402}
403
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000404static void buffer_up(int nlines)
405{
Rob Landley9200e792005-09-15 19:26:59 +0000406 int i;
407 int tilde_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000408
Rob Landley9200e792005-09-15 19:26:59 +0000409 if (!past_eof) {
410 if (line_pos - nlines >= 0) {
411 line_pos -= nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000412 for (i = 0; i < (height - 1); i++) {
413 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000414 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000415 }
Rob Landley9200e792005-09-15 19:26:59 +0000416 }
417 else {
418 /* As the requested number of lines to move was too large, we
419 move one line up at a time until we can't. */
420 while (line_pos != 0) {
421 line_pos -= 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000422 for (i = 0; i < (height - 1); i++) {
423 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000424 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000425 }
Rob Landley9200e792005-09-15 19:26:59 +0000426 }
427 }
428 }
429 else {
430 /* Work out where the tildes start */
431 tilde_line = num_flines - line_pos + 3;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000432
Rob Landley9200e792005-09-15 19:26:59 +0000433 line_pos -= nlines;
434 /* Going backwards nlines lines has taken us to a point where
435 nothing is past the EOF, so we revert to normal. */
436 if (line_pos < num_flines - height + 3) {
437 past_eof = 0;
438 buffer_up(nlines);
439 }
440 else {
441 /* We only move part of the buffer, as the rest
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000442 is past the EOF */
Rob Landley9200e792005-09-15 19:26:59 +0000443 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000444 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000445 if (i < tilde_line - nlines + 1)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000446 buffer[i] = bb_xstrdup(flines[line_pos + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000447 else {
448 if (line_pos >= num_flines - height + 2)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000449 buffer[i] = bb_xstrdup("~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000450 }
451 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000452 }
Rob Landley9200e792005-09-15 19:26:59 +0000453 }
454}
455
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000456static void buffer_line(int linenum)
457{
Rob Landley9200e792005-09-15 19:26:59 +0000458 int i;
Rob Landley9200e792005-09-15 19:26:59 +0000459 past_eof = 0;
460
Mike Frysingerf054be12006-04-29 04:21:10 +0000461 if (linenum < 0 || linenum > num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000462 clear_line();
Mike Frysingerf054be12006-04-29 04:21:10 +0000463 printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum + 1, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000464 }
465 else if (linenum < (num_flines - height - 2)) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000466 for (i = 0; i < (height - 1); i++) {
467 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000468 buffer[i] = bb_xstrdup(flines[linenum + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000469 }
Rob Landley9200e792005-09-15 19:26:59 +0000470 line_pos = linenum;
Mike Frysingerf054be12006-04-29 04:21:10 +0000471 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000472 }
473 else {
474 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000475 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000476 if (linenum + i < num_flines + 2)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000477 buffer[i] = bb_xstrdup(flines[linenum + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000478 else
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000479 buffer[i] = bb_xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000480 }
481 line_pos = linenum;
482 /* Set past_eof so buffer_down and buffer_up act differently */
483 past_eof = 1;
Mike Frysingerf054be12006-04-29 04:21:10 +0000484 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000485 }
486}
487
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000488/* Reinitialise everything for a new file - free the memory and start over */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000489static void reinitialise(void)
490{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000491 int i;
492
493 for (i = 0; i <= num_flines; i++)
494 free(flines[i]);
495 free(flines);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000496
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000497 data_readlines();
498 buffer_init();
499 buffer_print();
500}
501
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000502static void examine_file(void)
503{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000504 int newline_offset;
505
506 clear_line();
507 printf("Examine: ");
508 fgets(filename, 256, inp);
509
510 /* As fgets adds a newline to the end of an input string, we
511 need to remove it */
512 newline_offset = strlen(filename) - 1;
513 filename[newline_offset] = '\0';
514
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000515 files[num_files] = bb_xstrdup(filename);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000516 current_file = num_files + 1;
517 num_files++;
518
519 inp_stdin = 0;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000520 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000521}
522
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000523/* This function changes the file currently being paged. direction can be one of the following:
524 * -1: go back one file
525 * 0: go to the first file
526 * 1: go forward one file
527*/
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000528static void change_file(int direction)
529{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000530 if (current_file != ((direction > 0) ? num_files : 1)) {
531 current_file = direction ? current_file + direction : 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000532 strcpy(filename, files[current_file - 1]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000533 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000534 }
535 else {
536 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000537 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000538 }
539}
540
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000541static void remove_current_file(void)
542{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000543 int i;
544
545 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000546 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000547 for (i = 3; i <= num_files; i++)
548 files[i - 2] = files[i - 1];
549 num_files--;
550 buffer_print();
551 }
552 else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000553 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000554 for (i = 2; i <= num_files; i++)
555 files[i - 2] = files[i - 1];
556 num_files--;
557 current_file--;
558 buffer_print();
559 }
560}
561
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000562static void colon_process(void)
563{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000564 int keypress;
565
566 /* Clear the current line and print a prompt */
567 clear_line();
568 printf(" :");
569
570 keypress = tless_getch();
571 switch (keypress) {
572 case 'd':
573 remove_current_file();
574 break;
575 case 'e':
576 examine_file();
577 break;
578#ifdef CONFIG_FEATURE_LESS_FLAGS
579 case 'f':
580 clear_line();
581 m_status_print();
582 break;
583#endif
584 case 'n':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000585 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000586 break;
587 case 'p':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000588 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000589 break;
590 case 'q':
591 tless_exit(0);
592 break;
593 case 'x':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000594 change_file(0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000595 break;
596 default:
597 break;
598 }
599}
600
601#ifdef CONFIG_FEATURE_LESS_REGEXP
602/* The below two regular expression handler functions NEED development. */
603
604/* Get a regular expression from the user, and then go through the current
605 file line by line, running a processing regex function on each one. */
606
Mike Frysingerf054be12006-04-29 04:21:10 +0000607static char *process_regex_on_line(char *line, regex_t *pattern, int action)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000608{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000609 /* This function takes the regex and applies it to the line.
610 Each part of the line that matches has the HIGHLIGHT
611 and NORMAL escape sequences placed around it by
Mike Frysingerf054be12006-04-29 04:21:10 +0000612 insert_highlights if action = 1, or has the escape sequences
613 removed if action = 0, and then the line is returned. */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000614 int match_status;
Mike Frysingerbf2d9902006-04-16 21:30:47 +0000615 char *line2 = (char *) xmalloc((sizeof(char) * (strlen(line) + 1)) + 64);
Mike Frysingerf054be12006-04-29 04:21:10 +0000616 char *growline = "";
"Vladimir N. Oleynik"8d3c40d2005-09-16 13:16:01 +0000617 regmatch_t match_structs;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000618
Mike Frysingerf054be12006-04-29 04:21:10 +0000619 line2 = bb_xstrdup(line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000620
621 match_found = 0;
622 match_status = regexec(pattern, line2, 1, &match_structs, 0);
Mike Frysingerf054be12006-04-29 04:21:10 +0000623
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000624 while (match_status == 0) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000625 if (match_found == 0)
626 match_found = 1;
Mike Frysingerf054be12006-04-29 04:21:10 +0000627
628 if (action) {
629 growline = bb_xasprintf("%s%.*s%s%.*s%s", growline, match_structs.rm_so, line2, HIGHLIGHT, match_structs.rm_eo - match_structs.rm_so, line2 + match_structs.rm_so, NORMAL);
630 }
631 else {
632 growline = bb_xasprintf("%s%.*s%.*s", growline, match_structs.rm_so - 4, line2, match_structs.rm_eo - match_structs.rm_so, line2 + match_structs.rm_so);
633 }
634
635 line2 += match_structs.rm_eo;
636 match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000637 }
Mike Frysingerf054be12006-04-29 04:21:10 +0000638
639 growline = bb_xasprintf("%s%s", growline, line2);
640
641 return (match_found ? growline : line);
642
643 free(growline);
644 free(line2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000645}
646
Rob Landleya2e98042006-04-18 01:53:41 +0000647static void goto_match(int match)
648{
649 /* This goes to a specific match - all line positions of matches are
650 stored within the match_lines[] array. */
651 if ((match < num_matches) && (match >= 0)) {
652 buffer_line(match_lines[match]);
653 match_pos = match;
654 }
655}
656
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000657static void regex_process(void)
658{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000659 char uncomp_regex[100];
Mike Frysingerf054be12006-04-29 04:21:10 +0000660 char *current_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000661 int i;
662 int j = 0;
Mike Frysinger20c22e02006-04-16 21:41:00 +0000663 regex_t pattern;
Rob Landleya2e98042006-04-18 01:53:41 +0000664 /* Get the uncompiled regular expression from the user */
665 clear_line();
666 putchar((match_backwards) ? '?' : '/');
667 uncomp_regex[0] = 0;
668 fgets(uncomp_regex, sizeof(uncomp_regex), inp);
669
670 if (strlen(uncomp_regex) == 1) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000671 if (num_matches)
672 goto_match(match_backwards ? match_pos - 1 : match_pos + 1);
673 else
674 buffer_print();
Rob Landleya2e98042006-04-18 01:53:41 +0000675 return;
676 }
Rob Landleya2e98042006-04-18 01:53:41 +0000677 uncomp_regex[strlen(uncomp_regex) - 1] = '\0';
678
679 /* Compile the regex and check for errors */
680 xregcomp(&pattern, uncomp_regex, 0);
681
Mike Frysingerf054be12006-04-29 04:21:10 +0000682 if (num_matches) {
683 /* Get rid of all the highlights we added previously */
684 for (i = 0; i <= num_flines; i++) {
685 current_line = process_regex_on_line(flines[i], &old_pattern, 0);
686 flines[i] = bb_xstrdup(current_line);
687 }
688 }
689 old_pattern = pattern;
690
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000691 /* Reset variables */
Mike Frysingerf054be12006-04-29 04:21:10 +0000692 match_lines = xrealloc(match_lines, sizeof(int));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000693 match_lines[0] = -1;
694 match_pos = 0;
695 num_matches = 0;
696 match_found = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000697 /* Run the regex on each line of the current file here */
698 for (i = 0; i <= num_flines; i++) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000699 current_line = process_regex_on_line(flines[i], &pattern, 1);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000700 flines[i] = bb_xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000701 if (match_found) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000702 match_lines = xrealloc(match_lines, (j + 1) * sizeof(int));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000703 match_lines[j] = i;
704 j++;
705 }
706 }
Mike Frysingerf054be12006-04-29 04:21:10 +0000707
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000708 num_matches = j;
Rob Landleya2e98042006-04-18 01:53:41 +0000709 if ((match_lines[0] != -1) && (num_flines > height - 2)) {
710 if (match_backwards) {
711 for (i = 0; i < num_matches; i++) {
712 if (match_lines[i] > line_pos) {
713 match_pos = i - 1;
714 buffer_line(match_lines[match_pos]);
715 break;
716 }
717 }
718 }
719 else
720 buffer_line(match_lines[0]);
721 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000722 else
723 buffer_init();
724}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000725#endif
726
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000727static void number_process(int first_digit)
728{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000729 int i = 1;
730 int num;
731 char num_input[80];
732 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000733 char *endptr;
734
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000735 num_input[0] = first_digit;
736
737 /* Clear the current line, print a prompt, and then print the digit */
738 clear_line();
739 printf(":%c", first_digit);
740
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000741 /* Receive input until a letter is given (max 80 chars)*/
742 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000743 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000744 i++;
745 }
746
747 /* Take the final letter out of the digits string */
748 keypress = num_input[i];
749 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000750 num = strtol(num_input, &endptr, 10);
Rob Landleya2e98042006-04-18 01:53:41 +0000751 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES) {
752 buffer_print();
753 return;
754 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000755
756 /* We now know the number and the letter entered, so we process them */
757 switch (keypress) {
758 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
759 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000760 break;
761 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
762 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000763 break;
764 case 'g': case '<': case 'G': case '>':
765 if (num_flines >= height - 2)
766 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000767 break;
768 case 'p': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +0000769 buffer_line(((num / 100) * num_flines) - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000770 break;
771#ifdef CONFIG_FEATURE_LESS_REGEXP
772 case 'n':
Rob Landleya2e98042006-04-18 01:53:41 +0000773 goto_match(match_pos + num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000774 break;
775 case '/':
Rob Landleya2e98042006-04-18 01:53:41 +0000776 match_backwards = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000777 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000778 break;
779 case '?':
Rob Landleya2e98042006-04-18 01:53:41 +0000780 match_backwards = 1;
781 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000782 break;
783#endif
784 default:
785 break;
786 }
787}
788
789#ifdef CONFIG_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000790static void flag_change(void)
791{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000792 int keypress;
793
794 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000795 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000796 keypress = tless_getch();
797
798 switch (keypress) {
799 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000800 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000801 break;
802 case 'm':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000803 flags ^= FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000804 break;
805 case 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000806 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000807 break;
808 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000809 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000810 break;
811 default:
812 break;
813 }
814}
815
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000816static void show_flag_status(void)
817{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000818 int keypress;
819 int flag_val;
820
821 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000822 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000823 keypress = tless_getch();
824
825 switch (keypress) {
826 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000827 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000828 break;
829 case 'm':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000830 flag_val = flags & FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000831 break;
832 case '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000833 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000834 break;
835 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000836 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000837 break;
838 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000839 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000840 break;
841 default:
842 flag_val = 0;
843 break;
844 }
845
846 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000847 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 +0000848}
849#endif
850
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000851static void full_repaint(void)
852{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000853 int temp_line_pos = line_pos;
854 data_readlines();
855 buffer_init();
856 buffer_line(temp_line_pos);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000857}
858
859
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000860static void save_input_to_file(void)
861{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000862 char current_line[256];
863 int i;
864 FILE *fp;
865
866 clear_line();
867 printf("Log file: ");
868 fgets(current_line, 256, inp);
869 current_line[strlen(current_line) - 1] = '\0';
Mike Frysingerf054be12006-04-29 04:21:10 +0000870 if (strlen(current_line) > 1) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000871 fp = bb_xfopen(current_line, "w");
872 for (i = 0; i < num_flines; i++)
873 fprintf(fp, "%s", flines[i]);
874 fclose(fp);
875 buffer_print();
876 }
877 else
878 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
879}
880
881#ifdef CONFIG_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000882static void add_mark(void)
883{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000884 int letter;
885 int mark_line;
886
887 clear_line();
888 printf("Mark: ");
889 letter = tless_getch();
890
891 if (isalpha(letter)) {
892 mark_line = line_pos;
893
894 /* If we exceed 15 marks, start overwriting previous ones */
895 if (num_marks == 14)
896 num_marks = 0;
897
898 mark_lines[num_marks][0] = letter;
899 mark_lines[num_marks][1] = line_pos;
900 num_marks++;
901 }
902 else {
903 clear_line();
904 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
905 }
906}
907
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000908static void goto_mark(void)
909{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000910 int letter;
911 int i;
912
913 clear_line();
914 printf("Go to mark: ");
915 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000916 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000917
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000918 if (isalpha(letter)) {
919 for (i = 0; i <= num_marks; i++)
920 if (letter == mark_lines[i][0]) {
921 buffer_line(mark_lines[i][1]);
922 break;
923 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000924 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000925 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000926 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000927 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000928 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000929}
930#endif
931
932
933#ifdef CONFIG_FEATURE_LESS_BRACKETS
934
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000935static char opp_bracket(char bracket)
936{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000937 switch (bracket) {
938 case '{': case '[':
939 return bracket + 2;
940 break;
941 case '(':
942 return ')';
943 break;
944 case '}': case ']':
945 return bracket - 2;
946 break;
947 case ')':
948 return '(';
949 break;
950 default:
951 return 0;
952 break;
953 }
954}
955
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000956static void match_right_bracket(char bracket)
957{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000958 int bracket_line = -1;
959 int i;
960
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000961 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000962
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000963 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000964 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000965 else {
966 for (i = line_pos + 1; i < num_flines; i++) {
967 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
968 bracket_line = i;
969 break;
970 }
971 }
972
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000973 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000974 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000975
976 buffer_line(bracket_line - height + 2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000977 }
978}
979
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000980static void match_left_bracket(char bracket)
981{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000982 int bracket_line = -1;
983 int i;
984
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000985 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000986
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000987 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000988 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
989 printf("%s", flines[line_pos + height]);
990 sleep(4);
991 }
992 else {
993 for (i = line_pos + height - 2; i >= 0; i--) {
994 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
995 bracket_line = i;
996 break;
997 }
998 }
999
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001000 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001001 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001002
1003 buffer_line(bracket_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001004 }
1005}
1006
1007#endif /* CONFIG_FEATURE_LESS_BRACKETS */
1008
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001009static void keypress_process(int keypress)
1010{
Rob Landley9200e792005-09-15 19:26:59 +00001011 switch (keypress) {
1012 case KEY_DOWN: case 'e': case 'j': case '\015':
1013 buffer_down(1);
1014 buffer_print();
1015 break;
1016 case KEY_UP: case 'y': case 'k':
1017 buffer_up(1);
1018 buffer_print();
1019 break;
1020 case PAGE_DOWN: case ' ': case 'z':
1021 buffer_down(height - 1);
1022 buffer_print();
1023 break;
1024 case PAGE_UP: case 'w': case 'b':
1025 buffer_up(height - 1);
1026 buffer_print();
1027 break;
1028 case 'd':
1029 buffer_down((height - 1) / 2);
1030 buffer_print();
1031 break;
1032 case 'u':
1033 buffer_up((height - 1) / 2);
1034 buffer_print();
1035 break;
1036 case 'g': case 'p': case '<': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +00001037 buffer_line(0);
Rob Landley9200e792005-09-15 19:26:59 +00001038 break;
1039 case 'G': case '>':
Mike Frysingerf054be12006-04-29 04:21:10 +00001040 buffer_line(num_flines - height + 2);
Rob Landley9200e792005-09-15 19:26:59 +00001041 break;
1042 case 'q': case 'Q':
1043 tless_exit(0);
1044 break;
1045#ifdef CONFIG_FEATURE_LESS_MARKS
1046 case 'm':
1047 add_mark();
1048 buffer_print();
1049 break;
1050 case '\'':
1051 goto_mark();
1052 buffer_print();
1053 break;
1054#endif
1055 case 'r':
1056 buffer_print();
1057 break;
1058 case 'R':
1059 full_repaint();
1060 break;
1061 case 's':
1062 if (inp_stdin)
1063 save_input_to_file();
1064 break;
1065 case 'E':
1066 examine_file();
1067 break;
1068#ifdef CONFIG_FEATURE_LESS_FLAGS
1069 case '=':
1070 clear_line();
1071 m_status_print();
1072 break;
1073#endif
1074#ifdef CONFIG_FEATURE_LESS_REGEXP
1075 case '/':
Rob Landleya2e98042006-04-18 01:53:41 +00001076 match_backwards = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001077 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001078 break;
1079 case 'n':
1080 goto_match(match_pos + 1);
Rob Landley9200e792005-09-15 19:26:59 +00001081 break;
1082 case 'N':
1083 goto_match(match_pos - 1);
Rob Landley9200e792005-09-15 19:26:59 +00001084 break;
1085 case '?':
Rob Landleya2e98042006-04-18 01:53:41 +00001086 match_backwards = 1;
1087 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001088 break;
1089#endif
1090#ifdef CONFIG_FEATURE_LESS_FLAGCS
1091 case '-':
1092 flag_change();
1093 buffer_print();
1094 break;
1095 case '_':
1096 show_flag_status();
1097 break;
1098#endif
1099#ifdef CONFIG_FEATURE_LESS_BRACKETS
1100 case '{': case '(': case '[':
1101 match_right_bracket(keypress);
1102 break;
1103 case '}': case ')': case ']':
1104 match_left_bracket(keypress);
1105 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001106#endif
1107 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001108 colon_process();
1109 break;
1110 default:
1111 break;
1112 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001113
Rob Landley9200e792005-09-15 19:26:59 +00001114 if (isdigit(keypress))
1115 number_process(keypress);
1116}
1117
Rob Landley9200e792005-09-15 19:26:59 +00001118int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001119
Rob Landley9200e792005-09-15 19:26:59 +00001120 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001121
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001122 flags = bb_getopt_ulflags(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001123
1124 argc -= optind;
1125 argv += optind;
1126 files = argv;
1127 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001128
Rob Landley9200e792005-09-15 19:26:59 +00001129 if (!num_files) {
1130 if (ttyname(STDIN_FILENO) == NULL)
1131 inp_stdin = 1;
1132 else {
1133 bb_error_msg("Missing filename");
1134 bb_show_usage();
1135 }
1136 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001137
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001138 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
Rob Landley9200e792005-09-15 19:26:59 +00001139 tty_width_height();
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001140 data_readlines();
1141 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001142 term_vi = term_orig;
1143 term_vi.c_lflag &= (~ICANON & ~ECHO);
1144 term_vi.c_iflag &= (~IXON & ~ICRNL);
1145 term_vi.c_oflag &= (~ONLCR);
1146 term_vi.c_cc[VMIN] = 1;
1147 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001148 buffer_init();
1149 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001150
Rob Landley9200e792005-09-15 19:26:59 +00001151 while (1) {
1152 keypress = tless_getch();
1153 keypress_process(keypress);
1154 }
1155}