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