source: branches/3.2/mindi-busybox/util-linux/more.c @ 3232

Last change on this file since 3232 was 3232, checked in by Bruno Cornec, 7 years ago
  • Update mindi-busybox to 1.21.1
File size: 5.7 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini more implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 *
8 * Latest version blended together by Erik Andersen <andersen@codepoet.org>,
9 * based on the original more implementation by Bruce, and code from the
10 * Debian boot-floppies team.
11 *
12 * Termios corrects by Vladimir Oleynik <dzo@simtreas.ru>
13 *
14 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
15 */
16
17//usage:#define more_trivial_usage
18//usage:       "[FILE]..."
19//usage:#define more_full_usage "\n\n"
20//usage:       "View FILE (or stdin) one screenful at a time"
21//usage:
22//usage:#define more_example_usage
23//usage:       "$ dmesg | more\n"
24
25#include "libbb.h"
26
27/* Support for FEATURE_USE_TERMIOS */
28
29struct globals {
30    int cin_fileno;
31    struct termios initial_settings;
32    struct termios new_settings;
33} FIX_ALIASING;
34#define G (*(struct globals*)bb_common_bufsiz1)
35#define INIT_G() ((void)0)
36#define initial_settings (G.initial_settings)
37#define new_settings     (G.new_settings    )
38#define cin_fileno       (G.cin_fileno      )
39
40#define setTermSettings(fd, argp) \
41do { \
42    if (ENABLE_FEATURE_USE_TERMIOS) \
43        tcsetattr(fd, TCSANOW, argp); \
44} while (0)
45#define getTermSettings(fd, argp) tcgetattr(fd, argp)
46
47static void gotsig(int sig UNUSED_PARAM)
48{
49    /* bb_putchar_stderr doesn't use stdio buffering,
50     * therefore it is safe in signal handler */
51    bb_putchar_stderr('\n');
52    setTermSettings(cin_fileno, &initial_settings);
53    _exit(EXIT_FAILURE);
54}
55
56#define CONVERTED_TAB_SIZE 8
57
58int more_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
59int more_main(int argc UNUSED_PARAM, char **argv)
60{
61    int c = c; /* for compiler */
62    int lines;
63    int input = 0;
64    int spaces = 0;
65    int please_display_more_prompt;
66    struct stat st;
67    FILE *file;
68    FILE *cin;
69    int len;
70    unsigned terminal_width;
71    unsigned terminal_height;
72
73    INIT_G();
74
75    argv++;
76    /* Another popular pager, most, detects when stdout
77     * is not a tty and turns into cat. This makes sense. */
78    if (!isatty(STDOUT_FILENO))
79        return bb_cat(argv);
80    cin = fopen_for_read(CURRENT_TTY);
81    if (!cin)
82        return bb_cat(argv);
83
84    if (ENABLE_FEATURE_USE_TERMIOS) {
85        cin_fileno = fileno(cin);
86        getTermSettings(cin_fileno, &initial_settings);
87        new_settings = initial_settings;
88        new_settings.c_lflag &= ~(ICANON | ECHO);
89        new_settings.c_cc[VMIN] = 1;
90        new_settings.c_cc[VTIME] = 0;
91        setTermSettings(cin_fileno, &new_settings);
92        bb_signals(0
93            + (1 << SIGINT)
94            + (1 << SIGQUIT)
95            + (1 << SIGTERM)
96            , gotsig);
97    }
98
99    do {
100        file = stdin;
101        if (*argv) {
102            file = fopen_or_warn(*argv, "r");
103            if (!file)
104                continue;
105        }
106        st.st_size = 0;
107        fstat(fileno(file), &st);
108
109        please_display_more_prompt = 0;
110        /* never returns w, h <= 1 */
111        get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height);
112        terminal_height -= 1;
113
114        len = 0;
115        lines = 0;
116        while (spaces || (c = getc(file)) != EOF) {
117            int wrap;
118            if (spaces)
119                spaces--;
120 loop_top:
121            if (input != 'r' && please_display_more_prompt) {
122                len = printf("--More-- ");
123                if (st.st_size != 0) {
124                    uoff_t d = (uoff_t)st.st_size / 100;
125                    if (d == 0)
126                        d = 1;
127                    len += printf("(%u%% of %"OFF_FMT"u bytes)",
128                        (int) ((uoff_t)ftello(file) / d),
129                        st.st_size);
130                }
131                fflush_all();
132
133                /*
134                 * We've just displayed the "--More--" prompt, so now we need
135                 * to get input from the user.
136                 */
137                for (;;) {
138                    input = getc(cin);
139                    input = tolower(input);
140                    if (!ENABLE_FEATURE_USE_TERMIOS)
141                        printf("\033[A"); /* cursor up */
142                    /* Erase the last message */
143                    printf("\r%*s\r", len, "");
144
145                    /* Due to various multibyte escape
146                     * sequences, it's not ok to accept
147                     * any input as a command to scroll
148                     * the screen. We only allow known
149                     * commands, else we show help msg. */
150                    if (input == ' ' || input == '\n' || input == 'q' || input == 'r')
151                        break;
152                    len = printf("(Enter:next line Space:next page Q:quit R:show the rest)");
153                }
154                len = 0;
155                lines = 0;
156                please_display_more_prompt = 0;
157
158                if (input == 'q')
159                    goto end;
160
161                /* The user may have resized the terminal.
162                 * Re-read the dimensions. */
163                if (ENABLE_FEATURE_USE_TERMIOS) {
164                    get_terminal_width_height(cin_fileno, &terminal_width, &terminal_height);
165                    terminal_height -= 1;
166                }
167            }
168
169            /* Crudely convert tabs into spaces, which are
170             * a bajillion times easier to deal with. */
171            if (c == '\t') {
172                spaces = ((unsigned)~len) % CONVERTED_TAB_SIZE;
173                c = ' ';
174            }
175
176            /*
177             * There are two input streams to worry about here:
178             *
179             * c    : the character we are reading from the file being "mored"
180             * input: a character received from the keyboard
181             *
182             * If we hit a newline in the _file_ stream, we want to test and
183             * see if any characters have been hit in the _input_ stream. This
184             * allows the user to quit while in the middle of a file.
185             */
186            wrap = (++len > terminal_width);
187            if (c == '\n' || wrap) {
188                /* Then outputting this character
189                 * will move us to a new line. */
190                if (++lines >= terminal_height || input == '\n')
191                    please_display_more_prompt = 1;
192                len = 0;
193            }
194            if (c != '\n' && wrap) {
195                /* Then outputting this will also put a character on
196                 * the beginning of that new line. Thus we first want to
197                 * display the prompt (if any), so we skip the putchar()
198                 * and go back to the top of the loop, without reading
199                 * a new character. */
200                goto loop_top;
201            }
202            /* My small mind cannot fathom backspaces and UTF-8 */
203            putchar(c);
204            die_if_ferror_stdout(); /* if tty was destroyed (closed xterm, etc) */
205        }
206        fclose(file);
207        fflush_all();
208    } while (*argv && *++argv);
209 end:
210    setTermSettings(cin_fileno, &initial_settings);
211    return 0;
212}
Note: See TracBrowser for help on using the repository browser.