1 /* more.c - View FILE (or stdin) one screenfull at a time.
2 *
3 * Copyright 2013 Bilal Qureshi <bilal.jmi@gmail.com>
4 *
5 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/more.html
6
7 USE_MORE(NEWTOY(more, 0, TOYFLAG_USR|TOYFLAG_BIN))
8
9 config MORE
10 bool "more"
11 default n
12 help
13 usage: more [FILE...]
14
15 View FILE(s) (or stdin) one screenfull at a time.
16 */
17
18 #define FOR_more
19 #include "toys.h"
20
GLOBALS(struct termios inf;int cin_fd;)21 GLOBALS(
22 struct termios inf;
23 int cin_fd;
24 )
25
26 static void signal_handler(int sig)
27 {
28 // Reset the terminal whether we were signalled or exited normally.
29 tcsetattr(TT.cin_fd, TCSANOW, &TT.inf);
30
31 // If we were actually signalled, move to a new line and re-raise the signal.
32 if (sig != 0) {
33 xputc('\n');
34 signal(sig, SIG_DFL);
35 raise(sig);
36 _exit(sig | 128);
37 }
38 }
39
show_file_header(const char * name)40 static void show_file_header(const char *name)
41 {
42 printf(":::::::::::::::::::::::\n%s\n:::::::::::::::::::::::\n", name);
43 }
44
prompt(FILE * cin,const char * fmt,...)45 static int prompt(FILE *cin, const char* fmt, ...)
46 {
47 int input_key;
48 va_list ap;
49
50 printf("\33[7m"); // Reverse video before printing the prompt.
51
52 va_start(ap, fmt);
53 vfprintf(stdout, fmt, ap);
54 va_end(ap);
55
56 while (1) {
57 fflush(NULL);
58 input_key = tolower(getc(cin));
59 printf("\33[0m\33[1K\r"); // Reset all attributes, erase to start of line.
60 if (strchr(" \nrq", input_key)) {
61 fflush(NULL);
62 return input_key;
63 }
64 printf("\33[7m(Enter:Next line Space:Next page Q:Quit R:Show the rest)");
65 }
66 }
67
do_cat_operation(int fd,char * name)68 static void do_cat_operation(int fd, char *name)
69 {
70 if (toys.optc > 1) show_file_header(name);
71 xsendfile(fd, 1);
72 }
73
more_main()74 void more_main()
75 {
76 int ch, input_key = 0, show_prompt;
77 unsigned rows = 24, cols = 80, row = 0, col = 0;
78 struct stat st;
79 struct termios newf;
80 FILE *fp, *cin;
81
82 if (!isatty(1) || !(cin = fopen("/dev/tty", "r"))) {
83 loopfiles(toys.optargs, do_cat_operation);
84 return;
85 }
86
87 TT.cin_fd = fileno(cin);
88 tcgetattr(TT.cin_fd, &TT.inf);
89
90 //Prepare terminal for input
91 memcpy(&newf, &TT.inf, sizeof(struct termios));
92 newf.c_lflag &= ~(ICANON | ECHO);
93 newf.c_cc[VMIN] = 1;
94 newf.c_cc[VTIME] = 0;
95 tcsetattr(TT.cin_fd, TCSANOW, &newf);
96
97 sigatexit(signal_handler);
98
99 do {
100 fp = stdin;
101 if (*toys.optargs && !(fp = fopen(*toys.optargs, "r"))) {
102 perror_msg("%s", *toys.optargs);
103 goto next_file;
104 }
105 st.st_size = show_prompt = col = row = 0;
106 fstat(fileno(fp), &st);
107 terminal_size(&cols, &rows);
108 rows--;
109
110 if (toys.optc > 1) {
111 show_file_header(*toys.optargs);
112 row += 3;
113 }
114
115 while ((ch = getc(fp)) != EOF) {
116 if (input_key != 'r' && show_prompt) {
117 if (st.st_size)
118 input_key = prompt(cin, "--More--(%d%% of %lld bytes)",
119 (int) (100 * ( (double) ftell(fp) / (double) st.st_size)),
120 (long long)st.st_size);
121 else
122 input_key = prompt(cin, "--More--");
123 if (input_key == 'q') goto stop;
124
125 col = row = show_prompt = 0;
126 terminal_size(&cols, &rows);
127 rows--;
128 }
129
130 putchar(ch);
131 if (ch == '\t') col = (col | 0x7) + 1;
132 else col++;
133 if (col == cols) putchar(ch = '\n');
134 if (ch == '\n') {
135 col = 0;
136 if (++row >= rows || input_key == '\n') show_prompt = 1;
137 }
138 }
139 fclose(fp);
140
141 next_file:
142 if (*toys.optargs && *++toys.optargs) {
143 input_key = prompt(cin, "--More--(Next file: %s)", *toys.optargs);
144 if (input_key == 'q') goto stop;
145 }
146 } while (*toys.optargs);
147
148 stop:
149 tcsetattr(TT.cin_fd, TCSANOW, &TT.inf);
150 fclose(cin);
151 // Even if optarg not found, exit value still 0
152 toys.exitval = 0;
153 }
154