github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/util/outputpager/page_output.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // This file incorporates work covered by the following copyright and 16 // permission notice: 17 // 18 // Copyright 2016 Attic Labs, Inc. All rights reserved. 19 // Licensed under the Apache License, version 2.0: 20 // http://www.apache.org/licenses/LICENSE-2.0 21 22 package outputpager 23 24 import ( 25 "io" 26 "os" 27 "os/exec" 28 "sync" 29 30 flag "github.com/juju/gnuflag" 31 goisatty "github.com/mattn/go-isatty" 32 33 "github.com/dolthub/dolt/go/store/d" 34 ) 35 36 var ( 37 noPager bool 38 ) 39 40 type Pager struct { 41 Writer io.Writer 42 stdin, stdout *os.File 43 mtx *sync.Mutex 44 doneCh chan struct{} 45 } 46 47 func Start() *Pager { 48 if noPager || !IsStdoutTty() { 49 return &Pager{os.Stdout, nil, nil, nil, nil} 50 } 51 52 lessPath, err := exec.LookPath("less") 53 d.Chk.NoError(err) 54 55 // -F ... Quit if entire file fits on first screen. 56 // -S ... Chop (truncate) long lines rather than wrapping. 57 // -R ... Output "raw" control characters. 58 // -X ... Don't use termcap init/deinit strings. 59 cmd := exec.Command(lessPath, "-FSRX") 60 61 stdin, stdout, err := os.Pipe() 62 d.Chk.NoError(err) 63 cmd.Stdout = os.Stdout 64 cmd.Stderr = os.Stderr 65 cmd.Stdin = stdin 66 cmd.Start() 67 68 p := &Pager{stdout, stdin, stdout, &sync.Mutex{}, make(chan struct{})} 69 go func() { 70 err := cmd.Wait() 71 d.Chk.NoError(err) 72 p.closePipe() 73 p.doneCh <- struct{}{} 74 }() 75 return p 76 } 77 78 func (p *Pager) Stop() { 79 if p.Writer != os.Stdout { 80 p.closePipe() 81 // Wait until less has fully exited, otherwise it might not have printed the terminal restore characters. 82 <-p.doneCh 83 } 84 } 85 86 func (p *Pager) closePipe() { 87 p.mtx.Lock() 88 defer p.mtx.Unlock() 89 if p.stdin != nil { 90 // Closing the pipe will cause any outstanding writes to stdout fail, and fail from now on. 91 p.stdin.Close() 92 p.stdout.Close() 93 p.stdin, p.stdout = nil, nil 94 } 95 } 96 97 func RegisterOutputpagerFlags(flags *flag.FlagSet) { 98 flags.BoolVar(&noPager, "no-pager", false, "suppress paging functionality") 99 } 100 101 func IsStdoutTty() bool { 102 return goisatty.IsTerminal(os.Stdout.Fd()) 103 }