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
more_directory(char * path,struct stat * st)68 static int more_directory(char *path, struct stat *st)
69 {
70 if (!stat(path, st) && S_ISDIR(st->st_mode)) {
71 printf("\n*** %s: directory ***\n\n", path);
72 return 1;
73 }
74 return 0;
75 }
76
do_cat_operation(int fd,char * name)77 static void do_cat_operation(int fd, char *name)
78 {
79 struct stat st;
80
81 if (!more_directory(name, &st)) {
82 show_file_header(name);
83 fflush(stdout);
84 xsendfile(fd, 1);
85 }
86 }
87
more_main()88 void more_main()
89 {
90 int ch, input_key = 0, show_prompt;
91 unsigned rows = 24, cols = 80, row = 0, col = 0;
92 struct stat st;
93 struct termios newf;
94 FILE *fp, *cin;
95
96 if (!isatty(1) || !(cin = fopen("/dev/tty", "r"))) {
97 loopfiles(toys.optargs, do_cat_operation);
98 return;
99 }
100
101 TT.cin_fd = fileno(cin);
102 tcgetattr(TT.cin_fd, &TT.inf);
103
104 //Prepare terminal for input
105 memcpy(&newf, &TT.inf, sizeof(struct termios));
106 newf.c_lflag &= ~(ICANON | ECHO);
107 newf.c_cc[VMIN] = 1;
108 newf.c_cc[VTIME] = 0;
109 tcsetattr(TT.cin_fd, TCSANOW, &newf);
110
111 sigatexit(signal_handler);
112
113 do {
114 char *filename = *toys.optargs;
115
116 st.st_size = show_prompt = col = row = 0;
117 if (!filename) fp = stdin;
118 else {
119 if (more_directory(filename, &st)) goto next_file;
120 if (!(fp = fopen(filename, "r"))) {
121 perror_msg("%s", filename);
122 goto next_file;
123 }
124 }
125
126 terminal_size(&cols, &rows);
127 rows--;
128
129 if (toys.optc > 1) {
130 show_file_header(filename);
131 row += 3;
132 }
133
134 while ((ch = getc(fp)) != EOF) {
135 if (input_key != 'r' && show_prompt) {
136 if (st.st_size)
137 input_key = prompt(cin, "--More--(%d%% of %lld bytes)",
138 (int) (100 * ( (double) ftell(fp) / (double) st.st_size)),
139 (long long)st.st_size);
140 else
141 input_key = prompt(cin, "--More--");
142 if (input_key == 'q') goto stop;
143
144 col = row = show_prompt = 0;
145 terminal_size(&cols, &rows);
146 rows--;
147 }
148
149 putchar(ch);
150 if (ch == '\t') col = (col | 0x7) + 1;
151 else col++;
152 if (col == cols) putchar(ch = '\n');
153 if (ch == '\n') {
154 col = 0;
155 if (++row >= rows || input_key == '\n') show_prompt = 1;
156 }
157 }
158 fclose(fp);
159
160 next_file:
161 if (*toys.optargs && *++toys.optargs) {
162 input_key = prompt(cin, "--More--(Next file: %s)", *toys.optargs);
163 if (input_key == 'q') goto stop;
164 }
165 } while (*toys.optargs);
166
167 stop:
168 tcsetattr(TT.cin_fd, TCSANOW, &TT.inf);
169 fclose(cin);
170 // Even if optarg not found, exit value still 0
171 toys.exitval = 0;
172 }
173