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