toybox/toys/pending/more.c
<<
>>
Prefs
   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