1
1
mirror of https://github.com/walles/moar.git synced 2024-09-19 07:58:00 +03:00
moar/moar.go

295 lines
8.5 KiB
Go
Raw Normal View History

2019-06-09 08:47:55 +03:00
package main
import (
2019-07-07 19:18:29 +03:00
"flag"
2019-06-09 20:40:35 +03:00
"fmt"
2019-06-09 20:34:52 +03:00
"io"
"os"
"path/filepath"
"runtime"
2019-06-16 11:02:19 +03:00
"strings"
"time"
2019-06-09 20:34:52 +03:00
"github.com/alecthomas/chroma"
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/styles"
2020-10-30 10:19:13 +03:00
log "github.com/sirupsen/logrus"
2021-04-15 16:16:06 +03:00
"golang.org/x/term"
2020-10-30 10:19:13 +03:00
"github.com/walles/moar/m"
2021-04-15 16:16:06 +03:00
"github.com/walles/moar/twin"
2019-06-09 08:47:55 +03:00
)
2019-07-07 19:34:05 +03:00
var versionString = "Should be set when building, please use build.sh to build"
func printUsage(output io.Writer, flagSet *flag.FlagSet, printCommandline bool) {
2019-07-15 23:14:36 +03:00
// This controls where PrintDefaults() prints, see below
flagSet.SetOutput(output)
2019-07-15 23:14:36 +03:00
// FIXME: Log if any printouts fail?
2021-06-04 07:15:09 +03:00
moarEnv := os.Getenv("MOAR")
if printCommandline {
_, _ = fmt.Fprintln(output, "Commandline: moar", strings.Join(os.Args[1:], " "))
2021-06-04 07:15:09 +03:00
_, _ = fmt.Fprintf(output, "Environment: MOAR=\"%v\"\n", moarEnv)
_, _ = fmt.Fprintln(output)
}
2021-04-20 09:43:37 +03:00
_, _ = fmt.Fprintln(output, "Usage:")
_, _ = fmt.Fprintln(output, " moar [options] <file>")
_, _ = fmt.Fprintln(output, " ... | moar")
_, _ = fmt.Fprintln(output, " moar < file")
_, _ = fmt.Fprintln(output, "")
_, _ = fmt.Fprintln(output, "Shows file contents. Compressed files will be transparently decompressed.")
2021-04-20 09:43:37 +03:00
_, _ = fmt.Fprintln(output)
_, _ = fmt.Fprintln(output, "Environment:")
2021-06-04 07:15:09 +03:00
if len(moarEnv) == 0 {
_, _ = fmt.Fprintln(output, " Additional options are read from the MOAR environment variable if set.")
_, _ = fmt.Fprintln(output, " But currently, the MOAR environment variable is not set.")
} else {
_, _ = fmt.Fprintln(output, " Additional options are read from the MOAR environment variable.")
_, _ = fmt.Fprintf(output, " Current setting: MOAR=\"%s\"\n", moarEnv)
}
_, _ = fmt.Fprintln(output)
_, _ = fmt.Fprintln(output, "Options:")
2019-07-15 23:14:36 +03:00
flagSet.PrintDefaults()
2019-07-15 23:14:36 +03:00
moarPath, err := filepath.Abs(os.Args[0])
if err == nil {
2019-07-15 23:47:36 +03:00
pagerValue, err := filepath.Abs(os.Getenv("PAGER"))
if err != nil {
pagerValue = ""
}
if pagerValue != moarPath {
// We're not the default pager
2021-04-20 09:43:37 +03:00
_, _ = fmt.Fprintln(output)
_, _ = fmt.Fprintln(output, "To make Moar your default pager, put the following line in")
_, _ = fmt.Fprintln(output, "your .bashrc or .bash_profile and it will be default in all")
_, _ = fmt.Fprintln(output, "new terminal windows:")
_, _ = fmt.Fprintf(output, " export PAGER=%s\n", moarPath)
}
} else {
2021-04-19 21:29:05 +03:00
log.Warn("Unable to find moar binary ", err)
}
2019-07-15 23:14:36 +03:00
}
2020-12-30 00:57:44 +03:00
// printProblemsHeader prints bug reporting information to stderr
func printProblemsHeader() {
Print bug reporting info on log messages diff --git moar.go moar.go index 46b2898..906b310 100644 --- moar.go +++ moar.go @@ -58,6 +58,23 @@ func _PrintUsage(output io.Writer) { } } +// PrintProblemsHeader prints bug reporting information to stderr +func PrintProblemsHeader() { + fmt.Fprintln(os.Stderr, "Please post the following report at <https://github.com/walles/moar/issues>,") + fmt.Fprintln(os.Stderr, "or e-mail it to johan.walles@gmail.com.") + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "Version:", versionString) + fmt.Fprintln(os.Stderr, "LANG :", os.Getenv("LANG")) + fmt.Fprintln(os.Stderr, "TERM :", os.Getenv("TERM")) + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "GOOS :", runtime.GOOS) + fmt.Fprintln(os.Stderr, "GOARCH :", runtime.GOARCH) + fmt.Fprintln(os.Stderr, "Compiler:", runtime.Compiler) + fmt.Fprintln(os.Stderr, "NumCPU :", runtime.NumCPU()) + + fmt.Fprintln(os.Stderr) +} + func main() { // FIXME: If we get a CTRL-C, get terminal back into a useful state before terminating @@ -67,21 +84,7 @@ func main() { return } - // On any panic or warnings, also print system info and how to report bugs - fmt.Fprintln(os.Stderr, "Please post the following crash report at <https://github.com/walles/moar/issues>,") - fmt.Fprintln(os.Stderr, "or e-mail it to johan.walles@gmail.com.") - fmt.Fprintln(os.Stderr) - fmt.Fprintln(os.Stderr, "Version:", versionString) - fmt.Fprintln(os.Stderr, "LANG :", os.Getenv("LANG")) - fmt.Fprintln(os.Stderr, "TERM :", os.Getenv("TERM")) - fmt.Fprintln(os.Stderr) - fmt.Fprintln(os.Stderr, "GOOS :", runtime.GOOS) - fmt.Fprintln(os.Stderr, "GOARCH :", runtime.GOARCH) - fmt.Fprintln(os.Stderr, "Compiler:", runtime.Compiler) - fmt.Fprintln(os.Stderr, "NumCPU :", runtime.NumCPU()) - - fmt.Fprintln(os.Stderr) - + PrintProblemsHeader() panic(err) }() @@ -161,6 +164,8 @@ func _StartPaging(reader *m.Reader) { } if len(loglines.String()) > 0 { + PrintProblemsHeader() + // FIXME: Don't print duplicate log messages more than once, // maybe invent our own logger for this? fmt.Fprintf(os.Stderr, "%s", loglines.String()) Change-Id: If41c17e98daf9f05909ab7e3c31dc84e946cbbf5
2019-11-19 17:33:00 +03:00
fmt.Fprintln(os.Stderr, "Please post the following report at <https://github.com/walles/moar/issues>,")
fmt.Fprintln(os.Stderr, "or e-mail it to johan.walles@gmail.com.")
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, "Version:", versionString)
fmt.Fprintln(os.Stderr, "LANG :", os.Getenv("LANG"))
fmt.Fprintln(os.Stderr, "TERM :", os.Getenv("TERM"))
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, "GOOS :", runtime.GOOS)
fmt.Fprintln(os.Stderr, "GOARCH :", runtime.GOARCH)
fmt.Fprintln(os.Stderr, "Compiler:", runtime.Compiler)
fmt.Fprintln(os.Stderr, "NumCPU :", runtime.NumCPU())
fmt.Fprintln(os.Stderr)
}
func parseStyleOption(styleOption string, flagSet *flag.FlagSet) chroma.Style {
style, ok := styles.Registry[styleOption]
if !ok {
fmt.Fprintf(os.Stderr,
"ERROR: Unrecognized style \"%s\", pick a style from here: https://xyproto.github.io/splash/docs/longer/all.html\n",
styleOption)
fmt.Fprintln(os.Stderr)
printUsage(os.Stderr, flagSet, true)
os.Exit(1)
}
return *style
}
func parseColorsOption(colorsOption string, flagSet *flag.FlagSet) chroma.Formatter {
switch strings.ToUpper(colorsOption) {
case "8":
return formatters.TTY8
case "16":
return formatters.TTY16
case "256":
return formatters.TTY256
case "16M":
return formatters.TTY16m
}
fmt.Fprintf(os.Stderr, "ERROR: Invalid color count \"%s\", valid counts are 8, 16, 256 or 16M.\n", colorsOption)
fmt.Fprintln(os.Stderr)
printUsage(os.Stderr, flagSet, true)
os.Exit(1)
panic("We just did os.Exit(), why are we still executing?")
}
2019-06-09 08:47:55 +03:00
func main() {
// FIXME: If we get a CTRL-C, get terminal back into a useful state before terminating
defer func() {
err := recover()
if err == nil {
return
}
2020-12-30 00:57:44 +03:00
printProblemsHeader()
panic(err)
}()
2019-06-29 12:20:48 +03:00
flagSet := flag.NewFlagSet("", flag.ExitOnError)
flagSet.Usage = func() {
printUsage(os.Stdout, flagSet, false)
}
printVersion := flagSet.Bool("version", false, "Prints the moar version number")
debug := flagSet.Bool("debug", false, "Print debug logs after exiting")
trace := flagSet.Bool("trace", false, "Print trace logs after exiting")
wrap := flagSet.Bool("wrap", false, "Wrap long lines")
styleOption := flagSet.String("style", "native",
"Highlighting style from https://xyproto.github.io/splash/docs/longer/all.html")
colorsOption := flagSet.String("colors", "16M", "Highlighting palette size: 8, 16, 256, 16M")
noLineNumbers := flagSet.Bool("no-linenumbers", false, "Hide line numbers on startup, press left arrow key to show")
// Combine flags from environment and from command line
flags := os.Args[1:]
moarEnv := strings.Trim(os.Getenv("MOAR"), " ")
if len(moarEnv) > 0 {
// FIXME: It would be nice if we could debug log that we're doing this,
// but logging is not yet set up and depends on command line parameters.
flags = append(strings.Split(moarEnv, " "), flags...)
}
err := flagSet.Parse(flags)
if err != nil {
printProblemsHeader()
fmt.Fprintln(os.Stderr, "ERROR: Command line parsing failed:", err.Error())
fmt.Fprintln(os.Stderr)
printUsage(os.Stderr, flagSet, true)
os.Exit(1)
2019-07-15 23:14:36 +03:00
}
2019-07-07 19:18:29 +03:00
2019-07-07 19:34:05 +03:00
if *printVersion {
fmt.Println(versionString)
2019-07-07 19:18:29 +03:00
os.Exit(0)
}
style := parseStyleOption(*styleOption, flagSet)
formatter := parseColorsOption(*colorsOption, flagSet)
log.SetLevel(log.InfoLevel)
2021-04-15 16:16:06 +03:00
if *trace {
log.SetLevel(log.TraceLevel)
} else if *debug {
log.SetLevel(log.DebugLevel)
}
log.SetFormatter(&log.TextFormatter{
TimestampFormat: time.RFC3339Nano,
})
if len(flagSet.Args()) > 1 {
fmt.Fprintln(os.Stderr, "ERROR: Expected exactly one filename, or data piped from stdin, got:", flagSet.Args())
fmt.Fprintln(os.Stderr)
printUsage(os.Stderr, flagSet, true)
os.Exit(1)
2019-06-09 20:34:52 +03:00
}
stdinIsRedirected := !term.IsTerminal(int(os.Stdin.Fd()))
stdoutIsRedirected := !term.IsTerminal(int(os.Stdout.Fd()))
var inputFilename *string
if len(flagSet.Args()) == 1 {
word := flagSet.Arg(0)
inputFilename = &word
2019-06-09 22:58:12 +03:00
}
if inputFilename == nil && !stdinIsRedirected {
fmt.Fprintln(os.Stderr, "ERROR: Filename or input pipe required")
fmt.Fprintln(os.Stderr, "")
printUsage(os.Stderr, flagSet, true)
2019-06-09 20:40:35 +03:00
os.Exit(1)
}
if inputFilename != nil && stdoutIsRedirected {
// Pump file to stdout.
//
// If we get both redirected stdin and an input filename, we must prefer
// to copy the file, because that's how less works.
inputFile, err := os.Open(*inputFilename)
2019-06-09 22:52:27 +03:00
if err != nil {
fmt.Fprintln(os.Stderr, "ERROR: Failed to open", inputFile, ": ")
2019-06-09 22:52:27 +03:00
os.Exit(1)
}
_, err = io.Copy(os.Stdout, inputFile)
if err != nil {
log.Fatal("Failed to copy ", inputFilename, " to stdout: ", err)
}
os.Exit(0)
}
if stdinIsRedirected && stdoutIsRedirected {
// Must be done after trying to pump the input filename to stdout to be
// compatible with less, see above.
_, err := io.Copy(os.Stdout, os.Stdin)
2021-04-20 09:43:37 +03:00
if err != nil {
log.Fatal("Failed to copy stdin to stdout: ", err)
2021-04-20 09:43:37 +03:00
}
2019-06-09 22:58:12 +03:00
os.Exit(0)
}
2019-06-09 22:58:12 +03:00
// INVARIANT: At this point, stdoutIsRedirected is false and we should
// proceed with paging.
if stdinIsRedirected {
// Display input pipe contents
reader := m.NewReaderFromStream("", os.Stdin)
startPaging(reader, *wrap, *noLineNumbers)
return
}
// Display the input file contents
reader, err := m.NewReaderFromFilename(*inputFilename, style, formatter)
2019-06-16 11:02:19 +03:00
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
2019-06-16 11:02:19 +03:00
os.Exit(1)
}
startPaging(reader, *wrap, *noLineNumbers)
2019-06-16 11:02:19 +03:00
}
func startPaging(reader *m.Reader, wrapLongLines bool, noLineNumbers bool) {
2021-04-15 16:16:06 +03:00
screen, e := twin.NewScreen()
if e != nil {
panic(e)
}
var loglines strings.Builder
2019-06-29 23:27:18 +03:00
defer func() {
// Restore screen...
2021-04-15 16:16:06 +03:00
screen.Close()
2019-06-29 23:27:18 +03:00
// ... before printing panic() output, otherwise the output will have
// broken linefeeds and be hard to follow.
if err := recover(); err != nil {
panic(err)
}
if len(loglines.String()) > 0 {
2020-12-30 00:57:44 +03:00
printProblemsHeader()
Print bug reporting info on log messages diff --git moar.go moar.go index 46b2898..906b310 100644 --- moar.go +++ moar.go @@ -58,6 +58,23 @@ func _PrintUsage(output io.Writer) { } } +// PrintProblemsHeader prints bug reporting information to stderr +func PrintProblemsHeader() { + fmt.Fprintln(os.Stderr, "Please post the following report at <https://github.com/walles/moar/issues>,") + fmt.Fprintln(os.Stderr, "or e-mail it to johan.walles@gmail.com.") + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "Version:", versionString) + fmt.Fprintln(os.Stderr, "LANG :", os.Getenv("LANG")) + fmt.Fprintln(os.Stderr, "TERM :", os.Getenv("TERM")) + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, "GOOS :", runtime.GOOS) + fmt.Fprintln(os.Stderr, "GOARCH :", runtime.GOARCH) + fmt.Fprintln(os.Stderr, "Compiler:", runtime.Compiler) + fmt.Fprintln(os.Stderr, "NumCPU :", runtime.NumCPU()) + + fmt.Fprintln(os.Stderr) +} + func main() { // FIXME: If we get a CTRL-C, get terminal back into a useful state before terminating @@ -67,21 +84,7 @@ func main() { return } - // On any panic or warnings, also print system info and how to report bugs - fmt.Fprintln(os.Stderr, "Please post the following crash report at <https://github.com/walles/moar/issues>,") - fmt.Fprintln(os.Stderr, "or e-mail it to johan.walles@gmail.com.") - fmt.Fprintln(os.Stderr) - fmt.Fprintln(os.Stderr, "Version:", versionString) - fmt.Fprintln(os.Stderr, "LANG :", os.Getenv("LANG")) - fmt.Fprintln(os.Stderr, "TERM :", os.Getenv("TERM")) - fmt.Fprintln(os.Stderr) - fmt.Fprintln(os.Stderr, "GOOS :", runtime.GOOS) - fmt.Fprintln(os.Stderr, "GOARCH :", runtime.GOARCH) - fmt.Fprintln(os.Stderr, "Compiler:", runtime.Compiler) - fmt.Fprintln(os.Stderr, "NumCPU :", runtime.NumCPU()) - - fmt.Fprintln(os.Stderr) - + PrintProblemsHeader() panic(err) }() @@ -161,6 +164,8 @@ func _StartPaging(reader *m.Reader) { } if len(loglines.String()) > 0 { + PrintProblemsHeader() + // FIXME: Don't print duplicate log messages more than once, // maybe invent our own logger for this? fmt.Fprintf(os.Stderr, "%s", loglines.String()) Change-Id: If41c17e98daf9f05909ab7e3c31dc84e946cbbf5
2019-11-19 17:33:00 +03:00
// FIXME: Don't print duplicate log messages more than once,
// maybe invent our own logger for this?
fmt.Fprintf(os.Stderr, "%s", loglines.String())
os.Exit(1)
}
}()
log.SetOutput(&loglines)
pager := m.NewPager(reader)
pager.WrapLongLines = wrapLongLines
pager.ShowLineNumbers = !noLineNumbers
pager.StartPaging(screen)
2019-06-09 08:47:55 +03:00
}