blob: 940b1c865bc335b128149983b901973d3a7a5bc5 [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
Rob Landley9200e792005-09-15 19:26:59 +000073static int height;
74static int width;
75static char **files;
76static char filename[256];
Rob Landleyd57ae8b2005-09-18 00:58:49 +000077static char **buffer;
78static char **flines;
Rob Landley9200e792005-09-15 19:26:59 +000079static int current_file = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000080static int line_pos;
Rob Landley9200e792005-09-15 19:26:59 +000081static int num_flines;
82static int num_files = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000083static int past_eof;
Rob Landley9200e792005-09-15 19:26:59 +000084
85/* Command line options */
Rob Landleyd57ae8b2005-09-18 00:58:49 +000086static unsigned long flags;
87#define FLAG_E 1
88#define FLAG_M (1<<1)
89#define FLAG_m (1<<2)
90#define FLAG_N (1<<3)
91#define FLAG_TILDE (1<<4)
Rob Landley9200e792005-09-15 19:26:59 +000092
93/* This is needed so that program behaviour changes when input comes from
94 stdin */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000095static int inp_stdin;
Rob Landley9200e792005-09-15 19:26:59 +000096
97#ifdef CONFIG_FEATURE_LESS_MARKS
98static int mark_lines[15][2];
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000099static int num_marks;
Rob Landley9200e792005-09-15 19:26:59 +0000100#endif
101
102#ifdef CONFIG_FEATURE_LESS_REGEXP
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000103static int match_found;
Mike Frysingerf054be12006-04-29 04:21:10 +0000104static int *match_lines;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000105static int match_pos;
106static int num_matches;
107static int match_backwards;
Mike Frysingerf054be12006-04-29 04:21:10 +0000108static regex_t old_pattern;
Rob Landley9200e792005-09-15 19:26:59 +0000109#endif
110
111/* Needed termios structures */
112static struct termios term_orig, term_vi;
113
114/* File pointer to get input from */
115static FILE *inp;
116
117/* Reset terminal input to normal */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000118static void set_tty_cooked(void)
119{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000120 fflush(stdout);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000121 tcsetattr(fileno(inp), TCSANOW, &term_orig);
Rob Landley9200e792005-09-15 19:26:59 +0000122}
123
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000124/* Set terminal input to raw mode (taken from vi.c) */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000125static void set_tty_raw(void)
126{
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000127 tcsetattr(fileno(inp), TCSANOW, &term_vi);
Rob Landley9200e792005-09-15 19:26:59 +0000128}
129
130/* Exit the program gracefully */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000131static void tless_exit(int code)
132{
Rob Landley9200e792005-09-15 19:26:59 +0000133 /* TODO: We really should save the terminal state when we start,
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000134 and restore it when we exit. Less does this with the
Rob Landley9200e792005-09-15 19:26:59 +0000135 "ti" and "te" termcap commands; can this be done with
136 only termios.h? */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000137
Rob Landley9200e792005-09-15 19:26:59 +0000138 putchar('\n');
139 exit(code);
140}
141
142/* Grab a character from input without requiring the return key. If the
143 character is ASCII \033, get more characters and assign certain sequences
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000144 special return codes. Note that this function works best with raw input. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000145static int tless_getch(void)
146{
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000147 int input;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000148
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000149 set_tty_raw();
150
151 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000152 /* Detect escape sequences (i.e. arrow keys) and handle
153 them accordingly */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000154
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000155 if (input == '\033' && getc(inp) == '[') {
156 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000157 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000158 if (input == REAL_KEY_UP)
159 return KEY_UP;
160 else if (input == REAL_KEY_DOWN)
161 return KEY_DOWN;
162 else if (input == REAL_KEY_RIGHT)
163 return KEY_RIGHT;
164 else if (input == REAL_KEY_LEFT)
165 return KEY_LEFT;
166 else if (input == REAL_PAGE_UP)
167 return PAGE_UP;
168 else if (input == REAL_PAGE_DOWN)
169 return PAGE_DOWN;
Rob Landley9200e792005-09-15 19:26:59 +0000170 }
171 /* The input is a normal ASCII value */
172 else {
173 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000174 return input;
Rob Landley9200e792005-09-15 19:26:59 +0000175 }
176 return 0;
177}
178
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000179/* Move the cursor to a position (x,y), where (0,0) is the
Rob Landley9200e792005-09-15 19:26:59 +0000180 top-left corner of the console */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000181static void move_cursor(int x, int y)
182{
Rob Landley9200e792005-09-15 19:26:59 +0000183 printf("\033[%i;%iH", x, y);
184}
185
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000186static void clear_line(void)
187{
Rob Landley9200e792005-09-15 19:26:59 +0000188 move_cursor(height, 0);
189 printf("\033[K");
190}
191
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000192/* This adds line numbers to every line, as the -N flag necessitates */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000193static void add_linenumbers(void)
194{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000195 char current_line[256];
196 int i;
197
198 for (i = 0; i <= num_flines; i++) {
199 safe_strncpy(current_line, flines[i], 256);
"Vladimir N. Oleynik"39a841c2005-09-29 16:18:57 +0000200 flines[i] = bb_xasprintf("%5d %s", i + 1, current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000201 }
202}
203
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000204static void data_readlines(void)
205{
Rob Landley9200e792005-09-15 19:26:59 +0000206 int i;
207 char current_line[256];
208 FILE *fp;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000209
Bernhard Reutner-Fischera85a63f2006-05-19 12:22:11 +0000210 fp = (inp_stdin) ? stdin : bb_xfopen(filename, "r");
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000211 flines = NULL;
212 for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) {
Rob Landley9200e792005-09-15 19:26:59 +0000213 strcpy(current_line, "");
214 fgets(current_line, 256, fp);
Mike Frysinger00d10a92006-04-16 20:54:19 +0000215 if (fp != stdin)
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000216 bb_xferror(fp, filename);
217 flines = xrealloc(flines, (i+1) * sizeof(char *));
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000218 flines[i] = bb_xstrdup(current_line);
Rob Landley9200e792005-09-15 19:26:59 +0000219 }
220 num_flines = i - 2;
221
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000222 /* Reset variables for a new file */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000223
Rob Landley9200e792005-09-15 19:26:59 +0000224 line_pos = 0;
225 past_eof = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000226
Rob Landley9200e792005-09-15 19:26:59 +0000227 fclose(fp);
228
Mike Frysinger00d10a92006-04-16 20:54:19 +0000229 if (inp == NULL)
"Vladimir N. Oleynik"8315cd52005-12-15 11:53:22 +0000230 inp = (inp_stdin) ? bb_xfopen(CURRENT_TTY, "r") : stdin;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000231
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000232 if (flags & FLAG_N)
Rob Landley9200e792005-09-15 19:26:59 +0000233 add_linenumbers();
234}
235
Rob Landley9200e792005-09-15 19:26:59 +0000236#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000237
238/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
239 * on my build. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000240static int calc_percent(void)
241{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000242 return ((100 * (line_pos + height - 2) / num_flines) + 1);
243}
244
Rob Landley9200e792005-09-15 19:26:59 +0000245/* Print a status line if -M was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000246static void m_status_print(void)
247{
Rob Landley9200e792005-09-15 19:26:59 +0000248 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000249
Rob Landley9200e792005-09-15 19:26:59 +0000250 if (!past_eof) {
251 if (!line_pos) {
252 if (num_files > 1)
253 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);
254 else {
255 printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
256 }
257 }
258 else {
259 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
260 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000261
Rob Landley9200e792005-09-15 19:26:59 +0000262 if (line_pos == num_flines - height + 2) {
263 printf("(END) %s", NORMAL);
264 if ((num_files > 1) && (current_file != num_files))
265 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
266 }
267 else {
268 percentage = calc_percent();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000269 printf("%i%% %s", percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000270 }
271 }
272 else {
273 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
274 if ((num_files > 1) && (current_file != num_files))
275 printf("- Next: %s", files[current_file]);
276 printf("%s", NORMAL);
277 }
278}
279
280/* Print a status line if -m was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000281static void medium_status_print(void)
282{
Rob Landley9200e792005-09-15 19:26:59 +0000283 int percentage;
284 percentage = calc_percent();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000285
Rob Landley9200e792005-09-15 19:26:59 +0000286 if (!line_pos)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000287 printf("%s%s %i%%%s", HIGHLIGHT, filename, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000288 else if (line_pos == num_flines - height + 2)
289 printf("%s(END)%s", HIGHLIGHT, NORMAL);
290 else
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000291 printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000292}
293#endif
294
295/* Print the status line */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000296static void status_print(void)
297{
Rob Landley9200e792005-09-15 19:26:59 +0000298 /* Change the status if flags have been set */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000299#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000300 if (flags & FLAG_M)
Rob Landley9200e792005-09-15 19:26:59 +0000301 m_status_print();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000302 else if (flags & FLAG_m)
Rob Landley9200e792005-09-15 19:26:59 +0000303 medium_status_print();
304 /* No flags set */
305 else {
306#endif
307 if (!line_pos) {
308 printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
309 if (num_files > 1)
310 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
311 }
312 else if (line_pos == num_flines - height + 2) {
313 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
314 if ((num_files > 1) && (current_file != num_files))
315 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
316 }
317 else {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000318 putchar(':');
Rob Landley9200e792005-09-15 19:26:59 +0000319 }
320#ifdef CONFIG_FEATURE_LESS_FLAGS
321 }
322#endif
323}
324
325/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000326static void buffer_print(void)
327{
Rob Landley9200e792005-09-15 19:26:59 +0000328 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000329
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000330 printf("%s", CLEAR);
Rob Landley9200e792005-09-15 19:26:59 +0000331 if (num_flines >= height - 2) {
Rob Landley9200e792005-09-15 19:26:59 +0000332 for (i = 0; i < height - 1; i++)
333 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000334 }
335 else {
Rob Landley9200e792005-09-15 19:26:59 +0000336 for (i = 1; i < (height - 1 - num_flines); i++)
337 putchar('\n');
338 for (i = 0; i < height - 1; i++)
339 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000340 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000341
342 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000343}
344
345/* Initialise the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000346static void buffer_init(void)
347{
Rob Landley9200e792005-09-15 19:26:59 +0000348 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000349
Mike Frysinger00d10a92006-04-16 20:54:19 +0000350 if (buffer == NULL) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000351 /* malloc the number of lines needed for the buffer */
352 buffer = xrealloc(buffer, height * sizeof(char *));
353 } else {
354 for (i = 0; i < (height - 1); i++)
355 free(buffer[i]);
356 }
357
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000358 /* Fill the buffer until the end of the file or the
Rob Landley9200e792005-09-15 19:26:59 +0000359 end of the buffer is reached */
360 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000361 buffer[i] = bb_xstrdup(flines[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000362 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000363
Rob Landley9200e792005-09-15 19:26:59 +0000364 /* If the buffer still isn't full, fill it with blank lines */
365 for (; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000366 buffer[i] = bb_xstrdup("");
Rob Landley9200e792005-09-15 19:26:59 +0000367 }
368}
369
370/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000371static void buffer_down(int nlines)
372{
Rob Landley9200e792005-09-15 19:26:59 +0000373 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000374
Rob Landley9200e792005-09-15 19:26:59 +0000375 if (!past_eof) {
376 if (line_pos + (height - 3) + nlines < num_flines) {
377 line_pos += nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000378 for (i = 0; i < (height - 1); i++) {
379 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000380 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000381 }
Rob Landley9200e792005-09-15 19:26:59 +0000382 }
383 else {
384 /* As the number of lines requested was too large, we just move
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000385 to the end of the file */
386 while (line_pos + (height - 3) + 1 < num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000387 line_pos += 1;
"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 }
394
395 /* We exit if the -E flag has been set */
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000396 if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
Rob Landley9200e792005-09-15 19:26:59 +0000397 tless_exit(0);
398 }
399}
400
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000401static void buffer_up(int nlines)
402{
Rob Landley9200e792005-09-15 19:26:59 +0000403 int i;
404 int tilde_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000405
Rob Landley9200e792005-09-15 19:26:59 +0000406 if (!past_eof) {
407 if (line_pos - nlines >= 0) {
408 line_pos -= nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000409 for (i = 0; i < (height - 1); i++) {
410 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000411 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000412 }
Rob Landley9200e792005-09-15 19:26:59 +0000413 }
414 else {
415 /* As the requested number of lines to move was too large, we
416 move one line up at a time until we can't. */
417 while (line_pos != 0) {
418 line_pos -= 1;
"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 }
425 }
426 else {
427 /* Work out where the tildes start */
428 tilde_line = num_flines - line_pos + 3;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000429
Rob Landley9200e792005-09-15 19:26:59 +0000430 line_pos -= nlines;
431 /* Going backwards nlines lines has taken us to a point where
432 nothing is past the EOF, so we revert to normal. */
433 if (line_pos < num_flines - height + 3) {
434 past_eof = 0;
435 buffer_up(nlines);
436 }
437 else {
438 /* We only move part of the buffer, as the rest
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000439 is past the EOF */
Rob Landley9200e792005-09-15 19:26:59 +0000440 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000441 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000442 if (i < tilde_line - nlines + 1)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000443 buffer[i] = bb_xstrdup(flines[line_pos + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000444 else {
445 if (line_pos >= num_flines - height + 2)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000446 buffer[i] = bb_xstrdup("~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000447 }
448 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000449 }
Rob Landley9200e792005-09-15 19:26:59 +0000450 }
451}
452
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000453static void buffer_line(int linenum)
454{
Rob Landley9200e792005-09-15 19:26:59 +0000455 int i;
Rob Landley9200e792005-09-15 19:26:59 +0000456 past_eof = 0;
457
Mike Frysingerf054be12006-04-29 04:21:10 +0000458 if (linenum < 0 || linenum > num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000459 clear_line();
Mike Frysingerf054be12006-04-29 04:21:10 +0000460 printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum + 1, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000461 }
462 else if (linenum < (num_flines - height - 2)) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000463 for (i = 0; i < (height - 1); i++) {
464 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000465 buffer[i] = bb_xstrdup(flines[linenum + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000466 }
Rob Landley9200e792005-09-15 19:26:59 +0000467 line_pos = linenum;
Mike Frysingerf054be12006-04-29 04:21:10 +0000468 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000469 }
470 else {
471 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000472 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000473 if (linenum + i < num_flines + 2)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000474 buffer[i] = bb_xstrdup(flines[linenum + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000475 else
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000476 buffer[i] = bb_xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000477 }
478 line_pos = linenum;
479 /* Set past_eof so buffer_down and buffer_up act differently */
480 past_eof = 1;
Mike Frysingerf054be12006-04-29 04:21:10 +0000481 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000482 }
483}
484
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000485/* Reinitialise everything for a new file - free the memory and start over */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000486static void reinitialise(void)
487{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000488 int i;
489
490 for (i = 0; i <= num_flines; i++)
491 free(flines[i]);
492 free(flines);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000493
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000494 data_readlines();
495 buffer_init();
496 buffer_print();
497}
498
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000499static void examine_file(void)
500{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000501 int newline_offset;
502
503 clear_line();
504 printf("Examine: ");
505 fgets(filename, 256, inp);
506
507 /* As fgets adds a newline to the end of an input string, we
508 need to remove it */
509 newline_offset = strlen(filename) - 1;
510 filename[newline_offset] = '\0';
511
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000512 files[num_files] = bb_xstrdup(filename);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000513 current_file = num_files + 1;
514 num_files++;
515
516 inp_stdin = 0;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000517 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000518}
519
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000520/* This function changes the file currently being paged. direction can be one of the following:
521 * -1: go back one file
522 * 0: go to the first file
523 * 1: go forward one file
524*/
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000525static void change_file(int direction)
526{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000527 if (current_file != ((direction > 0) ? num_files : 1)) {
528 current_file = direction ? current_file + direction : 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000529 strcpy(filename, files[current_file - 1]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000530 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000531 }
532 else {
533 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000534 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000535 }
536}
537
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000538static void remove_current_file(void)
539{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000540 int i;
541
542 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000543 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000544 for (i = 3; i <= num_files; i++)
545 files[i - 2] = files[i - 1];
546 num_files--;
547 buffer_print();
548 }
549 else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000550 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000551 for (i = 2; i <= num_files; i++)
552 files[i - 2] = files[i - 1];
553 num_files--;
554 current_file--;
555 buffer_print();
556 }
557}
558
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000559static void colon_process(void)
560{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000561 int keypress;
562
563 /* Clear the current line and print a prompt */
564 clear_line();
565 printf(" :");
566
567 keypress = tless_getch();
568 switch (keypress) {
569 case 'd':
570 remove_current_file();
571 break;
572 case 'e':
573 examine_file();
574 break;
575#ifdef CONFIG_FEATURE_LESS_FLAGS
576 case 'f':
577 clear_line();
578 m_status_print();
579 break;
580#endif
581 case 'n':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000582 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000583 break;
584 case 'p':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000585 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000586 break;
587 case 'q':
588 tless_exit(0);
589 break;
590 case 'x':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000591 change_file(0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000592 break;
593 default:
594 break;
595 }
596}
597
598#ifdef CONFIG_FEATURE_LESS_REGEXP
599/* The below two regular expression handler functions NEED development. */
600
601/* Get a regular expression from the user, and then go through the current
602 file line by line, running a processing regex function on each one. */
603
Mike Frysingerf054be12006-04-29 04:21:10 +0000604static char *process_regex_on_line(char *line, regex_t *pattern, int action)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000605{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000606 /* This function takes the regex and applies it to the line.
607 Each part of the line that matches has the HIGHLIGHT
608 and NORMAL escape sequences placed around it by
Mike Frysingerf054be12006-04-29 04:21:10 +0000609 insert_highlights if action = 1, or has the escape sequences
610 removed if action = 0, and then the line is returned. */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000611 int match_status;
Mike Frysingerbf2d9902006-04-16 21:30:47 +0000612 char *line2 = (char *) xmalloc((sizeof(char) * (strlen(line) + 1)) + 64);
Mike Frysingerf054be12006-04-29 04:21:10 +0000613 char *growline = "";
"Vladimir N. Oleynik"8d3c40d2005-09-16 13:16:01 +0000614 regmatch_t match_structs;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000615
Mike Frysingerf054be12006-04-29 04:21:10 +0000616 line2 = bb_xstrdup(line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000617
618 match_found = 0;
619 match_status = regexec(pattern, line2, 1, &match_structs, 0);
Mike Frysingerf054be12006-04-29 04:21:10 +0000620
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000621 while (match_status == 0) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000622 if (match_found == 0)
623 match_found = 1;
Mike Frysingerf054be12006-04-29 04:21:10 +0000624
625 if (action) {
626 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);
627 }
628 else {
629 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);
630 }
631
632 line2 += match_structs.rm_eo;
633 match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000634 }
Mike Frysingerf054be12006-04-29 04:21:10 +0000635
636 growline = bb_xasprintf("%s%s", growline, line2);
637
638 return (match_found ? growline : line);
639
640 free(growline);
641 free(line2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000642}
643
Rob Landleya2e98042006-04-18 01:53:41 +0000644static void goto_match(int match)
645{
646 /* This goes to a specific match - all line positions of matches are
647 stored within the match_lines[] array. */
648 if ((match < num_matches) && (match >= 0)) {
649 buffer_line(match_lines[match]);
650 match_pos = match;
651 }
652}
653
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000654static void regex_process(void)
655{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000656 char uncomp_regex[100];
Mike Frysingerf054be12006-04-29 04:21:10 +0000657 char *current_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000658 int i;
659 int j = 0;
Mike Frysinger20c22e02006-04-16 21:41:00 +0000660 regex_t pattern;
Rob Landleya2e98042006-04-18 01:53:41 +0000661 /* Get the uncompiled regular expression from the user */
662 clear_line();
663 putchar((match_backwards) ? '?' : '/');
664 uncomp_regex[0] = 0;
665 fgets(uncomp_regex, sizeof(uncomp_regex), inp);
666
667 if (strlen(uncomp_regex) == 1) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000668 if (num_matches)
669 goto_match(match_backwards ? match_pos - 1 : match_pos + 1);
670 else
671 buffer_print();
Rob Landleya2e98042006-04-18 01:53:41 +0000672 return;
673 }
Rob Landleya2e98042006-04-18 01:53:41 +0000674 uncomp_regex[strlen(uncomp_regex) - 1] = '\0';
675
676 /* Compile the regex and check for errors */
677 xregcomp(&pattern, uncomp_regex, 0);
678
Mike Frysingerf054be12006-04-29 04:21:10 +0000679 if (num_matches) {
680 /* Get rid of all the highlights we added previously */
681 for (i = 0; i <= num_flines; i++) {
682 current_line = process_regex_on_line(flines[i], &old_pattern, 0);
683 flines[i] = bb_xstrdup(current_line);
684 }
685 }
686 old_pattern = pattern;
687
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000688 /* Reset variables */
Mike Frysingerf054be12006-04-29 04:21:10 +0000689 match_lines = xrealloc(match_lines, sizeof(int));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000690 match_lines[0] = -1;
691 match_pos = 0;
692 num_matches = 0;
693 match_found = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000694 /* Run the regex on each line of the current file here */
695 for (i = 0; i <= num_flines; i++) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000696 current_line = process_regex_on_line(flines[i], &pattern, 1);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000697 flines[i] = bb_xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000698 if (match_found) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000699 match_lines = xrealloc(match_lines, (j + 1) * sizeof(int));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000700 match_lines[j] = i;
701 j++;
702 }
703 }
Mike Frysingerf054be12006-04-29 04:21:10 +0000704
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000705 num_matches = j;
Rob Landleya2e98042006-04-18 01:53:41 +0000706 if ((match_lines[0] != -1) && (num_flines > height - 2)) {
707 if (match_backwards) {
708 for (i = 0; i < num_matches; i++) {
709 if (match_lines[i] > line_pos) {
710 match_pos = i - 1;
711 buffer_line(match_lines[match_pos]);
712 break;
713 }
714 }
715 }
716 else
717 buffer_line(match_lines[0]);
718 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000719 else
720 buffer_init();
721}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000722#endif
723
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000724static void number_process(int first_digit)
725{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000726 int i = 1;
727 int num;
728 char num_input[80];
729 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000730 char *endptr;
731
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000732 num_input[0] = first_digit;
733
734 /* Clear the current line, print a prompt, and then print the digit */
735 clear_line();
736 printf(":%c", first_digit);
737
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000738 /* Receive input until a letter is given (max 80 chars)*/
739 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000740 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000741 i++;
742 }
743
744 /* Take the final letter out of the digits string */
745 keypress = num_input[i];
746 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000747 num = strtol(num_input, &endptr, 10);
Rob Landleya2e98042006-04-18 01:53:41 +0000748 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES) {
749 buffer_print();
750 return;
751 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000752
753 /* We now know the number and the letter entered, so we process them */
754 switch (keypress) {
755 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
756 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000757 break;
758 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
759 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000760 break;
761 case 'g': case '<': case 'G': case '>':
762 if (num_flines >= height - 2)
763 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000764 break;
765 case 'p': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +0000766 buffer_line(((num / 100) * num_flines) - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000767 break;
768#ifdef CONFIG_FEATURE_LESS_REGEXP
769 case 'n':
Rob Landleya2e98042006-04-18 01:53:41 +0000770 goto_match(match_pos + num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000771 break;
772 case '/':
Rob Landleya2e98042006-04-18 01:53:41 +0000773 match_backwards = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000774 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000775 break;
776 case '?':
Rob Landleya2e98042006-04-18 01:53:41 +0000777 match_backwards = 1;
778 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000779 break;
780#endif
781 default:
782 break;
783 }
784}
785
786#ifdef CONFIG_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000787static void flag_change(void)
788{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000789 int keypress;
790
791 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000792 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000793 keypress = tless_getch();
794
795 switch (keypress) {
796 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000797 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000798 break;
799 case 'm':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000800 flags ^= FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000801 break;
802 case 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000803 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000804 break;
805 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000806 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000807 break;
808 default:
809 break;
810 }
811}
812
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000813static void show_flag_status(void)
814{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000815 int keypress;
816 int flag_val;
817
818 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000819 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000820 keypress = tless_getch();
821
822 switch (keypress) {
823 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000824 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000825 break;
826 case 'm':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000827 flag_val = flags & FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000828 break;
829 case '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000830 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000831 break;
832 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000833 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000834 break;
835 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000836 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000837 break;
838 default:
839 flag_val = 0;
840 break;
841 }
842
843 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000844 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 +0000845}
846#endif
847
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000848static void full_repaint(void)
849{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000850 int temp_line_pos = line_pos;
851 data_readlines();
852 buffer_init();
853 buffer_line(temp_line_pos);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000854}
855
856
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000857static void save_input_to_file(void)
858{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000859 char current_line[256];
860 int i;
861 FILE *fp;
862
863 clear_line();
864 printf("Log file: ");
865 fgets(current_line, 256, inp);
866 current_line[strlen(current_line) - 1] = '\0';
Mike Frysingerf054be12006-04-29 04:21:10 +0000867 if (strlen(current_line) > 1) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000868 fp = bb_xfopen(current_line, "w");
869 for (i = 0; i < num_flines; i++)
870 fprintf(fp, "%s", flines[i]);
871 fclose(fp);
872 buffer_print();
873 }
874 else
875 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
876}
877
878#ifdef CONFIG_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000879static void add_mark(void)
880{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000881 int letter;
882 int mark_line;
883
884 clear_line();
885 printf("Mark: ");
886 letter = tless_getch();
887
888 if (isalpha(letter)) {
889 mark_line = line_pos;
890
891 /* If we exceed 15 marks, start overwriting previous ones */
892 if (num_marks == 14)
893 num_marks = 0;
894
895 mark_lines[num_marks][0] = letter;
896 mark_lines[num_marks][1] = line_pos;
897 num_marks++;
898 }
899 else {
900 clear_line();
901 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
902 }
903}
904
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000905static void goto_mark(void)
906{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000907 int letter;
908 int i;
909
910 clear_line();
911 printf("Go to mark: ");
912 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000913 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000914
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000915 if (isalpha(letter)) {
916 for (i = 0; i <= num_marks; i++)
917 if (letter == mark_lines[i][0]) {
918 buffer_line(mark_lines[i][1]);
919 break;
920 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000921 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000922 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000923 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000924 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000925 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000926}
927#endif
928
929
930#ifdef CONFIG_FEATURE_LESS_BRACKETS
931
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000932static char opp_bracket(char bracket)
933{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000934 switch (bracket) {
935 case '{': case '[':
936 return bracket + 2;
937 break;
938 case '(':
939 return ')';
940 break;
941 case '}': case ']':
942 return bracket - 2;
943 break;
944 case ')':
945 return '(';
946 break;
947 default:
948 return 0;
949 break;
950 }
951}
952
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000953static void match_right_bracket(char bracket)
954{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000955 int bracket_line = -1;
956 int i;
957
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000958 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000959
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000960 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000961 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000962 else {
963 for (i = line_pos + 1; i < num_flines; i++) {
964 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
965 bracket_line = i;
966 break;
967 }
968 }
969
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000970 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000971 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000972
973 buffer_line(bracket_line - height + 2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000974 }
975}
976
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000977static void match_left_bracket(char bracket)
978{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000979 int bracket_line = -1;
980 int i;
981
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000982 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000983
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000984 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000985 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
986 printf("%s", flines[line_pos + height]);
987 sleep(4);
988 }
989 else {
990 for (i = line_pos + height - 2; i >= 0; i--) {
991 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
992 bracket_line = i;
993 break;
994 }
995 }
996
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000997 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000998 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000999
1000 buffer_line(bracket_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001001 }
1002}
1003
1004#endif /* CONFIG_FEATURE_LESS_BRACKETS */
1005
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001006static void keypress_process(int keypress)
1007{
Rob Landley9200e792005-09-15 19:26:59 +00001008 switch (keypress) {
1009 case KEY_DOWN: case 'e': case 'j': case '\015':
1010 buffer_down(1);
1011 buffer_print();
1012 break;
1013 case KEY_UP: case 'y': case 'k':
1014 buffer_up(1);
1015 buffer_print();
1016 break;
1017 case PAGE_DOWN: case ' ': case 'z':
1018 buffer_down(height - 1);
1019 buffer_print();
1020 break;
1021 case PAGE_UP: case 'w': case 'b':
1022 buffer_up(height - 1);
1023 buffer_print();
1024 break;
1025 case 'd':
1026 buffer_down((height - 1) / 2);
1027 buffer_print();
1028 break;
1029 case 'u':
1030 buffer_up((height - 1) / 2);
1031 buffer_print();
1032 break;
1033 case 'g': case 'p': case '<': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +00001034 buffer_line(0);
Rob Landley9200e792005-09-15 19:26:59 +00001035 break;
1036 case 'G': case '>':
Mike Frysingerf054be12006-04-29 04:21:10 +00001037 buffer_line(num_flines - height + 2);
Rob Landley9200e792005-09-15 19:26:59 +00001038 break;
1039 case 'q': case 'Q':
1040 tless_exit(0);
1041 break;
1042#ifdef CONFIG_FEATURE_LESS_MARKS
1043 case 'm':
1044 add_mark();
1045 buffer_print();
1046 break;
1047 case '\'':
1048 goto_mark();
1049 buffer_print();
1050 break;
1051#endif
1052 case 'r':
1053 buffer_print();
1054 break;
1055 case 'R':
1056 full_repaint();
1057 break;
1058 case 's':
1059 if (inp_stdin)
1060 save_input_to_file();
1061 break;
1062 case 'E':
1063 examine_file();
1064 break;
1065#ifdef CONFIG_FEATURE_LESS_FLAGS
1066 case '=':
1067 clear_line();
1068 m_status_print();
1069 break;
1070#endif
1071#ifdef CONFIG_FEATURE_LESS_REGEXP
1072 case '/':
Rob Landleya2e98042006-04-18 01:53:41 +00001073 match_backwards = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001074 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001075 break;
1076 case 'n':
1077 goto_match(match_pos + 1);
Rob Landley9200e792005-09-15 19:26:59 +00001078 break;
1079 case 'N':
1080 goto_match(match_pos - 1);
Rob Landley9200e792005-09-15 19:26:59 +00001081 break;
1082 case '?':
Rob Landleya2e98042006-04-18 01:53:41 +00001083 match_backwards = 1;
1084 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001085 break;
1086#endif
1087#ifdef CONFIG_FEATURE_LESS_FLAGCS
1088 case '-':
1089 flag_change();
1090 buffer_print();
1091 break;
1092 case '_':
1093 show_flag_status();
1094 break;
1095#endif
1096#ifdef CONFIG_FEATURE_LESS_BRACKETS
1097 case '{': case '(': case '[':
1098 match_right_bracket(keypress);
1099 break;
1100 case '}': case ')': case ']':
1101 match_left_bracket(keypress);
1102 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001103#endif
1104 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001105 colon_process();
1106 break;
1107 default:
1108 break;
1109 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001110
Rob Landley9200e792005-09-15 19:26:59 +00001111 if (isdigit(keypress))
1112 number_process(keypress);
1113}
1114
Rob Landley9200e792005-09-15 19:26:59 +00001115int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001116
Rob Landley9200e792005-09-15 19:26:59 +00001117 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001118
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001119 flags = bb_getopt_ulflags(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001120
1121 argc -= optind;
1122 argv += optind;
1123 files = argv;
1124 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001125
Rob Landley9200e792005-09-15 19:26:59 +00001126 if (!num_files) {
1127 if (ttyname(STDIN_FILENO) == NULL)
1128 inp_stdin = 1;
1129 else {
1130 bb_error_msg("Missing filename");
1131 bb_show_usage();
1132 }
1133 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001134
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001135 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
Rob Landley69d863b2006-05-25 21:13:30 +00001136 get_terminal_width_height(0, &width, &height);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001137 data_readlines();
1138 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001139 term_vi = term_orig;
1140 term_vi.c_lflag &= (~ICANON & ~ECHO);
1141 term_vi.c_iflag &= (~IXON & ~ICRNL);
1142 term_vi.c_oflag &= (~ONLCR);
1143 term_vi.c_cc[VMIN] = 1;
1144 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001145 buffer_init();
1146 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001147
Rob Landley9200e792005-09-15 19:26:59 +00001148 while (1) {
1149 keypress = tless_getch();
1150 keypress_process(keypress);
1151 }
1152}