source: MondoRescue/branches/2.2.2/mindi-busybox/miscutils/less.c@ 1247

Last change on this file since 1247 was 821, checked in by Bruno Cornec, 18 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 24.9 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini less implementation for busybox
4 *
5 * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
6 *
7 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8 */
9
10/*
11 * This program needs a lot of development, so consider it in a beta stage
12 * at best.
13 *
14 * TODO:
15 * - Add more regular expression support - search modifiers, certain matches, etc.
16 * - Add more complex bracket searching - currently, nested brackets are
17 * not considered.
18 * - Add support for "F" as an input. This causes less to act in
19 * a similar way to tail -f.
20 * - Check for binary files, and prompt the user if a binary file
21 * is detected.
22 * - Allow horizontal scrolling. Currently, lines simply continue onto
23 * the next line, per the terminal's discretion
24 *
25 * Notes:
26 * - filename is an array and not a pointer because that avoids all sorts
27 * of complications involving the fact that something that is pointed to
28 * will be changed if the pointer is changed.
29 * - the inp file pointer is used so that keyboard input works after
30 * redirected input has been read from stdin
31*/
32
33#include "busybox.h"
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <termios.h>
38#include <unistd.h>
39#include <ctype.h>
40
41#ifdef CONFIG_FEATURE_LESS_REGEXP
42#include "xregex.h"
43#endif
44
45
46/* These are the escape sequences corresponding to special keys */
47#define REAL_KEY_UP 'A'
48#define REAL_KEY_DOWN 'B'
49#define REAL_KEY_RIGHT 'C'
50#define REAL_KEY_LEFT 'D'
51#define REAL_PAGE_UP '5'
52#define REAL_PAGE_DOWN '6'
53
54/* These are the special codes assigned by this program to the special keys */
55#define PAGE_UP 20
56#define PAGE_DOWN 21
57#define KEY_UP 22
58#define KEY_DOWN 23
59#define KEY_RIGHT 24
60#define KEY_LEFT 25
61
62/* The escape codes for highlighted and normal text */
63#define HIGHLIGHT "\033[7m"
64#define NORMAL "\033[0m"
65
66/* The escape code to clear the screen */
67#define CLEAR "\033[H\033[J"
68
69/* Maximum number of lines in a file */
70#define MAXLINES 10000
71
72static int height;
73static int width;
74static char **files;
75static char filename[256];
76static char **buffer;
77static char **flines;
78static int current_file = 1;
79static int line_pos;
80static int num_flines;
81static int num_files = 1;
82static int past_eof;
83
84/* Command line options */
85static unsigned long flags;
86#define FLAG_E 1
87#define FLAG_M (1<<1)
88#define FLAG_m (1<<2)
89#define FLAG_N (1<<3)
90#define FLAG_TILDE (1<<4)
91
92/* This is needed so that program behaviour changes when input comes from
93 stdin */
94static int inp_stdin;
95
96#ifdef CONFIG_FEATURE_LESS_MARKS
97static int mark_lines[15][2];
98static int num_marks;
99#endif
100
101#ifdef CONFIG_FEATURE_LESS_REGEXP
102static int match_found;
103static int *match_lines;
104static int match_pos;
105static int num_matches;
106static int match_backwards;
107static regex_t old_pattern;
108#endif
109
110/* Needed termios structures */
111static struct termios term_orig, term_vi;
112
113/* File pointer to get input from */
114static FILE *inp;
115
116/* Reset terminal input to normal */
117static void set_tty_cooked(void)
118{
119 fflush(stdout);
120 tcsetattr(fileno(inp), TCSANOW, &term_orig);
121}
122
123/* Set terminal input to raw mode (taken from vi.c) */
124static void set_tty_raw(void)
125{
126 tcsetattr(fileno(inp), TCSANOW, &term_vi);
127}
128
129/* Exit the program gracefully */
130static void tless_exit(int code)
131{
132 /* TODO: We really should save the terminal state when we start,
133 and restore it when we exit. Less does this with the
134 "ti" and "te" termcap commands; can this be done with
135 only termios.h? */
136
137 putchar('\n');
138 exit(code);
139}
140
141/* Grab a character from input without requiring the return key. If the
142 character is ASCII \033, get more characters and assign certain sequences
143 special return codes. Note that this function works best with raw input. */
144static int tless_getch(void)
145{
146 int input;
147
148 set_tty_raw();
149
150 input = getc(inp);
151 /* Detect escape sequences (i.e. arrow keys) and handle
152 them accordingly */
153
154 if (input == '\033' && getc(inp) == '[') {
155 input = getc(inp);
156 set_tty_cooked();
157 if (input == REAL_KEY_UP)
158 return KEY_UP;
159 else if (input == REAL_KEY_DOWN)
160 return KEY_DOWN;
161 else if (input == REAL_KEY_RIGHT)
162 return KEY_RIGHT;
163 else if (input == REAL_KEY_LEFT)
164 return KEY_LEFT;
165 else if (input == REAL_PAGE_UP)
166 return PAGE_UP;
167 else if (input == REAL_PAGE_DOWN)
168 return PAGE_DOWN;
169 }
170 /* The input is a normal ASCII value */
171 else {
172 set_tty_cooked();
173 return input;
174 }
175 return 0;
176}
177
178/* Move the cursor to a position (x,y), where (0,0) is the
179 top-left corner of the console */
180static void move_cursor(int x, int y)
181{
182 printf("\033[%i;%iH", x, y);
183}
184
185static void clear_line(void)
186{
187 move_cursor(height, 0);
188 printf("\033[K");
189}
190
191/* This adds line numbers to every line, as the -N flag necessitates */
192static void add_linenumbers(void)
193{
194 char current_line[256];
195 int i;
196
197 for (i = 0; i <= num_flines; i++) {
198 safe_strncpy(current_line, flines[i], 256);
199 flines[i] = bb_xasprintf("%5d %s", i + 1, current_line);
200 }
201}
202
203static void data_readlines(void)
204{
205 int i;
206 char current_line[256];
207 FILE *fp;
208
209 fp = (inp_stdin) ? stdin : bb_xfopen(filename, "r");
210 flines = NULL;
211 for (i = 0; (feof(fp)==0) && (i <= MAXLINES); i++) {
212 strcpy(current_line, "");
213 fgets(current_line, 256, fp);
214 if (fp != stdin)
215 bb_xferror(fp, filename);
216 flines = xrealloc(flines, (i+1) * sizeof(char *));
217 flines[i] = bb_xstrdup(current_line);
218 }
219 num_flines = i - 2;
220
221 /* Reset variables for a new file */
222
223 line_pos = 0;
224 past_eof = 0;
225
226 fclose(fp);
227
228 if (inp == NULL)
229 inp = (inp_stdin) ? bb_xfopen(CURRENT_TTY, "r") : stdin;
230
231 if (flags & FLAG_N)
232 add_linenumbers();
233}
234
235#ifdef CONFIG_FEATURE_LESS_FLAGS
236
237/* Interestingly, writing calc_percent as a function and not a prototype saves around 32 bytes
238 * on my build. */
239static int calc_percent(void)
240{
241 return ((100 * (line_pos + height - 2) / num_flines) + 1);
242}
243
244/* Print a status line if -M was specified */
245static void m_status_print(void)
246{
247 int percentage;
248
249 if (!past_eof) {
250 if (!line_pos) {
251 if (num_files > 1)
252 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);
253 else {
254 printf("%s%s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
255 }
256 }
257 else {
258 printf("%s %s lines %i-%i/%i ", HIGHLIGHT, filename, line_pos + 1, line_pos + height - 1, num_flines + 1);
259 }
260
261 if (line_pos == num_flines - height + 2) {
262 printf("(END) %s", NORMAL);
263 if ((num_files > 1) && (current_file != num_files))
264 printf("%s- Next: %s%s", HIGHLIGHT, files[current_file], NORMAL);
265 }
266 else {
267 percentage = calc_percent();
268 printf("%i%% %s", percentage, NORMAL);
269 }
270 }
271 else {
272 printf("%s%s lines %i-%i/%i (END) ", HIGHLIGHT, filename, line_pos + 1, num_flines + 1, num_flines + 1);
273 if ((num_files > 1) && (current_file != num_files))
274 printf("- Next: %s", files[current_file]);
275 printf("%s", NORMAL);
276 }
277}
278
279/* Print a status line if -m was specified */
280static void medium_status_print(void)
281{
282 int percentage;
283 percentage = calc_percent();
284
285 if (!line_pos)
286 printf("%s%s %i%%%s", HIGHLIGHT, filename, percentage, NORMAL);
287 else if (line_pos == num_flines - height + 2)
288 printf("%s(END)%s", HIGHLIGHT, NORMAL);
289 else
290 printf("%s%i%%%s", HIGHLIGHT, percentage, NORMAL);
291}
292#endif
293
294/* Print the status line */
295static void status_print(void)
296{
297 /* Change the status if flags have been set */
298#ifdef CONFIG_FEATURE_LESS_FLAGS
299 if (flags & FLAG_M)
300 m_status_print();
301 else if (flags & FLAG_m)
302 medium_status_print();
303 /* No flags set */
304 else {
305#endif
306 if (!line_pos) {
307 printf("%s%s %s", HIGHLIGHT, filename, NORMAL);
308 if (num_files > 1)
309 printf("%s%s%i%s%i%s%s", HIGHLIGHT, "(file ", current_file, " of ", num_files, ")", NORMAL);
310 }
311 else if (line_pos == num_flines - height + 2) {
312 printf("%s%s %s", HIGHLIGHT, "(END)", NORMAL);
313 if ((num_files > 1) && (current_file != num_files))
314 printf("%s%s%s%s", HIGHLIGHT, "- Next: ", files[current_file], NORMAL);
315 }
316 else {
317 putchar(':');
318 }
319#ifdef CONFIG_FEATURE_LESS_FLAGS
320 }
321#endif
322}
323
324/* Print the buffer */
325static void buffer_print(void)
326{
327 int i;
328
329 printf("%s", CLEAR);
330 if (num_flines >= height - 2) {
331 for (i = 0; i < height - 1; i++)
332 printf("%s", buffer[i]);
333 }
334 else {
335 for (i = 1; i < (height - 1 - num_flines); i++)
336 putchar('\n');
337 for (i = 0; i < height - 1; i++)
338 printf("%s", buffer[i]);
339 }
340
341 status_print();
342}
343
344/* Initialise the buffer */
345static void buffer_init(void)
346{
347 int i;
348
349 if (buffer == NULL) {
350 /* malloc the number of lines needed for the buffer */
351 buffer = xrealloc(buffer, height * sizeof(char *));
352 } else {
353 for (i = 0; i < (height - 1); i++)
354 free(buffer[i]);
355 }
356
357 /* Fill the buffer until the end of the file or the
358 end of the buffer is reached */
359 for (i = 0; (i < (height - 1)) && (i <= num_flines); i++) {
360 buffer[i] = bb_xstrdup(flines[i]);
361 }
362
363 /* If the buffer still isn't full, fill it with blank lines */
364 for (; i < (height - 1); i++) {
365 buffer[i] = bb_xstrdup("");
366 }
367}
368
369/* Move the buffer up and down in the file in order to scroll */
370static void buffer_down(int nlines)
371{
372 int i;
373
374 if (!past_eof) {
375 if (line_pos + (height - 3) + nlines < num_flines) {
376 line_pos += nlines;
377 for (i = 0; i < (height - 1); i++) {
378 free(buffer[i]);
379 buffer[i] = bb_xstrdup(flines[line_pos + i]);
380 }
381 }
382 else {
383 /* As the number of lines requested was too large, we just move
384 to the end of the file */
385 while (line_pos + (height - 3) + 1 < num_flines) {
386 line_pos += 1;
387 for (i = 0; i < (height - 1); i++) {
388 free(buffer[i]);
389 buffer[i] = bb_xstrdup(flines[line_pos + i]);
390 }
391 }
392 }
393
394 /* We exit if the -E flag has been set */
395 if ((flags & FLAG_E) && (line_pos + (height - 2) == num_flines))
396 tless_exit(0);
397 }
398}
399
400static void buffer_up(int nlines)
401{
402 int i;
403 int tilde_line;
404
405 if (!past_eof) {
406 if (line_pos - nlines >= 0) {
407 line_pos -= nlines;
408 for (i = 0; i < (height - 1); i++) {
409 free(buffer[i]);
410 buffer[i] = bb_xstrdup(flines[line_pos + i]);
411 }
412 }
413 else {
414 /* As the requested number of lines to move was too large, we
415 move one line up at a time until we can't. */
416 while (line_pos != 0) {
417 line_pos -= 1;
418 for (i = 0; i < (height - 1); i++) {
419 free(buffer[i]);
420 buffer[i] = bb_xstrdup(flines[line_pos + i]);
421 }
422 }
423 }
424 }
425 else {
426 /* Work out where the tildes start */
427 tilde_line = num_flines - line_pos + 3;
428
429 line_pos -= nlines;
430 /* Going backwards nlines lines has taken us to a point where
431 nothing is past the EOF, so we revert to normal. */
432 if (line_pos < num_flines - height + 3) {
433 past_eof = 0;
434 buffer_up(nlines);
435 }
436 else {
437 /* We only move part of the buffer, as the rest
438 is past the EOF */
439 for (i = 0; i < (height - 1); i++) {
440 free(buffer[i]);
441 if (i < tilde_line - nlines + 1)
442 buffer[i] = bb_xstrdup(flines[line_pos + i]);
443 else {
444 if (line_pos >= num_flines - height + 2)
445 buffer[i] = bb_xstrdup("~\n");
446 }
447 }
448 }
449 }
450}
451
452static void buffer_line(int linenum)
453{
454 int i;
455 past_eof = 0;
456
457 if (linenum < 0 || linenum > num_flines) {
458 clear_line();
459 printf("%s%s%i%s", HIGHLIGHT, "Cannot seek to line number ", linenum + 1, NORMAL);
460 }
461 else if (linenum < (num_flines - height - 2)) {
462 for (i = 0; i < (height - 1); i++) {
463 free(buffer[i]);
464 buffer[i] = bb_xstrdup(flines[linenum + i]);
465 }
466 line_pos = linenum;
467 buffer_print();
468 }
469 else {
470 for (i = 0; i < (height - 1); i++) {
471 free(buffer[i]);
472 if (linenum + i < num_flines + 2)
473 buffer[i] = bb_xstrdup(flines[linenum + i]);
474 else
475 buffer[i] = bb_xstrdup((flags & FLAG_TILDE) ? "\n" : "~\n");
476 }
477 line_pos = linenum;
478 /* Set past_eof so buffer_down and buffer_up act differently */
479 past_eof = 1;
480 buffer_print();
481 }
482}
483
484/* Reinitialise everything for a new file - free the memory and start over */
485static void reinitialise(void)
486{
487 int i;
488
489 for (i = 0; i <= num_flines; i++)
490 free(flines[i]);
491 free(flines);
492
493 data_readlines();
494 buffer_init();
495 buffer_print();
496}
497
498static void examine_file(void)
499{
500 int newline_offset;
501
502 clear_line();
503 printf("Examine: ");
504 fgets(filename, 256, inp);
505
506 /* As fgets adds a newline to the end of an input string, we
507 need to remove it */
508 newline_offset = strlen(filename) - 1;
509 filename[newline_offset] = '\0';
510
511 files[num_files] = bb_xstrdup(filename);
512 current_file = num_files + 1;
513 num_files++;
514
515 inp_stdin = 0;
516 reinitialise();
517}
518
519/* This function changes the file currently being paged. direction can be one of the following:
520 * -1: go back one file
521 * 0: go to the first file
522 * 1: go forward one file
523*/
524static void change_file(int direction)
525{
526 if (current_file != ((direction > 0) ? num_files : 1)) {
527 current_file = direction ? current_file + direction : 1;
528 strcpy(filename, files[current_file - 1]);
529 reinitialise();
530 }
531 else {
532 clear_line();
533 printf("%s%s%s", HIGHLIGHT, (direction > 0) ? "No next file" : "No previous file", NORMAL);
534 }
535}
536
537static void remove_current_file(void)
538{
539 int i;
540
541 if (current_file != 1) {
542 change_file(-1);
543 for (i = 3; i <= num_files; i++)
544 files[i - 2] = files[i - 1];
545 num_files--;
546 buffer_print();
547 }
548 else {
549 change_file(1);
550 for (i = 2; i <= num_files; i++)
551 files[i - 2] = files[i - 1];
552 num_files--;
553 current_file--;
554 buffer_print();
555 }
556}
557
558static void colon_process(void)
559{
560 int keypress;
561
562 /* Clear the current line and print a prompt */
563 clear_line();
564 printf(" :");
565
566 keypress = tless_getch();
567 switch (keypress) {
568 case 'd':
569 remove_current_file();
570 break;
571 case 'e':
572 examine_file();
573 break;
574#ifdef CONFIG_FEATURE_LESS_FLAGS
575 case 'f':
576 clear_line();
577 m_status_print();
578 break;
579#endif
580 case 'n':
581 change_file(1);
582 break;
583 case 'p':
584 change_file(-1);
585 break;
586 case 'q':
587 tless_exit(0);
588 break;
589 case 'x':
590 change_file(0);
591 break;
592 default:
593 break;
594 }
595}
596
597#ifdef CONFIG_FEATURE_LESS_REGEXP
598/* The below two regular expression handler functions NEED development. */
599
600/* Get a regular expression from the user, and then go through the current
601 file line by line, running a processing regex function on each one. */
602
603static char *process_regex_on_line(char *line, regex_t *pattern, int action)
604{
605 /* This function takes the regex and applies it to the line.
606 Each part of the line that matches has the HIGHLIGHT
607 and NORMAL escape sequences placed around it by
608 insert_highlights if action = 1, or has the escape sequences
609 removed if action = 0, and then the line is returned. */
610 int match_status;
611 char *line2 = (char *) xmalloc((sizeof(char) * (strlen(line) + 1)) + 64);
612 char *growline = "";
613 regmatch_t match_structs;
614
615 line2 = bb_xstrdup(line);
616
617 match_found = 0;
618 match_status = regexec(pattern, line2, 1, &match_structs, 0);
619
620 while (match_status == 0) {
621 if (match_found == 0)
622 match_found = 1;
623
624 if (action) {
625 growline = bb_xasprintf("%s%.*s%s%.*s%s", growline, match_structs.rm_so, line2, HIGHLIGHT, match_structs.rm_eo - match_structs.rm_so, line2 + match_structs.rm_so, NORMAL);
626 }
627 else {
628 growline = bb_xasprintf("%s%.*s%.*s", growline, match_structs.rm_so - 4, line2, match_structs.rm_eo - match_structs.rm_so, line2 + match_structs.rm_so);
629 }
630
631 line2 += match_structs.rm_eo;
632 match_status = regexec(pattern, line2, 1, &match_structs, REG_NOTBOL);
633 }
634
635 growline = bb_xasprintf("%s%s", growline, line2);
636
637 return (match_found ? growline : line);
638
639 free(growline);
640 free(line2);
641}
642
643static void goto_match(int match)
644{
645 /* This goes to a specific match - all line positions of matches are
646 stored within the match_lines[] array. */
647 if ((match < num_matches) && (match >= 0)) {
648 buffer_line(match_lines[match]);
649 match_pos = match;
650 }
651}
652
653static void regex_process(void)
654{
655 char uncomp_regex[100];
656 char *current_line;
657 int i;
658 int j = 0;
659 regex_t pattern;
660 /* Get the uncompiled regular expression from the user */
661 clear_line();
662 putchar((match_backwards) ? '?' : '/');
663 uncomp_regex[0] = 0;
664 fgets(uncomp_regex, sizeof(uncomp_regex), inp);
665
666 if (strlen(uncomp_regex) == 1) {
667 if (num_matches)
668 goto_match(match_backwards ? match_pos - 1 : match_pos + 1);
669 else
670 buffer_print();
671 return;
672 }
673 uncomp_regex[strlen(uncomp_regex) - 1] = '\0';
674
675 /* Compile the regex and check for errors */
676 xregcomp(&pattern, uncomp_regex, 0);
677
678 if (num_matches) {
679 /* Get rid of all the highlights we added previously */
680 for (i = 0; i <= num_flines; i++) {
681 current_line = process_regex_on_line(flines[i], &old_pattern, 0);
682 flines[i] = bb_xstrdup(current_line);
683 }
684 }
685 old_pattern = pattern;
686
687 /* Reset variables */
688 match_lines = xrealloc(match_lines, sizeof(int));
689 match_lines[0] = -1;
690 match_pos = 0;
691 num_matches = 0;
692 match_found = 0;
693 /* Run the regex on each line of the current file here */
694 for (i = 0; i <= num_flines; i++) {
695 current_line = process_regex_on_line(flines[i], &pattern, 1);
696 flines[i] = bb_xstrdup(current_line);
697 if (match_found) {
698 match_lines = xrealloc(match_lines, (j + 1) * sizeof(int));
699 match_lines[j] = i;
700 j++;
701 }
702 }
703
704 num_matches = j;
705 if ((match_lines[0] != -1) && (num_flines > height - 2)) {
706 if (match_backwards) {
707 for (i = 0; i < num_matches; i++) {
708 if (match_lines[i] > line_pos) {
709 match_pos = i - 1;
710 buffer_line(match_lines[match_pos]);
711 break;
712 }
713 }
714 }
715 else
716 buffer_line(match_lines[0]);
717 }
718 else
719 buffer_init();
720}
721#endif
722
723static void number_process(int first_digit)
724{
725 int i = 1;
726 int num;
727 char num_input[80];
728 char keypress;
729 char *endptr;
730
731 num_input[0] = first_digit;
732
733 /* Clear the current line, print a prompt, and then print the digit */
734 clear_line();
735 printf(":%c", first_digit);
736
737 /* Receive input until a letter is given (max 80 chars)*/
738 while((i < 80) && (num_input[i] = tless_getch()) && isdigit(num_input[i])) {
739 putchar(num_input[i]);
740 i++;
741 }
742
743 /* Take the final letter out of the digits string */
744 keypress = num_input[i];
745 num_input[i] = '\0';
746 num = strtol(num_input, &endptr, 10);
747 if (endptr==num_input || *endptr!='\0' || num < 1 || num > MAXLINES) {
748 buffer_print();
749 return;
750 }
751
752 /* We now know the number and the letter entered, so we process them */
753 switch (keypress) {
754 case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
755 buffer_down(num);
756 break;
757 case KEY_UP: case 'b': case 'w': case 'y': case 'u':
758 buffer_up(num);
759 break;
760 case 'g': case '<': case 'G': case '>':
761 if (num_flines >= height - 2)
762 buffer_line(num - 1);
763 break;
764 case 'p': case '%':
765 buffer_line(((num / 100) * num_flines) - 1);
766 break;
767#ifdef CONFIG_FEATURE_LESS_REGEXP
768 case 'n':
769 goto_match(match_pos + num);
770 break;
771 case '/':
772 match_backwards = 0;
773 regex_process();
774 break;
775 case '?':
776 match_backwards = 1;
777 regex_process();
778 break;
779#endif
780 default:
781 break;
782 }
783}
784
785#ifdef CONFIG_FEATURE_LESS_FLAGCS
786static void flag_change(void)
787{
788 int keypress;
789
790 clear_line();
791 putchar('-');
792 keypress = tless_getch();
793
794 switch (keypress) {
795 case 'M':
796 flags ^= FLAG_M;
797 break;
798 case 'm':
799 flags ^= FLAG_m;
800 break;
801 case 'E':
802 flags ^= FLAG_E;
803 break;
804 case '~':
805 flags ^= FLAG_TILDE;
806 break;
807 default:
808 break;
809 }
810}
811
812static void show_flag_status(void)
813{
814 int keypress;
815 int flag_val;
816
817 clear_line();
818 putchar('_');
819 keypress = tless_getch();
820
821 switch (keypress) {
822 case 'M':
823 flag_val = flags & FLAG_M;
824 break;
825 case 'm':
826 flag_val = flags & FLAG_m;
827 break;
828 case '~':
829 flag_val = flags & FLAG_TILDE;
830 break;
831 case 'N':
832 flag_val = flags & FLAG_N;
833 break;
834 case 'E':
835 flag_val = flags & FLAG_E;
836 break;
837 default:
838 flag_val = 0;
839 break;
840 }
841
842 clear_line();
843 printf("%s%s%i%s", HIGHLIGHT, "The status of the flag is: ", flag_val != 0, NORMAL);
844}
845#endif
846
847static void full_repaint(void)
848{
849 int temp_line_pos = line_pos;
850 data_readlines();
851 buffer_init();
852 buffer_line(temp_line_pos);
853}
854
855
856static void save_input_to_file(void)
857{
858 char current_line[256];
859 int i;
860 FILE *fp;
861
862 clear_line();
863 printf("Log file: ");
864 fgets(current_line, 256, inp);
865 current_line[strlen(current_line) - 1] = '\0';
866 if (strlen(current_line) > 1) {
867 fp = bb_xfopen(current_line, "w");
868 for (i = 0; i < num_flines; i++)
869 fprintf(fp, "%s", flines[i]);
870 fclose(fp);
871 buffer_print();
872 }
873 else
874 printf("%sNo log file%s", HIGHLIGHT, NORMAL);
875}
876
877#ifdef CONFIG_FEATURE_LESS_MARKS
878static void add_mark(void)
879{
880 int letter;
881 int mark_line;
882
883 clear_line();
884 printf("Mark: ");
885 letter = tless_getch();
886
887 if (isalpha(letter)) {
888 mark_line = line_pos;
889
890 /* If we exceed 15 marks, start overwriting previous ones */
891 if (num_marks == 14)
892 num_marks = 0;
893
894 mark_lines[num_marks][0] = letter;
895 mark_lines[num_marks][1] = line_pos;
896 num_marks++;
897 }
898 else {
899 clear_line();
900 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
901 }
902}
903
904static void goto_mark(void)
905{
906 int letter;
907 int i;
908
909 clear_line();
910 printf("Go to mark: ");
911 letter = tless_getch();
912 clear_line();
913
914 if (isalpha(letter)) {
915 for (i = 0; i <= num_marks; i++)
916 if (letter == mark_lines[i][0]) {
917 buffer_line(mark_lines[i][1]);
918 break;
919 }
920 if ((num_marks == 14) && (letter != mark_lines[14][0]))
921 printf("%s%s%s", HIGHLIGHT, "Mark not set", NORMAL);
922 }
923 else
924 printf("%s%s%s", HIGHLIGHT, "Invalid mark letter", NORMAL);
925}
926#endif
927
928
929#ifdef CONFIG_FEATURE_LESS_BRACKETS
930
931static char opp_bracket(char bracket)
932{
933 switch (bracket) {
934 case '{': case '[':
935 return bracket + 2;
936 break;
937 case '(':
938 return ')';
939 break;
940 case '}': case ']':
941 return bracket - 2;
942 break;
943 case ')':
944 return '(';
945 break;
946 default:
947 return 0;
948 break;
949 }
950}
951
952static void match_right_bracket(char bracket)
953{
954 int bracket_line = -1;
955 int i;
956
957 clear_line();
958
959 if (strchr(flines[line_pos], bracket) == NULL)
960 printf("%s%s%s", HIGHLIGHT, "No bracket in top line", NORMAL);
961 else {
962 for (i = line_pos + 1; i < num_flines; i++) {
963 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
964 bracket_line = i;
965 break;
966 }
967 }
968
969 if (bracket_line == -1)
970 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
971
972 buffer_line(bracket_line - height + 2);
973 }
974}
975
976static void match_left_bracket(char bracket)
977{
978 int bracket_line = -1;
979 int i;
980
981 clear_line();
982
983 if (strchr(flines[line_pos + height - 2], bracket) == NULL) {
984 printf("%s%s%s", HIGHLIGHT, "No bracket in bottom line", NORMAL);
985 printf("%s", flines[line_pos + height]);
986 sleep(4);
987 }
988 else {
989 for (i = line_pos + height - 2; i >= 0; i--) {
990 if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
991 bracket_line = i;
992 break;
993 }
994 }
995
996 if (bracket_line == -1)
997 printf("%s%s%s", HIGHLIGHT, "No matching bracket found", NORMAL);
998
999 buffer_line(bracket_line);
1000 }
1001}
1002
1003#endif /* CONFIG_FEATURE_LESS_BRACKETS */
1004
1005static void keypress_process(int keypress)
1006{
1007 switch (keypress) {
1008 case KEY_DOWN: case 'e': case 'j': case '\015':
1009 buffer_down(1);
1010 buffer_print();
1011 break;
1012 case KEY_UP: case 'y': case 'k':
1013 buffer_up(1);
1014 buffer_print();
1015 break;
1016 case PAGE_DOWN: case ' ': case 'z':
1017 buffer_down(height - 1);
1018 buffer_print();
1019 break;
1020 case PAGE_UP: case 'w': case 'b':
1021 buffer_up(height - 1);
1022 buffer_print();
1023 break;
1024 case 'd':
1025 buffer_down((height - 1) / 2);
1026 buffer_print();
1027 break;
1028 case 'u':
1029 buffer_up((height - 1) / 2);
1030 buffer_print();
1031 break;
1032 case 'g': case 'p': case '<': case '%':
1033 buffer_line(0);
1034 break;
1035 case 'G': case '>':
1036 buffer_line(num_flines - height + 2);
1037 break;
1038 case 'q': case 'Q':
1039 tless_exit(0);
1040 break;
1041#ifdef CONFIG_FEATURE_LESS_MARKS
1042 case 'm':
1043 add_mark();
1044 buffer_print();
1045 break;
1046 case '\'':
1047 goto_mark();
1048 buffer_print();
1049 break;
1050#endif
1051 case 'r':
1052 buffer_print();
1053 break;
1054 case 'R':
1055 full_repaint();
1056 break;
1057 case 's':
1058 if (inp_stdin)
1059 save_input_to_file();
1060 break;
1061 case 'E':
1062 examine_file();
1063 break;
1064#ifdef CONFIG_FEATURE_LESS_FLAGS
1065 case '=':
1066 clear_line();
1067 m_status_print();
1068 break;
1069#endif
1070#ifdef CONFIG_FEATURE_LESS_REGEXP
1071 case '/':
1072 match_backwards = 0;
1073 regex_process();
1074 break;
1075 case 'n':
1076 goto_match(match_pos + 1);
1077 break;
1078 case 'N':
1079 goto_match(match_pos - 1);
1080 break;
1081 case '?':
1082 match_backwards = 1;
1083 regex_process();
1084 break;
1085#endif
1086#ifdef CONFIG_FEATURE_LESS_FLAGCS
1087 case '-':
1088 flag_change();
1089 buffer_print();
1090 break;
1091 case '_':
1092 show_flag_status();
1093 break;
1094#endif
1095#ifdef CONFIG_FEATURE_LESS_BRACKETS
1096 case '{': case '(': case '[':
1097 match_right_bracket(keypress);
1098 break;
1099 case '}': case ')': case ']':
1100 match_left_bracket(keypress);
1101 break;
1102#endif
1103 case ':':
1104 colon_process();
1105 break;
1106 default:
1107 break;
1108 }
1109
1110 if (isdigit(keypress))
1111 number_process(keypress);
1112}
1113
1114int less_main(int argc, char **argv) {
1115
1116 int keypress;
1117
1118 flags = bb_getopt_ulflags(argc, argv, "EMmN~");
1119
1120 argc -= optind;
1121 argv += optind;
1122 files = argv;
1123 num_files = argc;
1124
1125 if (!num_files) {
1126 if (ttyname(STDIN_FILENO) == NULL)
1127 inp_stdin = 1;
1128 else {
1129 bb_error_msg("Missing filename");
1130 bb_show_usage();
1131 }
1132 }
1133
1134 strcpy(filename, (inp_stdin) ? bb_msg_standard_input : files[0]);
1135 get_terminal_width_height(0, &width, &height);
1136 data_readlines();
1137 tcgetattr(fileno(inp), &term_orig);
1138 term_vi = term_orig;
1139 term_vi.c_lflag &= (~ICANON & ~ECHO);
1140 term_vi.c_iflag &= (~IXON & ~ICRNL);
1141 term_vi.c_oflag &= (~ONLCR);
1142 term_vi.c_cc[VMIN] = 1;
1143 term_vi.c_cc[VTIME] = 0;
1144 buffer_init();
1145 buffer_print();
1146
1147 while (1) {
1148 keypress = tless_getch();
1149 keypress_process(keypress);
1150 }
1151}
Note: See TracBrowser for help on using the repository browser.