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