-
Notifications
You must be signed in to change notification settings - Fork 1
/
pager.go
126 lines (111 loc) · 2.9 KB
/
pager.go
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// The Pager package allows the program to easily pipe it's
// standard output through a Pager program
// (like how the man command does).
//
// Borrowed from: https://gist.github.com/dchapes/1d0c538ce07902b76c75 and
// reworked slightly.
package pager
import (
"errors"
"io"
"os"
"os/exec"
"path"
"strings"
)
type Pager struct {
cmd *exec.Cmd
file io.WriteCloser
}
var pager Pager
// The environment variables to check for the name of (and arguments to)
// the Pager to run.
var PagerEnvVariables = []string{"PAGER"}
// The command names in $PATH to look for if none of the environment
// variables are set.
// Cannot include arguments.
var PagerCommands = []string{"less", "more"}
func pagerExecPath() (pagerPath string, args []string, err error) {
for _, testVar := range PagerEnvVariables {
pagerPath = os.Getenv(testVar)
if pagerPath != "" {
args = strings.Fields(pagerPath)
if len(args) > 1 {
return args[0], args[1:], nil
}
}
}
// This default only gets used if PagerCommands is empty.
err = exec.ErrNotFound
for _, testPath := range PagerCommands {
pagerPath, err = exec.LookPath(testPath)
if err == nil {
switch {
case path.Base(pagerPath) == "less":
// TODO(seanc@): Make the buffer size conditional
args := []string{"-X", "-F", "-R", "--buffers=65535"}
return pagerPath, args, nil
default:
return pagerPath, nil, nil
}
}
}
return "", nil, err
}
// New returns a new io.WriteCloser connected to a Pager.
// The returned WriteCloser can be used as a replacement to os.Stdout,
// everything written to it is piped to a Pager.
// To determine what Pager to run, the environment variables listed
// in PagerEnvVariables are checked.
// If all are empty/unset then the commands listed in PagerCommands
// are looked for in $PATH.
func New() (*Pager, error) {
p := &Pager{}
if p.cmd != nil {
return nil, errors.New("Pager: already exists")
}
pagerPath, args, err := pagerExecPath()
if err != nil {
return nil, err
}
// If the Pager is less(1), set some useful options
switch {
case path.Base(pagerPath) == "less":
os.Setenv("LESSSECURE", "1")
}
p.cmd = exec.Command(pagerPath, args...)
p.cmd.Stdout = os.Stdout
p.cmd.Stderr = os.Stderr
w, err := p.cmd.StdinPipe()
if err != nil {
return nil, err
}
f, ok := w.(io.WriteCloser)
if !ok {
return nil, errors.New("Pager: exec.Command.StdinPipe did not return type io.WriteCloser")
}
p.file = f
err = p.cmd.Start()
if err != nil {
return nil, err
}
return p, nil
}
// Wait closes the pipe to the Pager setup with New() or Stdout() and waits
// for it to exit.
//
// This should normally be called before the program exists,
// typically via a defer call in main().
func (p *Pager) Wait() error {
if p.cmd == nil {
return nil
}
p.file.Close()
return p.cmd.Wait()
}
func (p *Pager) Close() error {
return nil
}
func (p *Pager) Write(data []byte) (n int, err error) {
return p.file.Write(data)
}