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 7USE_MORE(NEWTOY(more, 0, TOYFLAG_USR|TOYFLAG_BIN)) 8 9config 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 21GLOBALS( 22 struct termios inf; 23 int cin_fd; 24) 25 26static 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 40static void show_file_header(const char *name) 41{ 42 printf("::::::::::::::\n%s\n::::::::::::::\n", name); 43} 44 45static 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 68static 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 77static 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 88void 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 160next_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 167stop: 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