10f66f451Sopenharmony_ci/* more.c - View FILE (or stdin) one screenfull at a time. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2013 Bilal Qureshi <bilal.jmi@gmail.com> 40f66f451Sopenharmony_ci * 50f66f451Sopenharmony_ci * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/more.html 60f66f451Sopenharmony_ci 70f66f451Sopenharmony_ciUSE_MORE(NEWTOY(more, 0, TOYFLAG_USR|TOYFLAG_BIN)) 80f66f451Sopenharmony_ci 90f66f451Sopenharmony_ciconfig MORE 100f66f451Sopenharmony_ci bool "more" 110f66f451Sopenharmony_ci default n 120f66f451Sopenharmony_ci help 130f66f451Sopenharmony_ci usage: more [FILE...] 140f66f451Sopenharmony_ci 150f66f451Sopenharmony_ci View FILE(s) (or stdin) one screenfull at a time. 160f66f451Sopenharmony_ci*/ 170f66f451Sopenharmony_ci 180f66f451Sopenharmony_ci#define FOR_more 190f66f451Sopenharmony_ci#include "toys.h" 200f66f451Sopenharmony_ci 210f66f451Sopenharmony_ciGLOBALS( 220f66f451Sopenharmony_ci struct termios inf; 230f66f451Sopenharmony_ci int cin_fd; 240f66f451Sopenharmony_ci) 250f66f451Sopenharmony_ci 260f66f451Sopenharmony_cistatic void signal_handler(int sig) 270f66f451Sopenharmony_ci{ 280f66f451Sopenharmony_ci // Reset the terminal whether we were signalled or exited normally. 290f66f451Sopenharmony_ci tcsetattr(TT.cin_fd, TCSANOW, &TT.inf); 300f66f451Sopenharmony_ci 310f66f451Sopenharmony_ci // If we were actually signalled, move to a new line and re-raise the signal. 320f66f451Sopenharmony_ci if (sig != 0) { 330f66f451Sopenharmony_ci xputc('\n'); 340f66f451Sopenharmony_ci signal(sig, SIG_DFL); 350f66f451Sopenharmony_ci raise(sig); 360f66f451Sopenharmony_ci _exit(sig | 128); 370f66f451Sopenharmony_ci } 380f66f451Sopenharmony_ci} 390f66f451Sopenharmony_ci 400f66f451Sopenharmony_cistatic void show_file_header(const char *name) 410f66f451Sopenharmony_ci{ 420f66f451Sopenharmony_ci printf("::::::::::::::\n%s\n::::::::::::::\n", name); 430f66f451Sopenharmony_ci} 440f66f451Sopenharmony_ci 450f66f451Sopenharmony_cistatic int prompt(FILE *cin, const char* fmt, ...) 460f66f451Sopenharmony_ci{ 470f66f451Sopenharmony_ci int input_key; 480f66f451Sopenharmony_ci va_list ap; 490f66f451Sopenharmony_ci 500f66f451Sopenharmony_ci printf("\33[7m"); // Reverse video before printing the prompt. 510f66f451Sopenharmony_ci 520f66f451Sopenharmony_ci va_start(ap, fmt); 530f66f451Sopenharmony_ci vfprintf(stdout, fmt, ap); 540f66f451Sopenharmony_ci va_end(ap); 550f66f451Sopenharmony_ci 560f66f451Sopenharmony_ci while (1) { 570f66f451Sopenharmony_ci fflush(NULL); 580f66f451Sopenharmony_ci input_key = tolower(getc(cin)); 590f66f451Sopenharmony_ci printf("\33[0m\33[1K\r"); // Reset all attributes, erase to start of line. 600f66f451Sopenharmony_ci if (strchr(" \nrq", input_key)) { 610f66f451Sopenharmony_ci fflush(NULL); 620f66f451Sopenharmony_ci return input_key; 630f66f451Sopenharmony_ci } 640f66f451Sopenharmony_ci printf("\33[7m(Enter:Next line Space:Next page Q:Quit R:Show the rest)"); 650f66f451Sopenharmony_ci } 660f66f451Sopenharmony_ci} 670f66f451Sopenharmony_ci 680f66f451Sopenharmony_cistatic int more_directory(char *path, struct stat *st) 690f66f451Sopenharmony_ci{ 700f66f451Sopenharmony_ci if (!stat(path, st) && S_ISDIR(st->st_mode)) { 710f66f451Sopenharmony_ci printf("\n*** %s: directory ***\n\n", path); 720f66f451Sopenharmony_ci return 1; 730f66f451Sopenharmony_ci } 740f66f451Sopenharmony_ci return 0; 750f66f451Sopenharmony_ci} 760f66f451Sopenharmony_ci 770f66f451Sopenharmony_cistatic void do_cat_operation(int fd, char *name) 780f66f451Sopenharmony_ci{ 790f66f451Sopenharmony_ci struct stat st; 800f66f451Sopenharmony_ci 810f66f451Sopenharmony_ci if (!more_directory(name, &st)) { 820f66f451Sopenharmony_ci show_file_header(name); 830f66f451Sopenharmony_ci fflush(stdout); 840f66f451Sopenharmony_ci xsendfile(fd, 1); 850f66f451Sopenharmony_ci } 860f66f451Sopenharmony_ci} 870f66f451Sopenharmony_ci 880f66f451Sopenharmony_civoid more_main() 890f66f451Sopenharmony_ci{ 900f66f451Sopenharmony_ci int ch, input_key = 0, show_prompt; 910f66f451Sopenharmony_ci unsigned rows = 24, cols = 80, row = 0, col = 0; 920f66f451Sopenharmony_ci struct stat st; 930f66f451Sopenharmony_ci struct termios newf; 940f66f451Sopenharmony_ci FILE *fp, *cin; 950f66f451Sopenharmony_ci 960f66f451Sopenharmony_ci if (!isatty(1) || !(cin = fopen("/dev/tty", "r"))) { 970f66f451Sopenharmony_ci loopfiles(toys.optargs, do_cat_operation); 980f66f451Sopenharmony_ci return; 990f66f451Sopenharmony_ci } 1000f66f451Sopenharmony_ci 1010f66f451Sopenharmony_ci TT.cin_fd = fileno(cin); 1020f66f451Sopenharmony_ci tcgetattr(TT.cin_fd, &TT.inf); 1030f66f451Sopenharmony_ci 1040f66f451Sopenharmony_ci //Prepare terminal for input 1050f66f451Sopenharmony_ci memcpy(&newf, &TT.inf, sizeof(struct termios)); 1060f66f451Sopenharmony_ci newf.c_lflag &= ~(ICANON | ECHO); 1070f66f451Sopenharmony_ci newf.c_cc[VMIN] = 1; 1080f66f451Sopenharmony_ci newf.c_cc[VTIME] = 0; 1090f66f451Sopenharmony_ci tcsetattr(TT.cin_fd, TCSANOW, &newf); 1100f66f451Sopenharmony_ci 1110f66f451Sopenharmony_ci sigatexit(signal_handler); 1120f66f451Sopenharmony_ci 1130f66f451Sopenharmony_ci do { 1140f66f451Sopenharmony_ci char *filename = *toys.optargs; 1150f66f451Sopenharmony_ci 1160f66f451Sopenharmony_ci st.st_size = show_prompt = col = row = 0; 1170f66f451Sopenharmony_ci if (!filename) fp = stdin; 1180f66f451Sopenharmony_ci else { 1190f66f451Sopenharmony_ci if (more_directory(filename, &st)) goto next_file; 1200f66f451Sopenharmony_ci if (!(fp = fopen(filename, "r"))) { 1210f66f451Sopenharmony_ci perror_msg("%s", filename); 1220f66f451Sopenharmony_ci goto next_file; 1230f66f451Sopenharmony_ci } 1240f66f451Sopenharmony_ci } 1250f66f451Sopenharmony_ci 1260f66f451Sopenharmony_ci terminal_size(&cols, &rows); 1270f66f451Sopenharmony_ci rows--; 1280f66f451Sopenharmony_ci 1290f66f451Sopenharmony_ci if (toys.optc > 1) { 1300f66f451Sopenharmony_ci show_file_header(filename); 1310f66f451Sopenharmony_ci row += 3; 1320f66f451Sopenharmony_ci } 1330f66f451Sopenharmony_ci 1340f66f451Sopenharmony_ci while ((ch = getc(fp)) != EOF) { 1350f66f451Sopenharmony_ci if (input_key != 'r' && show_prompt) { 1360f66f451Sopenharmony_ci if (st.st_size) 1370f66f451Sopenharmony_ci input_key = prompt(cin, "--More--(%d%% of %lld bytes)", 1380f66f451Sopenharmony_ci (int) (100 * ( (double) ftell(fp) / (double) st.st_size)), 1390f66f451Sopenharmony_ci (long long)st.st_size); 1400f66f451Sopenharmony_ci else 1410f66f451Sopenharmony_ci input_key = prompt(cin, "--More--"); 1420f66f451Sopenharmony_ci if (input_key == 'q') goto stop; 1430f66f451Sopenharmony_ci 1440f66f451Sopenharmony_ci col = row = show_prompt = 0; 1450f66f451Sopenharmony_ci terminal_size(&cols, &rows); 1460f66f451Sopenharmony_ci rows--; 1470f66f451Sopenharmony_ci } 1480f66f451Sopenharmony_ci 1490f66f451Sopenharmony_ci putchar(ch); 1500f66f451Sopenharmony_ci if (ch == '\t') col = (col | 0x7) + 1; 1510f66f451Sopenharmony_ci else col++; 1520f66f451Sopenharmony_ci if (col == cols) putchar(ch = '\n'); 1530f66f451Sopenharmony_ci if (ch == '\n') { 1540f66f451Sopenharmony_ci col = 0; 1550f66f451Sopenharmony_ci if (++row >= rows || input_key == '\n') show_prompt = 1; 1560f66f451Sopenharmony_ci } 1570f66f451Sopenharmony_ci } 1580f66f451Sopenharmony_ci fclose(fp); 1590f66f451Sopenharmony_ci 1600f66f451Sopenharmony_cinext_file: 1610f66f451Sopenharmony_ci if (*toys.optargs && *++toys.optargs) { 1620f66f451Sopenharmony_ci input_key = prompt(cin, "--More--(Next file: %s)", *toys.optargs); 1630f66f451Sopenharmony_ci if (input_key == 'q') goto stop; 1640f66f451Sopenharmony_ci } 1650f66f451Sopenharmony_ci } while (*toys.optargs); 1660f66f451Sopenharmony_ci 1670f66f451Sopenharmony_cistop: 1680f66f451Sopenharmony_ci tcsetattr(TT.cin_fd, TCSANOW, &TT.inf); 1690f66f451Sopenharmony_ci fclose(cin); 1700f66f451Sopenharmony_ci // Even if optarg not found, exit value still 0 1710f66f451Sopenharmony_ci toys.exitval = 0; 1720f66f451Sopenharmony_ci} 173