THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
5.1KB

  1. #include "cache.h"
  2. #include "config.h"
  3. #include "run-command.h"
  4. #include "sigchain.h"
  5. #include "alias.h"
  6. #ifndef DEFAULT_PAGER
  7. #define DEFAULT_PAGER "less"
  8. #endif
  9. static struct child_process pager_process = CHILD_PROCESS_INIT;
  10. static const char *pager_program;
  11. static void wait_for_pager(int in_signal)
  12. {
  13. if (!in_signal) {
  14. fflush(stdout);
  15. fflush(stderr);
  16. }
  17. /* signal EOF to pager */
  18. close(1);
  19. close(2);
  20. if (in_signal)
  21. finish_command_in_signal(&pager_process);
  22. else
  23. finish_command(&pager_process);
  24. }
  25. static void wait_for_pager_atexit(void)
  26. {
  27. wait_for_pager(0);
  28. }
  29. static void wait_for_pager_signal(int signo)
  30. {
  31. wait_for_pager(1);
  32. sigchain_pop(signo);
  33. raise(signo);
  34. }
  35. static int core_pager_config(const char *var, const char *value, void *data)
  36. {
  37. if (!strcmp(var, "core.pager"))
  38. return git_config_string(&pager_program, var, value);
  39. return 0;
  40. }
  41. const char *git_pager(int stdout_is_tty)
  42. {
  43. const char *pager;
  44. if (!stdout_is_tty)
  45. return NULL;
  46. pager = getenv("GIT_PAGER");
  47. if (!pager) {
  48. if (!pager_program)
  49. read_early_config(core_pager_config, NULL);
  50. pager = pager_program;
  51. }
  52. if (!pager)
  53. pager = getenv("PAGER");
  54. if (!pager)
  55. pager = DEFAULT_PAGER;
  56. if (!*pager || !strcmp(pager, "cat"))
  57. pager = NULL;
  58. return pager;
  59. }
  60. static void setup_pager_env(struct argv_array *env)
  61. {
  62. const char **argv;
  63. int i;
  64. char *pager_env = xstrdup(PAGER_ENV);
  65. int n = split_cmdline(pager_env, &argv);
  66. if (n < 0)
  67. die("malformed build-time PAGER_ENV: %s",
  68. split_cmdline_strerror(n));
  69. for (i = 0; i < n; i++) {
  70. char *cp = strchr(argv[i], '=');
  71. if (!cp)
  72. die("malformed build-time PAGER_ENV");
  73. *cp = '\0';
  74. if (!getenv(argv[i])) {
  75. *cp = '=';
  76. argv_array_push(env, argv[i]);
  77. }
  78. }
  79. free(pager_env);
  80. free(argv);
  81. }
  82. void prepare_pager_args(struct child_process *pager_process, const char *pager)
  83. {
  84. argv_array_push(&pager_process->args, pager);
  85. pager_process->use_shell = 1;
  86. setup_pager_env(&pager_process->env_array);
  87. pager_process->trace2_child_class = "pager";
  88. }
  89. void setup_pager(void)
  90. {
  91. const char *pager = git_pager(isatty(1));
  92. if (!pager)
  93. return;
  94. /*
  95. * After we redirect standard output, we won't be able to use an ioctl
  96. * to get the terminal size. Let's grab it now, and then set $COLUMNS
  97. * to communicate it to any sub-processes.
  98. */
  99. {
  100. char buf[64];
  101. xsnprintf(buf, sizeof(buf), "%d", term_columns());
  102. setenv("COLUMNS", buf, 0);
  103. }
  104. setenv("GIT_PAGER_IN_USE", "true", 1);
  105. /* spawn the pager */
  106. prepare_pager_args(&pager_process, pager);
  107. pager_process.in = -1;
  108. argv_array_push(&pager_process.env_array, "GIT_PAGER_IN_USE");
  109. if (start_command(&pager_process))
  110. return;
  111. /* original process continues, but writes to the pipe */
  112. dup2(pager_process.in, 1);
  113. if (isatty(2))
  114. dup2(pager_process.in, 2);
  115. close(pager_process.in);
  116. /* this makes sure that the parent terminates after the pager */
  117. sigchain_push_common(wait_for_pager_signal);
  118. atexit(wait_for_pager_atexit);
  119. }
  120. int pager_in_use(void)
  121. {
  122. return git_env_bool("GIT_PAGER_IN_USE", 0);
  123. }
  124. /*
  125. * Return cached value (if set) or $COLUMNS environment variable (if
  126. * set and positive) or ioctl(1, TIOCGWINSZ).ws_col (if positive),
  127. * and default to 80 if all else fails.
  128. */
  129. int term_columns(void)
  130. {
  131. static int term_columns_at_startup;
  132. char *col_string;
  133. int n_cols;
  134. if (term_columns_at_startup)
  135. return term_columns_at_startup;
  136. term_columns_at_startup = 80;
  137. col_string = getenv("COLUMNS");
  138. if (col_string && (n_cols = atoi(col_string)) > 0)
  139. term_columns_at_startup = n_cols;
  140. #ifdef TIOCGWINSZ
  141. else {
  142. struct winsize ws;
  143. if (!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_col)
  144. term_columns_at_startup = ws.ws_col;
  145. }
  146. #endif
  147. return term_columns_at_startup;
  148. }
  149. /*
  150. * Clear the entire line, leave cursor in first column.
  151. */
  152. void term_clear_line(void)
  153. {
  154. if (is_terminal_dumb())
  155. /*
  156. * Fall back to print a terminal width worth of space
  157. * characters (hoping that the terminal is still as wide
  158. * as it was upon the first call to term_columns()).
  159. */
  160. fprintf(stderr, "\r%*s\r", term_columns(), "");
  161. else
  162. /*
  163. * On non-dumb terminals use an escape sequence to clear
  164. * the whole line, no matter how wide the terminal.
  165. */
  166. fputs("\r\033[K", stderr);
  167. }
  168. /*
  169. * How many columns do we need to show this number in decimal?
  170. */
  171. int decimal_width(uintmax_t number)
  172. {
  173. int width;
  174. for (width = 1; number >= 10; width++)
  175. number /= 10;
  176. return width;
  177. }
  178. struct pager_command_config_data {
  179. const char *cmd;
  180. int want;
  181. char *value;
  182. };
  183. static int pager_command_config(const char *var, const char *value, void *vdata)
  184. {
  185. struct pager_command_config_data *data = vdata;
  186. const char *cmd;
  187. if (skip_prefix(var, "pager.", &cmd) && !strcmp(cmd, data->cmd)) {
  188. int b = git_parse_maybe_bool(value);
  189. if (b >= 0)
  190. data->want = b;
  191. else {
  192. data->want = 1;
  193. data->value = xstrdup(value);
  194. }
  195. }
  196. return 0;
  197. }
  198. /* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
  199. int check_pager_config(const char *cmd)
  200. {
  201. struct pager_command_config_data data;
  202. data.cmd = cmd;
  203. data.want = -1;
  204. data.value = NULL;
  205. read_early_config(pager_command_config, &data);
  206. if (data.value)
  207. pager_program = data.value;
  208. return data.want;
  209. }