github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/display/pager.go (about) 1 package display 2 3 import ( 4 "bufio" 5 "context" 6 "fmt" 7 "os" 8 "os/exec" 9 "runtime" 10 "strings" 11 12 "github.com/karrick/gows" 13 "github.com/spf13/viper" 14 "github.com/turbot/steampipe/pkg/constants" 15 "github.com/turbot/steampipe/pkg/error_helpers" 16 ) 17 18 // ShowPaged displays the `content` in a system dependent pager 19 func ShowPaged(ctx context.Context, content string) { 20 if isPagerNeeded(content) && (runtime.GOOS == "darwin" || runtime.GOOS == "linux") { 21 nixPager(ctx, content) 22 } else { 23 nullPager(content) 24 } 25 } 26 27 func isPagerNeeded(content string) bool { 28 // only show pager in interactive mode 29 if !viper.GetBool(constants.ConfigKeyInteractive) { 30 return false 31 } 32 33 maxCols, maxRow, _ := gows.GetWinSize() 34 35 // let's scan through it instead of iterating over it fully 36 sc := bufio.NewScanner(strings.NewReader(content)) 37 38 // explicitly allocate a large bugger for the scanner to use - otherwise we may fail for large rows 39 buffSize := 256 * 1024 40 buff := make([]byte, buffSize) 41 sc.Buffer(buff, buffSize) 42 43 lineCount := 0 44 for sc.Scan() { 45 line := sc.Text() 46 lineCount++ 47 if lineCount > maxRow { 48 return true 49 } 50 if len(line) > maxCols { 51 return true 52 } 53 } 54 return false 55 } 56 57 func nullPager(content string) { 58 // just dump the whole thing out 59 // we will use this for non-tty environments as well 60 fmt.Print(content) 61 } 62 63 func nixPager(ctx context.Context, content string) { 64 if isLessAvailable() { 65 execPager(ctx, exec.Command("less", "-SRXF"), content) 66 } else if isMoreAvailable() { 67 execPager(ctx, exec.Command("more"), content) 68 } else { 69 nullPager(content) 70 } 71 } 72 73 func isLessAvailable() bool { 74 _, err := exec.LookPath("less") 75 return err == nil 76 } 77 78 func isMoreAvailable() bool { 79 _, err := exec.LookPath("more") 80 return err == nil 81 } 82 83 func execPager(ctx context.Context, cmd *exec.Cmd, content string) { 84 cmd.Stdout = os.Stdout 85 cmd.Stderr = os.Stderr 86 cmd.Stdin = strings.NewReader(content) 87 // run the command - it will block until the pager is exited 88 err := cmd.Run() 89 if err != nil { 90 error_helpers.ShowErrorWithMessage(ctx, err, "could not display results") 91 } 92 }