1/* more.c - View FILE (or stdin) one screenful at a time.
2 *
3 * Copyright 2013 Bilal Qureshi <bilal.jmi@gmail.com>
4 *
5 * No Standard
6
7USE_MORE(NEWTOY(more, NULL, TOYFLAG_USR|TOYFLAG_BIN))
8
9config 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
21GLOBALS(
22  struct termios inf;
23  int cin_fd;
24)
25
26static 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
35static void show_file_header(const char *name)
36{
37  printf(":::::::::::::::::::::::\n%s\n:::::::::::::::::::::::\n", name);
38}
39
40static 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
63static 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
71void 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
137next_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
144stop:
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