more.c 5.3 KB

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