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