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