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 * No Standard 6 7 USE_MORE(NEWTOY(more, NULL, TOYFLAG_USR|TOYFLAG_BIN)) 8 9 config MORE 10 bool "more" 11 default n 12 help 13 usage: more [FILE]... 14 15 View FILE (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 tcsetattr(TT.cin_fd, TCSANOW, &TT.inf); 29 xputc('\n'); 30 signal(sig, SIG_DFL); 31 raise(sig); 32 _exit(sig | 128); 33 } 34 35 static void show_file_header(const char *name) 36 { 37 printf(":::::::::::::::::::::::\n%s\n:::::::::::::::::::::::\n", name); 38 } 39 40 static int prompt(FILE *cin, const char* fmt, ...) 41 { 42 int input_key; 43 va_list ap; 44 45 printf("\33[7m"); // Reverse video before printing the prompt. 46 47 va_start(ap, fmt); 48 vfprintf(stdout, fmt, ap); 49 va_end(ap); 50 51 while (1) { 52 fflush(NULL); 53 input_key = tolower(getc(cin)); 54 printf("\33[0m\33[1K\r"); // Reset all attributes, erase to start of line. 55 if (strchr(" \nrq", input_key)) { 56 fflush(NULL); 57 return input_key; 58 } 59 printf("\33[7m(Enter:Next line Space:Next page Q:Quit R:Show the rest)"); 60 } 61 } 62 63 static void do_cat_operation(int fd, char *name) 64 { 65 char *buf = NULL; 66 67 if (toys.optc > 1) show_file_header(name); 68 for (; (buf = get_line(fd)); free(buf)) printf("%s\n", buf); 69 } 70 71 void more_main() 72 { 73 int ch, input_key = 0, show_prompt; 74 unsigned rows = 24, cols = 80, row = 0, col = 0; 75 struct stat st; 76 struct termios newf; 77 FILE *fp, *cin; 78 79 if (!isatty(STDOUT_FILENO) || !(cin = fopen("/dev/tty", "r"))) { 80 loopfiles(toys.optargs, do_cat_operation); 81 return; 82 } 83 84 TT.cin_fd = fileno(cin); 85 tcgetattr(TT.cin_fd, &TT.inf); 86 87 //Prepare terminal for input 88 memcpy(&newf, &TT.inf, sizeof(struct termios)); 89 newf.c_lflag &= ~(ICANON | ECHO); 90 newf.c_cc[VMIN] = 1; 91 newf.c_cc[VTIME] = 0; 92 tcsetattr(TT.cin_fd, TCSANOW, &newf); 93 94 sigatexit(signal_handler); 95 96 do { 97 fp = stdin; 98 if (*toys.optargs && !(fp = fopen(*toys.optargs, "r"))) { 99 perror_msg("%s", *toys.optargs); 100 goto next_file; 101 } 102 st.st_size = show_prompt = col = row = 0; 103 fstat(fileno(fp), &st); 104 terminal_size(&cols, &rows); 105 rows--; 106 107 if (toys.optc > 1) { 108 show_file_header(*toys.optargs); 109 row += 3; 110 } 111 112 while ((ch = getc(fp)) != EOF) { 113 if (input_key != 'r' && show_prompt) { 114 if (st.st_size) 115 input_key = prompt(cin, "--More--(%d%% of %lld bytes)", 116 (int) (100 * ( (double) ftell(fp) / (double) st.st_size)), 117 (long long)st.st_size); 118 else 119 input_key = prompt(cin, "--More--"); 120 if (input_key == 'q') goto stop; 121 122 col = row = show_prompt = 0; 123 terminal_size(&cols, &rows); 124 rows--; 125 } 126 127 putchar(ch); 128 if (ch == '\t') col = (col | 0x7) + 1; else col++; 129 if (col == cols) putchar(ch = '\n'); 130 if (ch == '\n') { 131 col = 0; 132 if (++row >= rows || input_key == '\n') show_prompt = 1; 133 } 134 } 135 fclose(fp); 136 137 next_file: 138 if (*toys.optargs && *++toys.optargs) { 139 input_key = prompt(cin, "--More--(Next file: %s)", *toys.optargs); 140 if (input_key == 'q') goto stop; 141 } 142 } while (*toys.optargs); 143 144 stop: 145 tcsetattr(TT.cin_fd, TCSANOW, &TT.inf); 146 fclose(cin); 147 // Even if optarg not found, exit value still 0 148 toys.exitval = 0; 149 } 150