blob: eee960821e0a496c99312854ea5e7185fee8db52 [file] [log] [blame]
Rob Landley9200e792005-09-15 19:26:59 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Mini less implementation for busybox
4 *
Rob Landley9200e792005-09-15 19:26:59 +00005 * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
6 *
Mike Frysingerf284c762006-04-16 20:38:26 +00007 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8 */
9
10/*
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000011 * This program needs a lot of development, so consider it in a beta stage
12 * at best.
Rob Landley9200e792005-09-15 19:26:59 +000013 *
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000014 * TODO:
15 * - Add more regular expression support - search modifiers, certain matches, etc.
16 * - Add more complex bracket searching - currently, nested brackets are
17 * not considered.
18 * - Add support for "F" as an input. This causes less to act in
19 * a similar way to tail -f.
20 * - Check for binary files, and prompt the user if a binary file
21 * is detected.
22 * - Allow horizontal scrolling. Currently, lines simply continue onto
23 * the next line, per the terminal's discretion
Rob Landley9200e792005-09-15 19:26:59 +000024 *
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000025 * Notes:
26 * - filename is an array and not a pointer because that avoids all sorts
27 * of complications involving the fact that something that is pointed to
28 * will be changed if the pointer is changed.
29 * - the inp file pointer is used so that keyboard input works after
30 * redirected input has been read from stdin
Rob Landley9200e792005-09-15 19:26:59 +000031*/
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <termios.h>
37#include <unistd.h>
Rob Landley9200e792005-09-15 19:26:59 +000038#include <ctype.h>
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000039
Rob Landley9200e792005-09-15 19:26:59 +000040#include "busybox.h"
41
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000042#ifdef CONFIG_FEATURE_LESS_REGEXP
43#include "xregex.h"
44#endif
45
46
Rob Landley9200e792005-09-15 19:26:59 +000047/* These are the escape sequences corresponding to special keys */
48#define REAL_KEY_UP 'A'
49#define REAL_KEY_DOWN 'B'
50#define REAL_KEY_RIGHT 'C'
51#define REAL_KEY_LEFT 'D'
52#define REAL_PAGE_UP '5'
53#define REAL_PAGE_DOWN '6'
54
55/* These are the special codes assigned by this program to the special keys */
56#define PAGE_UP 20
57#define PAGE_DOWN 21
58#define KEY_UP 22
59#define KEY_DOWN 23
60#define KEY_RIGHT 24
61#define KEY_LEFT 25
62
63/* The escape codes for highlighted and normal text */
64#define HIGHLIGHT "\033[7m"
65#define NORMAL "\033[0m"
66
67/* The escape code to clear the screen */
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000068#define CLEAR "\033[H\033[J"
Rob Landley9200e792005-09-15 19:26:59 +000069
70/* Maximum number of lines in a file */
71#define MAXLINES 10000
72
73/* Get height and width of terminal */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000074#define tty_width_height() get_terminal_width_height(0, &width, &height)
Rob Landley9200e792005-09-15 19:26:59 +000075
76static int height;
77static int width;
78static char **files;
79static char filename[256];
Rob Landleyd57ae8b2005-09-18 00:58:49 +000080static char **buffer;
81static char **flines;
Rob Landley9200e792005-09-15 19:26:59 +000082static int current_file = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000083static int line_pos;
Rob Landley9200e792005-09-15 19:26:59 +000084static int num_flines;
85static int num_files = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000086static int past_eof;
Rob Landley9200e792005-09-15 19:26:59 +000087
88/* Command line options */
Rob Landleyd57ae8b2005-09-18 00:58:49 +000089static unsigned long flags;
90#define FLAG_E 1
91#define FLAG_M (1<<1)
92#define FLAG_m (1<<2)
93#define FLAG_N (1<<3)
94#define FLAG_TILDE (1<<4)
Rob Landley9200e792005-09-15 19:26:59 +000095
96/* This is needed so that program behaviour changes when input comes from
97 stdin */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000098static int inp_stdin;
Rob Landley9200e792005-09-15 19:26:59 +000099
100#ifdef CONFIG_FEATURE_LESS_MARKS
101static int mark_lines[15][2];
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000102static int num_marks;
Rob Landley9200e792005-09-15 19:26:59 +0000103#endif
104
105#ifdef CONFIG_FEATURE_LESS_REGEXP
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000106static int match_found;
Rob Landley9200e792005-09-15 19:26:59 +0000107static int match_lines[100];
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000108static int match_pos;
109static int num_matches;
110static int match_backwards;
Rob Landley9200e792005-09-15 19:26:59 +0000111#endif
112
113/* Needed termios structures */
114static struct termios term_orig, term_vi;
115
116/* File pointer to get input from */
117static FILE *inp;
118
119/* Reset terminal input to normal */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000120static void set_tty_cooked(void)
121{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000122 fflush(stdout);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000123 tcsetattr(fileno(inp), TCSANOW, &term_orig);
Rob Landley9200e792005-09-15 19:26:59 +0000124}
125
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000126/* Set terminal input to raw mode (taken from vi.c) */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000127static void set_tty_raw(void)
128{
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000129 tcsetattr(fileno(inp), TCSANOW, &term_vi);
Rob Landley9200e792005-09-15 19:26:59 +0000130}
131
132/* Exit the program gracefully */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000133static void tless_exit(int code)
134{
Rob Landley9200e792005-09-15 19:26:59 +0000135 /* TODO: We really should save the terminal state when we start,
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000136 and restore it when we exit. Less does this with the
Rob Landley9200e792005-09-15 19:26:59 +0000137 "ti" and "te" termcap commands; can this be done with
138 only termios.h? */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000139
Rob Landley9200e792005-09-15 19:26:59 +0000140 putchar('\n');
141 exit(code);
142}
143
144/* Grab a character from input without requiring the return key. If the
145 character is ASCII \033, get more characters and assign certain sequences
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000146 special return codes. Note that this function works best with raw input. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000147static int tless_getch(void)
148{
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000149 int input;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000150
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000151 set_tty_raw();
152
153 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000154 /* Detect escape sequences (i.e. arrow keys) and handle
155 them accordingly */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000156
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000157 if (input == '\033' && getc(inp) == '[') {
158 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000159 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000160 if (input == REAL_KEY_UP)
161 return KEY_UP;
162 else if (input == REAL_KEY_DOWN)
163 return KEY_DOWN;
164 else if (input == REAL_KEY_RIGHT)
165 return KEY_RIGHT;
166 else if (input == REAL_KEY_LEFT)
167 return KEY_LEFT;
168 else if (input == REAL_PAGE_UP)
169 return PAGE_UP;
170 else if (input == REAL_PAGE_DOWN)
171 return PAGE_DOWN;
Rob Landley9200e792005-09-15 19:26:59 +0000172 }
173 /* The input is a normal ASCII value */
174 else {
175 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000176 return input;
Rob Landley9200e792005-09-15 19:26:59 +0000177 }
178 return 0;
179}
180
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000181/* Move the cursor to a position (x,y), where (0,0) is the
Rob Landley9200e792005-09-15 19:26:59 +0000182 top-left corner of the console */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000183static void move_cursor(int x, int y)
184{
Rob Landley9200e792005-09-15 19:26:59 +0000185 printf("\033[%i;%iH", x, y);
186}
187
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000188static void clear_line(void)
189{
Rob Landley9200e792005-09-15 19:26:59 +0000190 move_cursor(height, 0);
191 printf("\033[K");
192}
193
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000194/* This adds line numbers to every line, as the -N flag necessitates */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000195static void add_linenumbers(void)
196{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000197 char current_line[256];
198 int i;
199
200 for (i = 0; i <= num_flines; i++) {
201 safe_strncpy(current_line, flines[i], 256);
"Vladimir N. Oleynik"39a841c2005-09-29 16:18:57 +0000202 flines[i] = bb_xasprintf("%5d %s", i + 1, current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000203 }
204}
205
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000206static void data_readlines(void)
207{
Rob Landley9200e792005-09-15 19:26:59 +0000208 int i;
209 char current_line[256];
210 FILE *fp;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000211
Rob Landley9200e792005-09-15 19:26:59 +0000212 fp = (inp_stdin) ? stdin : bb_xfopen(filename, "rt");
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000213 flines = NULL;
214 for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) {
Rob Landley9200e792005-09-15 19:26:59 +0000215 strcpy(current_line, "");
216 fgets(current_line, 256, fp);
Mike Frysinger00d10a92006-04-16 20:54:19 +0000217 if (fp != stdin)
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000218 bb_xferror(fp, filename);
219 flines = xrealloc(flines, (i+1) * sizeof(char *));
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000220 flines[i] = bb_xstrdup(current_line);
Rob Landley9200e792005-09-15 19:26:59 +0000221 }
222 num_flines = i - 2;
223
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000224 /* Reset variables for a new file */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000225
Rob Landley9200e792005-09-15 19:26:59 +0000226 line_pos = 0;
227 past_eof = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000228
Rob Landley9200e792005-09-15 19:26:59 +0000229 fclose(fp);
230
Mike Frysinger00d10a92006-04-16 20:54:19 +0000231 if (inp == NULL)
"Vladimir N. Oleynik"8315cd52005-12-15 11:53:22 +0000232 inp = (inp_stdin) ? bb_xfopen(CURRENT_TTY, "r") : stdin;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000233
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000234 if (flags & FLAG_N)
Rob Landley9200e792005-09-15 19:26:59 +0000235 add_linenumbers();
236}
237
Rob Landley9200e792005-09-15 19:26:59 +0000238/* Turn a percentage into a line number */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000239static int reverse_percent(int percentage)
240{
Rob Landley9200e792005-09-15 19:26:59 +0000241 double linenum = percentage;
242 linenum = ((linenum / 100) * num_flines) - 1;
243 return(linenum);
244}
245
246#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000247
248/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
249 * on my build. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000250static int calc_percent(void)
251{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000252 return ((100 * (line_pos + height - 2) / num_flines) + 1);
253}
254
Rob Landley9200e792005-09-15 19:26:59 +0000255/* Print a status line if -M was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000256static void m_status_print(void)
257{
Rob Landley9200e792005-09-15 19:26:59 +0000258 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000259
Rob Landley9200e792005-09-15 19:26:59 +0000260 if (!past_eof) {
261 if (!line_pos) {
262 if (num_files > 1)
263 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);
264 else {
265 printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
266 }
267 }
268 else {
269 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
270 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000271
Rob Landley9200e792005-09-15 19:26:59 +0000272 if (line_pos == num_flines - height + 2) {
273 printf("(END) %s", NORMAL);
274 if ((num_files > 1) && (current_file != num_files))
275 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
276 }
277 else {
278 percentage = calc_percent();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000279 printf("%i%% %s", percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000280 }
281 }
282 else {
283 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
284 if ((num_files > 1) && (current_file != num_files))
285 printf("- Next: %s", files[current_file]);
286 printf("%s", NORMAL);
287 }
288}
289
290/* Print a status line if -m was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000291static void medium_status_print(void)
292{
Rob Landley9200e792005-09-15 19:26:59 +0000293 int percentage;
294 percentage = calc_percent();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000295
Rob Landley9200e792005-09-15 19:26:59 +0000296 if (!line_pos)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000297 printf("%s%s %i%%%s", HIGHLIGHT, filename, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000298 else if (line_pos == num_flines - height + 2)
299 printf("%s(END)%s", HIGHLIGHT, NORMAL);
300 else
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000301 printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000302}
303#endif
304
305/* Print the status line */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000306static void status_print(void)
307{
Rob Landley9200e792005-09-15 19:26:59 +0000308 /* Change the status if flags have been set */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000309#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000310 if (flags & FLAG_M)
Rob Landley9200e792005-09-15 19:26:59 +0000311 m_status_print();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000312 else if (flags & FLAG_m)
Rob Landley9200e792005-09-15 19:26:59 +0000313 medium_status_print();
314 /* No flags set */
315 else {
316#endif
317 if (!line_pos) {
318 printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
319 if (num_files > 1)
320 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
321 }
322 else if (line_pos == num_flines - height + 2) {
323 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
324 if ((num_files > 1) && (current_file != num_files))
325 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
326 }
327 else {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000328 putchar(':');
Rob Landley9200e792005-09-15 19:26:59 +0000329 }
330#ifdef CONFIG_FEATURE_LESS_FLAGS
331 }
332#endif
333}
334
335/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000336static void buffer_print(void)
337{
Rob Landley9200e792005-09-15 19:26:59 +0000338 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000339
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000340 printf("%s", CLEAR);
Rob Landley9200e792005-09-15 19:26:59 +0000341 if (num_flines >= height - 2) {
Rob Landley9200e792005-09-15 19:26:59 +0000342 for (i = 0; i < height - 1; i++)
343 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000344 }
345 else {
Rob Landley9200e792005-09-15 19:26:59 +0000346 for (i = 1; i < (height - 1 - num_flines); i++)
347 putchar('\n');
348 for (i = 0; i < height - 1; i++)
349 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000350 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000351
352 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000353}
354
355/* Initialise the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000356static void buffer_init(void)
357{
Rob Landley9200e792005-09-15 19:26:59 +0000358 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000359
Mike Frysinger00d10a92006-04-16 20:54:19 +0000360 if (buffer == NULL) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000361 /* malloc the number of lines needed for the buffer */
362 buffer = xrealloc(buffer, height * sizeof(char *));
363 } else {
364 for (i = 0; i < (height - 1); i++)
365 free(buffer[i]);
366 }
367
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000368 /* Fill the buffer until the end of the file or the
Rob Landley9200e792005-09-15 19:26:59 +0000369 end of the buffer is reached */
370 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000371 buffer[i] = bb_xstrdup(flines[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000372 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000373
Rob Landley9200e792005-09-15 19:26:59 +0000374 /* If the buffer still isn't full, fill it with blank lines */
375 for (; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000376 buffer[i] = bb_xstrdup("");
Rob Landley9200e792005-09-15 19:26:59 +0000377 }
378}
379
380/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000381static void buffer_down(int nlines)
382{
Rob Landley9200e792005-09-15 19:26:59 +0000383 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000384
Rob Landley9200e792005-09-15 19:26:59 +0000385 if (!past_eof) {
386 if (line_pos + (height - 3) + nlines < num_flines) {
387 line_pos += nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000388 for (i = 0; i < (height - 1); i++) {
389 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000390 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000391 }
Rob Landley9200e792005-09-15 19:26:59 +0000392 }
393 else {
394 /* As the number of lines requested was too large, we just move
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000395 to the end of the file */
396 while (line_pos + (height - 3) + 1 < num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000397 line_pos += 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000398 for (i = 0; i < (height - 1); i++) {
399 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000400 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000401 }
Rob Landley9200e792005-09-15 19:26:59 +0000402 }
403 }
404
405 /* We exit if the -E flag has been set */
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000406 if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
Rob Landley9200e792005-09-15 19:26:59 +0000407 tless_exit(0);
408 }
409}
410
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000411static void buffer_up(int nlines)
412{
Rob Landley9200e792005-09-15 19:26:59 +0000413 int i;
414 int tilde_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000415
Rob Landley9200e792005-09-15 19:26:59 +0000416 if (!past_eof) {
417 if (line_pos - nlines >= 0) {
418 line_pos -= nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000419 for (i = 0; i < (height - 1); i++) {
420 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000421 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000422 }
Rob Landley9200e792005-09-15 19:26:59 +0000423 }
424 else {
425 /* As the requested number of lines to move was too large, we
426 move one line up at a time until we can't. */
427 while (line_pos != 0) {
428 line_pos -= 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000429 for (i = 0; i < (height - 1); i++) {
430 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000431 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000432 }
Rob Landley9200e792005-09-15 19:26:59 +0000433 }
434 }
435 }
436 else {
437 /* Work out where the tildes start */
438 tilde_line = num_flines - line_pos + 3;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000439
Rob Landley9200e792005-09-15 19:26:59 +0000440 line_pos -= nlines;
441 /* Going backwards nlines lines has taken us to a point where
442 nothing is past the EOF, so we revert to normal. */
443 if (line_pos < num_flines - height + 3) {
444 past_eof = 0;
445 buffer_up(nlines);
446 }
447 else {
448 /* We only move part of the buffer, as the rest
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000449 is past the EOF */
Rob Landley9200e792005-09-15 19:26:59 +0000450 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000451 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000452 if (i < tilde_line - nlines + 1)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000453 buffer[i] = bb_xstrdup(flines[line_pos + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000454 else {
455 if (line_pos >= num_flines - height + 2)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000456 buffer[i] = bb_xstrdup("~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000457 }
458 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000459 }
Rob Landley9200e792005-09-15 19:26:59 +0000460 }
461}
462
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000463static void buffer_line(int linenum)
464{
Rob Landley9200e792005-09-15 19:26:59 +0000465 int i;
466
467 past_eof = 0;
468
469 if (linenum < 1 || linenum > num_flines) {
470 clear_line();
471 printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum, NORMAL);
472 }
473 else if (linenum < (num_flines - height - 2)) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000474 for (i = 0; i < (height - 1); i++) {
475 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000476 buffer[i] = bb_xstrdup(flines[linenum + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000477 }
Rob Landley9200e792005-09-15 19:26:59 +0000478 line_pos = linenum;
479 }
480 else {
481 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000482 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000483 if (linenum + i < num_flines + 2)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000484 buffer[i] = bb_xstrdup(flines[linenum + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000485 else
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000486 buffer[i] = bb_xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000487 }
488 line_pos = linenum;
489 /* Set past_eof so buffer_down and buffer_up act differently */
490 past_eof = 1;
491 }
492}
493
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000494/* Reinitialise everything for a new file - free the memory and start over */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000495static void reinitialise(void)
496{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000497 int i;
498
499 for (i = 0; i <= num_flines; i++)
500 free(flines[i]);
501 free(flines);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000502
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000503 data_readlines();
504 buffer_init();
505 buffer_print();
506}
507
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000508static void examine_file(void)
509{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000510 int newline_offset;
511
512 clear_line();
513 printf("Examine: ");
514 fgets(filename, 256, inp);
515
516 /* As fgets adds a newline to the end of an input string, we
517 need to remove it */
518 newline_offset = strlen(filename) - 1;
519 filename[newline_offset] = '\0';
520
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000521 files[num_files] = bb_xstrdup(filename);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000522 current_file = num_files + 1;
523 num_files++;
524
525 inp_stdin = 0;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000526 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000527}
528
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000529/* This function changes the file currently being paged. direction can be one of the following:
530 * -1: go back one file
531 * 0: go to the first file
532 * 1: go forward one file
533*/
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000534static void change_file(int direction)
535{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000536 if (current_file != ((direction > 0) ? num_files : 1)) {
537 current_file = direction ? current_file + direction : 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000538 strcpy(filename, files[current_file - 1]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000539 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000540 }
541 else {
542 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000543 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000544 }
545}
546
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000547static void remove_current_file(void)
548{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000549 int i;
550
551 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000552 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000553 for (i = 3; i <= num_files; i++)
554 files[i - 2] = files[i - 1];
555 num_files--;
556 buffer_print();
557 }
558 else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000559 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000560 for (i = 2; i <= num_files; i++)
561 files[i - 2] = files[i - 1];
562 num_files--;
563 current_file--;
564 buffer_print();
565 }
566}
567
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000568static void colon_process(void)
569{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000570 int keypress;
571
572 /* Clear the current line and print a prompt */
573 clear_line();
574 printf(" :");
575
576 keypress = tless_getch();
577 switch (keypress) {
578 case 'd':
579 remove_current_file();
580 break;
581 case 'e':
582 examine_file();
583 break;
584#ifdef CONFIG_FEATURE_LESS_FLAGS
585 case 'f':
586 clear_line();
587 m_status_print();
588 break;
589#endif
590 case 'n':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000591 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000592 break;
593 case 'p':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000594 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000595 break;
596 case 'q':
597 tless_exit(0);
598 break;
599 case 'x':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000600 change_file(0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000601 break;
602 default:
603 break;
604 }
605}
606
607#ifdef CONFIG_FEATURE_LESS_REGEXP
608/* The below two regular expression handler functions NEED development. */
609
610/* Get a regular expression from the user, and then go through the current
611 file line by line, running a processing regex function on each one. */
612
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000613static char *insert_highlights(char *line, int start, int end)
614{
"Vladimir N. Oleynik"39a841c2005-09-29 16:18:57 +0000615 return bb_xasprintf("%.*s%s%.*s%s%s", start, line, HIGHLIGHT,
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000616 end - start, line + start, NORMAL, line + end);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000617}
618
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000619static char *process_regex_on_line(char *line, regex_t *pattern)
620{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000621 /* This function takes the regex and applies it to the line.
622 Each part of the line that matches has the HIGHLIGHT
623 and NORMAL escape sequences placed around it by
624 insert_highlights, and then the line is returned. */
625
626 int match_status;
Mike Frysingerbf2d9902006-04-16 21:30:47 +0000627 char *line2 = (char *) xmalloc((sizeof(char) * (strlen(line) + 1)) + 64);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000628 char sub_line[256];
629 int prev_eo = 0;
"Vladimir N. Oleynik"8d3c40d2005-09-16 13:16:01 +0000630 regmatch_t match_structs;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000631
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000632 strcpy(line2, line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000633
634 match_found = 0;
635 match_status = regexec(pattern, line2, 1, &match_structs, 0);
636
637 while (match_status == 0) {
638
Mike Frysingerbf2d9902006-04-16 21:30:47 +0000639 memset(sub_line, 0, sizeof(sub_line));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000640
641 if (match_found == 0)
642 match_found = 1;
643
644 line2 = insert_highlights(line2, match_structs.rm_so + prev_eo, match_structs.rm_eo + prev_eo);
"Vladimir N. Oleynik"73ffd762006-02-01 12:56:19 +0000645 if ((size_t)match_structs.rm_eo + 11 + prev_eo < strlen(line2))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000646 strcat(sub_line, line2 + match_structs.rm_eo + 11 + prev_eo);
647
648 prev_eo += match_structs.rm_eo + 11;
649 match_status = regexec(pattern, sub_line, 1, &match_structs, REG_NOTBOL);
650 }
651
652 return line2;
653}
654
Rob Landleya2e98042006-04-18 01:53:41 +0000655static void goto_match(int match)
656{
657 /* This goes to a specific match - all line positions of matches are
658 stored within the match_lines[] array. */
659 if ((match < num_matches) && (match >= 0)) {
660 buffer_line(match_lines[match]);
661 match_pos = match;
662 }
663}
664
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000665static void regex_process(void)
666{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000667 char uncomp_regex[100];
668 char current_line[256];
669 int i;
670 int j = 0;
Mike Frysinger20c22e02006-04-16 21:41:00 +0000671 regex_t pattern;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000672
Rob Landleya2e98042006-04-18 01:53:41 +0000673 /* Get the uncompiled regular expression from the user */
674 clear_line();
675 putchar((match_backwards) ? '?' : '/');
676 uncomp_regex[0] = 0;
677 fgets(uncomp_regex, sizeof(uncomp_regex), inp);
678
679 if (strlen(uncomp_regex) == 1) {
680 goto_match(match_backwards ? match_pos - 1 : match_pos + 1);
681 buffer_print();
682 return;
683 }
684
685 uncomp_regex[strlen(uncomp_regex) - 1] = '\0';
686
687 /* Compile the regex and check for errors */
688 xregcomp(&pattern, uncomp_regex, 0);
689
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000690 /* Reset variables */
691 match_lines[0] = -1;
692 match_pos = 0;
693 num_matches = 0;
694 match_found = 0;
695
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000696 /* Run the regex on each line of the current file here */
697 for (i = 0; i <= num_flines; i++) {
Mike Frysinger20c22e02006-04-16 21:41:00 +0000698 strcpy(current_line, process_regex_on_line(flines[i], &pattern));
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000699 flines[i] = bb_xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000700 if (match_found) {
701 match_lines[j] = i;
702 j++;
703 }
704 }
705
706 num_matches = j;
Rob Landleya2e98042006-04-18 01:53:41 +0000707 if ((match_lines[0] != -1) && (num_flines > height - 2)) {
708 if (match_backwards) {
709 for (i = 0; i < num_matches; i++) {
710 if (match_lines[i] > line_pos) {
711 match_pos = i - 1;
712 buffer_line(match_lines[match_pos]);
713 break;
714 }
715 }
716 }
717 else
718 buffer_line(match_lines[0]);
719 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000720 else
721 buffer_init();
722}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000723#endif
724
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000725static void number_process(int first_digit)
726{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000727 int i = 1;
728 int num;
729 char num_input[80];
730 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000731 char *endptr;
732
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000733 num_input[0] = first_digit;
734
735 /* Clear the current line, print a prompt, and then print the digit */
736 clear_line();
737 printf(":%c", first_digit);
738
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000739 /* Receive input until a letter is given (max 80 chars)*/
740 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000741 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000742 i++;
743 }
744
745 /* Take the final letter out of the digits string */
746 keypress = num_input[i];
747 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000748 num = strtol(num_input, &endptr, 10);
Rob Landleya2e98042006-04-18 01:53:41 +0000749 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES) {
750 buffer_print();
751 return;
752 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000753
754 /* We now know the number and the letter entered, so we process them */
755 switch (keypress) {
756 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
757 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000758 break;
759 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
760 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000761 break;
762 case 'g': case '<': case 'G': case '>':
763 if (num_flines >= height - 2)
764 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000765 break;
766 case 'p': case '%':
767 buffer_line(reverse_percent(num));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000768 break;
769#ifdef CONFIG_FEATURE_LESS_REGEXP
770 case 'n':
Rob Landleya2e98042006-04-18 01:53:41 +0000771 goto_match(match_pos + num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000772 break;
773 case '/':
Rob Landleya2e98042006-04-18 01:53:41 +0000774 match_backwards = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000775 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000776 break;
777 case '?':
Rob Landleya2e98042006-04-18 01:53:41 +0000778 match_backwards = 1;
779 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000780 break;
781#endif
782 default:
783 break;
784 }
785}
786
787#ifdef CONFIG_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000788static void flag_change(void)
789{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000790 int keypress;
791
792 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000793 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000794 keypress = tless_getch();
795
796 switch (keypress) {
797 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000798 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000799 break;
800 case 'm':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000801 flags ^= FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000802 break;
803 case 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000804 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000805 break;
806 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000807 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000808 break;
809 default:
810 break;
811 }
812}
813
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000814static void show_flag_status(void)
815{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000816 int keypress;
817 int flag_val;
818
819 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000820 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000821 keypress = tless_getch();
822
823 switch (keypress) {
824 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000825 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000826 break;
827 case 'm':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000828 flag_val = flags & FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000829 break;
830 case '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000831 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000832 break;
833 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000834 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000835 break;
836 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000837 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000838 break;
839 default:
840 flag_val = 0;
841 break;
842 }
843
844 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000845 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 +0000846}
847#endif
848
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000849static void full_repaint(void)
850{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000851 int temp_line_pos = line_pos;
852 data_readlines();
853 buffer_init();
854 buffer_line(temp_line_pos);
855 buffer_print();
856}
857
858
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000859static void save_input_to_file(void)
860{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000861 char current_line[256];
862 int i;
863 FILE *fp;
864
865 clear_line();
866 printf("Log file: ");
867 fgets(current_line, 256, inp);
868 current_line[strlen(current_line) - 1] = '\0';
869 if (strlen(current_line)) {
870 fp = bb_xfopen(current_line, "w");
871 for (i = 0; i < num_flines; i++)
872 fprintf(fp, "%s", flines[i]);
873 fclose(fp);
874 buffer_print();
875 }
876 else
877 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
878}
879
880#ifdef CONFIG_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000881static void add_mark(void)
882{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000883 int letter;
884 int mark_line;
885
886 clear_line();
887 printf("Mark: ");
888 letter = tless_getch();
889
890 if (isalpha(letter)) {
891 mark_line = line_pos;
892
893 /* If we exceed 15 marks, start overwriting previous ones */
894 if (num_marks == 14)
895 num_marks = 0;
896
897 mark_lines[num_marks][0] = letter;
898 mark_lines[num_marks][1] = line_pos;
899 num_marks++;
900 }
901 else {
902 clear_line();
903 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
904 }
905}
906
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000907static void goto_mark(void)
908{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000909 int letter;
910 int i;
911
912 clear_line();
913 printf("Go to mark: ");
914 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000915 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000916
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000917 if (isalpha(letter)) {
918 for (i = 0; i <= num_marks; i++)
919 if (letter == mark_lines[i][0]) {
920 buffer_line(mark_lines[i][1]);
921 break;
922 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000923 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000924 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000925 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000926 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000927 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000928}
929#endif
930
931
932#ifdef CONFIG_FEATURE_LESS_BRACKETS
933
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000934static char opp_bracket(char bracket)
935{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000936 switch (bracket) {
937 case '{': case '[':
938 return bracket + 2;
939 break;
940 case '(':
941 return ')';
942 break;
943 case '}': case ']':
944 return bracket - 2;
945 break;
946 case ')':
947 return '(';
948 break;
949 default:
950 return 0;
951 break;
952 }
953}
954
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000955static void match_right_bracket(char bracket)
956{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000957 int bracket_line = -1;
958 int i;
959
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000960 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000961
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000962 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000963 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000964 else {
965 for (i = line_pos + 1; i < num_flines; i++) {
966 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
967 bracket_line = i;
968 break;
969 }
970 }
971
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000972 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000973 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000974
975 buffer_line(bracket_line - height + 2);
976 buffer_print();
977 }
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);
1004 buffer_print();
1005 }
1006}
1007
1008#endif /* CONFIG_FEATURE_LESS_BRACKETS */
1009
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001010static void keypress_process(int keypress)
1011{
Rob Landley9200e792005-09-15 19:26:59 +00001012 switch (keypress) {
1013 case KEY_DOWN: case 'e': case 'j': case '\015':
1014 buffer_down(1);
1015 buffer_print();
1016 break;
1017 case KEY_UP: case 'y': case 'k':
1018 buffer_up(1);
1019 buffer_print();
1020 break;
1021 case PAGE_DOWN: case ' ': case 'z':
1022 buffer_down(height - 1);
1023 buffer_print();
1024 break;
1025 case PAGE_UP: case 'w': case 'b':
1026 buffer_up(height - 1);
1027 buffer_print();
1028 break;
1029 case 'd':
1030 buffer_down((height - 1) / 2);
1031 buffer_print();
1032 break;
1033 case 'u':
1034 buffer_up((height - 1) / 2);
1035 buffer_print();
1036 break;
1037 case 'g': case 'p': case '<': case '%':
1038 buffer_up(num_flines + 1);
1039 buffer_print();
1040 break;
1041 case 'G': case '>':
1042 buffer_down(num_flines + 1);
1043 buffer_print();
1044 break;
1045 case 'q': case 'Q':
1046 tless_exit(0);
1047 break;
1048#ifdef CONFIG_FEATURE_LESS_MARKS
1049 case 'm':
1050 add_mark();
1051 buffer_print();
1052 break;
1053 case '\'':
1054 goto_mark();
1055 buffer_print();
1056 break;
1057#endif
1058 case 'r':
1059 buffer_print();
1060 break;
1061 case 'R':
1062 full_repaint();
1063 break;
1064 case 's':
1065 if (inp_stdin)
1066 save_input_to_file();
1067 break;
1068 case 'E':
1069 examine_file();
1070 break;
1071#ifdef CONFIG_FEATURE_LESS_FLAGS
1072 case '=':
1073 clear_line();
1074 m_status_print();
1075 break;
1076#endif
1077#ifdef CONFIG_FEATURE_LESS_REGEXP
1078 case '/':
Rob Landleya2e98042006-04-18 01:53:41 +00001079 match_backwards = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001080 regex_process();
1081 buffer_print();
1082 break;
1083 case 'n':
1084 goto_match(match_pos + 1);
1085 buffer_print();
1086 break;
1087 case 'N':
1088 goto_match(match_pos - 1);
1089 buffer_print();
1090 break;
1091 case '?':
Rob Landleya2e98042006-04-18 01:53:41 +00001092 match_backwards = 1;
1093 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001094 buffer_print();
1095 break;
1096#endif
1097#ifdef CONFIG_FEATURE_LESS_FLAGCS
1098 case '-':
1099 flag_change();
1100 buffer_print();
1101 break;
1102 case '_':
1103 show_flag_status();
1104 break;
1105#endif
1106#ifdef CONFIG_FEATURE_LESS_BRACKETS
1107 case '{': case '(': case '[':
1108 match_right_bracket(keypress);
1109 break;
1110 case '}': case ')': case ']':
1111 match_left_bracket(keypress);
1112 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001113#endif
1114 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001115 colon_process();
1116 break;
1117 default:
1118 break;
1119 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001120
Rob Landley9200e792005-09-15 19:26:59 +00001121 if (isdigit(keypress))
1122 number_process(keypress);
1123}
1124
Rob Landley9200e792005-09-15 19:26:59 +00001125int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001126
Rob Landley9200e792005-09-15 19:26:59 +00001127 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001128
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001129 flags = bb_getopt_ulflags(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001130
1131 argc -= optind;
1132 argv += optind;
1133 files = argv;
1134 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001135
Rob Landley9200e792005-09-15 19:26:59 +00001136 if (!num_files) {
1137 if (ttyname(STDIN_FILENO) == NULL)
1138 inp_stdin = 1;
1139 else {
1140 bb_error_msg("Missing filename");
1141 bb_show_usage();
1142 }
1143 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001144
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001145 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
Rob Landley9200e792005-09-15 19:26:59 +00001146 tty_width_height();
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001147 data_readlines();
1148 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001149 term_vi = term_orig;
1150 term_vi.c_lflag &= (~ICANON & ~ECHO);
1151 term_vi.c_iflag &= (~IXON & ~ICRNL);
1152 term_vi.c_oflag &= (~ONLCR);
1153 term_vi.c_cc[VMIN] = 1;
1154 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001155 buffer_init();
1156 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001157
Rob Landley9200e792005-09-15 19:26:59 +00001158 while (1) {
1159 keypress = tless_getch();
1160 keypress_process(keypress);
1161 }
1162}