• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* more.c - View FILE (or stdin) one screenful 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 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   // Reset the terminal whether we were signalled or exited normally.
29   tcsetattr(TT.cin_fd, TCSANOW, &TT.inf);
30 
31   if (sig == 0) _exit(0);
32 
33   // We were actually signalled, so move to a new line and re-raise the signal.
34   xputc('\n');
35   signal(sig, SIG_DFL);
36   raise(sig);
37   _exit(sig | 128);
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 
do_cat_operation(int fd,char * name)68 static void do_cat_operation(int fd, char *name)
69 {
70   if (toys.optc > 1) show_file_header(name);
71   xsendfile(fd, 1);
72 }
73 
more_main()74 void more_main()
75 {
76   int ch, input_key = 0, show_prompt;
77   unsigned rows = 24, cols = 80, row = 0, col = 0;
78   struct stat st;
79   struct termios newf;
80   FILE *fp, *cin;
81 
82   if (!isatty(1) || !(cin = fopen("/dev/tty", "r"))) {
83     loopfiles(toys.optargs, do_cat_operation);
84     return;
85   }
86 
87   TT.cin_fd = fileno(cin);
88   tcgetattr(TT.cin_fd, &TT.inf);
89 
90   //Prepare terminal for input
91   memcpy(&newf, &TT.inf, sizeof(struct termios));
92   newf.c_lflag &= ~(ICANON | ECHO);
93   newf.c_cc[VMIN] = 1;
94   newf.c_cc[VTIME] = 0;
95   tcsetattr(TT.cin_fd, TCSANOW, &newf);
96 
97   sigatexit(signal_handler);
98 
99   do {
100     fp = stdin;
101     if (*toys.optargs && !(fp = fopen(*toys.optargs, "r"))) {
102         perror_msg("%s", *toys.optargs);
103         goto next_file;
104     }
105     st.st_size = show_prompt = col = row = 0;
106     fstat(fileno(fp), &st);
107     terminal_size(&cols, &rows);
108     rows--;
109 
110     if (toys.optc > 1) {
111       show_file_header(*toys.optargs);
112       row += 3;
113     }
114 
115     while ((ch = getc(fp)) != EOF) {
116       if (input_key != 'r' && show_prompt) {
117         if (st.st_size)
118           input_key = prompt(cin, "--More--(%d%% of %lld bytes)",
119               (int) (100 * ( (double) ftell(fp) / (double) st.st_size)),
120               (long long)st.st_size);
121         else
122           input_key = prompt(cin, "--More--");
123         if (input_key == 'q') goto stop;
124 
125         col = row = show_prompt = 0;
126         terminal_size(&cols, &rows);
127         rows--;
128       }
129 
130       putchar(ch);
131       if (ch == '\t') col = (col | 0x7) + 1;
132       else col++;
133       if (col == cols) putchar(ch = '\n');
134       if (ch == '\n') {
135         col = 0;
136         if (++row >= rows || input_key == '\n') show_prompt = 1;
137       }
138     }
139     fclose(fp);
140 
141 next_file:
142     if (*toys.optargs && *++toys.optargs) {
143       input_key = prompt(cin, "--More--(Next file: %s)", *toys.optargs);
144       if (input_key == 'q') goto stop;
145     }
146   } while (*toys.optargs);
147 
148 stop:
149   tcsetattr(TT.cin_fd, TCSANOW, &TT.inf);
150   fclose(cin);
151   // Even if optarg not found, exit value still 0
152   toys.exitval = 0;
153 }
154