• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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