more.c 5.2 KB

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