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 127 128 129 130 131 132 133 134
|
package main
import (
"errors"
"io"
"log"
"os"
"os/exec"
"reflect"
"github.com/google/shlex"
)
type pager interface {
io.WriteCloser
Running() bool
IsDone(any, int) bool
}
var pagerDone error = errors.New("paging is done")
func newPager(expected int) pager {
if !isStdoutTerminal || expected != 0 {
return &staticPager{os.Stdout, expected, 0}
}
name, ok := os.LookupEnv("PAGER")
if !ok {
name = "/usr/bin/pager"
}
commandSplit, err := shlex.Split(name)
if err != nil {
log.Fatalf("Failed to parse pager command: %v", err)
}
cmd := exec.Command(commandSplit[0], commandSplit[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = append(os.Environ(), "LESS=FRX")
w, err := cmd.StdinPipe()
if err != nil {
log.Fatalf("Failed to create stdin pipe for pager: %v", err)
}
if err := cmd.Start(); err != nil {
log.Fatalf("Failed to start pager %q: %v", cmd.Args[0], err)
}
done := make(chan struct{})
go func() {
defer close(done)
if err := cmd.Wait(); err != nil {
log.Fatalf("Failed to run pager: %v", err)
}
}()
return &cmdPager{w, done}
}
type pagerifyFn func(p pager) error
func pagerify(fn pagerifyFn, expected int) error {
pager := newPager(expected)
defer pager.Close()
for pager.Running() {
err := fn(pager)
if err == pagerDone {
return nil
} else if err != nil {
return err
}
}
return nil
}
type staticPager struct {
io.WriteCloser
objectsExpected int
objectsGot int
}
func (p *staticPager) Write(b []byte) (int, error) {
return p.WriteCloser.Write(b)
}
func (p *staticPager) Running() bool {
return true
}
func (p *staticPager) IsDone(cursor any, objectsGot int) bool {
v := reflect.ValueOf(cursor)
if v.Kind() == reflect.Pointer && v.IsNil() {
return true
}
// TODO: Use API to request number of objects
p.objectsGot += objectsGot
return p.objectsGot >= p.objectsExpected
}
type cmdPager struct {
io.WriteCloser
done <-chan struct{}
}
func (p *cmdPager) Close() error {
if err := p.WriteCloser.Close(); err != nil {
return err
}
<-p.done
return nil
}
func (p *cmdPager) Running() bool {
select {
case <-p.done:
return false
default:
return true
}
}
func (p *cmdPager) IsDone(cursor any, _ int) bool {
v := reflect.ValueOf(cursor)
if v.Kind() == reflect.Pointer && v.IsNil() {
return true
}
return false
}
|