1 /* more.c - View FILE (or stdin) one screenful at a time. 2 * 3 * Copyright 2013 Bilal Qureshi <bilal.jmi (at) 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 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 40 static void show_file_header(const char *name) 41 { 42 printf(":::::::::::::::::::::::\n%s\n:::::::::::::::::::::::\n", name); 43 } 44 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 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 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