more.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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. USE_MORE(NEWTOY(more, 0, TOYFLAG_USR|TOYFLAG_BIN))
  7. config MORE
  8. bool "more"
  9. default n
  10. help
  11. usage: more [FILE...]
  12. View FILE(s) (or stdin) one screenfull at a time.
  13. */
  14. #define FOR_more
  15. #include "toys.h"
  16. GLOBALS(
  17. struct termios inf;
  18. int cin_fd;
  19. )
  20. static void signal_handler(int sig)
  21. {
  22. // Reset the terminal whether we were signalled or exited normally.
  23. tcsetattr(TT.cin_fd, TCSANOW, &TT.inf);
  24. // If we were actually signalled, move to a new line and re-raise the signal.
  25. if (sig != 0) {
  26. xputc('\n');
  27. signal(sig, SIG_DFL);
  28. raise(sig);
  29. _exit(sig | 128);
  30. }
  31. }
  32. static void show_file_header(const char *name)
  33. {
  34. printf("::::::::::::::\n%s\n::::::::::::::\n", name);
  35. }
  36. static int prompt(FILE *cin, const char* fmt, ...)
  37. {
  38. int input_key;
  39. va_list ap;
  40. printf("\33[7m"); // Reverse video before printing the prompt.
  41. va_start(ap, fmt);
  42. vfprintf(stdout, fmt, ap);
  43. va_end(ap);
  44. while (1) {
  45. fflush(NULL);
  46. input_key = tolower(getc(cin));
  47. printf("\33[0m\33[1K\r"); // Reset all attributes, erase to start of line.
  48. if (strchr(" \nrq", input_key)) {
  49. fflush(NULL);
  50. return input_key;
  51. }
  52. printf("\33[7m(Enter:Next line Space:Next page Q:Quit R:Show the rest)");
  53. }
  54. }
  55. static int more_directory(char *path, struct stat *st)
  56. {
  57. if (!stat(path, st) && S_ISDIR(st->st_mode)) {
  58. printf("\n*** %s: directory ***\n\n", path);
  59. return 1;
  60. }
  61. return 0;
  62. }
  63. static void do_cat_operation(int fd, char *name)
  64. {
  65. struct stat st;
  66. if (!more_directory(name, &st)) {
  67. show_file_header(name);
  68. fflush(stdout);
  69. xsendfile(fd, 1);
  70. }
  71. }
  72. void more_main()
  73. {
  74. int ch, input_key = 0, show_prompt;
  75. unsigned rows = 24, cols = 80, row = 0, col = 0;
  76. struct stat st;
  77. struct termios newf;
  78. FILE *fp, *cin;
  79. if (!isatty(1) || !(cin = fopen("/dev/tty", "r"))) {
  80. loopfiles(toys.optargs, do_cat_operation);
  81. return;
  82. }
  83. TT.cin_fd = fileno(cin);
  84. tcgetattr(TT.cin_fd, &TT.inf);
  85. //Prepare terminal for input
  86. memcpy(&newf, &TT.inf, sizeof(struct termios));
  87. newf.c_lflag &= ~(ICANON | ECHO);
  88. newf.c_cc[VMIN] = 1;
  89. newf.c_cc[VTIME] = 0;
  90. tcsetattr(TT.cin_fd, TCSANOW, &newf);
  91. sigatexit(signal_handler);
  92. do {
  93. char *filename = *toys.optargs;
  94. st.st_size = show_prompt = col = row = 0;
  95. if (!filename) fp = stdin;
  96. else {
  97. if (more_directory(filename, &st)) goto next_file;
  98. if (!(fp = fopen(filename, "r"))) {
  99. perror_msg("%s", filename);
  100. goto next_file;
  101. }
  102. }
  103. terminal_size(&cols, &rows);
  104. rows--;
  105. if (toys.optc > 1) {
  106. show_file_header(filename);
  107. row += 3;
  108. }
  109. while ((ch = getc(fp)) != EOF) {
  110. if (input_key != 'r' && show_prompt) {
  111. if (st.st_size)
  112. input_key = prompt(cin, "--More--(%d%% of %lld bytes)",
  113. (int) (100 * ( (double) ftell(fp) / (double) st.st_size)),
  114. (long long)st.st_size);
  115. else
  116. input_key = prompt(cin, "--More--");
  117. if (input_key == 'q') goto stop;
  118. col = row = show_prompt = 0;
  119. terminal_size(&cols, &rows);
  120. rows--;
  121. }
  122. putchar(ch);
  123. if (ch == '\t') col = (col | 0x7) + 1;
  124. else col++;
  125. if (col == cols) putchar(ch = '\n');
  126. if (ch == '\n') {
  127. col = 0;
  128. if (++row >= rows || input_key == '\n') show_prompt = 1;
  129. }
  130. }
  131. fclose(fp);
  132. next_file:
  133. if (*toys.optargs && *++toys.optargs) {
  134. input_key = prompt(cin, "--More--(Next file: %s)", *toys.optargs);
  135. if (input_key == 'q') goto stop;
  136. }
  137. } while (*toys.optargs);
  138. stop:
  139. tcsetattr(TT.cin_fd, TCSANOW, &TT.inf);
  140. fclose(cin);
  141. // Even if optarg not found, exit value still 0
  142. toys.exitval = 0;
  143. }