blob: 03ffd78eda08e487ccc10f0d8ac5f7ccde82e64d [file] [log] [blame]
Rob Landley9200e792005-09-15 19:26:59 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Mini less implementation for busybox
4 *
Rob Landley9200e792005-09-15 19:26:59 +00005 * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
6 *
Mike Frysingerf284c762006-04-16 20:38:26 +00007 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8 */
9
10/*
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000011 * This program needs a lot of development, so consider it in a beta stage
12 * at best.
Rob Landley9200e792005-09-15 19:26:59 +000013 *
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000014 * TODO:
15 * - Add more regular expression support - search modifiers, certain matches, etc.
16 * - Add more complex bracket searching - currently, nested brackets are
17 * not considered.
18 * - Add support for "F" as an input. This causes less to act in
19 * a similar way to tail -f.
20 * - Check for binary files, and prompt the user if a binary file
21 * is detected.
22 * - Allow horizontal scrolling. Currently, lines simply continue onto
23 * the next line, per the terminal's discretion
Rob Landley9200e792005-09-15 19:26:59 +000024 *
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000025 * Notes:
26 * - filename is an array and not a pointer because that avoids all sorts
27 * of complications involving the fact that something that is pointed to
28 * will be changed if the pointer is changed.
29 * - the inp file pointer is used so that keyboard input works after
30 * redirected input has been read from stdin
Rob Landley9200e792005-09-15 19:26:59 +000031*/
32
Bernhard Reutner-Fischerc89982d2006-06-03 19:49:21 +000033#include "busybox.h"
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000034
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000035#ifdef CONFIG_FEATURE_LESS_REGEXP
36#include "xregex.h"
37#endif
38
39
Rob Landley9200e792005-09-15 19:26:59 +000040/* These are the escape sequences corresponding to special keys */
41#define REAL_KEY_UP 'A'
42#define REAL_KEY_DOWN 'B'
43#define REAL_KEY_RIGHT 'C'
44#define REAL_KEY_LEFT 'D'
45#define REAL_PAGE_UP '5'
46#define REAL_PAGE_DOWN '6'
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +000047#define REAL_KEY_HOME '7'
48#define REAL_KEY_END '8'
Rob Landley9200e792005-09-15 19:26:59 +000049
50/* These are the special codes assigned by this program to the special keys */
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +000051#define KEY_UP 20
52#define KEY_DOWN 21
53#define KEY_RIGHT 22
54#define KEY_LEFT 23
55#define PAGE_UP 24
56#define PAGE_DOWN 25
57#define KEY_HOME 26
58#define KEY_END 27
Rob Landley9200e792005-09-15 19:26:59 +000059
60/* The escape codes for highlighted and normal text */
61#define HIGHLIGHT "\033[7m"
62#define NORMAL "\033[0m"
63
64/* The escape code to clear the screen */
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000065#define CLEAR "\033[H\033[J"
Rob Landley9200e792005-09-15 19:26:59 +000066
67/* Maximum number of lines in a file */
68#define MAXLINES 10000
69
Rob Landley9200e792005-09-15 19:26:59 +000070static int height;
71static int width;
72static char **files;
73static char filename[256];
Rob Landleyd57ae8b2005-09-18 00:58:49 +000074static char **buffer;
75static char **flines;
Rob Landley9200e792005-09-15 19:26:59 +000076static int current_file = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000077static int line_pos;
Rob Landley9200e792005-09-15 19:26:59 +000078static int num_flines;
79static int num_files = 1;
Rob Landley9200e792005-09-15 19:26:59 +000080
81/* Command line options */
Denis Vlasenkof0ed3762006-10-26 23:21:47 +000082static unsigned flags;
Rob Landleyd57ae8b2005-09-18 00:58:49 +000083#define FLAG_E 1
84#define FLAG_M (1<<1)
85#define FLAG_m (1<<2)
86#define FLAG_N (1<<3)
87#define FLAG_TILDE (1<<4)
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +000088/* hijack command line options variable for internal state vars */
89#define LESS_STATE_INP_STDIN (1<<5)
90#define LESS_STATE_PAST_EOF (1<<6)
91#define LESS_STATE_MATCH_BACKWARDS (1<<7)
92/* INP_STDIN is used to change behaviour when input comes from stdin */
Rob Landley9200e792005-09-15 19:26:59 +000093
94#ifdef CONFIG_FEATURE_LESS_MARKS
95static int mark_lines[15][2];
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000096static int num_marks;
Rob Landley9200e792005-09-15 19:26:59 +000097#endif
98
99#ifdef CONFIG_FEATURE_LESS_REGEXP
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000100static int match_found;
Mike Frysingerf054be12006-04-29 04:21:10 +0000101static int *match_lines;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000102static int match_pos;
103static int num_matches;
Mike Frysingerf054be12006-04-29 04:21:10 +0000104static regex_t old_pattern;
Rob Landley9200e792005-09-15 19:26:59 +0000105#endif
106
107/* Needed termios structures */
108static struct termios term_orig, term_vi;
109
110/* File pointer to get input from */
111static FILE *inp;
112
113/* Reset terminal input to normal */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000114static void set_tty_cooked(void)
115{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000116 fflush(stdout);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000117 tcsetattr(fileno(inp), TCSANOW, &term_orig);
Rob Landley9200e792005-09-15 19:26:59 +0000118}
119
Rob Landley9200e792005-09-15 19:26:59 +0000120/* Exit the program gracefully */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000121static void tless_exit(int code)
122{
Rob Landley9200e792005-09-15 19:26:59 +0000123 /* TODO: We really should save the terminal state when we start,
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000124 and restore it when we exit. Less does this with the
Rob Landley9200e792005-09-15 19:26:59 +0000125 "ti" and "te" termcap commands; can this be done with
126 only termios.h? */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000127
Rob Landley9200e792005-09-15 19:26:59 +0000128 putchar('\n');
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000129 fflush_stdout_and_exit(code);
Rob Landley9200e792005-09-15 19:26:59 +0000130}
131
132/* Grab a character from input without requiring the return key. If the
133 character is ASCII \033, get more characters and assign certain sequences
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000134 special return codes. Note that this function works best with raw input. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000135static int tless_getch(void)
136{
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000137 int input;
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000138 /* Set terminal input to raw mode (taken from vi.c) */
139 tcsetattr(fileno(inp), TCSANOW, &term_vi);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000140
141 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000142 /* Detect escape sequences (i.e. arrow keys) and handle
143 them accordingly */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000144
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000145 if (input == '\033' && getc(inp) == '[') {
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000146 unsigned int i;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000147 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000148 set_tty_cooked();
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000149
150 i = input - REAL_KEY_UP;
151 if (i < 4)
152 return 20 + i;
153 else if ((i = input - REAL_PAGE_UP) < 4)
154 return 24 + i;
Rob Landley9200e792005-09-15 19:26:59 +0000155 }
156 /* The input is a normal ASCII value */
157 else {
158 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000159 return input;
Rob Landley9200e792005-09-15 19:26:59 +0000160 }
161 return 0;
162}
163
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000164/* Move the cursor to a position (x,y), where (0,0) is the
Rob Landley9200e792005-09-15 19:26:59 +0000165 top-left corner of the console */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000166static void move_cursor(int x, int y)
167{
Rob Landley9200e792005-09-15 19:26:59 +0000168 printf("\033[%i;%iH", x, y);
169}
170
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000171static void clear_line(void)
172{
Rob Landley9200e792005-09-15 19:26:59 +0000173 move_cursor(height, 0);
174 printf("\033[K");
175}
176
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000177/* This adds line numbers to every line, as the -N flag necessitates */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000178static void add_linenumbers(void)
179{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000180 int i;
181
182 for (i = 0; i <= num_flines; i++) {
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000183 char *new = xasprintf("%5d %s", i + 1, flines[i]);
184 free(flines[i]);
185 flines[i] = new;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000186 }
187}
188
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000189static void data_readlines(void)
190{
Rob Landley9200e792005-09-15 19:26:59 +0000191 int i;
192 char current_line[256];
193 FILE *fp;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000194
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000195 fp = (flags & LESS_STATE_INP_STDIN) ? stdin : xfopen(filename, "r");
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000196 flines = NULL;
197 for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) {
Rob Landley9200e792005-09-15 19:26:59 +0000198 strcpy(current_line, "");
199 fgets(current_line, 256, fp);
Mike Frysinger00d10a92006-04-16 20:54:19 +0000200 if (fp != stdin)
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000201 die_if_ferror(fp, filename);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000202 flines = xrealloc(flines, (i+1) * sizeof(char *));
Rob Landleyd921b2e2006-08-03 15:41:12 +0000203 flines[i] = xstrdup(current_line);
Rob Landley9200e792005-09-15 19:26:59 +0000204 }
205 num_flines = i - 2;
206
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000207 /* Reset variables for a new file */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000208
Rob Landley9200e792005-09-15 19:26:59 +0000209 line_pos = 0;
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000210 flags &= ~LESS_STATE_PAST_EOF;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000211
Rob Landley9200e792005-09-15 19:26:59 +0000212 fclose(fp);
213
Mike Frysinger00d10a92006-04-16 20:54:19 +0000214 if (inp == NULL)
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000215 inp = (flags & LESS_STATE_INP_STDIN) ? xfopen(CURRENT_TTY, "r") : stdin;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000216
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000217 if (flags & FLAG_N)
Rob Landley9200e792005-09-15 19:26:59 +0000218 add_linenumbers();
219}
220
Rob Landley9200e792005-09-15 19:26:59 +0000221#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000222
223/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
224 * on my build. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000225static int calc_percent(void)
226{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000227 return ((100 * (line_pos + height - 2) / num_flines) + 1);
228}
229
Rob Landley9200e792005-09-15 19:26:59 +0000230/* Print a status line if -M was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000231static void m_status_print(void)
232{
Rob Landley9200e792005-09-15 19:26:59 +0000233 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000234
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000235 if (!(flags & LESS_STATE_PAST_EOF)) {
Rob Landley9200e792005-09-15 19:26:59 +0000236 if (!line_pos) {
237 if (num_files > 1)
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000238 printf("%s%s %s%i%s%i%s%i-%i/%i ", HIGHLIGHT,
239 filename, "(file ", current_file, " of ", num_files, ") lines ",
240 line_pos + 1, line_pos + height - 1, num_flines + 1);
Rob Landley9200e792005-09-15 19:26:59 +0000241 else {
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000242 printf("%s%s lines %i-%i/%i ", HIGHLIGHT,
243 filename, line_pos + 1, line_pos + height - 1,
244 num_flines + 1);
Rob Landley9200e792005-09-15 19:26:59 +0000245 }
246 }
247 else {
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000248 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename,
249 line_pos + 1, line_pos + height - 1, num_flines + 1);
Rob Landley9200e792005-09-15 19:26:59 +0000250 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000251
Rob Landley9200e792005-09-15 19:26:59 +0000252 if (line_pos == num_flines - height + 2) {
253 printf("(END) %s", NORMAL);
254 if ((num_files > 1) && (current_file != num_files))
255 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
256 }
257 else {
258 percentage = calc_percent();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000259 printf("%i%% %s", percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000260 }
261 }
262 else {
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000263 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename,
264 line_pos + 1, num_flines + 1, num_flines + 1);
Rob Landley9200e792005-09-15 19:26:59 +0000265 if ((num_files > 1) && (current_file != num_files))
266 printf("- Next: %s", files[current_file]);
267 printf("%s", NORMAL);
268 }
269}
270
271/* Print a status line if -m was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000272static void medium_status_print(void)
273{
Rob Landley9200e792005-09-15 19:26:59 +0000274 int percentage;
275 percentage = calc_percent();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000276
Rob Landley9200e792005-09-15 19:26:59 +0000277 if (!line_pos)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000278 printf("%s%s %i%%%s", HIGHLIGHT, filename, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000279 else if (line_pos == num_flines - height + 2)
280 printf("%s(END)%s", HIGHLIGHT, NORMAL);
281 else
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000282 printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000283}
284#endif
285
286/* Print the status line */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000287static void status_print(void)
288{
Rob Landley9200e792005-09-15 19:26:59 +0000289 /* Change the status if flags have been set */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000290#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000291 if (flags & FLAG_M)
Rob Landley9200e792005-09-15 19:26:59 +0000292 m_status_print();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000293 else if (flags & FLAG_m)
Rob Landley9200e792005-09-15 19:26:59 +0000294 medium_status_print();
295 /* No flags set */
296 else {
297#endif
298 if (!line_pos) {
299 printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
300 if (num_files > 1)
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000301 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ",
302 current_file, " of ", num_files, ")", NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000303 }
304 else if (line_pos == num_flines - height + 2) {
305 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
306 if ((num_files > 1) && (current_file != num_files))
307 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
308 }
309 else {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000310 putchar(':');
Rob Landley9200e792005-09-15 19:26:59 +0000311 }
312#ifdef CONFIG_FEATURE_LESS_FLAGS
313 }
314#endif
315}
316
317/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000318static void buffer_print(void)
319{
Rob Landley9200e792005-09-15 19:26:59 +0000320 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000321
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000322 printf("%s", CLEAR);
Rob Landley9200e792005-09-15 19:26:59 +0000323 if (num_flines >= height - 2) {
Rob Landley9200e792005-09-15 19:26:59 +0000324 for (i = 0; i < height - 1; i++)
325 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000326 }
327 else {
Rob Landley9200e792005-09-15 19:26:59 +0000328 for (i = 1; i < (height - 1 - num_flines); i++)
329 putchar('\n');
330 for (i = 0; i < height - 1; i++)
331 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000332 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000333
334 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000335}
336
337/* Initialise the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000338static void buffer_init(void)
339{
Rob Landley9200e792005-09-15 19:26:59 +0000340 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000341
Mike Frysinger00d10a92006-04-16 20:54:19 +0000342 if (buffer == NULL) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000343 /* malloc the number of lines needed for the buffer */
344 buffer = xrealloc(buffer, height * sizeof(char *));
345 } else {
346 for (i = 0; i < (height - 1); i++)
347 free(buffer[i]);
348 }
349
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000350 /* Fill the buffer until the end of the file or the
Rob Landley9200e792005-09-15 19:26:59 +0000351 end of the buffer is reached */
352 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
Rob Landleyd921b2e2006-08-03 15:41:12 +0000353 buffer[i] = xstrdup(flines[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000354 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000355
Rob Landley9200e792005-09-15 19:26:59 +0000356 /* If the buffer still isn't full, fill it with blank lines */
357 for (; i < (height - 1); i++) {
Rob Landleyd921b2e2006-08-03 15:41:12 +0000358 buffer[i] = xstrdup("");
Rob Landley9200e792005-09-15 19:26:59 +0000359 }
360}
361
362/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000363static void buffer_down(int nlines)
364{
Rob Landley9200e792005-09-15 19:26:59 +0000365 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000366
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000367 if (!(flags & LESS_STATE_PAST_EOF)) {
Rob Landley9200e792005-09-15 19:26:59 +0000368 if (line_pos + (height - 3) + nlines < num_flines) {
369 line_pos += nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000370 for (i = 0; i < (height - 1); i++) {
371 free(buffer[i]);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000372 buffer[i] = xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000373 }
Rob Landley9200e792005-09-15 19:26:59 +0000374 }
375 else {
376 /* As the number of lines requested was too large, we just move
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000377 to the end of the file */
378 while (line_pos + (height - 3) + 1 < num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000379 line_pos += 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000380 for (i = 0; i < (height - 1); i++) {
381 free(buffer[i]);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000382 buffer[i] = xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000383 }
Rob Landley9200e792005-09-15 19:26:59 +0000384 }
385 }
386
387 /* We exit if the -E flag has been set */
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000388 if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
Rob Landley9200e792005-09-15 19:26:59 +0000389 tless_exit(0);
390 }
391}
392
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000393static void buffer_up(int nlines)
394{
Rob Landley9200e792005-09-15 19:26:59 +0000395 int i;
396 int tilde_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000397
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000398 if (!(flags & LESS_STATE_PAST_EOF)) {
Rob Landley9200e792005-09-15 19:26:59 +0000399 if (line_pos - nlines >= 0) {
400 line_pos -= nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000401 for (i = 0; i < (height - 1); i++) {
402 free(buffer[i]);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000403 buffer[i] = xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000404 }
Rob Landley9200e792005-09-15 19:26:59 +0000405 }
406 else {
407 /* As the requested number of lines to move was too large, we
408 move one line up at a time until we can't. */
409 while (line_pos != 0) {
410 line_pos -= 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000411 for (i = 0; i < (height - 1); i++) {
412 free(buffer[i]);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000413 buffer[i] = xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000414 }
Rob Landley9200e792005-09-15 19:26:59 +0000415 }
416 }
417 }
418 else {
419 /* Work out where the tildes start */
420 tilde_line = num_flines - line_pos + 3;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000421
Rob Landley9200e792005-09-15 19:26:59 +0000422 line_pos -= nlines;
423 /* Going backwards nlines lines has taken us to a point where
424 nothing is past the EOF, so we revert to normal. */
425 if (line_pos < num_flines - height + 3) {
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000426 flags &= ~LESS_STATE_PAST_EOF;
Rob Landley9200e792005-09-15 19:26:59 +0000427 buffer_up(nlines);
428 }
429 else {
430 /* We only move part of the buffer, as the rest
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000431 is past the EOF */
Rob Landley9200e792005-09-15 19:26:59 +0000432 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000433 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000434 if (i < tilde_line - nlines + 1)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000435 buffer[i] = xstrdup(flines[line_pos + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000436 else {
437 if (line_pos >= num_flines - height + 2)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000438 buffer[i] = xstrdup("~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000439 }
440 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000441 }
Rob Landley9200e792005-09-15 19:26:59 +0000442 }
443}
444
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000445static void buffer_line(int linenum)
446{
Rob Landley9200e792005-09-15 19:26:59 +0000447 int i;
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000448 flags &= ~LESS_STATE_PAST_EOF;
Rob Landley9200e792005-09-15 19:26:59 +0000449
Mike Frysingerf054be12006-04-29 04:21:10 +0000450 if (linenum < 0 || linenum > num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000451 clear_line();
Mike Frysingerf054be12006-04-29 04:21:10 +0000452 printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum + 1, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000453 }
454 else if (linenum < (num_flines - height - 2)) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000455 for (i = 0; i < (height - 1); i++) {
456 free(buffer[i]);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000457 buffer[i] = xstrdup(flines[linenum + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000458 }
Rob Landley9200e792005-09-15 19:26:59 +0000459 line_pos = linenum;
Mike Frysingerf054be12006-04-29 04:21:10 +0000460 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000461 }
462 else {
463 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000464 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000465 if (linenum + i < num_flines + 2)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000466 buffer[i] = xstrdup(flines[linenum + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000467 else
Rob Landleyd921b2e2006-08-03 15:41:12 +0000468 buffer[i] = xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000469 }
470 line_pos = linenum;
471 /* Set past_eof so buffer_down and buffer_up act differently */
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000472 flags |= LESS_STATE_PAST_EOF;
Mike Frysingerf054be12006-04-29 04:21:10 +0000473 buffer_print();
Rob Landley9200e792005-09-15 19:26:59 +0000474 }
475}
476
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000477/* Reinitialise everything for a new file - free the memory and start over */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000478static void reinitialise(void)
479{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000480 int i;
481
482 for (i = 0; i <= num_flines; i++)
483 free(flines[i]);
484 free(flines);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000485
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000486 data_readlines();
487 buffer_init();
488 buffer_print();
489}
490
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000491static void examine_file(void)
492{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000493 int newline_offset;
494
495 clear_line();
496 printf("Examine: ");
497 fgets(filename, 256, inp);
498
499 /* As fgets adds a newline to the end of an input string, we
500 need to remove it */
501 newline_offset = strlen(filename) - 1;
502 filename[newline_offset] = '\0';
503
Rob Landleyd921b2e2006-08-03 15:41:12 +0000504 files[num_files] = xstrdup(filename);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000505 current_file = num_files + 1;
506 num_files++;
507
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000508 flags &= ~LESS_STATE_INP_STDIN;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000509 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000510}
511
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000512/* This function changes the file currently being paged. direction can be one of the following:
513 * -1: go back one file
514 * 0: go to the first file
515 * 1: go forward one file
516*/
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000517static void change_file(int direction)
518{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000519 if (current_file != ((direction > 0) ? num_files : 1)) {
520 current_file = direction ? current_file + direction : 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000521 strcpy(filename, files[current_file - 1]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000522 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000523 }
524 else {
525 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000526 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000527 }
528}
529
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000530static void remove_current_file(void)
531{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000532 int i;
533
534 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000535 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000536 for (i = 3; i <= num_files; i++)
537 files[i - 2] = files[i - 1];
538 num_files--;
539 buffer_print();
540 }
541 else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000542 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000543 for (i = 2; i <= num_files; i++)
544 files[i - 2] = files[i - 1];
545 num_files--;
546 current_file--;
547 buffer_print();
548 }
549}
550
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000551static void colon_process(void)
552{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000553 int keypress;
554
555 /* Clear the current line and print a prompt */
556 clear_line();
557 printf(" :");
558
559 keypress = tless_getch();
560 switch (keypress) {
561 case 'd':
562 remove_current_file();
563 break;
564 case 'e':
565 examine_file();
566 break;
567#ifdef CONFIG_FEATURE_LESS_FLAGS
568 case 'f':
569 clear_line();
570 m_status_print();
571 break;
572#endif
573 case 'n':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000574 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000575 break;
576 case 'p':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000577 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000578 break;
579 case 'q':
580 tless_exit(0);
581 break;
582 case 'x':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000583 change_file(0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000584 break;
585 default:
586 break;
587 }
588}
589
590#ifdef CONFIG_FEATURE_LESS_REGEXP
591/* The below two regular expression handler functions NEED development. */
592
593/* Get a regular expression from the user, and then go through the current
594 file line by line, running a processing regex function on each one. */
595
Mike Frysingerf054be12006-04-29 04:21:10 +0000596static char *process_regex_on_line(char *line, regex_t *pattern, int action)
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000597{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000598 /* This function takes the regex and applies it to the line.
599 Each part of the line that matches has the HIGHLIGHT
600 and NORMAL escape sequences placed around it by
Mike Frysingerf054be12006-04-29 04:21:10 +0000601 insert_highlights if action = 1, or has the escape sequences
602 removed if action = 0, and then the line is returned. */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000603 int match_status;
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000604 char *line2 = xmalloc((sizeof(char) * (strlen(line) + 1)) + 64);
Mike Frysingerf054be12006-04-29 04:21:10 +0000605 char *growline = "";
"Vladimir N. Oleynik"8d3c40d2005-09-16 13:16:01 +0000606 regmatch_t match_structs;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000607
Rob Landleyd921b2e2006-08-03 15:41:12 +0000608 line2 = xstrdup(line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000609
610 match_found = 0;
611 match_status = regexec(pattern, line2, 1, &match_structs, 0);
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000612
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000613 while (match_status == 0) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000614 if (match_found == 0)
615 match_found = 1;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000616
Mike Frysingerf054be12006-04-29 04:21:10 +0000617 if (action) {
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000618 growline = xasprintf("%s%.*s%s%.*s%s", growline,
619 match_structs.rm_so, line2, HIGHLIGHT,
620 match_structs.rm_eo - match_structs.rm_so,
621 line2 + match_structs.rm_so, NORMAL);
Mike Frysingerf054be12006-04-29 04:21:10 +0000622 }
623 else {
Denis Vlasenkof0ed3762006-10-26 23:21:47 +0000624 growline = xasprintf("%s%.*s%.*s", growline,
625 match_structs.rm_so - 4, line2,
626 match_structs.rm_eo - match_structs.rm_so,
627 line2 + match_structs.rm_so);
Mike Frysingerf054be12006-04-29 04:21:10 +0000628 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000629
Mike Frysingerf054be12006-04-29 04:21:10 +0000630 line2 += match_structs.rm_eo;
631 match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000632 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000633
Rob Landleyd921b2e2006-08-03 15:41:12 +0000634 growline = xasprintf("%s%s", growline, line2);
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000635
Mike Frysingerf054be12006-04-29 04:21:10 +0000636 return (match_found ? growline : line);
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000637
Mike Frysingerf054be12006-04-29 04:21:10 +0000638 free(growline);
639 free(line2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000640}
641
Rob Landleya2e98042006-04-18 01:53:41 +0000642static void goto_match(int match)
643{
644 /* This goes to a specific match - all line positions of matches are
645 stored within the match_lines[] array. */
646 if ((match < num_matches) && (match >= 0)) {
647 buffer_line(match_lines[match]);
648 match_pos = match;
649 }
650}
651
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000652static void regex_process(void)
653{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000654 char uncomp_regex[100];
Mike Frysingerf054be12006-04-29 04:21:10 +0000655 char *current_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000656 int i;
657 int j = 0;
Mike Frysinger20c22e02006-04-16 21:41:00 +0000658 regex_t pattern;
Rob Landleya2e98042006-04-18 01:53:41 +0000659 /* Get the uncompiled regular expression from the user */
660 clear_line();
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000661 putchar((flags & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
Rob Landleya2e98042006-04-18 01:53:41 +0000662 uncomp_regex[0] = 0;
663 fgets(uncomp_regex, sizeof(uncomp_regex), inp);
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000664
Rob Landleya2e98042006-04-18 01:53:41 +0000665 if (strlen(uncomp_regex) == 1) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000666 if (num_matches)
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000667 goto_match((flags & LESS_STATE_MATCH_BACKWARDS)
668 ? match_pos - 1 : match_pos + 1);
Mike Frysingerf054be12006-04-29 04:21:10 +0000669 else
670 buffer_print();
Rob Landleya2e98042006-04-18 01:53:41 +0000671 return;
672 }
Rob Landleya2e98042006-04-18 01:53:41 +0000673 uncomp_regex[strlen(uncomp_regex) - 1] = '\0';
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000674
Rob Landleya2e98042006-04-18 01:53:41 +0000675 /* Compile the regex and check for errors */
676 xregcomp(&pattern, uncomp_regex, 0);
677
Mike Frysingerf054be12006-04-29 04:21:10 +0000678 if (num_matches) {
679 /* Get rid of all the highlights we added previously */
680 for (i = 0; i <= num_flines; i++) {
681 current_line = process_regex_on_line(flines[i], &old_pattern, 0);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000682 flines[i] = xstrdup(current_line);
Mike Frysingerf054be12006-04-29 04:21:10 +0000683 }
684 }
685 old_pattern = pattern;
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000686
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000687 /* Reset variables */
Mike Frysingerf054be12006-04-29 04:21:10 +0000688 match_lines = xrealloc(match_lines, sizeof(int));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000689 match_lines[0] = -1;
690 match_pos = 0;
691 num_matches = 0;
692 match_found = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000693 /* Run the regex on each line of the current file here */
694 for (i = 0; i <= num_flines; i++) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000695 current_line = process_regex_on_line(flines[i], &pattern, 1);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000696 flines[i] = xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000697 if (match_found) {
Mike Frysingerf054be12006-04-29 04:21:10 +0000698 match_lines = xrealloc(match_lines, (j + 1) * sizeof(int));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000699 match_lines[j] = i;
700 j++;
701 }
702 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000703
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000704 num_matches = j;
Rob Landleya2e98042006-04-18 01:53:41 +0000705 if ((match_lines[0] != -1) && (num_flines > height - 2)) {
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000706 if (flags & LESS_STATE_MATCH_BACKWARDS) {
Rob Landleya2e98042006-04-18 01:53:41 +0000707 for (i = 0; i < num_matches; i++) {
708 if (match_lines[i] > line_pos) {
709 match_pos = i - 1;
710 buffer_line(match_lines[match_pos]);
711 break;
712 }
713 }
714 }
715 else
716 buffer_line(match_lines[0]);
717 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000718 else
719 buffer_init();
720}
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000721#endif
722
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000723static void number_process(int first_digit)
724{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000725 int i = 1;
726 int num;
727 char num_input[80];
728 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000729 char *endptr;
730
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000731 num_input[0] = first_digit;
732
733 /* Clear the current line, print a prompt, and then print the digit */
734 clear_line();
735 printf(":%c", first_digit);
736
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000737 /* Receive input until a letter is given (max 80 chars)*/
738 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000739 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000740 i++;
741 }
742
743 /* Take the final letter out of the digits string */
744 keypress = num_input[i];
745 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000746 num = strtol(num_input, &endptr, 10);
Rob Landleya2e98042006-04-18 01:53:41 +0000747 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES) {
748 buffer_print();
749 return;
750 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000751
752 /* We now know the number and the letter entered, so we process them */
753 switch (keypress) {
754 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
755 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000756 break;
757 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
758 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000759 break;
760 case 'g': case '<': case 'G': case '>':
761 if (num_flines >= height - 2)
762 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000763 break;
764 case 'p': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +0000765 buffer_line(((num / 100) * num_flines) - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000766 break;
767#ifdef CONFIG_FEATURE_LESS_REGEXP
768 case 'n':
Rob Landleya2e98042006-04-18 01:53:41 +0000769 goto_match(match_pos + num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000770 break;
771 case '/':
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000772 flags &= ~LESS_STATE_MATCH_BACKWARDS;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000773 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000774 break;
775 case '?':
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000776 flags |= LESS_STATE_MATCH_BACKWARDS;
Rob Landleya2e98042006-04-18 01:53:41 +0000777 regex_process();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000778 break;
779#endif
780 default:
781 break;
782 }
783}
784
785#ifdef CONFIG_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000786static void flag_change(void)
787{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000788 int keypress;
789
790 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000791 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000792 keypress = tless_getch();
793
794 switch (keypress) {
795 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000796 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000797 break;
798 case 'm':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000799 flags ^= FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000800 break;
801 case 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000802 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000803 break;
804 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000805 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000806 break;
807 default:
808 break;
809 }
810}
811
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000812static void show_flag_status(void)
813{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000814 int keypress;
815 int flag_val;
816
817 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000818 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000819 keypress = tless_getch();
820
821 switch (keypress) {
822 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000823 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000824 break;
825 case 'm':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000826 flag_val = flags & FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000827 break;
828 case '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000829 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000830 break;
831 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000832 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000833 break;
834 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000835 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000836 break;
837 default:
838 flag_val = 0;
839 break;
840 }
841
842 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000843 printf("%s%s%i%s", HIGHLIGHT, "The status of the flag is: ", flag_val != 0, NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000844}
845#endif
846
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000847static void full_repaint(void)
848{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000849 int temp_line_pos = line_pos;
850 data_readlines();
851 buffer_init();
852 buffer_line(temp_line_pos);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000853}
854
855
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000856static void save_input_to_file(void)
857{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000858 char current_line[256];
859 int i;
860 FILE *fp;
861
862 clear_line();
863 printf("Log file: ");
864 fgets(current_line, 256, inp);
865 current_line[strlen(current_line) - 1] = '\0';
Mike Frysingerf054be12006-04-29 04:21:10 +0000866 if (strlen(current_line) > 1) {
Rob Landleyd921b2e2006-08-03 15:41:12 +0000867 fp = xfopen(current_line, "w");
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000868 for (i = 0; i < num_flines; i++)
869 fprintf(fp, "%s", flines[i]);
870 fclose(fp);
871 buffer_print();
872 }
873 else
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +0000874 printf("%s%s%s", HIGHLIGHT, "No log file", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000875}
876
877#ifdef CONFIG_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000878static void add_mark(void)
879{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000880 int letter;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000881
882 clear_line();
883 printf("Mark: ");
884 letter = tless_getch();
885
886 if (isalpha(letter)) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000887
888 /* If we exceed 15 marks, start overwriting previous ones */
889 if (num_marks == 14)
890 num_marks = 0;
891
892 mark_lines[num_marks][0] = letter;
893 mark_lines[num_marks][1] = line_pos;
894 num_marks++;
895 }
896 else {
897 clear_line();
898 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
899 }
900}
901
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000902static void goto_mark(void)
903{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000904 int letter;
905 int i;
906
907 clear_line();
908 printf("Go to mark: ");
909 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000910 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000911
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000912 if (isalpha(letter)) {
913 for (i = 0; i <= num_marks; i++)
914 if (letter == mark_lines[i][0]) {
915 buffer_line(mark_lines[i][1]);
916 break;
917 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000918 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000919 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000920 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000921 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000922 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000923}
924#endif
925
926
927#ifdef CONFIG_FEATURE_LESS_BRACKETS
928
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000929static char opp_bracket(char bracket)
930{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000931 switch (bracket) {
932 case '{': case '[':
933 return bracket + 2;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000934 case '(':
935 return ')';
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000936 case '}': case ']':
937 return bracket - 2;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000938 case ')':
939 return '(';
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000940 default:
941 return 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000942 }
943}
944
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000945static void match_right_bracket(char bracket)
946{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000947 int bracket_line = -1;
948 int i;
949
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000950 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000951
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000952 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000953 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000954 else {
955 for (i = line_pos + 1; i < num_flines; i++) {
956 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
957 bracket_line = i;
958 break;
959 }
960 }
961
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000962 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000963 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000964
965 buffer_line(bracket_line - height + 2);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000966 }
967}
968
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000969static void match_left_bracket(char bracket)
970{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000971 int bracket_line = -1;
972 int i;
973
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000974 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000975
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000976 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000977 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
978 printf("%s", flines[line_pos + height]);
979 sleep(4);
980 }
981 else {
982 for (i = line_pos + height - 2; i >= 0; i--) {
983 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
984 bracket_line = i;
985 break;
986 }
987 }
988
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000989 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000990 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000991
992 buffer_line(bracket_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000993 }
994}
995
996#endif /* CONFIG_FEATURE_LESS_BRACKETS */
997
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000998static void keypress_process(int keypress)
999{
Rob Landley9200e792005-09-15 19:26:59 +00001000 switch (keypress) {
1001 case KEY_DOWN: case 'e': case 'j': case '\015':
1002 buffer_down(1);
1003 buffer_print();
1004 break;
1005 case KEY_UP: case 'y': case 'k':
1006 buffer_up(1);
1007 buffer_print();
1008 break;
1009 case PAGE_DOWN: case ' ': case 'z':
1010 buffer_down(height - 1);
1011 buffer_print();
1012 break;
1013 case PAGE_UP: case 'w': case 'b':
1014 buffer_up(height - 1);
1015 buffer_print();
1016 break;
1017 case 'd':
1018 buffer_down((height - 1) / 2);
1019 buffer_print();
1020 break;
1021 case 'u':
1022 buffer_up((height - 1) / 2);
1023 buffer_print();
1024 break;
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +00001025 case KEY_HOME: case 'g': case 'p': case '<': case '%':
Mike Frysingerf054be12006-04-29 04:21:10 +00001026 buffer_line(0);
Rob Landley9200e792005-09-15 19:26:59 +00001027 break;
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +00001028 case KEY_END: case 'G': case '>':
Mike Frysingerf054be12006-04-29 04:21:10 +00001029 buffer_line(num_flines - height + 2);
Rob Landley9200e792005-09-15 19:26:59 +00001030 break;
1031 case 'q': case 'Q':
1032 tless_exit(0);
1033 break;
1034#ifdef CONFIG_FEATURE_LESS_MARKS
1035 case 'm':
1036 add_mark();
1037 buffer_print();
1038 break;
1039 case '\'':
1040 goto_mark();
1041 buffer_print();
1042 break;
1043#endif
1044 case 'r':
1045 buffer_print();
1046 break;
1047 case 'R':
1048 full_repaint();
1049 break;
1050 case 's':
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +00001051 if (flags & LESS_STATE_INP_STDIN)
Rob Landley9200e792005-09-15 19:26:59 +00001052 save_input_to_file();
1053 break;
1054 case 'E':
1055 examine_file();
1056 break;
1057#ifdef CONFIG_FEATURE_LESS_FLAGS
1058 case '=':
1059 clear_line();
1060 m_status_print();
1061 break;
1062#endif
1063#ifdef CONFIG_FEATURE_LESS_REGEXP
1064 case '/':
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +00001065 flags &= ~LESS_STATE_MATCH_BACKWARDS;
Rob Landley9200e792005-09-15 19:26:59 +00001066 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001067 break;
1068 case 'n':
1069 goto_match(match_pos + 1);
Rob Landley9200e792005-09-15 19:26:59 +00001070 break;
1071 case 'N':
1072 goto_match(match_pos - 1);
Rob Landley9200e792005-09-15 19:26:59 +00001073 break;
1074 case '?':
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +00001075 flags |= LESS_STATE_MATCH_BACKWARDS;
Rob Landleya2e98042006-04-18 01:53:41 +00001076 regex_process();
Rob Landley9200e792005-09-15 19:26:59 +00001077 break;
1078#endif
1079#ifdef CONFIG_FEATURE_LESS_FLAGCS
1080 case '-':
1081 flag_change();
1082 buffer_print();
1083 break;
1084 case '_':
1085 show_flag_status();
1086 break;
1087#endif
1088#ifdef CONFIG_FEATURE_LESS_BRACKETS
1089 case '{': case '(': case '[':
1090 match_right_bracket(keypress);
1091 break;
1092 case '}': case ')': case ']':
1093 match_left_bracket(keypress);
1094 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001095#endif
1096 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001097 colon_process();
1098 break;
1099 default:
1100 break;
1101 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001102
Rob Landley9200e792005-09-15 19:26:59 +00001103 if (isdigit(keypress))
1104 number_process(keypress);
1105}
1106
Rob Landley9200e792005-09-15 19:26:59 +00001107int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001108
Rob Landley9200e792005-09-15 19:26:59 +00001109 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001110
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001111 flags = getopt32(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001112
1113 argc -= optind;
1114 argv += optind;
1115 files = argv;
1116 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001117
Rob Landley9200e792005-09-15 19:26:59 +00001118 if (!num_files) {
1119 if (ttyname(STDIN_FILENO) == NULL)
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +00001120 flags |= LESS_STATE_INP_STDIN;
Rob Landley9200e792005-09-15 19:26:59 +00001121 else {
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00001122 bb_error_msg("missing filename");
Rob Landley9200e792005-09-15 19:26:59 +00001123 bb_show_usage();
1124 }
1125 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001126
Bernhard Reutner-Fischer882e60c2006-12-04 16:04:50 +00001127 strcpy(filename, (flags & LESS_STATE_INP_STDIN) ? bb_msg_standard_input : files[0]);
Rob Landley69d863b2006-05-25 21:13:30 +00001128 get_terminal_width_height(0, &width, &height);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001129 data_readlines();
1130 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001131 term_vi = term_orig;
1132 term_vi.c_lflag &= (~ICANON & ~ECHO);
1133 term_vi.c_iflag &= (~IXON & ~ICRNL);
1134 term_vi.c_oflag &= (~ONLCR);
1135 term_vi.c_cc[VMIN] = 1;
1136 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001137 buffer_init();
1138 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001139
Rob Landley9200e792005-09-15 19:26:59 +00001140 while (1) {
1141 keypress = tless_getch();
1142 keypress_process(keypress);
1143 }
1144}