blob: 189b886f712c5480e35516db3d4f0014c16936e4 [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"39a841c2005-09-29 16:18:57 +0000211 flines[i] = bb_xasprintf("%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"39a841c2005-09-29 16:18:57 +0000621 return bb_xasprintf("%.*s%s%.*s%s%s", start, line, HIGHLIGHT,
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000622 end - start, line + start, NORMAL, line + end);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000623}
624
625static char *process_regex_on_line(char *line, regex_t *pattern) {
626 /* This function takes the regex and applies it to the line.
627 Each part of the line that matches has the HIGHLIGHT
628 and NORMAL escape sequences placed around it by
629 insert_highlights, and then the line is returned. */
630
631 int match_status;
632 char *line2 = (char *) malloc((sizeof(char) * (strlen(line) + 1)) + 64);
633 char sub_line[256];
634 int prev_eo = 0;
"Vladimir N. Oleynik"8d3c40d2005-09-16 13:16:01 +0000635 regmatch_t match_structs;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000636
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000637 memset(sub_line, 0, 256);
638 strcpy(line2, line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000639
640 match_found = 0;
641 match_status = regexec(pattern, line2, 1, &match_structs, 0);
642
643 while (match_status == 0) {
644
645 memset(sub_line, 0, 256);
646
647 if (match_found == 0)
648 match_found = 1;
649
650 line2 = insert_highlights(line2, match_structs.rm_so + prev_eo, match_structs.rm_eo + prev_eo);
651 if (match_structs.rm_eo + 11 + prev_eo < strlen(line2))
652 strcat(sub_line, line2 + match_structs.rm_eo + 11 + prev_eo);
653
654 prev_eo += match_structs.rm_eo + 11;
655 match_status = regexec(pattern, sub_line, 1, &match_structs, REG_NOTBOL);
656 }
657
658 return line2;
659}
660
661static void regex_process(void) {
662
663 char uncomp_regex[100];
664 char current_line[256];
665 int i;
666 int j = 0;
667 regex_t *pattern;
668
669 /* Reset variables */
670 match_lines[0] = -1;
671 match_pos = 0;
672 num_matches = 0;
673 match_found = 0;
674
675 pattern = (regex_t *) malloc(sizeof(regex_t));
676 memset(pattern, 0, sizeof(regex_t));
677
678 /* Get the uncompiled regular expression from the user */
679 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000680 putchar((match_backwards) ? '?' : '/');
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000681 uncomp_regex[0] = 0;
682 fgets(uncomp_regex, sizeof(uncomp_regex), stdin);
683 i = strlen(uncomp_regex);
684 if(i > 0) {
685 if(uncomp_regex[i-1] == '\n')
686 uncomp_regex[i-1] = '\0';
687 else
688 while((i = getchar()) != '\n' && i != EOF);
689 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000690
691 /* Compile the regex and check for errors */
692 xregcomp(pattern, uncomp_regex, 0);
693
694 /* Run the regex on each line of the current file here */
695 for (i = 0; i <= num_flines; i++) {
696 strcpy(current_line, process_regex_on_line(flines[i], pattern));
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000697 flines[i] = bb_xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000698 if (match_found) {
699 match_lines[j] = i;
700 j++;
701 }
702 }
703
704 num_matches = j;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000705 if ((match_lines[0] != -1) && (num_flines > height - 2))
706 buffer_line(match_lines[0]);
707 else
708 buffer_init();
709}
710
711static void goto_match(int match) {
712
713 /* This goes to a specific match - all line positions of matches are
714 stored within the match_lines[] array. */
715 if ((match < num_matches) && (match >= 0)) {
716 buffer_line(match_lines[match]);
717 match_pos = match;
718 }
719}
720
721static void search_backwards(void) {
722
723 int current_linepos = line_pos;
724 int i;
725
726 match_backwards = 1;
727 regex_process();
728
729 for (i = 0; i < num_matches; i++) {
730 if (match_lines[i] > current_linepos) {
731 buffer_line(match_lines[i - num_back_match]);
732 break;
733 }
734 }
735
736 /* Reset variables */
737 match_backwards = 0;
738 num_back_match = 1;
739
740}
741#endif
742
743static void number_process(int first_digit) {
744
745 int i = 1;
746 int num;
747 char num_input[80];
748 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000749 char *endptr;
750
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000751 num_input[0] = first_digit;
752
753 /* Clear the current line, print a prompt, and then print the digit */
754 clear_line();
755 printf(":%c", first_digit);
756
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000757 /* Receive input until a letter is given (max 80 chars)*/
758 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000759 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000760 i++;
761 }
762
763 /* Take the final letter out of the digits string */
764 keypress = num_input[i];
765 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000766 num = strtol(num_input, &endptr, 10);
767 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES)
768 goto END;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000769
770 /* We now know the number and the letter entered, so we process them */
771 switch (keypress) {
772 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
773 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000774 break;
775 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
776 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000777 break;
778 case 'g': case '<': case 'G': case '>':
779 if (num_flines >= height - 2)
780 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000781 break;
782 case 'p': case '%':
783 buffer_line(reverse_percent(num));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000784 break;
785#ifdef CONFIG_FEATURE_LESS_REGEXP
786 case 'n':
787 goto_match(match_pos + num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000788 break;
789 case '/':
790 regex_process();
791 goto_match(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000792 break;
793 case '?':
794 num_back_match = num;
795 search_backwards();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000796 break;
797#endif
798 default:
799 break;
800 }
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000801END:
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000802 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000803}
804
805#ifdef CONFIG_FEATURE_LESS_FLAGCS
806static void flag_change(void) {
807
808 int keypress;
809
810 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000811 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000812 keypress = tless_getch();
813
814 switch (keypress) {
815 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000816 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000817 break;
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 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000822 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000823 break;
824 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000825 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000826 break;
827 default:
828 break;
829 }
830}
831
832static void show_flag_status(void) {
833
834 int keypress;
835 int flag_val;
836
837 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000838 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000839 keypress = tless_getch();
840
841 switch (keypress) {
842 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000843 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000844 break;
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 '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000849 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000850 break;
851 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000852 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000853 break;
854 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000855 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000856 break;
857 default:
858 flag_val = 0;
859 break;
860 }
861
862 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000863 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 +0000864}
865#endif
866
867static void full_repaint(void) {
868
869 int temp_line_pos = line_pos;
870 data_readlines();
871 buffer_init();
872 buffer_line(temp_line_pos);
873 buffer_print();
874}
875
876
877static void save_input_to_file(void) {
878
879 char current_line[256];
880 int i;
881 FILE *fp;
882
883 clear_line();
884 printf("Log file: ");
885 fgets(current_line, 256, inp);
886 current_line[strlen(current_line) - 1] = '\0';
887 if (strlen(current_line)) {
888 fp = bb_xfopen(current_line, "w");
889 for (i = 0; i < num_flines; i++)
890 fprintf(fp, "%s", flines[i]);
891 fclose(fp);
892 buffer_print();
893 }
894 else
895 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
896}
897
898#ifdef CONFIG_FEATURE_LESS_MARKS
899static void add_mark(void) {
900
901 int letter;
902 int mark_line;
903
904 clear_line();
905 printf("Mark: ");
906 letter = tless_getch();
907
908 if (isalpha(letter)) {
909 mark_line = line_pos;
910
911 /* If we exceed 15 marks, start overwriting previous ones */
912 if (num_marks == 14)
913 num_marks = 0;
914
915 mark_lines[num_marks][0] = letter;
916 mark_lines[num_marks][1] = line_pos;
917 num_marks++;
918 }
919 else {
920 clear_line();
921 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
922 }
923}
924
925static void goto_mark(void) {
926
927 int letter;
928 int i;
929
930 clear_line();
931 printf("Go to mark: ");
932 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000933 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000934
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000935 if (isalpha(letter)) {
936 for (i = 0; i <= num_marks; i++)
937 if (letter == mark_lines[i][0]) {
938 buffer_line(mark_lines[i][1]);
939 break;
940 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000941 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000942 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000943 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000944 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000945 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000946}
947#endif
948
949
950#ifdef CONFIG_FEATURE_LESS_BRACKETS
951
952static char opp_bracket (char bracket) {
953
954 switch (bracket) {
955 case '{': case '[':
956 return bracket + 2;
957 break;
958 case '(':
959 return ')';
960 break;
961 case '}': case ']':
962 return bracket - 2;
963 break;
964 case ')':
965 return '(';
966 break;
967 default:
968 return 0;
969 break;
970 }
971}
972
973static void match_right_bracket(char bracket) {
974
975 int bracket_line = -1;
976 int i;
977
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000978 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000979
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000980 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000981 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000982 else {
983 for (i = line_pos + 1; i < num_flines; i++) {
984 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
985 bracket_line = i;
986 break;
987 }
988 }
989
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000990 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000991 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000992
993 buffer_line(bracket_line - height + 2);
994 buffer_print();
995 }
996}
997
998static void match_left_bracket (char bracket) {
999
1000 int bracket_line = -1;
1001 int i;
1002
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001003 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001004
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001005 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001006 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
1007 printf("%s", flines[line_pos + height]);
1008 sleep(4);
1009 }
1010 else {
1011 for (i = line_pos + height - 2; i >= 0; i--) {
1012 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
1013 bracket_line = i;
1014 break;
1015 }
1016 }
1017
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001018 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001019 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001020
1021 buffer_line(bracket_line);
1022 buffer_print();
1023 }
1024}
1025
1026#endif /* CONFIG_FEATURE_LESS_BRACKETS */
1027
Rob Landley9200e792005-09-15 19:26:59 +00001028static void keypress_process(int keypress) {
1029 switch (keypress) {
1030 case KEY_DOWN: case 'e': case 'j': case '\015':
1031 buffer_down(1);
1032 buffer_print();
1033 break;
1034 case KEY_UP: case 'y': case 'k':
1035 buffer_up(1);
1036 buffer_print();
1037 break;
1038 case PAGE_DOWN: case ' ': case 'z':
1039 buffer_down(height - 1);
1040 buffer_print();
1041 break;
1042 case PAGE_UP: case 'w': case 'b':
1043 buffer_up(height - 1);
1044 buffer_print();
1045 break;
1046 case 'd':
1047 buffer_down((height - 1) / 2);
1048 buffer_print();
1049 break;
1050 case 'u':
1051 buffer_up((height - 1) / 2);
1052 buffer_print();
1053 break;
1054 case 'g': case 'p': case '<': case '%':
1055 buffer_up(num_flines + 1);
1056 buffer_print();
1057 break;
1058 case 'G': case '>':
1059 buffer_down(num_flines + 1);
1060 buffer_print();
1061 break;
1062 case 'q': case 'Q':
1063 tless_exit(0);
1064 break;
1065#ifdef CONFIG_FEATURE_LESS_MARKS
1066 case 'm':
1067 add_mark();
1068 buffer_print();
1069 break;
1070 case '\'':
1071 goto_mark();
1072 buffer_print();
1073 break;
1074#endif
1075 case 'r':
1076 buffer_print();
1077 break;
1078 case 'R':
1079 full_repaint();
1080 break;
1081 case 's':
1082 if (inp_stdin)
1083 save_input_to_file();
1084 break;
1085 case 'E':
1086 examine_file();
1087 break;
1088#ifdef CONFIG_FEATURE_LESS_FLAGS
1089 case '=':
1090 clear_line();
1091 m_status_print();
1092 break;
1093#endif
1094#ifdef CONFIG_FEATURE_LESS_REGEXP
1095 case '/':
1096 regex_process();
1097 buffer_print();
1098 break;
1099 case 'n':
1100 goto_match(match_pos + 1);
1101 buffer_print();
1102 break;
1103 case 'N':
1104 goto_match(match_pos - 1);
1105 buffer_print();
1106 break;
1107 case '?':
1108 search_backwards();
1109 buffer_print();
1110 break;
1111#endif
1112#ifdef CONFIG_FEATURE_LESS_FLAGCS
1113 case '-':
1114 flag_change();
1115 buffer_print();
1116 break;
1117 case '_':
1118 show_flag_status();
1119 break;
1120#endif
1121#ifdef CONFIG_FEATURE_LESS_BRACKETS
1122 case '{': case '(': case '[':
1123 match_right_bracket(keypress);
1124 break;
1125 case '}': case ')': case ']':
1126 match_left_bracket(keypress);
1127 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001128#endif
1129 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001130 colon_process();
1131 break;
1132 default:
1133 break;
1134 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001135
Rob Landley9200e792005-09-15 19:26:59 +00001136 if (isdigit(keypress))
1137 number_process(keypress);
1138}
1139
Rob Landley9200e792005-09-15 19:26:59 +00001140int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001141
Rob Landley9200e792005-09-15 19:26:59 +00001142 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001143
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001144 flags = bb_getopt_ulflags(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001145
1146 argc -= optind;
1147 argv += optind;
1148 files = argv;
1149 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001150
Rob Landley9200e792005-09-15 19:26:59 +00001151 if (!num_files) {
1152 if (ttyname(STDIN_FILENO) == NULL)
1153 inp_stdin = 1;
1154 else {
1155 bb_error_msg("Missing filename");
1156 bb_show_usage();
1157 }
1158 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001159
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001160 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
Rob Landley9200e792005-09-15 19:26:59 +00001161 tty_width_height();
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001162 data_readlines();
1163 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001164 term_vi = term_orig;
1165 term_vi.c_lflag &= (~ICANON & ~ECHO);
1166 term_vi.c_iflag &= (~IXON & ~ICRNL);
1167 term_vi.c_oflag &= (~ONLCR);
1168 term_vi.c_cc[VMIN] = 1;
1169 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001170 buffer_init();
1171 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001172
Rob Landley9200e792005-09-15 19:26:59 +00001173 while (1) {
1174 keypress = tless_getch();
1175 keypress_process(keypress);
1176 }
1177}