blob: 7b1b96c6d8c66415333b8a62df17078a2bc0f0eb [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 *
5 *
6 * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 * 02111-1307 USA
22 *
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000023 * This program needs a lot of development, so consider it in a beta stage
24 * at best.
Rob Landley9200e792005-09-15 19:26:59 +000025 *
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000026 * TODO:
27 * - Add more regular expression support - search modifiers, certain matches, etc.
28 * - Add more complex bracket searching - currently, nested brackets are
29 * not considered.
30 * - Add support for "F" as an input. This causes less to act in
31 * a similar way to tail -f.
32 * - Check for binary files, and prompt the user if a binary file
33 * is detected.
34 * - Allow horizontal scrolling. Currently, lines simply continue onto
35 * the next line, per the terminal's discretion
Rob Landley9200e792005-09-15 19:26:59 +000036 *
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000037 * Notes:
38 * - filename is an array and not a pointer because that avoids all sorts
39 * of complications involving the fact that something that is pointed to
40 * will be changed if the pointer is changed.
41 * - the inp file pointer is used so that keyboard input works after
42 * redirected input has been read from stdin
Rob Landley9200e792005-09-15 19:26:59 +000043*/
44
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <termios.h>
49#include <unistd.h>
Rob Landley9200e792005-09-15 19:26:59 +000050#include <ctype.h>
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000051
Rob Landley9200e792005-09-15 19:26:59 +000052#include "busybox.h"
53
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000054#ifdef CONFIG_FEATURE_LESS_REGEXP
55#include "xregex.h"
56#endif
57
58
Rob Landley9200e792005-09-15 19:26:59 +000059/* These are the escape sequences corresponding to special keys */
60#define REAL_KEY_UP 'A'
61#define REAL_KEY_DOWN 'B'
62#define REAL_KEY_RIGHT 'C'
63#define REAL_KEY_LEFT 'D'
64#define REAL_PAGE_UP '5'
65#define REAL_PAGE_DOWN '6'
66
67/* These are the special codes assigned by this program to the special keys */
68#define PAGE_UP 20
69#define PAGE_DOWN 21
70#define KEY_UP 22
71#define KEY_DOWN 23
72#define KEY_RIGHT 24
73#define KEY_LEFT 25
74
75/* The escape codes for highlighted and normal text */
76#define HIGHLIGHT "\033[7m"
77#define NORMAL "\033[0m"
78
79/* The escape code to clear the screen */
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +000080#define CLEAR "\033[H\033[J"
Rob Landley9200e792005-09-15 19:26:59 +000081
82/* Maximum number of lines in a file */
83#define MAXLINES 10000
84
85/* Get height and width of terminal */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000086#define tty_width_height() get_terminal_width_height(0, &width, &height)
Rob Landley9200e792005-09-15 19:26:59 +000087
88static int height;
89static int width;
90static char **files;
91static char filename[256];
Rob Landleyd57ae8b2005-09-18 00:58:49 +000092static char **buffer;
93static char **flines;
Rob Landley9200e792005-09-15 19:26:59 +000094static int current_file = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000095static int line_pos;
Rob Landley9200e792005-09-15 19:26:59 +000096static int num_flines;
97static int num_files = 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +000098static int past_eof;
Rob Landley9200e792005-09-15 19:26:59 +000099
100/* Command line options */
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000101static unsigned long flags;
102#define FLAG_E 1
103#define FLAG_M (1<<1)
104#define FLAG_m (1<<2)
105#define FLAG_N (1<<3)
106#define FLAG_TILDE (1<<4)
Rob Landley9200e792005-09-15 19:26:59 +0000107
108/* This is needed so that program behaviour changes when input comes from
109 stdin */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000110static int inp_stdin;
Rob Landley9200e792005-09-15 19:26:59 +0000111
112#ifdef CONFIG_FEATURE_LESS_MARKS
113static int mark_lines[15][2];
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000114static int num_marks;
Rob Landley9200e792005-09-15 19:26:59 +0000115#endif
116
117#ifdef CONFIG_FEATURE_LESS_REGEXP
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000118static int match_found;
Rob Landley9200e792005-09-15 19:26:59 +0000119static int match_lines[100];
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000120static int match_pos;
121static int num_matches;
122static int match_backwards;
Rob Landley9200e792005-09-15 19:26:59 +0000123static int num_back_match = 1;
124#endif
125
126/* Needed termios structures */
127static struct termios term_orig, term_vi;
128
129/* File pointer to get input from */
130static FILE *inp;
131
132/* Reset terminal input to normal */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000133static void set_tty_cooked(void) {
134 fflush(stdout);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000135 tcsetattr(fileno(inp), TCSANOW, &term_orig);
Rob Landley9200e792005-09-15 19:26:59 +0000136}
137
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000138/* Set terminal input to raw mode (taken from vi.c) */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000139static void set_tty_raw(void) {
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000140 tcsetattr(fileno(inp), TCSANOW, &term_vi);
Rob Landley9200e792005-09-15 19:26:59 +0000141}
142
143/* Exit the program gracefully */
144static void tless_exit(int code) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000145
Rob Landley9200e792005-09-15 19:26:59 +0000146 /* TODO: We really should save the terminal state when we start,
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000147 and restore it when we exit. Less does this with the
Rob Landley9200e792005-09-15 19:26:59 +0000148 "ti" and "te" termcap commands; can this be done with
149 only termios.h? */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000150
Rob Landley9200e792005-09-15 19:26:59 +0000151 putchar('\n');
152 exit(code);
153}
154
155/* Grab a character from input without requiring the return key. If the
156 character is ASCII \033, get more characters and assign certain sequences
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000157 special return codes. Note that this function works best with raw input. */
158static int tless_getch(void) {
159
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000160 int input;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000161
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000162 set_tty_raw();
163
164 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000165 /* Detect escape sequences (i.e. arrow keys) and handle
166 them accordingly */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000167
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000168 if (input == '\033' && getc(inp) == '[') {
169 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000170 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000171 if (input == REAL_KEY_UP)
172 return KEY_UP;
173 else if (input == REAL_KEY_DOWN)
174 return KEY_DOWN;
175 else if (input == REAL_KEY_RIGHT)
176 return KEY_RIGHT;
177 else if (input == REAL_KEY_LEFT)
178 return KEY_LEFT;
179 else if (input == REAL_PAGE_UP)
180 return PAGE_UP;
181 else if (input == REAL_PAGE_DOWN)
182 return PAGE_DOWN;
Rob Landley9200e792005-09-15 19:26:59 +0000183 }
184 /* The input is a normal ASCII value */
185 else {
186 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000187 return input;
Rob Landley9200e792005-09-15 19:26:59 +0000188 }
189 return 0;
190}
191
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000192/* Move the cursor to a position (x,y), where (0,0) is the
Rob Landley9200e792005-09-15 19:26:59 +0000193 top-left corner of the console */
194static void move_cursor(int x, int y) {
195 printf("\033[%i;%iH", x, y);
196}
197
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000198static void clear_line(void) {
Rob Landley9200e792005-09-15 19:26:59 +0000199 move_cursor(height, 0);
200 printf("\033[K");
201}
202
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000203/* This adds line numbers to every line, as the -N flag necessitates */
204static void add_linenumbers(void) {
205
206 char current_line[256];
207 int i;
208
209 for (i = 0; i <= num_flines; i++) {
210 safe_strncpy(current_line, flines[i], 256);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000211 bb_xasprintf(&flines[i],"%5d %s", i + 1, current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000212 }
213}
214
215static void data_readlines(void) {
216
Rob Landley9200e792005-09-15 19:26:59 +0000217 int i;
218 char current_line[256];
219 FILE *fp;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000220
Rob Landley9200e792005-09-15 19:26:59 +0000221 fp = (inp_stdin) ? stdin : bb_xfopen(filename, "rt");
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000222 flines = NULL;
223 for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) {
Rob Landley9200e792005-09-15 19:26:59 +0000224 strcpy(current_line, "");
225 fgets(current_line, 256, fp);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000226 if(fp != stdin)
227 bb_xferror(fp, filename);
228 flines = xrealloc(flines, (i+1) * sizeof(char *));
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000229 flines[i] = bb_xstrdup(current_line);
Rob Landley9200e792005-09-15 19:26:59 +0000230 }
231 num_flines = i - 2;
232
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000233 /* Reset variables for a new file */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000234
Rob Landley9200e792005-09-15 19:26:59 +0000235 line_pos = 0;
236 past_eof = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000237
Rob Landley9200e792005-09-15 19:26:59 +0000238 fclose(fp);
239
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000240 if(inp == NULL)
241 inp = (inp_stdin) ? fopen(CURRENT_TTY, "r") : stdin;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000242
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000243 if (flags & FLAG_N)
Rob Landley9200e792005-09-15 19:26:59 +0000244 add_linenumbers();
245}
246
Rob Landley9200e792005-09-15 19:26:59 +0000247/* Turn a percentage into a line number */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000248static int reverse_percent(int percentage) {
Rob Landley9200e792005-09-15 19:26:59 +0000249 double linenum = percentage;
250 linenum = ((linenum / 100) * num_flines) - 1;
251 return(linenum);
252}
253
254#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000255
256/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
257 * on my build. */
258static int calc_percent(void) {
259 return ((100 * (line_pos + height - 2) / num_flines) + 1);
260}
261
Rob Landley9200e792005-09-15 19:26:59 +0000262/* Print a status line if -M was specified */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000263static void m_status_print(void) {
Rob Landley9200e792005-09-15 19:26:59 +0000264
265 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000266
Rob Landley9200e792005-09-15 19:26:59 +0000267 if (!past_eof) {
268 if (!line_pos) {
269 if (num_files > 1)
270 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);
271 else {
272 printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
273 }
274 }
275 else {
276 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
277 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000278
Rob Landley9200e792005-09-15 19:26:59 +0000279 if (line_pos == num_flines - height + 2) {
280 printf("(END) %s", NORMAL);
281 if ((num_files > 1) && (current_file != num_files))
282 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
283 }
284 else {
285 percentage = calc_percent();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000286 printf("%i%% %s", percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000287 }
288 }
289 else {
290 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
291 if ((num_files > 1) && (current_file != num_files))
292 printf("- Next: %s", files[current_file]);
293 printf("%s", NORMAL);
294 }
295}
296
297/* Print a status line if -m was specified */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000298static void medium_status_print(void) {
Rob Landley9200e792005-09-15 19:26:59 +0000299
300 int percentage;
301 percentage = calc_percent();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000302
Rob Landley9200e792005-09-15 19:26:59 +0000303 if (!line_pos)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000304 printf("%s%s %i%%%s", HIGHLIGHT, filename, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000305 else if (line_pos == num_flines - height + 2)
306 printf("%s(END)%s", HIGHLIGHT, NORMAL);
307 else
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000308 printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000309}
310#endif
311
312/* Print the status line */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000313static void status_print(void) {
314
Rob Landley9200e792005-09-15 19:26:59 +0000315 /* Change the status if flags have been set */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000316#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000317 if (flags & FLAG_M)
Rob Landley9200e792005-09-15 19:26:59 +0000318 m_status_print();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000319 else if (flags & FLAG_m)
Rob Landley9200e792005-09-15 19:26:59 +0000320 medium_status_print();
321 /* No flags set */
322 else {
323#endif
324 if (!line_pos) {
325 printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
326 if (num_files > 1)
327 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
328 }
329 else if (line_pos == num_flines - height + 2) {
330 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
331 if ((num_files > 1) && (current_file != num_files))
332 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
333 }
334 else {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000335 putchar(':');
Rob Landley9200e792005-09-15 19:26:59 +0000336 }
337#ifdef CONFIG_FEATURE_LESS_FLAGS
338 }
339#endif
340}
341
342/* Print the buffer */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000343static void buffer_print(void) {
344
Rob Landley9200e792005-09-15 19:26:59 +0000345 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000346
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000347 printf("%s", CLEAR);
Rob Landley9200e792005-09-15 19:26:59 +0000348 if (num_flines >= height - 2) {
Rob Landley9200e792005-09-15 19:26:59 +0000349 for (i = 0; i < height - 1; i++)
350 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000351 }
352 else {
Rob Landley9200e792005-09-15 19:26:59 +0000353 for (i = 1; i < (height - 1 - num_flines); i++)
354 putchar('\n');
355 for (i = 0; i < height - 1; i++)
356 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000357 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000358
359 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000360}
361
362/* Initialise the buffer */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000363static void buffer_init(void) {
364
Rob Landley9200e792005-09-15 19:26:59 +0000365 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000366
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000367 if(buffer == NULL) {
368 /* malloc the number of lines needed for the buffer */
369 buffer = xrealloc(buffer, height * sizeof(char *));
370 } else {
371 for (i = 0; i < (height - 1); i++)
372 free(buffer[i]);
373 }
374
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000375 /* Fill the buffer until the end of the file or the
Rob Landley9200e792005-09-15 19:26:59 +0000376 end of the buffer is reached */
377 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000378 buffer[i] = bb_xstrdup(flines[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000379 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000380
Rob Landley9200e792005-09-15 19:26:59 +0000381 /* If the buffer still isn't full, fill it with blank lines */
382 for (; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000383 buffer[i] = bb_xstrdup("");
Rob Landley9200e792005-09-15 19:26:59 +0000384 }
385}
386
387/* Move the buffer up and down in the file in order to scroll */
388static void buffer_down(int nlines) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000389
Rob Landley9200e792005-09-15 19:26:59 +0000390 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000391
Rob Landley9200e792005-09-15 19:26:59 +0000392 if (!past_eof) {
393 if (line_pos + (height - 3) + nlines < num_flines) {
394 line_pos += nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000395 for (i = 0; i < (height - 1); i++) {
396 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000397 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000398 }
Rob Landley9200e792005-09-15 19:26:59 +0000399 }
400 else {
401 /* As the number of lines requested was too large, we just move
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000402 to the end of the file */
403 while (line_pos + (height - 3) + 1 < num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000404 line_pos += 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000405 for (i = 0; i < (height - 1); i++) {
406 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000407 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000408 }
Rob Landley9200e792005-09-15 19:26:59 +0000409 }
410 }
411
412 /* We exit if the -E flag has been set */
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000413 if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
Rob Landley9200e792005-09-15 19:26:59 +0000414 tless_exit(0);
415 }
416}
417
418static void buffer_up(int nlines) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000419
Rob Landley9200e792005-09-15 19:26:59 +0000420 int i;
421 int tilde_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000422
Rob Landley9200e792005-09-15 19:26:59 +0000423 if (!past_eof) {
424 if (line_pos - nlines >= 0) {
425 line_pos -= nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000426 for (i = 0; i < (height - 1); i++) {
427 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000428 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000429 }
Rob Landley9200e792005-09-15 19:26:59 +0000430 }
431 else {
432 /* As the requested number of lines to move was too large, we
433 move one line up at a time until we can't. */
434 while (line_pos != 0) {
435 line_pos -= 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000436 for (i = 0; i < (height - 1); i++) {
437 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000438 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000439 }
Rob Landley9200e792005-09-15 19:26:59 +0000440 }
441 }
442 }
443 else {
444 /* Work out where the tildes start */
445 tilde_line = num_flines - line_pos + 3;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000446
Rob Landley9200e792005-09-15 19:26:59 +0000447 line_pos -= nlines;
448 /* Going backwards nlines lines has taken us to a point where
449 nothing is past the EOF, so we revert to normal. */
450 if (line_pos < num_flines - height + 3) {
451 past_eof = 0;
452 buffer_up(nlines);
453 }
454 else {
455 /* We only move part of the buffer, as the rest
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000456 is past the EOF */
Rob Landley9200e792005-09-15 19:26:59 +0000457 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000458 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000459 if (i < tilde_line - nlines + 1)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000460 buffer[i] = bb_xstrdup(flines[line_pos + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000461 else {
462 if (line_pos >= num_flines - height + 2)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000463 buffer[i] = bb_xstrdup("~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000464 }
465 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000466 }
Rob Landley9200e792005-09-15 19:26:59 +0000467 }
468}
469
470static void buffer_line(int linenum) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000471
Rob Landley9200e792005-09-15 19:26:59 +0000472 int i;
473
474 past_eof = 0;
475
476 if (linenum < 1 || linenum > num_flines) {
477 clear_line();
478 printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum, NORMAL);
479 }
480 else if (linenum < (num_flines - height - 2)) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000481 for (i = 0; i < (height - 1); i++) {
482 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000483 buffer[i] = bb_xstrdup(flines[linenum + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000484 }
Rob Landley9200e792005-09-15 19:26:59 +0000485 line_pos = linenum;
486 }
487 else {
488 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000489 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000490 if (linenum + i < num_flines + 2)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000491 buffer[i] = bb_xstrdup(flines[linenum + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000492 else
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000493 buffer[i] = bb_xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000494 }
495 line_pos = linenum;
496 /* Set past_eof so buffer_down and buffer_up act differently */
497 past_eof = 1;
498 }
499}
500
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000501/* Reinitialise everything for a new file - free the memory and start over */
502static void reinitialise(void) {
503
504 int i;
505
506 for (i = 0; i <= num_flines; i++)
507 free(flines[i]);
508 free(flines);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000509
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000510 data_readlines();
511 buffer_init();
512 buffer_print();
513}
514
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000515static void examine_file(void) {
516
517 int newline_offset;
518
519 clear_line();
520 printf("Examine: ");
521 fgets(filename, 256, inp);
522
523 /* As fgets adds a newline to the end of an input string, we
524 need to remove it */
525 newline_offset = strlen(filename) - 1;
526 filename[newline_offset] = '\0';
527
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000528 files[num_files] = bb_xstrdup(filename);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000529 current_file = num_files + 1;
530 num_files++;
531
532 inp_stdin = 0;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000533 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000534}
535
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000536/* This function changes the file currently being paged. direction can be one of the following:
537 * -1: go back one file
538 * 0: go to the first file
539 * 1: go forward one file
540*/
541static void change_file (int direction) {
542 if (current_file != ((direction > 0) ? num_files : 1)) {
543 current_file = direction ? current_file + direction : 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000544 strcpy(filename, files[current_file - 1]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000545 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000546 }
547 else {
548 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000549 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000550 }
551}
552
553static void remove_current_file(void) {
554
555 int i;
556
557 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000558 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000559 for (i = 3; i <= num_files; i++)
560 files[i - 2] = files[i - 1];
561 num_files--;
562 buffer_print();
563 }
564 else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000565 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000566 for (i = 2; i <= num_files; i++)
567 files[i - 2] = files[i - 1];
568 num_files--;
569 current_file--;
570 buffer_print();
571 }
572}
573
574static void colon_process(void) {
575
576 int keypress;
577
578 /* Clear the current line and print a prompt */
579 clear_line();
580 printf(" :");
581
582 keypress = tless_getch();
583 switch (keypress) {
584 case 'd':
585 remove_current_file();
586 break;
587 case 'e':
588 examine_file();
589 break;
590#ifdef CONFIG_FEATURE_LESS_FLAGS
591 case 'f':
592 clear_line();
593 m_status_print();
594 break;
595#endif
596 case 'n':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000597 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000598 break;
599 case 'p':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000600 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000601 break;
602 case 'q':
603 tless_exit(0);
604 break;
605 case 'x':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000606 change_file(0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000607 break;
608 default:
609 break;
610 }
611}
612
613#ifdef CONFIG_FEATURE_LESS_REGEXP
614/* The below two regular expression handler functions NEED development. */
615
616/* Get a regular expression from the user, and then go through the current
617 file line by line, running a processing regex function on each one. */
618
619static char *insert_highlights (char *line, int start, int end) {
620
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000621 char *new_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000622
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000623 bb_xasprintf(&new_line, "%.*s%s%.*s%s%s", start, line, HIGHLIGHT,
624 end - start, line + start, NORMAL, line + end);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000625 return new_line;
626}
627
628static char *process_regex_on_line(char *line, regex_t *pattern) {
629 /* This function takes the regex and applies it to the line.
630 Each part of the line that matches has the HIGHLIGHT
631 and NORMAL escape sequences placed around it by
632 insert_highlights, and then the line is returned. */
633
634 int match_status;
635 char *line2 = (char *) malloc((sizeof(char) * (strlen(line) + 1)) + 64);
636 char sub_line[256];
637 int prev_eo = 0;
"Vladimir N. Oleynik"8d3c40d2005-09-16 13:16:01 +0000638 regmatch_t match_structs;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000639
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000640 memset(sub_line, 0, 256);
641 strcpy(line2, line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000642
643 match_found = 0;
644 match_status = regexec(pattern, line2, 1, &match_structs, 0);
645
646 while (match_status == 0) {
647
648 memset(sub_line, 0, 256);
649
650 if (match_found == 0)
651 match_found = 1;
652
653 line2 = insert_highlights(line2, match_structs.rm_so + prev_eo, match_structs.rm_eo + prev_eo);
654 if (match_structs.rm_eo + 11 + prev_eo < strlen(line2))
655 strcat(sub_line, line2 + match_structs.rm_eo + 11 + prev_eo);
656
657 prev_eo += match_structs.rm_eo + 11;
658 match_status = regexec(pattern, sub_line, 1, &match_structs, REG_NOTBOL);
659 }
660
661 return line2;
662}
663
664static void regex_process(void) {
665
666 char uncomp_regex[100];
667 char current_line[256];
668 int i;
669 int j = 0;
670 regex_t *pattern;
671
672 /* Reset variables */
673 match_lines[0] = -1;
674 match_pos = 0;
675 num_matches = 0;
676 match_found = 0;
677
678 pattern = (regex_t *) malloc(sizeof(regex_t));
679 memset(pattern, 0, sizeof(regex_t));
680
681 /* Get the uncompiled regular expression from the user */
682 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000683 putchar((match_backwards) ? '?' : '/');
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000684 uncomp_regex[0] = 0;
685 fgets(uncomp_regex, sizeof(uncomp_regex), stdin);
686 i = strlen(uncomp_regex);
687 if(i > 0) {
688 if(uncomp_regex[i-1] == '\n')
689 uncomp_regex[i-1] = '\0';
690 else
691 while((i = getchar()) != '\n' && i != EOF);
692 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000693
694 /* Compile the regex and check for errors */
695 xregcomp(pattern, uncomp_regex, 0);
696
697 /* Run the regex on each line of the current file here */
698 for (i = 0; i <= num_flines; i++) {
699 strcpy(current_line, process_regex_on_line(flines[i], pattern));
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000700 flines[i] = bb_xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000701 if (match_found) {
702 match_lines[j] = i;
703 j++;
704 }
705 }
706
707 num_matches = j;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000708 if ((match_lines[0] != -1) && (num_flines > height - 2))
709 buffer_line(match_lines[0]);
710 else
711 buffer_init();
712}
713
714static void goto_match(int match) {
715
716 /* This goes to a specific match - all line positions of matches are
717 stored within the match_lines[] array. */
718 if ((match < num_matches) && (match >= 0)) {
719 buffer_line(match_lines[match]);
720 match_pos = match;
721 }
722}
723
724static void search_backwards(void) {
725
726 int current_linepos = line_pos;
727 int i;
728
729 match_backwards = 1;
730 regex_process();
731
732 for (i = 0; i < num_matches; i++) {
733 if (match_lines[i] > current_linepos) {
734 buffer_line(match_lines[i - num_back_match]);
735 break;
736 }
737 }
738
739 /* Reset variables */
740 match_backwards = 0;
741 num_back_match = 1;
742
743}
744#endif
745
746static void number_process(int first_digit) {
747
748 int i = 1;
749 int num;
750 char num_input[80];
751 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000752 char *endptr;
753
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000754 num_input[0] = first_digit;
755
756 /* Clear the current line, print a prompt, and then print the digit */
757 clear_line();
758 printf(":%c", first_digit);
759
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000760 /* Receive input until a letter is given (max 80 chars)*/
761 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000762 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000763 i++;
764 }
765
766 /* Take the final letter out of the digits string */
767 keypress = num_input[i];
768 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000769 num = strtol(num_input, &endptr, 10);
770 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES)
771 goto END;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000772
773 /* We now know the number and the letter entered, so we process them */
774 switch (keypress) {
775 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
776 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000777 break;
778 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
779 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000780 break;
781 case 'g': case '<': case 'G': case '>':
782 if (num_flines >= height - 2)
783 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000784 break;
785 case 'p': case '%':
786 buffer_line(reverse_percent(num));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000787 break;
788#ifdef CONFIG_FEATURE_LESS_REGEXP
789 case 'n':
790 goto_match(match_pos + num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000791 break;
792 case '/':
793 regex_process();
794 goto_match(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000795 break;
796 case '?':
797 num_back_match = num;
798 search_backwards();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000799 break;
800#endif
801 default:
802 break;
803 }
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000804END:
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000805 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000806}
807
808#ifdef CONFIG_FEATURE_LESS_FLAGCS
809static void flag_change(void) {
810
811 int keypress;
812
813 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000814 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000815 keypress = tless_getch();
816
817 switch (keypress) {
818 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000819 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000820 break;
821 case 'm':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000822 flags ^= FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000823 break;
824 case 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000825 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000826 break;
827 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000828 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000829 break;
830 default:
831 break;
832 }
833}
834
835static void show_flag_status(void) {
836
837 int keypress;
838 int flag_val;
839
840 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000841 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000842 keypress = tless_getch();
843
844 switch (keypress) {
845 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000846 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000847 break;
848 case 'm':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000849 flag_val = flags & FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000850 break;
851 case '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000852 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000853 break;
854 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000855 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000856 break;
857 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000858 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000859 break;
860 default:
861 flag_val = 0;
862 break;
863 }
864
865 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000866 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 +0000867}
868#endif
869
870static void full_repaint(void) {
871
872 int temp_line_pos = line_pos;
873 data_readlines();
874 buffer_init();
875 buffer_line(temp_line_pos);
876 buffer_print();
877}
878
879
880static void save_input_to_file(void) {
881
882 char current_line[256];
883 int i;
884 FILE *fp;
885
886 clear_line();
887 printf("Log file: ");
888 fgets(current_line, 256, inp);
889 current_line[strlen(current_line) - 1] = '\0';
890 if (strlen(current_line)) {
891 fp = bb_xfopen(current_line, "w");
892 for (i = 0; i < num_flines; i++)
893 fprintf(fp, "%s", flines[i]);
894 fclose(fp);
895 buffer_print();
896 }
897 else
898 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
899}
900
901#ifdef CONFIG_FEATURE_LESS_MARKS
902static void add_mark(void) {
903
904 int letter;
905 int mark_line;
906
907 clear_line();
908 printf("Mark: ");
909 letter = tless_getch();
910
911 if (isalpha(letter)) {
912 mark_line = line_pos;
913
914 /* If we exceed 15 marks, start overwriting previous ones */
915 if (num_marks == 14)
916 num_marks = 0;
917
918 mark_lines[num_marks][0] = letter;
919 mark_lines[num_marks][1] = line_pos;
920 num_marks++;
921 }
922 else {
923 clear_line();
924 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
925 }
926}
927
928static void goto_mark(void) {
929
930 int letter;
931 int i;
932
933 clear_line();
934 printf("Go to mark: ");
935 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000936 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000937
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000938 if (isalpha(letter)) {
939 for (i = 0; i <= num_marks; i++)
940 if (letter == mark_lines[i][0]) {
941 buffer_line(mark_lines[i][1]);
942 break;
943 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000944 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000945 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000946 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000947 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000948 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000949}
950#endif
951
952
953#ifdef CONFIG_FEATURE_LESS_BRACKETS
954
955static char opp_bracket (char bracket) {
956
957 switch (bracket) {
958 case '{': case '[':
959 return bracket + 2;
960 break;
961 case '(':
962 return ')';
963 break;
964 case '}': case ']':
965 return bracket - 2;
966 break;
967 case ')':
968 return '(';
969 break;
970 default:
971 return 0;
972 break;
973 }
974}
975
976static void match_right_bracket(char bracket) {
977
978 int bracket_line = -1;
979 int i;
980
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000981 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000982
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000983 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000984 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000985 else {
986 for (i = line_pos + 1; i < num_flines; i++) {
987 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
988 bracket_line = i;
989 break;
990 }
991 }
992
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000993 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000994 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000995
996 buffer_line(bracket_line - height + 2);
997 buffer_print();
998 }
999}
1000
1001static void match_left_bracket (char bracket) {
1002
1003 int bracket_line = -1;
1004 int i;
1005
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001006 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001007
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001008 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001009 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
1010 printf("%s", flines[line_pos + height]);
1011 sleep(4);
1012 }
1013 else {
1014 for (i = line_pos + height - 2; i >= 0; i--) {
1015 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
1016 bracket_line = i;
1017 break;
1018 }
1019 }
1020
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001021 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001022 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001023
1024 buffer_line(bracket_line);
1025 buffer_print();
1026 }
1027}
1028
1029#endif /* CONFIG_FEATURE_LESS_BRACKETS */
1030
Rob Landley9200e792005-09-15 19:26:59 +00001031static void keypress_process(int keypress) {
1032 switch (keypress) {
1033 case KEY_DOWN: case 'e': case 'j': case '\015':
1034 buffer_down(1);
1035 buffer_print();
1036 break;
1037 case KEY_UP: case 'y': case 'k':
1038 buffer_up(1);
1039 buffer_print();
1040 break;
1041 case PAGE_DOWN: case ' ': case 'z':
1042 buffer_down(height - 1);
1043 buffer_print();
1044 break;
1045 case PAGE_UP: case 'w': case 'b':
1046 buffer_up(height - 1);
1047 buffer_print();
1048 break;
1049 case 'd':
1050 buffer_down((height - 1) / 2);
1051 buffer_print();
1052 break;
1053 case 'u':
1054 buffer_up((height - 1) / 2);
1055 buffer_print();
1056 break;
1057 case 'g': case 'p': case '<': case '%':
1058 buffer_up(num_flines + 1);
1059 buffer_print();
1060 break;
1061 case 'G': case '>':
1062 buffer_down(num_flines + 1);
1063 buffer_print();
1064 break;
1065 case 'q': case 'Q':
1066 tless_exit(0);
1067 break;
1068#ifdef CONFIG_FEATURE_LESS_MARKS
1069 case 'm':
1070 add_mark();
1071 buffer_print();
1072 break;
1073 case '\'':
1074 goto_mark();
1075 buffer_print();
1076 break;
1077#endif
1078 case 'r':
1079 buffer_print();
1080 break;
1081 case 'R':
1082 full_repaint();
1083 break;
1084 case 's':
1085 if (inp_stdin)
1086 save_input_to_file();
1087 break;
1088 case 'E':
1089 examine_file();
1090 break;
1091#ifdef CONFIG_FEATURE_LESS_FLAGS
1092 case '=':
1093 clear_line();
1094 m_status_print();
1095 break;
1096#endif
1097#ifdef CONFIG_FEATURE_LESS_REGEXP
1098 case '/':
1099 regex_process();
1100 buffer_print();
1101 break;
1102 case 'n':
1103 goto_match(match_pos + 1);
1104 buffer_print();
1105 break;
1106 case 'N':
1107 goto_match(match_pos - 1);
1108 buffer_print();
1109 break;
1110 case '?':
1111 search_backwards();
1112 buffer_print();
1113 break;
1114#endif
1115#ifdef CONFIG_FEATURE_LESS_FLAGCS
1116 case '-':
1117 flag_change();
1118 buffer_print();
1119 break;
1120 case '_':
1121 show_flag_status();
1122 break;
1123#endif
1124#ifdef CONFIG_FEATURE_LESS_BRACKETS
1125 case '{': case '(': case '[':
1126 match_right_bracket(keypress);
1127 break;
1128 case '}': case ')': case ']':
1129 match_left_bracket(keypress);
1130 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001131#endif
1132 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001133 colon_process();
1134 break;
1135 default:
1136 break;
1137 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001138
Rob Landley9200e792005-09-15 19:26:59 +00001139 if (isdigit(keypress))
1140 number_process(keypress);
1141}
1142
Rob Landley9200e792005-09-15 19:26:59 +00001143int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001144
Rob Landley9200e792005-09-15 19:26:59 +00001145 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001146
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001147 flags = bb_getopt_ulflags(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001148
1149 argc -= optind;
1150 argv += optind;
1151 files = argv;
1152 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001153
Rob Landley9200e792005-09-15 19:26:59 +00001154 if (!num_files) {
1155 if (ttyname(STDIN_FILENO) == NULL)
1156 inp_stdin = 1;
1157 else {
1158 bb_error_msg("Missing filename");
1159 bb_show_usage();
1160 }
1161 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001162
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001163 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
Rob Landley9200e792005-09-15 19:26:59 +00001164 tty_width_height();
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001165 data_readlines();
1166 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001167 term_vi = term_orig;
1168 term_vi.c_lflag &= (~ICANON & ~ECHO);
1169 term_vi.c_iflag &= (~IXON & ~ICRNL);
1170 term_vi.c_oflag &= (~ONLCR);
1171 term_vi.c_cc[VMIN] = 1;
1172 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001173 buffer_init();
1174 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001175
Rob Landley9200e792005-09-15 19:26:59 +00001176 while (1) {
1177 keypress = tless_getch();
1178 keypress_process(keypress);
1179 }
1180}