source: MondoRescue/branches/stable/mindi-busybox/miscutils/less.c @ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 14 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.