Home | History | Annotate | Download | only in pending
      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