blob: ce34701d624de0f3079d6a2d09d5b74b1350ccbd [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 */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000133static void set_tty_cooked(void)
134{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000135 fflush(stdout);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000136 tcsetattr(fileno(inp), TCSANOW, &term_orig);
Rob Landley9200e792005-09-15 19:26:59 +0000137}
138
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000139/* Set terminal input to raw mode (taken from vi.c) */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000140static void set_tty_raw(void)
141{
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000142 tcsetattr(fileno(inp), TCSANOW, &term_vi);
Rob Landley9200e792005-09-15 19:26:59 +0000143}
144
145/* Exit the program gracefully */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000146static void tless_exit(int code)
147{
Rob Landley9200e792005-09-15 19:26:59 +0000148 /* TODO: We really should save the terminal state when we start,
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000149 and restore it when we exit. Less does this with the
Rob Landley9200e792005-09-15 19:26:59 +0000150 "ti" and "te" termcap commands; can this be done with
151 only termios.h? */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000152
Rob Landley9200e792005-09-15 19:26:59 +0000153 putchar('\n');
154 exit(code);
155}
156
157/* Grab a character from input without requiring the return key. If the
158 character is ASCII \033, get more characters and assign certain sequences
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000159 special return codes. Note that this function works best with raw input. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000160static int tless_getch(void)
161{
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000162 int input;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000163
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000164 set_tty_raw();
165
166 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000167 /* Detect escape sequences (i.e. arrow keys) and handle
168 them accordingly */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000169
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000170 if (input == '\033' && getc(inp) == '[') {
171 input = getc(inp);
Rob Landley9200e792005-09-15 19:26:59 +0000172 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000173 if (input == REAL_KEY_UP)
174 return KEY_UP;
175 else if (input == REAL_KEY_DOWN)
176 return KEY_DOWN;
177 else if (input == REAL_KEY_RIGHT)
178 return KEY_RIGHT;
179 else if (input == REAL_KEY_LEFT)
180 return KEY_LEFT;
181 else if (input == REAL_PAGE_UP)
182 return PAGE_UP;
183 else if (input == REAL_PAGE_DOWN)
184 return PAGE_DOWN;
Rob Landley9200e792005-09-15 19:26:59 +0000185 }
186 /* The input is a normal ASCII value */
187 else {
188 set_tty_cooked();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000189 return input;
Rob Landley9200e792005-09-15 19:26:59 +0000190 }
191 return 0;
192}
193
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000194/* Move the cursor to a position (x,y), where (0,0) is the
Rob Landley9200e792005-09-15 19:26:59 +0000195 top-left corner of the console */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000196static void move_cursor(int x, int y)
197{
Rob Landley9200e792005-09-15 19:26:59 +0000198 printf("\033[%i;%iH", x, y);
199}
200
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000201static void clear_line(void)
202{
Rob Landley9200e792005-09-15 19:26:59 +0000203 move_cursor(height, 0);
204 printf("\033[K");
205}
206
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000207/* This adds line numbers to every line, as the -N flag necessitates */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000208static void add_linenumbers(void)
209{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000210 char current_line[256];
211 int i;
212
213 for (i = 0; i <= num_flines; i++) {
214 safe_strncpy(current_line, flines[i], 256);
"Vladimir N. Oleynik"39a841c2005-09-29 16:18:57 +0000215 flines[i] = bb_xasprintf("%5d %s", i + 1, current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000216 }
217}
218
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000219static void data_readlines(void)
220{
Rob Landley9200e792005-09-15 19:26:59 +0000221 int i;
222 char current_line[256];
223 FILE *fp;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000224
Rob Landley9200e792005-09-15 19:26:59 +0000225 fp = (inp_stdin) ? stdin : bb_xfopen(filename, "rt");
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000226 flines = NULL;
227 for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) {
Rob Landley9200e792005-09-15 19:26:59 +0000228 strcpy(current_line, "");
229 fgets(current_line, 256, fp);
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000230 if(fp != stdin)
231 bb_xferror(fp, filename);
232 flines = xrealloc(flines, (i+1) * sizeof(char *));
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000233 flines[i] = bb_xstrdup(current_line);
Rob Landley9200e792005-09-15 19:26:59 +0000234 }
235 num_flines = i - 2;
236
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000237 /* Reset variables for a new file */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000238
Rob Landley9200e792005-09-15 19:26:59 +0000239 line_pos = 0;
240 past_eof = 0;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000241
Rob Landley9200e792005-09-15 19:26:59 +0000242 fclose(fp);
243
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000244 if(inp == NULL)
"Vladimir N. Oleynik"8315cd52005-12-15 11:53:22 +0000245 inp = (inp_stdin) ? bb_xfopen(CURRENT_TTY, "r") : stdin;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000246
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000247 if (flags & FLAG_N)
Rob Landley9200e792005-09-15 19:26:59 +0000248 add_linenumbers();
249}
250
Rob Landley9200e792005-09-15 19:26:59 +0000251/* Turn a percentage into a line number */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000252static int reverse_percent(int percentage)
253{
Rob Landley9200e792005-09-15 19:26:59 +0000254 double linenum = percentage;
255 linenum = ((linenum / 100) * num_flines) - 1;
256 return(linenum);
257}
258
259#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000260
261/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
262 * on my build. */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000263static int calc_percent(void)
264{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000265 return ((100 * (line_pos + height - 2) / num_flines) + 1);
266}
267
Rob Landley9200e792005-09-15 19:26:59 +0000268/* Print a status line if -M was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000269static void m_status_print(void)
270{
Rob Landley9200e792005-09-15 19:26:59 +0000271 int percentage;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000272
Rob Landley9200e792005-09-15 19:26:59 +0000273 if (!past_eof) {
274 if (!line_pos) {
275 if (num_files > 1)
276 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);
277 else {
278 printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
279 }
280 }
281 else {
282 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
283 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000284
Rob Landley9200e792005-09-15 19:26:59 +0000285 if (line_pos == num_flines - height + 2) {
286 printf("(END) %s", NORMAL);
287 if ((num_files > 1) && (current_file != num_files))
288 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
289 }
290 else {
291 percentage = calc_percent();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000292 printf("%i%% %s", percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000293 }
294 }
295 else {
296 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
297 if ((num_files > 1) && (current_file != num_files))
298 printf("- Next: %s", files[current_file]);
299 printf("%s", NORMAL);
300 }
301}
302
303/* Print a status line if -m was specified */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000304static void medium_status_print(void)
305{
Rob Landley9200e792005-09-15 19:26:59 +0000306 int percentage;
307 percentage = calc_percent();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000308
Rob Landley9200e792005-09-15 19:26:59 +0000309 if (!line_pos)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000310 printf("%s%s %i%%%s", HIGHLIGHT, filename, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000311 else if (line_pos == num_flines - height + 2)
312 printf("%s(END)%s", HIGHLIGHT, NORMAL);
313 else
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000314 printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
Rob Landley9200e792005-09-15 19:26:59 +0000315}
316#endif
317
318/* Print the status line */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000319static void status_print(void)
320{
Rob Landley9200e792005-09-15 19:26:59 +0000321 /* Change the status if flags have been set */
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000322#ifdef CONFIG_FEATURE_LESS_FLAGS
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000323 if (flags & FLAG_M)
Rob Landley9200e792005-09-15 19:26:59 +0000324 m_status_print();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000325 else if (flags & FLAG_m)
Rob Landley9200e792005-09-15 19:26:59 +0000326 medium_status_print();
327 /* No flags set */
328 else {
329#endif
330 if (!line_pos) {
331 printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
332 if (num_files > 1)
333 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
334 }
335 else if (line_pos == num_flines - height + 2) {
336 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
337 if ((num_files > 1) && (current_file != num_files))
338 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
339 }
340 else {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000341 putchar(':');
Rob Landley9200e792005-09-15 19:26:59 +0000342 }
343#ifdef CONFIG_FEATURE_LESS_FLAGS
344 }
345#endif
346}
347
348/* Print the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000349static void buffer_print(void)
350{
Rob Landley9200e792005-09-15 19:26:59 +0000351 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000352
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000353 printf("%s", CLEAR);
Rob Landley9200e792005-09-15 19:26:59 +0000354 if (num_flines >= height - 2) {
Rob Landley9200e792005-09-15 19:26:59 +0000355 for (i = 0; i < height - 1; i++)
356 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000357 }
358 else {
Rob Landley9200e792005-09-15 19:26:59 +0000359 for (i = 1; i < (height - 1 - num_flines); i++)
360 putchar('\n');
361 for (i = 0; i < height - 1; i++)
362 printf("%s", buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000363 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000364
365 status_print();
Rob Landley9200e792005-09-15 19:26:59 +0000366}
367
368/* Initialise the buffer */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000369static void buffer_init(void)
370{
Rob Landley9200e792005-09-15 19:26:59 +0000371 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000372
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000373 if(buffer == NULL) {
374 /* malloc the number of lines needed for the buffer */
375 buffer = xrealloc(buffer, height * sizeof(char *));
376 } else {
377 for (i = 0; i < (height - 1); i++)
378 free(buffer[i]);
379 }
380
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000381 /* Fill the buffer until the end of the file or the
Rob Landley9200e792005-09-15 19:26:59 +0000382 end of the buffer is reached */
383 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000384 buffer[i] = bb_xstrdup(flines[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000385 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000386
Rob Landley9200e792005-09-15 19:26:59 +0000387 /* If the buffer still isn't full, fill it with blank lines */
388 for (; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000389 buffer[i] = bb_xstrdup("");
Rob Landley9200e792005-09-15 19:26:59 +0000390 }
391}
392
393/* Move the buffer up and down in the file in order to scroll */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000394static void buffer_down(int nlines)
395{
Rob Landley9200e792005-09-15 19:26:59 +0000396 int i;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000397
Rob Landley9200e792005-09-15 19:26:59 +0000398 if (!past_eof) {
399 if (line_pos + (height - 3) + nlines < num_flines) {
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]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000403 buffer[i] = bb_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 number of lines requested was too large, we just move
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000408 to the end of the file */
409 while (line_pos + (height - 3) + 1 < num_flines) {
Rob Landley9200e792005-09-15 19:26:59 +0000410 line_pos += 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000411 for (i = 0; i < (height - 1); i++) {
412 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000413 buffer[i] = bb_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 /* We exit if the -E flag has been set */
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000419 if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
Rob Landley9200e792005-09-15 19:26:59 +0000420 tless_exit(0);
421 }
422}
423
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000424static void buffer_up(int nlines)
425{
Rob Landley9200e792005-09-15 19:26:59 +0000426 int i;
427 int tilde_line;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000428
Rob Landley9200e792005-09-15 19:26:59 +0000429 if (!past_eof) {
430 if (line_pos - nlines >= 0) {
431 line_pos -= nlines;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000432 for (i = 0; i < (height - 1); i++) {
433 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000434 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000435 }
Rob Landley9200e792005-09-15 19:26:59 +0000436 }
437 else {
438 /* As the requested number of lines to move was too large, we
439 move one line up at a time until we can't. */
440 while (line_pos != 0) {
441 line_pos -= 1;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000442 for (i = 0; i < (height - 1); i++) {
443 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000444 buffer[i] = bb_xstrdup(flines[line_pos + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000445 }
Rob Landley9200e792005-09-15 19:26:59 +0000446 }
447 }
448 }
449 else {
450 /* Work out where the tildes start */
451 tilde_line = num_flines - line_pos + 3;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000452
Rob Landley9200e792005-09-15 19:26:59 +0000453 line_pos -= nlines;
454 /* Going backwards nlines lines has taken us to a point where
455 nothing is past the EOF, so we revert to normal. */
456 if (line_pos < num_flines - height + 3) {
457 past_eof = 0;
458 buffer_up(nlines);
459 }
460 else {
461 /* We only move part of the buffer, as the rest
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000462 is past the EOF */
Rob Landley9200e792005-09-15 19:26:59 +0000463 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 (i < tilde_line - nlines + 1)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000466 buffer[i] = bb_xstrdup(flines[line_pos + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000467 else {
468 if (line_pos >= num_flines - height + 2)
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000469 buffer[i] = bb_xstrdup("~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000470 }
471 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000472 }
Rob Landley9200e792005-09-15 19:26:59 +0000473 }
474}
475
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000476static void buffer_line(int linenum)
477{
Rob Landley9200e792005-09-15 19:26:59 +0000478 int i;
479
480 past_eof = 0;
481
482 if (linenum < 1 || linenum > num_flines) {
483 clear_line();
484 printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum, NORMAL);
485 }
486 else if (linenum < (num_flines - height - 2)) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000487 for (i = 0; i < (height - 1); i++) {
488 free(buffer[i]);
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000489 buffer[i] = bb_xstrdup(flines[linenum + i]);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000490 }
Rob Landley9200e792005-09-15 19:26:59 +0000491 line_pos = linenum;
492 }
493 else {
494 for (i = 0; i < (height - 1); i++) {
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000495 free(buffer[i]);
Rob Landley9200e792005-09-15 19:26:59 +0000496 if (linenum + i < num_flines + 2)
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000497 buffer[i] = bb_xstrdup(flines[linenum + i]);
Rob Landley9200e792005-09-15 19:26:59 +0000498 else
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000499 buffer[i] = bb_xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
Rob Landley9200e792005-09-15 19:26:59 +0000500 }
501 line_pos = linenum;
502 /* Set past_eof so buffer_down and buffer_up act differently */
503 past_eof = 1;
504 }
505}
506
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000507/* Reinitialise everything for a new file - free the memory and start over */
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000508static void reinitialise(void)
509{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000510 int i;
511
512 for (i = 0; i <= num_flines; i++)
513 free(flines[i]);
514 free(flines);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000515
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000516 data_readlines();
517 buffer_init();
518 buffer_print();
519}
520
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000521static void examine_file(void)
522{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000523 int newline_offset;
524
525 clear_line();
526 printf("Examine: ");
527 fgets(filename, 256, inp);
528
529 /* As fgets adds a newline to the end of an input string, we
530 need to remove it */
531 newline_offset = strlen(filename) - 1;
532 filename[newline_offset] = '\0';
533
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000534 files[num_files] = bb_xstrdup(filename);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000535 current_file = num_files + 1;
536 num_files++;
537
538 inp_stdin = 0;
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000539 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000540}
541
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000542/* This function changes the file currently being paged. direction can be one of the following:
543 * -1: go back one file
544 * 0: go to the first file
545 * 1: go forward one file
546*/
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000547static void change_file(int direction)
548{
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000549 if (current_file != ((direction > 0) ? num_files : 1)) {
550 current_file = direction ? current_file + direction : 1;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000551 strcpy(filename, files[current_file - 1]);
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000552 reinitialise();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000553 }
554 else {
555 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000556 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000557 }
558}
559
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000560static void remove_current_file(void)
561{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000562 int i;
563
564 if (current_file != 1) {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000565 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000566 for (i = 3; i <= num_files; i++)
567 files[i - 2] = files[i - 1];
568 num_files--;
569 buffer_print();
570 }
571 else {
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000572 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000573 for (i = 2; i <= num_files; i++)
574 files[i - 2] = files[i - 1];
575 num_files--;
576 current_file--;
577 buffer_print();
578 }
579}
580
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000581static void colon_process(void)
582{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000583 int keypress;
584
585 /* Clear the current line and print a prompt */
586 clear_line();
587 printf(" :");
588
589 keypress = tless_getch();
590 switch (keypress) {
591 case 'd':
592 remove_current_file();
593 break;
594 case 'e':
595 examine_file();
596 break;
597#ifdef CONFIG_FEATURE_LESS_FLAGS
598 case 'f':
599 clear_line();
600 m_status_print();
601 break;
602#endif
603 case 'n':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000604 change_file(1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000605 break;
606 case 'p':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000607 change_file(-1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000608 break;
609 case 'q':
610 tless_exit(0);
611 break;
612 case 'x':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000613 change_file(0);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000614 break;
615 default:
616 break;
617 }
618}
619
620#ifdef CONFIG_FEATURE_LESS_REGEXP
621/* The below two regular expression handler functions NEED development. */
622
623/* Get a regular expression from the user, and then go through the current
624 file line by line, running a processing regex function on each one. */
625
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000626static char *insert_highlights(char *line, int start, int end)
627{
"Vladimir N. Oleynik"39a841c2005-09-29 16:18:57 +0000628 return bb_xasprintf("%.*s%s%.*s%s%s", start, line, HIGHLIGHT,
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000629 end - start, line + start, NORMAL, line + end);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000630}
631
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000632static char *process_regex_on_line(char *line, regex_t *pattern)
633{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000634 /* This function takes the regex and applies it to the line.
635 Each part of the line that matches has the HIGHLIGHT
636 and NORMAL escape sequences placed around it by
637 insert_highlights, and then the line is returned. */
638
639 int match_status;
640 char *line2 = (char *) malloc((sizeof(char) * (strlen(line) + 1)) + 64);
641 char sub_line[256];
642 int prev_eo = 0;
"Vladimir N. Oleynik"8d3c40d2005-09-16 13:16:01 +0000643 regmatch_t match_structs;
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000644
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000645 memset(sub_line, 0, 256);
646 strcpy(line2, line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000647
648 match_found = 0;
649 match_status = regexec(pattern, line2, 1, &match_structs, 0);
650
651 while (match_status == 0) {
652
653 memset(sub_line, 0, 256);
654
655 if (match_found == 0)
656 match_found = 1;
657
658 line2 = insert_highlights(line2, match_structs.rm_so + prev_eo, match_structs.rm_eo + prev_eo);
"Vladimir N. Oleynik"73ffd762006-02-01 12:56:19 +0000659 if ((size_t)match_structs.rm_eo + 11 + prev_eo < strlen(line2))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000660 strcat(sub_line, line2 + match_structs.rm_eo + 11 + prev_eo);
661
662 prev_eo += match_structs.rm_eo + 11;
663 match_status = regexec(pattern, sub_line, 1, &match_structs, REG_NOTBOL);
664 }
665
666 return line2;
667}
668
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000669static void regex_process(void)
670{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000671 char uncomp_regex[100];
672 char current_line[256];
673 int i;
674 int j = 0;
675 regex_t *pattern;
676
677 /* Reset variables */
678 match_lines[0] = -1;
679 match_pos = 0;
680 num_matches = 0;
681 match_found = 0;
682
683 pattern = (regex_t *) malloc(sizeof(regex_t));
684 memset(pattern, 0, sizeof(regex_t));
685
686 /* Get the uncompiled regular expression from the user */
687 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000688 putchar((match_backwards) ? '?' : '/');
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000689 uncomp_regex[0] = 0;
690 fgets(uncomp_regex, sizeof(uncomp_regex), stdin);
691 i = strlen(uncomp_regex);
692 if(i > 0) {
693 if(uncomp_regex[i-1] == '\n')
694 uncomp_regex[i-1] = '\0';
695 else
696 while((i = getchar()) != '\n' && i != EOF);
697 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000698
699 /* Compile the regex and check for errors */
700 xregcomp(pattern, uncomp_regex, 0);
701
702 /* Run the regex on each line of the current file here */
703 for (i = 0; i <= num_flines; i++) {
704 strcpy(current_line, process_regex_on_line(flines[i], pattern));
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000705 flines[i] = bb_xstrdup(current_line);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000706 if (match_found) {
707 match_lines[j] = i;
708 j++;
709 }
710 }
711
712 num_matches = j;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000713 if ((match_lines[0] != -1) && (num_flines > height - 2))
714 buffer_line(match_lines[0]);
715 else
716 buffer_init();
717}
718
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000719static void goto_match(int match)
720{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000721 /* This goes to a specific match - all line positions of matches are
722 stored within the match_lines[] array. */
723 if ((match < num_matches) && (match >= 0)) {
724 buffer_line(match_lines[match]);
725 match_pos = match;
726 }
727}
728
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000729static void search_backwards(void)
730{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000731 int current_linepos = line_pos;
732 int i;
733
734 match_backwards = 1;
735 regex_process();
736
737 for (i = 0; i < num_matches; i++) {
738 if (match_lines[i] > current_linepos) {
739 buffer_line(match_lines[i - num_back_match]);
740 break;
741 }
742 }
743
744 /* Reset variables */
745 match_backwards = 0;
746 num_back_match = 1;
747
748}
749#endif
750
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000751static void number_process(int first_digit)
752{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000753 int i = 1;
754 int num;
755 char num_input[80];
756 char keypress;
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000757 char *endptr;
758
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000759 num_input[0] = first_digit;
760
761 /* Clear the current line, print a prompt, and then print the digit */
762 clear_line();
763 printf(":%c", first_digit);
764
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000765 /* Receive input until a letter is given (max 80 chars)*/
766 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000767 putchar(num_input[i]);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000768 i++;
769 }
770
771 /* Take the final letter out of the digits string */
772 keypress = num_input[i];
773 num_input[i] = '\0';
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000774 num = strtol(num_input, &endptr, 10);
775 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES)
776 goto END;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000777
778 /* We now know the number and the letter entered, so we process them */
779 switch (keypress) {
780 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
781 buffer_down(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000782 break;
783 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
784 buffer_up(num);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000785 break;
786 case 'g': case '<': case 'G': case '>':
787 if (num_flines >= height - 2)
788 buffer_line(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000789 break;
790 case 'p': case '%':
791 buffer_line(reverse_percent(num));
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000792 break;
793#ifdef CONFIG_FEATURE_LESS_REGEXP
794 case 'n':
795 goto_match(match_pos + num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000796 break;
797 case '/':
798 regex_process();
799 goto_match(num - 1);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000800 break;
801 case '?':
802 num_back_match = num;
803 search_backwards();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000804 break;
805#endif
806 default:
807 break;
808 }
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +0000809END:
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000810 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000811}
812
813#ifdef CONFIG_FEATURE_LESS_FLAGCS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000814static void flag_change(void)
815{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000816 int keypress;
817
818 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000819 putchar('-');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000820 keypress = tless_getch();
821
822 switch (keypress) {
823 case 'M':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000824 flags ^= FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000825 break;
826 case 'm':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000827 flags ^= FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000828 break;
829 case 'E':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000830 flags ^= FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000831 break;
832 case '~':
"Vladimir N. Oleynik"b8fa7e82005-09-22 14:19:34 +0000833 flags ^= FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000834 break;
835 default:
836 break;
837 }
838}
839
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000840static void show_flag_status(void)
841{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000842 int keypress;
843 int flag_val;
844
845 clear_line();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000846 putchar('_');
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000847 keypress = tless_getch();
848
849 switch (keypress) {
850 case 'M':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000851 flag_val = flags & FLAG_M;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000852 break;
853 case 'm':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000854 flag_val = flags & FLAG_m;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000855 break;
856 case '~':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000857 flag_val = flags & FLAG_TILDE;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000858 break;
859 case 'N':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000860 flag_val = flags & FLAG_N;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000861 break;
862 case 'E':
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000863 flag_val = flags & FLAG_E;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000864 break;
865 default:
866 flag_val = 0;
867 break;
868 }
869
870 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000871 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 +0000872}
873#endif
874
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000875static void full_repaint(void)
876{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000877 int temp_line_pos = line_pos;
878 data_readlines();
879 buffer_init();
880 buffer_line(temp_line_pos);
881 buffer_print();
882}
883
884
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000885static void save_input_to_file(void)
886{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000887 char current_line[256];
888 int i;
889 FILE *fp;
890
891 clear_line();
892 printf("Log file: ");
893 fgets(current_line, 256, inp);
894 current_line[strlen(current_line) - 1] = '\0';
895 if (strlen(current_line)) {
896 fp = bb_xfopen(current_line, "w");
897 for (i = 0; i < num_flines; i++)
898 fprintf(fp, "%s", flines[i]);
899 fclose(fp);
900 buffer_print();
901 }
902 else
903 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
904}
905
906#ifdef CONFIG_FEATURE_LESS_MARKS
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000907static void add_mark(void)
908{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000909 int letter;
910 int mark_line;
911
912 clear_line();
913 printf("Mark: ");
914 letter = tless_getch();
915
916 if (isalpha(letter)) {
917 mark_line = line_pos;
918
919 /* If we exceed 15 marks, start overwriting previous ones */
920 if (num_marks == 14)
921 num_marks = 0;
922
923 mark_lines[num_marks][0] = letter;
924 mark_lines[num_marks][1] = line_pos;
925 num_marks++;
926 }
927 else {
928 clear_line();
929 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
930 }
931}
932
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000933static void goto_mark(void)
934{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000935 int letter;
936 int i;
937
938 clear_line();
939 printf("Go to mark: ");
940 letter = tless_getch();
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000941 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000942
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000943 if (isalpha(letter)) {
944 for (i = 0; i <= num_marks; i++)
945 if (letter == mark_lines[i][0]) {
946 buffer_line(mark_lines[i][1]);
947 break;
948 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000949 if ((num_marks == 14) && (letter != mark_lines[14][0]))
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000950 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000951 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000952 else
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000953 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000954}
955#endif
956
957
958#ifdef CONFIG_FEATURE_LESS_BRACKETS
959
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000960static char opp_bracket(char bracket)
961{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000962 switch (bracket) {
963 case '{': case '[':
964 return bracket + 2;
965 break;
966 case '(':
967 return ')';
968 break;
969 case '}': case ']':
970 return bracket - 2;
971 break;
972 case ')':
973 return '(';
974 break;
975 default:
976 return 0;
977 break;
978 }
979}
980
Mike Frysinger3a2b1032006-04-16 20:34:26 +0000981static void match_right_bracket(char bracket)
982{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000983 int bracket_line = -1;
984 int i;
985
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000986 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +0000987
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000988 if (strchr(flines[line_pos], bracket) == NULL)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000989 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000990 else {
991 for (i = line_pos + 1; i < num_flines; i++) {
992 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
993 bracket_line = i;
994 break;
995 }
996 }
997
Rob Landleyd57ae8b2005-09-18 00:58:49 +0000998 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +0000999 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001000
1001 buffer_line(bracket_line - height + 2);
1002 buffer_print();
1003 }
1004}
1005
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001006static void match_left_bracket(char bracket)
1007{
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001008 int bracket_line = -1;
1009 int i;
1010
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001011 clear_line();
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001012
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001013 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001014 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
1015 printf("%s", flines[line_pos + height]);
1016 sleep(4);
1017 }
1018 else {
1019 for (i = line_pos + height - 2; i >= 0; i--) {
1020 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
1021 bracket_line = i;
1022 break;
1023 }
1024 }
1025
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001026 if (bracket_line == -1)
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001027 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001028
1029 buffer_line(bracket_line);
1030 buffer_print();
1031 }
1032}
1033
1034#endif /* CONFIG_FEATURE_LESS_BRACKETS */
1035
Mike Frysinger3a2b1032006-04-16 20:34:26 +00001036static void keypress_process(int keypress)
1037{
Rob Landley9200e792005-09-15 19:26:59 +00001038 switch (keypress) {
1039 case KEY_DOWN: case 'e': case 'j': case '\015':
1040 buffer_down(1);
1041 buffer_print();
1042 break;
1043 case KEY_UP: case 'y': case 'k':
1044 buffer_up(1);
1045 buffer_print();
1046 break;
1047 case PAGE_DOWN: case ' ': case 'z':
1048 buffer_down(height - 1);
1049 buffer_print();
1050 break;
1051 case PAGE_UP: case 'w': case 'b':
1052 buffer_up(height - 1);
1053 buffer_print();
1054 break;
1055 case 'd':
1056 buffer_down((height - 1) / 2);
1057 buffer_print();
1058 break;
1059 case 'u':
1060 buffer_up((height - 1) / 2);
1061 buffer_print();
1062 break;
1063 case 'g': case 'p': case '<': case '%':
1064 buffer_up(num_flines + 1);
1065 buffer_print();
1066 break;
1067 case 'G': case '>':
1068 buffer_down(num_flines + 1);
1069 buffer_print();
1070 break;
1071 case 'q': case 'Q':
1072 tless_exit(0);
1073 break;
1074#ifdef CONFIG_FEATURE_LESS_MARKS
1075 case 'm':
1076 add_mark();
1077 buffer_print();
1078 break;
1079 case '\'':
1080 goto_mark();
1081 buffer_print();
1082 break;
1083#endif
1084 case 'r':
1085 buffer_print();
1086 break;
1087 case 'R':
1088 full_repaint();
1089 break;
1090 case 's':
1091 if (inp_stdin)
1092 save_input_to_file();
1093 break;
1094 case 'E':
1095 examine_file();
1096 break;
1097#ifdef CONFIG_FEATURE_LESS_FLAGS
1098 case '=':
1099 clear_line();
1100 m_status_print();
1101 break;
1102#endif
1103#ifdef CONFIG_FEATURE_LESS_REGEXP
1104 case '/':
1105 regex_process();
1106 buffer_print();
1107 break;
1108 case 'n':
1109 goto_match(match_pos + 1);
1110 buffer_print();
1111 break;
1112 case 'N':
1113 goto_match(match_pos - 1);
1114 buffer_print();
1115 break;
1116 case '?':
1117 search_backwards();
1118 buffer_print();
1119 break;
1120#endif
1121#ifdef CONFIG_FEATURE_LESS_FLAGCS
1122 case '-':
1123 flag_change();
1124 buffer_print();
1125 break;
1126 case '_':
1127 show_flag_status();
1128 break;
1129#endif
1130#ifdef CONFIG_FEATURE_LESS_BRACKETS
1131 case '{': case '(': case '[':
1132 match_right_bracket(keypress);
1133 break;
1134 case '}': case ')': case ']':
1135 match_left_bracket(keypress);
1136 break;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001137#endif
1138 case ':':
Rob Landley9200e792005-09-15 19:26:59 +00001139 colon_process();
1140 break;
1141 default:
1142 break;
1143 }
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001144
Rob Landley9200e792005-09-15 19:26:59 +00001145 if (isdigit(keypress))
1146 number_process(keypress);
1147}
1148
Rob Landley9200e792005-09-15 19:26:59 +00001149int less_main(int argc, char **argv) {
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001150
Rob Landley9200e792005-09-15 19:26:59 +00001151 int keypress;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001152
Rob Landleyd57ae8b2005-09-18 00:58:49 +00001153 flags = bb_getopt_ulflags(argc, argv, "EMmN~");
Rob Landley9200e792005-09-15 19:26:59 +00001154
1155 argc -= optind;
1156 argv += optind;
1157 files = argv;
1158 num_files = argc;
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001159
Rob Landley9200e792005-09-15 19:26:59 +00001160 if (!num_files) {
1161 if (ttyname(STDIN_FILENO) == NULL)
1162 inp_stdin = 1;
1163 else {
1164 bb_error_msg("Missing filename");
1165 bb_show_usage();
1166 }
1167 }
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001168
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001169 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
Rob Landley9200e792005-09-15 19:26:59 +00001170 tty_width_height();
"Vladimir N. Oleynik"bc374802005-09-19 14:23:46 +00001171 data_readlines();
1172 tcgetattr(fileno(inp), &term_orig);
"Vladimir N. Oleynik"a0ae6de2005-09-19 10:28:43 +00001173 term_vi = term_orig;
1174 term_vi.c_lflag &= (~ICANON & ~ECHO);
1175 term_vi.c_iflag &= (~IXON & ~ICRNL);
1176 term_vi.c_oflag &= (~ONLCR);
1177 term_vi.c_cc[VMIN] = 1;
1178 term_vi.c_cc[VTIME] = 0;
Rob Landley9200e792005-09-15 19:26:59 +00001179 buffer_init();
1180 buffer_print();
"Vladimir N. Oleynik"2b306e92005-09-16 12:32:22 +00001181
Rob Landley9200e792005-09-15 19:26:59 +00001182 while (1) {
1183 keypress = tless_getch();
1184 keypress_process(keypress);
1185 }
1186}