blob: 596490483e419ae5e2b3023183282bf476c3500a [file] [log] [blame]
Rob Landley9200e792005-09-15 19:26:59 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Mini less implementation for busybox
4 *
Rob Landley9200e792005-09-15 19:26:59 +00005 * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
6 *
Mike Frysingerf284c762006-04-16 20:38:26 +00007 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8 */
9
10/*
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000011 * This program needs a lot of development, so consider it in a beta stage
12 * at best.
Rob Landley9200e792005-09-15 19:26:59 +000013 *
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000014 * TODO:
15 * - Add more regular expression support - search modifiers, certain matches, etc.
16 * - Add more complex bracket searching - currently, nested brackets are
17 * not considered.
18 * - Add support for "F" as an input. This causes less to act in
19 * a similar way to tail -f.
20 * - Check for binary files, and prompt the user if a binary file
21 * is detected.
22 * - Allow horizontal scrolling. Currently, lines simply continue onto
23 * the next line, per the terminal's discretion
Rob Landley9200e792005-09-15 19:26:59 +000024 *
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000025 * Notes:
26 * - filename is an array and not a pointer because that avoids all sorts
27 * of complications involving the fact that something that is pointed to
28 * will be changed if the pointer is changed.
29 * - the inp file pointer is used so that keyboard input works after
30 * redirected input has been read from stdin
Rob Landley9200e792005-09-15 19:26:59 +000031*/
32
Bernhard Reutner-Fischerc89982d2006-06-03 19:49:21 +000033#include "busybox.h"
Rob Landley9200e792005-09-15 19:26:59 +000034#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <termios.h>
38#include <unistd.h>
Rob Landley9200e792005-09-15 19:26:59 +000039#include <ctype.h>
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000040
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000041#ifdef CONFIG_FEATURE_LESS_REGEXP
42#include "xregex.h"
43#endif
44
45
Rob Landley9200e792005-09-15 19:26:59 +000046/* These are the escape sequences corresponding to special keys */
47#define REAL_KEY_UP 'A'
48#define REAL_KEY_DOWN 'B'
49#define REAL_KEY_RIGHT 'C'
50#define REAL_KEY_LEFT 'D'
51#define REAL_PAGE_UP '5'
52#define REAL_PAGE_DOWN '6'
53
54/* These are the special codes assigned by this program to the special keys */
55#define PAGE_UP 20
56#define PAGE_DOWN 21
57#define KEY_UP 22
58#define KEY_DOWN 23
59#define KEY_RIGHT 24
60#define KEY_LEFT 25
61
62/* The escape codes for highlighted and normal text */
63#define HIGHLIGHT "\033[7m"
64#define NORMAL "\033[0m"
65
66/* The escape code to clear the screen */
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000067#define CLEAR "\033[H\033[J"
Rob Landley9200e792005-09-15 19:26:59 +000068
69/* Maximum number of lines in a file */
70#define MAXLINES 10000
71
Rob Landley9200e792005-09-15 19:26:59 +000072static int height;
73static int width;
74static char **files;
75static char filename[256];
Rob Landleyd57ae8b2005-09-18 00:58:49 +000076static char **buffer;
77static char **flines;
Rob Landley9200e792005-09-15 19:26:59 +000078static int current_file = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000079static int line_pos;
Rob Landley9200e792005-09-15 19:26:59 +000080static int num_flines;
81static int num_files = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000082static int past_eof;
Rob Landley9200e792005-09-15 19:26:59 +000083
84/* Command line options */
Rob Landleyd57ae8b2005-09-18 00:58:49 +000085static unsigned long flags;
86#define FLAG_E 1
87#define FLAG_M (1<<1)
88#define FLAG_m (1<<2)
89#define FLAG_N (1<<3)
90#define FLAG_TILDE (1<<4)
Rob Landley9200e792005-09-15 19:26:59 +000091
92/* This is needed so that program behaviour changes when input comes from
93 stdin */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000094static int inp_stdin;
Rob Landley9200e792005-09-15 19:26:59 +000095
96#ifdef CONFIG_FEATURE_LESS_MARKS
97static int mark_lines[15][2];
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000098static int num_marks;
Rob Landley9200e792005-09-15 19:26:59 +000099#endif
100
101#ifdef CONFIG_FEATURE_LESS_REGEXP
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000102static int match_found;
Mike Frysingerf054be12006-04-29 04:21:10 +0000103static int *match_lines;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000104static int match_pos;
105static int num_matches;
106static int match_backwards;
Mike Frysingerf054be12006-04-29 04:21:10 +0000107static regex_t old_pattern;
Rob Landley9200e792005-09-15 19:26:59 +0000108#endif
109
110/* Needed termios structures */
111static struct termios term_orig, term_vi;
112
113/* File pointer to get input from */
114static FILE *inp;
115
116/* Reset terminal input to normal */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000117static void set_tty_cooked(void)
118{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000119 fflush(stdout);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000120 tcsetattr(fileno(inp), TCSANOW, &term_orig);
Rob Landley9200e792005-09-15 19:26:59 +0000121}
122
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000123/* Set terminal input to raw mode (taken from vi.c) */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000124static void set_tty_raw(void)
125{
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000126 tcsetattr(fileno(inp), TCSANOW, &term_vi);
Rob Landley9200e792005-09-15 19:26:59 +0000127}
128
129/* Exit the program gracefully */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000130static void tless_exit(int code)
131{
Rob Landley9200e792005-09-15 19:26:59 +0000132 /* TODO: We really should save the terminal state when we start,
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000133 and restore it when we exit. Less does this with the
Rob Landley9200e792005-09-15 19:26:59 +0000134 "ti" and "te" termcap commands; can this be done with
135 only termios.h? */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000136
Rob Landley9200e792005-09-15 19:26:59 +0000137 putchar('\n');
138 exit(code);
139}
140
141/* Grab a character from input without requiring the return key. If the
142 character is ASCII \033, get more characters and assign certain sequences
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000143 special return codes. Note that this function works best with raw input. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000144static int tless_getch(void)
145{
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000146 int input;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000147
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000148 set_tty_raw();
149
150 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000151 /* Detect escape sequences (i.e. arrow keys) and handle
152 them accordingly */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000153
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000154 if (input == '\033' && getc(inp) == '[') {
155 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000156 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000157 if (input == REAL_KEY_UP)
158 return KEY_UP;
159 else if (input == REAL_KEY_DOWN)
160 return KEY_DOWN;
161 else if (input == REAL_KEY_RIGHT)
162 return KEY_RIGHT;
163 else if (input == REAL_KEY_LEFT)
164 return KEY_LEFT;
165 else if (input == REAL_PAGE_UP)
166 return PAGE_UP;
167 else if (input == REAL_PAGE_DOWN)
168 return PAGE_DOWN;
Rob Landley9200e792005-09-15 19:26:59 +0000169 }
170 /* The input is a normal ASCII value */
171 else {
172 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000173 return input;
Rob Landley9200e792005-09-15 19:26:59 +0000174 }
175 return 0;
176}
177
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000178/* Move the cursor to a position (x,y), where (0,0) is the
Rob Landley9200e792005-09-15 19:26:59 +0000179 top-left corner of the console */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000180static void move_cursor(int x, int y)
181{
Rob Landley9200e792005-09-15 19:26:59 +0000182 printf("\033[%i;%iH", x, y);
183}
184
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000185static void clear_line(void)
186{
Rob Landley9200e792005-09-15 19:26:59 +0000187 move_cursor(height, 0);
188 printf("\033[K");
189}
190
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000191/* This adds line numbers to every line, as the -N flag necessitates */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000192static void add_linenumbers(void)
193{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000194 char current_line[256];
195 int i;
196
197 for (i = 0; i <= num_flines; i++) {
198 safe_strncpy(current_line, flines[i], 256);
"Vladimir N. Oleynik"39a841c2005-09-29 16:18:57 +0000199 flines[i] = bb_xasprintf("%5d %s", i + 1, current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000200 }
201}
202
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000203static void data_readlines(void)
204{
Rob Landley9200e792005-09-15 19:26:59 +0000205 int i;
206 char current_line[256];
207 FILE *fp;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000208
Bernhard Reutner-Fischera85a63f2006-05-19 12:22:11 +0000209 fp = (inp_stdin) ? stdin : bb_xfopen(filename, "r");
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000210 flines = NULL;
211 for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) {
Rob Landley9200e792005-09-15 19:26:59 +0000212 strcpy(current_line, "");
213 fgets(current_line, 256, fp);
Mike Frysinger00d10a92006-04-16 20:54:19 +0000214 if (fp != stdin)
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000215 bb_xferror(fp, filename);
216 flines = xrealloc(flines, (i+1) * sizeof(char *));
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000217 flines[i] = bb_xstrdup(current_line);
Rob Landley9200e792005-09-15 19:26:59 +0000218 }
219 num_flines = i - 2;
220
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000221 /* Reset variables for a new file */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000222
Rob Landley9200e792005-09-15 19:26:59 +0000223 line_pos = 0;
224 past_eof = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000225
Rob Landley9200e792005-09-15 19:26:59 +0000226 fclose(fp);
227
Mike Frysinger00d10a92006-04-16 20:54:19 +0000228 if (inp == NULL)
"Vladimir N. Oleynik"8315cd52005-12-15 11:53:22 +0000229 inp = (inp_stdin) ? bb_xfopen(CURRENT_TTY, "r") : stdin;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000230
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000231 if (flags & FLAG_N)
Rob Landley9200e792005-09-15 19:26:59 +0000232 add_linenumbers();
233}
234
Rob Landley9200e792005-09-15 19:26:59 +0000235#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000236
237/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
238 * on my build. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000239static int calc_percent(void)
240{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000241 return ((100 * (line_pos + height - 2) / num_flines) + 1);
242}
243
Rob Landley9200e792005-09-15 19:26:59 +0000244/* Print a status line if -M was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000245static void m_status_print(void)
246{
Rob Landley9200e792005-09-15 19:26:59 +0000247 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000248
Rob Landley9200e792005-09-15 19:26:59 +0000249 if (!past_eof) {
250 if (!line_pos) {
251 if (num_files > 1)
252 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);
253 else {
254 printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
255 }
256 }
257 else {
258 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
259 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000260
Rob Landley9200e792005-09-15 19:26:59 +0000261 if (line_pos == num_flines - height + 2) {
262 printf("(END) %s", NORMAL);
263 if ((num_files > 1) && (current_file != num_files))
264 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
265 }
266 else {
267 percentage = calc_percent();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000268 printf("%i%% %s", percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000269 }
270 }
271 else {
272 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
273 if ((num_files > 1) && (current_file != num_files))
274 printf("- Next: %s", files[current_file]);
275 printf("%s", NORMAL);
276 }
277}
278
279/* Print a status line if -m was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000280static void medium_status_print(void)
281{
Rob Landley9200e792005-09-15 19:26:59 +0000282 int percentage;
283 percentage = calc_percent();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000284
Rob Landley9200e792005-09-15 19:26:59 +0000285 if (!line_pos)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000286 printf("%s%s %i%%%s", HIGHLIGHT, filename, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000287 else if (line_pos == num_flines - height + 2)
288 printf("%s(END)%s", HIGHLIGHT, NORMAL);
289 else
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000290 printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000291}
292#endif
293
294/* Print the status line */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000295static void status_print(void)
296{
Rob Landley9200e792005-09-15 19:26:59 +0000297 /* Change the status if flags have been set */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000298#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000299 if (flags & FLAG_M)
Rob Landley9200e792005-09-15 19:26:59 +0000300 m_status_print();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000301 else if (flags & FLAG_m)
Rob Landley9200e792005-09-15 19:26:59 +0000302 medium_status_print();
303 /* No flags set */
304 else {
305#endif
306 if (!line_pos) {
307 printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
308 if (num_files > 1)
309 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
310 }
311 else if (line_pos == num_flines - height + 2) {
312 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
313 if ((num_files > 1) && (current_file != num_files))
314 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
315 }
316 else {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000317 putchar(':');
Rob Landley9200e792005-09-15 19:26:59 +0000318 }
319#ifdef CONFIG_FEATURE_LESS_FLAGS
320 }
321#endif
322}
323
324/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000325static void buffer_print(void)
326{
Rob Landley9200e792005-09-15 19:26:59 +0000327 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000328
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000329 printf("%s", CLEAR);
Rob Landley9200e792005-09-15 19:26:59 +0000330 if (num_flines >= height - 2) {
Rob Landley9200e792005-09-15 19:26:59 +0000331 for (i = 0; i < height - 1; i++)
332 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000333 }
334 else {
Rob Landley9200e792005-09-15 19:26:59 +0000335 for (i = 1; i < (height - 1 - num_flines); i++)
336 putchar('\n');
337 for (i = 0; i < height - 1; i++)
338 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000339 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000340
341 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000342}
343
344/* Initialise the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000345static void buffer_init(void)
346{
Rob Landley9200e792005-09-15 19:26:59 +0000347 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000348
Mike Frysinger00d10a92006-04-16 20:54:19 +0000349 if (buffer == NULL) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000350 /* malloc the number of lines needed for the buffer */
351 buffer = xrealloc(buffer, height * sizeof(char *));
352 } else {
353 for (i = 0; i < (height - 1); i++)
354 free(buffer[i]);
355 }
356
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000357 /* Fill the buffer until the end of the file or the
Rob Landley9200e792005-09-15 19:26:59 +0000358 end of the buffer is reached */
359 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000360 buffer[i] = bb_xstrdup(flines[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000361 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000362
Rob Landley9200e792005-09-15 19:26:59 +0000363 /* If the buffer still isn't full, fill it with blank lines */
364 for (; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000365 buffer[i] = bb_xstrdup("");
Rob Landley9200e792005-09-15 19:26:59 +0000366 }
367}
368
369/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000370static void buffer_down(int nlines)
371{
Rob Landley9200e792005-09-15 19:26:59 +0000372 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000373
Rob Landley9200e792005-09-15 19:26:59 +0000374 if (!past_eof) {
375 if (line_pos + (height - 3) + nlines < num_flines) {
376 line_pos += nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000377 for (i = 0; i < (height - 1); i++) {
378 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000379 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000380 }
Rob Landley9200e792005-09-15 19:26:59 +0000381 }
382 else {
383 /* As the number of lines requested was too large, we just move
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000384 to the end of the file */
385 while (line_pos + (height - 3) + 1 < num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000386 line_pos += 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000387 for (i = 0; i < (height - 1); i++) {
388 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000389 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000390 }
Rob Landley9200e792005-09-15 19:26:59 +0000391 }
392 }
393
394 /* We exit if the -E flag has been set */
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000395 if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
Rob Landley9200e792005-09-15 19:26:59 +0000396 tless_exit(0);
397 }
398}
399
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000400static void buffer_up(int nlines)
401{
Rob Landley9200e792005-09-15 19:26:59 +0000402 int i;
403 int tilde_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000404
Rob Landley9200e792005-09-15 19:26:59 +0000405 if (!past_eof) {
406 if (line_pos - nlines >= 0) {
407 line_pos -= nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000408 for (i = 0; i < (height - 1); i++) {
409 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000410 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000411 }
Rob Landley9200e792005-09-15 19:26:59 +0000412 }
413 else {
414 /* As the requested number of lines to move was too large, we
415 move one line up at a time until we can't. */
416 while (line_pos != 0) {
417 line_pos -= 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000418 for (i = 0; i < (height - 1); i++) {
419 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000420 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000421 }
Rob Landley9200e792005-09-15 19:26:59 +0000422 }
423 }
424 }
425 else {
426 /* Work out where the tildes start */
427 tilde_line = num_flines - line_pos + 3;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000428
Rob Landley9200e792005-09-15 19:26:59 +0000429 line_pos -= nlines;
430 /* Going backwards nlines lines has taken us to a point where
431 nothing is past the EOF, so we revert to normal. */
432 if (line_pos < num_flines - height + 3) {
433 past_eof = 0;
434 buffer_up(nlines);
435 }
436 else {
437 /* We only move part of the buffer, as the rest
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000438 is past the EOF */
Rob Landley9200e792005-09-15 19:26:59 +0000439 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000440 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000441 if (i < tilde_line - nlines + 1)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000442 buffer[i] = bb_xstrdup(flines[line_pos + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000443 else {
444 if (line_pos >= num_flines - height + 2)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000445 buffer[i] = bb_xstrdup("~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000446 }
447 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000448 }
Rob Landley9200e792005-09-15 19:26:59 +0000449 }
450}
451
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000452static void buffer_line(int linenum)
453{
Rob Landley9200e792005-09-15 19:26:59 +0000454 int i;
Rob Landley9200e792005-09-15 19:26:59 +0000455 past_eof = 0;
456
Mike Frysingerf054be12006-04-29 04:21:10 +0000457 if (linenum < 0 || linenum > num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000458 clear_line();
Mike Frysingerf054be12006-04-29 04:21:10 +0000459 printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum + 1, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000460 }
461 else if (linenum < (num_flines - height - 2)) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000462 for (i = 0; i < (height - 1); i++) {
463 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000464 buffer[i] = bb_xstrdup(flines[linenum + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000465 }
Rob Landley9200e792005-09-15 19:26:59 +0000466 line_pos = linenum;
Mike Frysingerf054be12006-04-29 04:21:10 +0000467 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000468 }
469 else {
470 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000471 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000472 if (linenum + i < num_flines + 2)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000473 buffer[i] = bb_xstrdup(flines[linenum + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000474 else
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000475 buffer[i] = bb_xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000476 }
477 line_pos = linenum;
478 /* Set past_eof so buffer_down and buffer_up act differently */
479 past_eof = 1;
Mike Frysingerf054be12006-04-29 04:21:10 +0000480 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000481 }
482}
483
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000484/* Reinitialise everything for a new file - free the memory and start over */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000485static void reinitialise(void)
486{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000487 int i;
488
489 for (i = 0; i <= num_flines; i++)
490 free(flines[i]);
491 free(flines);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000492
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000493 data_readlines();
494 buffer_init();
495 buffer_print();
496}
497
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000498static void examine_file(void)
499{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000500 int newline_offset;
501
502 clear_line();
503 printf("Examine: ");
504 fgets(filename, 256, inp);
505
506 /* As fgets adds a newline to the end of an input string, we
507 need to remove it */
508 newline_offset = strlen(filename) - 1;
509 filename[newline_offset] = '\0';
510
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000511 files[num_files] = bb_xstrdup(filename);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000512 current_file = num_files + 1;
513 num_files++;
514
515 inp_stdin = 0;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000516 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000517}
518
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000519/* This function changes the file currently being paged. direction can be one of the following:
520 * -1: go back one file
521 * 0: go to the first file
522 * 1: go forward one file
523*/
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000524static void change_file(int direction)
525{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000526 if (current_file != ((direction > 0) ? num_files : 1)) {
527 current_file = direction ? current_file + direction : 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000528 strcpy(filename, files[current_file - 1]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000529 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000530 }
531 else {
532 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000533 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000534 }
535}
536
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000537static void remove_current_file(void)
538{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000539 int i;
540
541 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000542 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000543 for (i = 3; i <= num_files; i++)
544 files[i - 2] = files[i - 1];
545 num_files--;
546 buffer_print();
547 }
548 else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000549 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000550 for (i = 2; i <= num_files; i++)
551 files[i - 2] = files[i - 1];
552 num_files--;
553 current_file--;
554 buffer_print();
555 }
556}
557
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000558static void colon_process(void)
559{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000560 int keypress;
561
562 /* Clear the current line and print a prompt */
563 clear_line();
564 printf(" :");
565
566 keypress = tless_getch();
567 switch (keypress) {
568 case 'd':
569 remove_current_file();
570 break;
571 case 'e':
572 examine_file();
573 break;
574#ifdef CONFIG_FEATURE_LESS_FLAGS
575 case 'f':
576 clear_line();
577 m_status_print();
578 break;
579#endif
580 case 'n':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000581 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000582 break;
583 case 'p':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000584 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000585 break;
586 case 'q':
587 tless_exit(0);
588 break;
589 case 'x':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000590 change_file(0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000591 break;
592 default:
593 break;
594 }
595}
596
597#ifdef CONFIG_FEATURE_LESS_REGEXP
598/* The below two regular expression handler functions NEED development. */
599
600/* Get a regular expression from the user, and then go through the current
601 file line by line, running a processing regex function on each one. */
602
Mike Frysingerf054be12006-04-29 04:21:10 +0000603static char *process_regex_on_line(char *line, regex_t *pattern, int action)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000604{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000605 /* This function takes the regex and applies it to the line.
606 Each part of the line that matches has the HIGHLIGHT
607 and NORMAL escape sequences placed around it by
Mike Frysingerf054be12006-04-29 04:21:10 +0000608 insert_highlights if action = 1, or has the escape sequences
609 removed if action = 0, and then the line is returned. */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000610 int match_status;
Mike Frysingerbf2d9902006-04-16 21:30:47 +0000611 char *line2 = (char *) xmalloc((sizeof(char) * (strlen(line) + 1)) + 64);
Mike Frysingerf054be12006-04-29 04:21:10 +0000612 char *growline = "";
"Vladimir N. Oleynik"8d3c40d2005-09-16 13:16:01 +0000613 regmatch_t match_structs;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000614
Mike Frysingerf054be12006-04-29 04:21:10 +0000615 line2 = bb_xstrdup(line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000616
617 match_found = 0;
618 match_status = regexec(pattern, line2, 1, &match_structs, 0);
Mike Frysingerf054be12006-04-29 04:21:10 +0000619
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000620 while (match_status == 0) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000621 if (match_found == 0)
622 match_found = 1;
Mike Frysingerf054be12006-04-29 04:21:10 +0000623
624 if (action) {
625 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);
626 }
627 else {
628 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);
629 }
630
631 line2 += match_structs.rm_eo;
632 match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000633 }
Mike Frysingerf054be12006-04-29 04:21:10 +0000634
635 growline = bb_xasprintf("%s%s", growline, line2);
636
637 return (match_found ? growline : line);
638
639 free(growline);
640 free(line2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000641}
642
Rob Landleya2e98042006-04-18 01:53:41 +0000643static void goto_match(int match)
644{
645 /* This goes to a specific match - all line positions of matches are
646 stored within the match_lines[] array. */
647 if ((match < num_matches) && (match >= 0)) {
648 buffer_line(match_lines[match]);
649 match_pos = match;
650 }
651}
652
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000653static void regex_process(void)
654{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000655 char uncomp_regex[100];
Mike Frysingerf054be12006-04-29 04:21:10 +0000656 char *current_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000657 int i;
658 int j = 0;
Mike Frysinger20c22e02006-04-16 21:41:00 +0000659 regex_t pattern;
Rob Landleya2e98042006-04-18 01:53:41 +0000660 /* Get the uncompiled regular expression from the user */
661 clear_line();
662 putchar((match_backwards) ? '?' : '/');
663 uncomp_regex[0] = 0;
664 fgets(uncomp_regex, sizeof(uncomp_regex), inp);
665
666 if (strlen(uncomp_regex) == 1) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000667 if (num_matches)
668 goto_match(match_backwards ? match_pos - 1 : match_pos + 1);
669 else
670 buffer_print();
Rob Landleya2e98042006-04-18 01:53:41 +0000671 return;
672 }
Rob Landleya2e98042006-04-18 01:53:41 +0000673 uncomp_regex[strlen(uncomp_regex) - 1] = '\0';
674
675 /* Compile the regex and check for errors */
676 xregcomp(&pattern, uncomp_regex, 0);
677
Mike Frysingerf054be12006-04-29 04:21:10 +0000678 if (num_matches) {
679 /* Get rid of all the highlights we added previously */
680 for (i = 0; i <= num_flines; i++) {
681 current_line = process_regex_on_line(flines[i], &old_pattern, 0);
682 flines[i] = bb_xstrdup(current_line);
683 }
684 }
685 old_pattern = pattern;
686
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000687 /* Reset variables */
Mike Frysingerf054be12006-04-29 04:21:10 +0000688 match_lines = xrealloc(match_lines, sizeof(int));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000689 match_lines[0] = -1;
690 match_pos = 0;
691 num_matches = 0;
692 match_found = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000693 /* Run the regex on each line of the current file here */
694 for (i = 0; i <= num_flines; i++) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000695 current_line = process_regex_on_line(flines[i], &pattern, 1);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000696 flines[i] = bb_xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000697 if (match_found) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000698 match_lines = xrealloc(match_lines, (j + 1) * sizeof(int));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000699 match_lines[j] = i;
700 j++;
701 }
702 }
Mike Frysingerf054be12006-04-29 04:21:10 +0000703
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000704 num_matches = j;
Rob Landleya2e98042006-04-18 01:53:41 +0000705 if ((match_lines[0] != -1) && (num_flines > height - 2)) {
706 if (match_backwards) {
707 for (i = 0; i < num_matches; i++) {
708 if (match_lines[i] > line_pos) {
709 match_pos = i - 1;
710 buffer_line(match_lines[match_pos]);
711 break;
712 }
713 }
714 }
715 else
716 buffer_line(match_lines[0]);
717 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000718 else
719 buffer_init();
720}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000721#endif
722
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000723static void number_process(int first_digit)
724{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000725 int i = 1;
726 int num;
727 char num_input[80];
728 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000729 char *endptr;
730
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000731 num_input[0] = first_digit;
732
733 /* Clear the current line, print a prompt, and then print the digit */
734 clear_line();
735 printf(":%c", first_digit);
736
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000737 /* Receive input until a letter is given (max 80 chars)*/
738 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000739 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000740 i++;
741 }
742
743 /* Take the final letter out of the digits string */
744 keypress = num_input[i];
745 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000746 num = strtol(num_input, &endptr, 10);
Rob Landleya2e98042006-04-18 01:53:41 +0000747 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES) {
748 buffer_print();
749 return;
750 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000751
752 /* We now know the number and the letter entered, so we process them */
753 switch (keypress) {
754 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
755 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000756 break;
757 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
758 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000759 break;
760 case 'g': case '<': case 'G': case '>':
761 if (num_flines >= height - 2)
762 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000763 break;
764 case 'p': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +0000765 buffer_line(((num / 100) * num_flines) - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000766 break;
767#ifdef CONFIG_FEATURE_LESS_REGEXP
768 case 'n':
Rob Landleya2e98042006-04-18 01:53:41 +0000769 goto_match(match_pos + num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000770 break;
771 case '/':
Rob Landleya2e98042006-04-18 01:53:41 +0000772 match_backwards = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000773 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000774 break;
775 case '?':
Rob Landleya2e98042006-04-18 01:53:41 +0000776 match_backwards = 1;
777 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000778 break;
779#endif
780 default:
781 break;
782 }
783}
784
785#ifdef CONFIG_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000786static void flag_change(void)
787{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000788 int keypress;
789
790 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000791 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000792 keypress = tless_getch();
793
794 switch (keypress) {
795 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000796 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000797 break;
798 case 'm':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000799 flags ^= FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000800 break;
801 case 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000802 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000803 break;
804 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000805 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000806 break;
807 default:
808 break;
809 }
810}
811
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000812static void show_flag_status(void)
813{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000814 int keypress;
815 int flag_val;
816
817 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000818 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000819 keypress = tless_getch();
820
821 switch (keypress) {
822 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000823 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000824 break;
825 case 'm':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000826 flag_val = flags & FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000827 break;
828 case '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000829 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000830 break;
831 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000832 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000833 break;
834 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000835 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000836 break;
837 default:
838 flag_val = 0;
839 break;
840 }
841
842 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000843 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 +0000844}
845#endif
846
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000847static void full_repaint(void)
848{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000849 int temp_line_pos = line_pos;
850 data_readlines();
851 buffer_init();
852 buffer_line(temp_line_pos);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000853}
854
855
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000856static void save_input_to_file(void)
857{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000858 char current_line[256];
859 int i;
860 FILE *fp;
861
862 clear_line();
863 printf("Log file: ");
864 fgets(current_line, 256, inp);
865 current_line[strlen(current_line) - 1] = '\0';
Mike Frysingerf054be12006-04-29 04:21:10 +0000866 if (strlen(current_line) > 1) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000867 fp = bb_xfopen(current_line, "w");
868 for (i = 0; i < num_flines; i++)
869 fprintf(fp, "%s", flines[i]);
870 fclose(fp);
871 buffer_print();
872 }
873 else
874 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
875}
876
877#ifdef CONFIG_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000878static void add_mark(void)
879{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000880 int letter;
881 int mark_line;
882
883 clear_line();
884 printf("Mark: ");
885 letter = tless_getch();
886
887 if (isalpha(letter)) {
888 mark_line = line_pos;
889
890 /* If we exceed 15 marks, start overwriting previous ones */
891 if (num_marks == 14)
892 num_marks = 0;
893
894 mark_lines[num_marks][0] = letter;
895 mark_lines[num_marks][1] = line_pos;
896 num_marks++;
897 }
898 else {
899 clear_line();
900 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
901 }
902}
903
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000904static void goto_mark(void)
905{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000906 int letter;
907 int i;
908
909 clear_line();
910 printf("Go to mark: ");
911 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000912 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000913
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000914 if (isalpha(letter)) {
915 for (i = 0; i <= num_marks; i++)
916 if (letter == mark_lines[i][0]) {
917 buffer_line(mark_lines[i][1]);
918 break;
919 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000920 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000921 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000922 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000923 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000924 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000925}
926#endif
927
928
929#ifdef CONFIG_FEATURE_LESS_BRACKETS
930
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000931static char opp_bracket(char bracket)
932{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000933 switch (bracket) {
934 case '{': case '[':
935 return bracket + 2;
936 break;
937 case '(':
938 return ')';
939 break;
940 case '}': case ']':
941 return bracket - 2;
942 break;
943 case ')':
944 return '(';
945 break;
946 default:
947 return 0;
948 break;
949 }
950}
951
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000952static void match_right_bracket(char bracket)
953{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000954 int bracket_line = -1;
955 int i;
956
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000957 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000958
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000959 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000960 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000961 else {
962 for (i = line_pos + 1; i < num_flines; i++) {
963 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
964 bracket_line = i;
965 break;
966 }
967 }
968
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000969 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000970 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000971
972 buffer_line(bracket_line - height + 2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000973 }
974}
975
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000976static void match_left_bracket(char bracket)
977{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000978 int bracket_line = -1;
979 int i;
980
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000981 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000982
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000983 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000984 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
985 printf("%s", flines[line_pos + height]);
986 sleep(4);
987 }
988 else {
989 for (i = line_pos + height - 2; i >= 0; i--) {
990 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
991 bracket_line = i;
992 break;
993 }
994 }
995
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000996 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000997 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000998
999 buffer_line(bracket_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001000 }
1001}
1002
1003#endif /* CONFIG_FEATURE_LESS_BRACKETS */
1004
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001005static void keypress_process(int keypress)
1006{
Rob Landley9200e792005-09-15 19:26:59 +00001007 switch (keypress) {
1008 case KEY_DOWN: case 'e': case 'j': case '\015':
1009 buffer_down(1);
1010 buffer_print();
1011 break;
1012 case KEY_UP: case 'y': case 'k':
1013 buffer_up(1);
1014 buffer_print();
1015 break;
1016 case PAGE_DOWN: case ' ': case 'z':
1017 buffer_down(height - 1);
1018 buffer_print();
1019 break;
1020 case PAGE_UP: case 'w': case 'b':
1021 buffer_up(height - 1);
1022 buffer_print();
1023 break;
1024 case 'd':
1025 buffer_down((height - 1) / 2);
1026 buffer_print();
1027 break;
1028 case 'u':
1029 buffer_up((height - 1) / 2);
1030 buffer_print();
1031 break;
1032 case 'g': case 'p': case '<': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +00001033 buffer_line(0);
Rob Landley9200e792005-09-15 19:26:59 +00001034 break;
1035 case 'G': case '>':
Mike Frysingerf054be12006-04-29 04:21:10 +00001036 buffer_line(num_flines - height + 2);
Rob Landley9200e792005-09-15 19:26:59 +00001037 break;
1038 case 'q': case 'Q':
1039 tless_exit(0);
1040 break;
1041#ifdef CONFIG_FEATURE_LESS_MARKS
1042 case 'm':
1043 add_mark();
1044 buffer_print();
1045 break;
1046 case '\'':
1047 goto_mark();
1048 buffer_print();
1049 break;
1050#endif
1051 case 'r':
1052 buffer_print();
1053 break;
1054 case 'R':
1055 full_repaint();
1056 break;
1057 case 's':
1058 if (inp_stdin)
1059 save_input_to_file();
1060 break;
1061 case 'E':
1062 examine_file();
1063 break;
1064#ifdef CONFIG_FEATURE_LESS_FLAGS
1065 case '=':
1066 clear_line();
1067 m_status_print();
1068 break;
1069#endif
1070#ifdef CONFIG_FEATURE_LESS_REGEXP
1071 case '/':
Rob Landleya2e98042006-04-18 01:53:41 +00001072 match_backwards = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001073 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001074 break;
1075 case 'n':
1076 goto_match(match_pos + 1);
Rob Landley9200e792005-09-15 19:26:59 +00001077 break;
1078 case 'N':
1079 goto_match(match_pos - 1);
Rob Landley9200e792005-09-15 19:26:59 +00001080 break;
1081 case '?':
Rob Landleya2e98042006-04-18 01:53:41 +00001082 match_backwards = 1;
1083 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001084 break;
1085#endif
1086#ifdef CONFIG_FEATURE_LESS_FLAGCS
1087 case '-':
1088 flag_change();
1089 buffer_print();
1090 break;
1091 case '_':
1092 show_flag_status();
1093 break;
1094#endif
1095#ifdef CONFIG_FEATURE_LESS_BRACKETS
1096 case '{': case '(': case '[':
1097 match_right_bracket(keypress);
1098 break;
1099 case '}': case ')': case ']':
1100 match_left_bracket(keypress);
1101 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001102#endif
1103 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001104 colon_process();
1105 break;
1106 default:
1107 break;
1108 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001109
Rob Landley9200e792005-09-15 19:26:59 +00001110 if (isdigit(keypress))
1111 number_process(keypress);
1112}
1113
Rob Landley9200e792005-09-15 19:26:59 +00001114int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001115
Rob Landley9200e792005-09-15 19:26:59 +00001116 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001117
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001118 flags = bb_getopt_ulflags(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001119
1120 argc -= optind;
1121 argv += optind;
1122 files = argv;
1123 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001124
Rob Landley9200e792005-09-15 19:26:59 +00001125 if (!num_files) {
1126 if (ttyname(STDIN_FILENO) == NULL)
1127 inp_stdin = 1;
1128 else {
1129 bb_error_msg("Missing filename");
1130 bb_show_usage();
1131 }
1132 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001133
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001134 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
Rob Landley69d863b2006-05-25 21:13:30 +00001135 get_terminal_width_height(0, &width, &height);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001136 data_readlines();
1137 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001138 term_vi = term_orig;
1139 term_vi.c_lflag &= (~ICANON & ~ECHO);
1140 term_vi.c_iflag &= (~IXON & ~ICRNL);
1141 term_vi.c_oflag &= (~ONLCR);
1142 term_vi.c_cc[VMIN] = 1;
1143 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001144 buffer_init();
1145 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001146
Rob Landley9200e792005-09-15 19:26:59 +00001147 while (1) {
1148 keypress = tless_getch();
1149 keypress_process(keypress);
1150 }
1151}