more.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Mini more implementation for busybox
  4. *
  5. * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
  6. * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  7. *
  8. * Latest version blended together by Erik Andersen <andersen@codepoet.org>,
  9. * based on the original more implementation by Bruce, and code from the
  10. * Debian boot-floppies team.
  11. *
  12. * Termios corrects by Vladimir Oleynik <dzo@simtreas.ru>
  13. *
  14. * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  15. */
  16. #include "libbb.h"
  17. /* Support for FEATURE_USE_TERMIOS */
  18. struct globals {
  19. int cin_fileno;
  20. struct termios initial_settings;
  21. struct termios new_settings;
  22. } FIX_ALIASING;
  23. #define G (*(struct globals*)bb_common_bufsiz1)
  24. #define INIT_G() ((void)0)
  25. #define initial_settings (G.initial_settings)
  26. #define new_settings (G.new_settings )
  27. #define cin_fileno (G.cin_fileno )
  28. #define setTermSettings(fd, argp) \
  29. do { \
  30. if (ENABLE_FEATURE_USE_TERMIOS) \
  31. tcsetattr(fd, TCSANOW, argp); \
  32. } while (0)
  33. #define getTermSettings(fd, argp) tcgetattr(fd, argp)
  34. static void gotsig(int sig UNUSED_PARAM)
  35. {
  36. /* bb_putchar_stderr doesn't use stdio buffering,
  37. * therefore it is safe in signal handler */
  38. bb_putchar_stderr('\n');
  39. setTermSettings(cin_fileno, &initial_settings);
  40. _exit(EXIT_FAILURE);
  41. }
  42. #define CONVERTED_TAB_SIZE 8
  43. int more_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  44. int more_main(int argc UNUSED_PARAM, char **argv)
  45. {
  46. int c = c; /* for compiler */
  47. int lines;
  48. int input = 0;
  49. int spaces = 0;
  50. int please_display_more_prompt;
  51. struct stat st;
  52. FILE *file;
  53. FILE *cin;
  54. int len;
  55. unsigned terminal_width;
  56. unsigned terminal_height;
  57. INIT_G();
  58. argv++;
  59. /* Another popular pager, most, detects when stdout
  60. * is not a tty and turns into cat. This makes sense. */
  61. if (!isatty(STDOUT_FILENO))
  62. return bb_cat(argv);
  63. cin = fopen_for_read(CURRENT_TTY);
  64. if (!cin)
  65. return bb_cat(argv);
  66. if (ENABLE_FEATURE_USE_TERMIOS) {
  67. cin_fileno = fileno(cin);
  68. getTermSettings(cin_fileno, &initial_settings);
  69. new_settings = initial_settings;
  70. new_settings.c_lflag &= ~ICANON;
  71. new_settings.c_lflag &= ~ECHO;
  72. new_settings.c_cc[VMIN] = 1;
  73. new_settings.c_cc[VTIME] = 0;
  74. setTermSettings(cin_fileno, &new_settings);
  75. bb_signals(0
  76. + (1 << SIGINT)
  77. + (1 << SIGQUIT)
  78. + (1 << SIGTERM)
  79. , gotsig);
  80. }
  81. do {
  82. file = stdin;
  83. if (*argv) {
  84. file = fopen_or_warn(*argv, "r");
  85. if (!file)
  86. continue;
  87. }
  88. st.st_size = 0;
  89. fstat(fileno(file), &st);
  90. please_display_more_prompt = 0;
  91. /* never returns w, h <= 1 */
  92. get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height);
  93. terminal_height -= 1;
  94. len = 0;
  95. lines = 0;
  96. while (spaces || (c = getc(file)) != EOF) {
  97. int wrap;
  98. if (spaces)
  99. spaces--;
  100. loop_top:
  101. if (input != 'r' && please_display_more_prompt) {
  102. len = printf("--More-- ");
  103. if (st.st_size > 0) {
  104. len += printf("(%u%% of %"OFF_FMT"u bytes)",
  105. (int) (ftello(file)*100 / st.st_size),
  106. st.st_size);
  107. }
  108. fflush_all();
  109. /*
  110. * We've just displayed the "--More--" prompt, so now we need
  111. * to get input from the user.
  112. */
  113. for (;;) {
  114. input = getc(cin);
  115. input = tolower(input);
  116. if (!ENABLE_FEATURE_USE_TERMIOS)
  117. printf("\033[A"); /* cursor up */
  118. /* Erase the last message */
  119. printf("\r%*s\r", len, "");
  120. /* Due to various multibyte escape
  121. * sequences, it's not ok to accept
  122. * any input as a command to scroll
  123. * the screen. We only allow known
  124. * commands, else we show help msg. */
  125. if (input == ' ' || input == '\n' || input == 'q' || input == 'r')
  126. break;
  127. len = printf("(Enter:next line Space:next page Q:quit R:show the rest)");
  128. }
  129. len = 0;
  130. lines = 0;
  131. please_display_more_prompt = 0;
  132. if (input == 'q')
  133. goto end;
  134. /* The user may have resized the terminal.
  135. * Re-read the dimensions. */
  136. if (ENABLE_FEATURE_USE_TERMIOS) {
  137. get_terminal_width_height(cin_fileno, &terminal_width, &terminal_height);
  138. terminal_height -= 1;
  139. }
  140. }
  141. /* Crudely convert tabs into spaces, which are
  142. * a bajillion times easier to deal with. */
  143. if (c == '\t') {
  144. spaces = CONVERTED_TAB_SIZE - 1;
  145. c = ' ';
  146. }
  147. /*
  148. * There are two input streams to worry about here:
  149. *
  150. * c : the character we are reading from the file being "mored"
  151. * input: a character received from the keyboard
  152. *
  153. * If we hit a newline in the _file_ stream, we want to test and
  154. * see if any characters have been hit in the _input_ stream. This
  155. * allows the user to quit while in the middle of a file.
  156. */
  157. wrap = (++len > terminal_width);
  158. if (c == '\n' || wrap) {
  159. /* Then outputting this character
  160. * will move us to a new line. */
  161. if (++lines >= terminal_height || input == '\n')
  162. please_display_more_prompt = 1;
  163. len = 0;
  164. }
  165. if (c != '\n' && wrap) {
  166. /* Then outputting this will also put a character on
  167. * the beginning of that new line. Thus we first want to
  168. * display the prompt (if any), so we skip the putchar()
  169. * and go back to the top of the loop, without reading
  170. * a new character. */
  171. goto loop_top;
  172. }
  173. /* My small mind cannot fathom backspaces and UTF-8 */
  174. putchar(c);
  175. }
  176. fclose(file);
  177. fflush_all();
  178. } while (*argv && *++argv);
  179. end:
  180. setTermSettings(cin_fileno, &initial_settings);
  181. return 0;
  182. }