1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
package cli
import (
"io"
"os"
"os/exec"
"strings"
"github.com/lukaszkasprzak/lectio/internal/config"
)
// pagerRequested decides whether the reading-output tail should be paged,
// given -P/--pager, --no-pager, and cfg.Pager. --no-pager always wins (an
// explicit "never page" beats everything); otherwise -P forces paging on;
// otherwise a non-empty cfg.Pager turns paging on by default. Pure and
// side-effect free -- the actual TTY gate lives in isTerminalWriter.
func pagerRequested(pagerFlag, noPager bool, cfg config.Config) bool {
if noPager {
return false
}
if pagerFlag {
return true
}
return cfg.Pager != ""
}
// pagerCommand resolves the pager argv to run: cfg.Pager wins, then $PAGER,
// then the "less -R" built-in default. The resolved command string is split
// on whitespace via strings.Fields into an argv slice (so "less -R" or a
// $PAGER value with flags works without a shell).
func pagerCommand(cfg config.Config) []string {
cmd := cfg.Pager
if cmd == "" {
cmd = os.Getenv("PAGER")
}
if cmd == "" {
cmd = "less -R"
}
return strings.Fields(cmd)
}
// isTerminalWriter reports whether w is a real terminal -- only then is
// paging worth doing. Reuses ttyWidth (termwidth_unix.go/termwidth_other.go)
// via the *os.File it already knows how to probe; anything else (notably
// the bytes.Buffer every test uses for stdout) is never a terminal, so
// paging never triggers in tests, and `lectio | cat` (stdout redirected to
// a pipe, not a *os.File terminal) writes directly too.
func isTerminalWriter(w io.Writer) bool {
if f, ok := w.(*os.File); ok {
return ttyWidth(f) > 0
}
return false
}
// startPager launches argv[0] as a pager subprocess wired to the real
// terminal (stdout/stderr passed through), returning a WriteCloser the
// caller renders reading output into. ok=false (with out, finish both nil)
// on any setup failure -- an empty argv, a StdinPipe error, or the pager
// binary not being found/startable -- so the caller can fall back to
// writing directly to stdout without crashing.
func startPager(argv []string, stdout, stderr io.Writer) (out io.WriteCloser, finish func(), ok bool) {
if len(argv) == 0 {
return nil, nil, false
}
cmd := exec.Command(argv[0], argv[1:]...)
cmd.Stdout = stdout
cmd.Stderr = stderr
w, err := cmd.StdinPipe()
if err != nil {
return nil, nil, false
}
if err := cmd.Start(); err != nil {
return nil, nil, false
}
finish = func() {
w.Close()
_ = cmd.Wait()
}
return w, finish, true
}
|