1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <sys/select.h> |
3 | #include <stdlib.h> |
4 | #include <stdio.h> |
5 | #include <string.h> |
6 | #include <signal.h> |
7 | #include <sys/ioctl.h> |
8 | #include "pager.h" |
9 | #include "run-command.h" |
10 | #include "sigchain.h" |
11 | #include "subcmd-config.h" |
12 | |
13 | /* |
14 | * This is split up from the rest of git so that we can do |
15 | * something different on Windows. |
16 | */ |
17 | |
18 | static int ; |
19 | static int pager_columns; |
20 | |
21 | void (const char *) |
22 | { |
23 | subcmd_config.pager_env = pager_env; |
24 | } |
25 | |
26 | static const char *; |
27 | |
28 | void (const char *) |
29 | { |
30 | forced_pager = pager; |
31 | } |
32 | |
33 | static void (void) |
34 | { |
35 | /* |
36 | * Work around bug in "less" by not starting it until we |
37 | * have real input |
38 | */ |
39 | fd_set in; |
40 | fd_set exception; |
41 | |
42 | FD_ZERO(&in); |
43 | FD_ZERO(&exception); |
44 | FD_SET(0, &in); |
45 | FD_SET(0, &exception); |
46 | select(1, &in, NULL, &exception, NULL); |
47 | |
48 | setenv("LESS" , "FRSX" , 0); |
49 | } |
50 | |
51 | static const char *[] = { "sh" , "-c" , NULL, NULL }; |
52 | static struct child_process ; |
53 | |
54 | static void (void) |
55 | { |
56 | fflush(stdout); |
57 | fflush(stderr); |
58 | /* signal EOF to pager */ |
59 | close(1); |
60 | close(2); |
61 | finish_command(&pager_process); |
62 | } |
63 | |
64 | static void (int signo) |
65 | { |
66 | wait_for_pager(); |
67 | sigchain_pop(sig: signo); |
68 | raise(signo); |
69 | } |
70 | |
71 | void (void) |
72 | { |
73 | const char * = getenv(subcmd_config.pager_env); |
74 | struct winsize sz; |
75 | |
76 | if (forced_pager) |
77 | pager = forced_pager; |
78 | if (!isatty(1) && !forced_pager) |
79 | return; |
80 | if (ioctl(1, TIOCGWINSZ, &sz) == 0) |
81 | pager_columns = sz.ws_col; |
82 | if (!pager) |
83 | pager = getenv("PAGER" ); |
84 | if (!(pager || access("/usr/bin/pager" , X_OK))) |
85 | pager = "/usr/bin/pager" ; |
86 | if (!(pager || access("/usr/bin/less" , X_OK))) |
87 | pager = "/usr/bin/less" ; |
88 | if (!pager) |
89 | pager = "cat" ; |
90 | if (!*pager || !strcmp(pager, "cat" )) |
91 | return; |
92 | |
93 | spawned_pager = 1; /* means we are emitting to terminal */ |
94 | |
95 | /* spawn the pager */ |
96 | pager_argv[2] = pager; |
97 | pager_process.argv = pager_argv; |
98 | pager_process.in = -1; |
99 | pager_process.preexec_cb = pager_preexec; |
100 | |
101 | if (start_command(&pager_process)) |
102 | return; |
103 | |
104 | /* original process continues, but writes to the pipe */ |
105 | dup2(pager_process.in, 1); |
106 | if (isatty(2)) |
107 | dup2(pager_process.in, 2); |
108 | close(pager_process.in); |
109 | |
110 | /* this makes sure that the parent terminates after the pager */ |
111 | sigchain_push_common(f: wait_for_pager_signal); |
112 | atexit(wait_for_pager); |
113 | } |
114 | |
115 | int (void) |
116 | { |
117 | return spawned_pager; |
118 | } |
119 | |
120 | int pager_get_columns(void) |
121 | { |
122 | char *s; |
123 | |
124 | s = getenv("COLUMNS" ); |
125 | if (s) |
126 | return atoi(s); |
127 | |
128 | return (pager_columns ? pager_columns : 80) - 2; |
129 | } |
130 | |