mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-17 16:38:54 +00:00
Remove os.Exit callsites below the entrypoint: phase 4 (plans/exit.md) (#2205)
Phase 4 of plans/exit.md: the aux/terminal sub-entrypoints. pkg/auxents and
pkg/terminals are now os.Exit-free; even the dispatchers return exit codes
instead of exiting.
- auxents.Dispatch returns (handled bool, exitCode int); entrypoint.Main --
the one sanctioned exit point -- exits with the code.
- terminals.Dispatch returns an exit code; the climain caller wraps it in a
lib.ExitRequest, which also retires the 'terminal did not exit the process'
panic.
- Usage functions (hex, unhex, lecat, termcvt, repl, script, regtest) no
longer take an exitCode parameter and exit; call sites print usage and
return the code explicitly.
- Inner I/O helpers (hexDumpFile, unhexFile, lecatFile, termcvtFile) return
errors; each sub-main prints 'mlr {verb}: ...' as before and returns 1.
lecatFile formerly looped forever on a non-EOF read error; it now returns
the error.
- The regtest directory walk (Execute / executeSinglePath /
executeSingleDirectory / hasCaseSubdirectories) plumbs errors up to
RegTestMain; regression_test.go updated for the new Execute signature.
Stderr messages and exit codes are unchanged across mlr aux-list, hex, unhex,
lecat, termcvt, help, version, repl, script, and regtest surfaces (checked by
hand); all 4779 regression cases pass; make lint is clean; docs rebuild with
zero churn.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
314b26cfaa
commit
2f3e54d1f6
13 changed files with 195 additions and 133 deletions
|
|
@ -32,22 +32,23 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch is called from Miller main. Here we indicate if argv[1] is handled
|
// Dispatch is called from Miller main. The boolean return says whether
|
||||||
// by us, or not. If so, we handle it and exit, not returning control to Miller
|
// argv[1] named an auxent handled here; if so, the int is the process exit
|
||||||
// main.
|
// code for the entrypoint layer to exit with. Otherwise control returns to
|
||||||
func Dispatch(args []string) {
|
// main for the rest of Miller.
|
||||||
|
func Dispatch(args []string) (bool, int) {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return
|
return false, 0
|
||||||
}
|
}
|
||||||
verb := args[1]
|
verb := args[1]
|
||||||
|
|
||||||
for _, entry := range _AUX_LOOKUP_TABLE {
|
for _, entry := range _AUX_LOOKUP_TABLE {
|
||||||
if verb == entry.name {
|
if verb == entry.name {
|
||||||
os.Exit(entry.main(args))
|
return true, entry.main(args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Else, return control to main for the rest of Miller.
|
return false, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// auxListMain is the handler for 'mlr aux-list'.
|
// auxListMain is the handler for 'mlr aux-list'.
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,13 @@ import (
|
||||||
// 00000060: 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f |`abcdefghijklmno|
|
// 00000060: 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f |`abcdefghijklmno|
|
||||||
// 00000070: 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f |pqrstuvwxyz{|}~.|
|
// 00000070: 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f |pqrstuvwxyz{|}~.|
|
||||||
|
|
||||||
func hexUsage(verbName string, o *os.File, exitCode int) {
|
func hexUsage(verbName string, o *os.File) {
|
||||||
fmt.Fprintf(o, "Usage: mlr %s [options] {zero or more file names}\n", verbName)
|
fmt.Fprintf(o, "Usage: mlr %s [options] {zero or more file names}\n", verbName)
|
||||||
fmt.Fprintf(o, "Simple hex-dump.\n")
|
fmt.Fprintf(o, "Simple hex-dump.\n")
|
||||||
fmt.Fprintf(o, "If zero file names are supplied, standard input is read.\n")
|
fmt.Fprintf(o, "If zero file names are supplied, standard input is read.\n")
|
||||||
fmt.Fprintf(o, "Options:\n")
|
fmt.Fprintf(o, "Options:\n")
|
||||||
fmt.Fprintf(o, "-r: print only raw hex without leading offset indicators or trailing ASCII dump.\n")
|
fmt.Fprintf(o, "-r: print only raw hex without leading offset indicators or trailing ASCII dump.\n")
|
||||||
fmt.Fprintf(o, "-h or --help: print this message\n")
|
fmt.Fprintf(o, "-h or --help: print this message\n")
|
||||||
os.Exit(exitCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func hexMain(args []string) int {
|
func hexMain(args []string) int {
|
||||||
|
|
@ -45,12 +44,16 @@ func hexMain(args []string) int {
|
||||||
doRaw = true
|
doRaw = true
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
case "-h", "--help":
|
case "-h", "--help":
|
||||||
hexUsage(verb, os.Stdout, 0)
|
hexUsage(verb, os.Stdout)
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
hexDumpFile(os.Stdin, doRaw)
|
if err := hexDumpFile(os.Stdin, doRaw); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "mlr hex: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, filename := range args {
|
for _, filename := range args {
|
||||||
// Print filename if there is more than one file, unless raw output
|
// Print filename if there is more than one file, unless raw output
|
||||||
|
|
@ -61,14 +64,17 @@ func hexMain(args []string) int {
|
||||||
|
|
||||||
istream, err := os.Open(filename)
|
istream, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: "mlr"
|
|
||||||
fmt.Fprintf(os.Stderr, "mlr hex: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr hex: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
hexDumpFile(istream, doRaw)
|
err = hexDumpFile(istream, doRaw)
|
||||||
|
|
||||||
_ = istream.Close()
|
_ = istream.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "mlr hex: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
if !doRaw && len(args) > 1 {
|
if !doRaw && len(args) > 1 {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +84,7 @@ func hexMain(args []string) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func hexDumpFile(istream *os.File, doRaw bool) {
|
func hexDumpFile(istream *os.File, doRaw bool) error {
|
||||||
const bytesPerClump = 4
|
const bytesPerClump = 4
|
||||||
const clumpsPerLine = 4
|
const clumpsPerLine = 4
|
||||||
const bufferSize = bytesPerClump * clumpsPerLine
|
const bufferSize = bytesPerClump * clumpsPerLine
|
||||||
|
|
@ -97,8 +103,7 @@ func hexDumpFile(istream *os.File, doRaw bool) {
|
||||||
// exact multiple of our buffer size. We'll break the loop after
|
// exact multiple of our buffer size. We'll break the loop after
|
||||||
// hex-dumping this last, partial fragment.
|
// hex-dumping this last, partial fragment.
|
||||||
if err != nil && err != io.ErrUnexpectedEOF {
|
if err != nil && err != io.ErrUnexpectedEOF {
|
||||||
fmt.Fprintf(os.Stderr, "mlr hex: %v\n", err)
|
return err
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print offset "pre" part
|
// Print offset "pre" part
|
||||||
|
|
@ -148,4 +153,5 @@ func hexDumpFile(istream *os.File, doRaw bool) {
|
||||||
|
|
||||||
offset += numBytesRead
|
offset += numBytesRead
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,13 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func lecatUsage(verbName string, o *os.File, exitCode int) {
|
func lecatUsage(verbName string, o *os.File) {
|
||||||
fmt.Fprintf(o, "Usage: mlr %s [options] {zero or more file names}\n", verbName)
|
fmt.Fprintf(o, "Usage: mlr %s [options] {zero or more file names}\n", verbName)
|
||||||
fmt.Fprintf(o, "Simply echoes input, but flags CR characters in red and LF characters in green.\n")
|
fmt.Fprintf(o, "Simply echoes input, but flags CR characters in red and LF characters in green.\n")
|
||||||
fmt.Fprintf(o, "If zero file names are supplied, standard input is read.\n")
|
fmt.Fprintf(o, "If zero file names are supplied, standard input is read.\n")
|
||||||
fmt.Fprintf(o, "Options:\n")
|
fmt.Fprintf(o, "Options:\n")
|
||||||
fmt.Fprintf(o, "--mono: don't try to colorize the output\n")
|
fmt.Fprintf(o, "--mono: don't try to colorize the output\n")
|
||||||
fmt.Fprintf(o, "-h or --help: print this message\n")
|
fmt.Fprintf(o, "-h or --help: print this message\n")
|
||||||
os.Exit(exitCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func lecatMain(args []string) int {
|
func lecatMain(args []string) int {
|
||||||
|
|
@ -25,7 +24,8 @@ func lecatMain(args []string) int {
|
||||||
args = args[2:]
|
args = args[2:]
|
||||||
if len(args) >= 1 {
|
if len(args) >= 1 {
|
||||||
if args[0] == "-h" || args[0] == "--help" {
|
if args[0] == "-h" || args[0] == "--help" {
|
||||||
lecatUsage(verb, os.Stdout, 0)
|
lecatUsage(verb, os.Stdout)
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if args[0][0] == '-' {
|
if args[0][0] == '-' {
|
||||||
|
|
@ -36,37 +36,46 @@ func lecatMain(args []string) int {
|
||||||
fmt.Fprintf(os.Stderr, "mlr %s: unrecognized option \"%s\".\n",
|
fmt.Fprintf(os.Stderr, "mlr %s: unrecognized option \"%s\".\n",
|
||||||
verb, args[0],
|
verb, args[0],
|
||||||
)
|
)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
lecatFile(os.Stdin, doColor)
|
if err := lecatFile(os.Stdin, doColor); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "mlr lecat: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, filename := range args {
|
for _, filename := range args {
|
||||||
|
|
||||||
istream, err := os.Open(filename)
|
istream, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr lecat: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr lecat: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
lecatFile(istream, doColor)
|
err = lecatFile(istream, doColor)
|
||||||
|
|
||||||
_ = istream.Close()
|
_ = istream.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "mlr lecat: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func lecatFile(istream *os.File, doColor bool) {
|
func lecatFile(istream *os.File, doColor bool) error {
|
||||||
reader := bufio.NewReader(istream)
|
reader := bufio.NewReader(istream)
|
||||||
for {
|
for {
|
||||||
c, err := reader.ReadByte()
|
c, err := reader.ReadByte()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
switch c {
|
switch c {
|
||||||
case '\r':
|
case '\r':
|
||||||
if doColor {
|
if doColor {
|
||||||
|
|
@ -88,4 +97,5 @@ func lecatFile(istream *os.File, doColor bool) {
|
||||||
fmt.Printf("%c", c)
|
fmt.Printf("%c", c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func termcvtUsage(verbName string, o *os.File, exitCode int) {
|
func termcvtUsage(verbName string, o *os.File) {
|
||||||
fmt.Fprintf(o, "Usage: mlr %s [option] {zero or more file names}\n", verbName)
|
fmt.Fprintf(o, "Usage: mlr %s [option] {zero or more file names}\n", verbName)
|
||||||
fmt.Fprintf(o, "Option (exactly one is required):\n")
|
fmt.Fprintf(o, "Option (exactly one is required):\n")
|
||||||
fmt.Fprintf(o, "--cr2crlf\n")
|
fmt.Fprintf(o, "--cr2crlf\n")
|
||||||
|
|
@ -21,7 +21,6 @@ func termcvtUsage(verbName string, o *os.File, exitCode int) {
|
||||||
fmt.Fprintf(o, "-h or --help: print this message\n")
|
fmt.Fprintf(o, "-h or --help: print this message\n")
|
||||||
fmt.Fprintf(o, "Zero file names means read from standard input.\n")
|
fmt.Fprintf(o, "Zero file names means read from standard input.\n")
|
||||||
fmt.Fprintf(o, "Output is always to standard output; files are not written in-place.\n")
|
fmt.Fprintf(o, "Output is always to standard output; files are not written in-place.\n")
|
||||||
os.Exit(exitCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func termcvtMain(args []string) int {
|
func termcvtMain(args []string) int {
|
||||||
|
|
@ -33,7 +32,8 @@ func termcvtMain(args []string) int {
|
||||||
verb := args[1]
|
verb := args[1]
|
||||||
args = args[2:]
|
args = args[2:]
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
termcvtUsage(verb, os.Stderr, 1)
|
termcvtUsage(verb, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
for len(args) >= 1 {
|
for len(args) >= 1 {
|
||||||
|
|
@ -45,7 +45,8 @@ func termcvtMain(args []string) int {
|
||||||
|
|
||||||
switch opt {
|
switch opt {
|
||||||
case "-h", "--help":
|
case "-h", "--help":
|
||||||
termcvtUsage(verb, os.Stdout, 0)
|
termcvtUsage(verb, os.Stdout)
|
||||||
|
return 0
|
||||||
case "-I":
|
case "-I":
|
||||||
doInPlace = true
|
doInPlace = true
|
||||||
case "--cr2crlf":
|
case "--cr2crlf":
|
||||||
|
|
@ -67,12 +68,16 @@ func termcvtMain(args []string) int {
|
||||||
inputTerminator = "\r"
|
inputTerminator = "\r"
|
||||||
outputTerminator = "\n"
|
outputTerminator = "\n"
|
||||||
default:
|
default:
|
||||||
termcvtUsage(verb, os.Stderr, 1)
|
termcvtUsage(verb, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
termcvtFile(os.Stdin, os.Stdout, inputTerminator, outputTerminator)
|
if err := termcvtFile(os.Stdin, os.Stdout, inputTerminator, outputTerminator); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
} else if doInPlace {
|
} else if doInPlace {
|
||||||
for _, filename := range args {
|
for _, filename := range args {
|
||||||
|
|
@ -82,31 +87,31 @@ func termcvtMain(args []string) int {
|
||||||
|
|
||||||
istream, err := os.Open(filename)
|
istream, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: "mlr"
|
|
||||||
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
ostream, err := os.Open(tempname)
|
ostream, err := os.Open(tempname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: "mlr"
|
|
||||||
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
termcvtFile(istream, ostream, inputTerminator, outputTerminator)
|
if err := termcvtFile(istream, ostream, inputTerminator, outputTerminator); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
_ = istream.Close()
|
_ = istream.Close()
|
||||||
if err := ostream.Close(); err != nil {
|
if err := ostream.Close(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.Rename(tempname, filename)
|
err = os.Rename(tempname, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: "mlr"
|
|
||||||
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,20 +120,22 @@ func termcvtMain(args []string) int {
|
||||||
|
|
||||||
istream, err := os.Open(filename)
|
istream, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: "mlr"
|
|
||||||
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
termcvtFile(istream, os.Stdout, inputTerminator, outputTerminator)
|
err = termcvtFile(istream, os.Stdout, inputTerminator, outputTerminator)
|
||||||
|
|
||||||
_ = istream.Close()
|
_ = istream.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func termcvtFile(istream *os.File, ostream *os.File, inputTerminator string, outputTerminator string) {
|
func termcvtFile(istream *os.File, ostream *os.File, inputTerminator string, outputTerminator string) error {
|
||||||
lineReader := bufio.NewReader(istream)
|
lineReader := bufio.NewReader(istream)
|
||||||
inputTerminatorBytes := []byte(inputTerminator[len(inputTerminator)-1:])[0] // bufio.Reader.ReadString takes char not string delimiter :(
|
inputTerminatorBytes := []byte(inputTerminator[len(inputTerminator)-1:])[0] // bufio.Reader.ReadString takes char not string delimiter :(
|
||||||
|
|
||||||
|
|
@ -139,16 +146,14 @@ func termcvtFile(istream *os.File, ostream *os.File, inputTerminator string, out
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: "mlr"
|
return err
|
||||||
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is how to do a chomp:
|
// This is how to do a chomp:
|
||||||
line = strings.TrimRight(line, inputTerminator)
|
line = strings.TrimRight(line, inputTerminator)
|
||||||
if _, err := ostream.Write([]byte(line + outputTerminator)); err != nil {
|
if _, err := ostream.Write([]byte(line + outputTerminator)); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr termcvt: %v\n", err)
|
return err
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package auxents
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -9,13 +10,12 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func unhexUsage(verbName string, o *os.File, exitCode int) {
|
func unhexUsage(verbName string, o *os.File) {
|
||||||
fmt.Fprintf(o, "Usage: mlr %s [option] {zero or more file names}\n", verbName)
|
fmt.Fprintf(o, "Usage: mlr %s [option] {zero or more file names}\n", verbName)
|
||||||
fmt.Fprintf(o, "Options:\n")
|
fmt.Fprintf(o, "Options:\n")
|
||||||
fmt.Fprintf(o, "-h or --help: print this message\n")
|
fmt.Fprintf(o, "-h or --help: print this message\n")
|
||||||
fmt.Fprintf(o, "Zero file names means read from standard input.\n")
|
fmt.Fprintf(o, "Zero file names means read from standard input.\n")
|
||||||
fmt.Fprintf(o, "Output is always to standard output; files are not written in-place.\n")
|
fmt.Fprintf(o, "Output is always to standard output; files are not written in-place.\n")
|
||||||
os.Exit(exitCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func unhexMain(args []string) int {
|
func unhexMain(args []string) int {
|
||||||
|
|
@ -25,28 +25,36 @@ func unhexMain(args []string) int {
|
||||||
|
|
||||||
if len(args) >= 1 {
|
if len(args) >= 1 {
|
||||||
if args[0] == "-h" || args[0] == "--help" {
|
if args[0] == "-h" || args[0] == "--help" {
|
||||||
unhexUsage(verb, os.Stdout, 0)
|
unhexUsage(verb, os.Stdout)
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
unhexFile(os.Stdin, os.Stdout)
|
if err := unhexFile(os.Stdin, os.Stdout); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "mlr unhex: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, filename := range args {
|
for _, filename := range args {
|
||||||
istream, err := os.Open(filename)
|
istream, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr unhex: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr unhex: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
unhexFile(istream, os.Stdout)
|
err = unhexFile(istream, os.Stdout)
|
||||||
_ = istream.Close()
|
_ = istream.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "mlr unhex: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func unhexFile(istream *os.File, ostream *os.File) {
|
func unhexFile(istream *os.File, ostream *os.File) error {
|
||||||
// Key insight is os.File implements io.Reader
|
// Key insight is os.File implements io.Reader
|
||||||
lineReader := bufio.NewReader(istream)
|
lineReader := bufio.NewReader(istream)
|
||||||
|
|
||||||
|
|
@ -62,8 +70,7 @@ func unhexFile(istream *os.File, ostream *os.File) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr unhex: %v\n", err)
|
return err
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is how to do a chomp:
|
// This is how to do a chomp:
|
||||||
|
|
@ -75,19 +82,17 @@ func unhexFile(istream *os.File, ostream *os.File) {
|
||||||
if field != "" {
|
if field != "" {
|
||||||
n, err := fmt.Sscanf(field, "%x", &scanValue)
|
n, err := fmt.Sscanf(field, "%x", &scanValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr unhex: %v\n", err)
|
return err
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
if n != 1 {
|
if n != 1 {
|
||||||
fmt.Fprintln(os.Stderr, "mlr unhex: internal coding error")
|
return errors.New("internal coding error")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
byteArray[0] = byte(scanValue)
|
byteArray[0] = byte(scanValue)
|
||||||
if _, err := ostream.Write(byteArray); err != nil {
|
if _, err := ostream.Write(byteArray); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "mlr unhex:", err)
|
return err
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -396,9 +396,9 @@ func parseCommandLinePassTwo(
|
||||||
}
|
}
|
||||||
|
|
||||||
if terminalSequence != nil {
|
if terminalSequence != nil {
|
||||||
terminals.Dispatch(terminalSequence)
|
// The terminal (help, repl, regtest, ...) runs here; its exit code
|
||||||
// They are expected to exit the process
|
// rides an ExitRequest up to the entrypoint layer.
|
||||||
panic("mlr: internal coding error: terminal did not exit the process")
|
return nil, nil, &lib.ExitRequest{Code: terminals.Dispatch(terminalSequence)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now process the verb-sequences from pass one, with options-struct set up
|
// Now process the verb-sequences from pass one, with options-struct set up
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,10 @@ func Main() MainReturn {
|
||||||
|
|
||||||
// 'mlr repl' or 'mlr lecat' or any other non-miller-per-se toolery which
|
// 'mlr repl' or 'mlr lecat' or any other non-miller-per-se toolery which
|
||||||
// is delivered (for convenience) within the mlr executable. If argv[1] is
|
// is delivered (for convenience) within the mlr executable. If argv[1] is
|
||||||
// found then this function will not return.
|
// an auxent name, it's run here and its exit code is the process's.
|
||||||
auxents.Dispatch(os.Args)
|
if handled, exitCode := auxents.Dispatch(os.Args); handled {
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
if lib.IsTruthyEnvValue(os.Getenv("MLR_NO_SHELL")) {
|
if lib.IsTruthyEnvValue(os.Getenv("MLR_NO_SHELL")) {
|
||||||
lib.DisableShellOut()
|
lib.DisableShellOut()
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
const defaultPath = "./test/cases"
|
const defaultPath = "./test/cases"
|
||||||
|
|
||||||
func regTestUsage(verbName string, o *os.File, exitCode int) {
|
func regTestUsage(verbName string, o *os.File) {
|
||||||
fmt.Fprintf(o, "Usage: mlr %s [options] [one or more directories/files]\n", verbName)
|
fmt.Fprintf(o, "Usage: mlr %s [options] [one or more directories/files]\n", verbName)
|
||||||
fmt.Fprintf(o, "If no directories/files are specified, the directory %s is used by default.\n", defaultPath)
|
fmt.Fprintf(o, "If no directories/files are specified, the directory %s is used by default.\n", defaultPath)
|
||||||
fmt.Fprintf(o, "Recursively walks the directory/ies looking for foo.cmd files having Miller command-lines,\n")
|
fmt.Fprintf(o, "Recursively walks the directory/ies looking for foo.cmd files having Miller command-lines,\n")
|
||||||
|
|
@ -30,7 +30,6 @@ func regTestUsage(verbName string, o *os.File, exitCode int) {
|
||||||
fmt.Fprintf(o, "-j Just show the Miller command-line, put/filter script if any, and output.\n")
|
fmt.Fprintf(o, "-j Just show the Miller command-line, put/filter script if any, and output.\n")
|
||||||
fmt.Fprintf(o, "-s {n} After running tests, re-run first n failed .cmd files with verbosity level 3.\n")
|
fmt.Fprintf(o, "-s {n} After running tests, re-run first n failed .cmd files with verbosity level 3.\n")
|
||||||
fmt.Fprintf(o, "-S After running tests, re-run all failed .cmd files with verbosity level 3.\n")
|
fmt.Fprintf(o, "-S After running tests, re-run all failed .cmd files with verbosity level 3.\n")
|
||||||
os.Exit(exitCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here the args are the full Miller command line: "mlr regtest --foo bar".
|
// Here the args are the full Miller command line: "mlr regtest --foo bar".
|
||||||
|
|
@ -55,22 +54,26 @@ func RegTestMain(args []string) int {
|
||||||
|
|
||||||
switch arg {
|
switch arg {
|
||||||
case "-h", "--help":
|
case "-h", "--help":
|
||||||
regTestUsage(verbName, os.Stdout, 0)
|
regTestUsage(verbName, os.Stdout)
|
||||||
|
return 0
|
||||||
|
|
||||||
case "-m":
|
case "-m":
|
||||||
if argi >= argc {
|
if argi >= argc {
|
||||||
regTestUsage(verbName, os.Stderr, 1)
|
regTestUsage(verbName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
exeName = args[argi]
|
exeName = args[argi]
|
||||||
argi++
|
argi++
|
||||||
|
|
||||||
case "-s":
|
case "-s":
|
||||||
if argi >= argc {
|
if argi >= argc {
|
||||||
regTestUsage(verbName, os.Stderr, 1)
|
regTestUsage(verbName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
temp, err := strconv.Atoi(args[argi])
|
temp, err := strconv.Atoi(args[argi])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
regTestUsage(verbName, os.Stderr, 1)
|
regTestUsage(verbName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
firstNFailsToShow = temp
|
firstNFailsToShow = temp
|
||||||
argi++
|
argi++
|
||||||
|
|
@ -88,7 +91,8 @@ func RegTestMain(args []string) int {
|
||||||
plainMode = true
|
plainMode = true
|
||||||
|
|
||||||
default:
|
default:
|
||||||
regTestUsage(verbName, os.Stderr, 1)
|
regTestUsage(verbName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
casePaths := args[argi:]
|
casePaths := args[argi:]
|
||||||
|
|
@ -104,7 +108,11 @@ func RegTestMain(args []string) int {
|
||||||
firstNFailsToShow,
|
firstNFailsToShow,
|
||||||
)
|
)
|
||||||
|
|
||||||
ok := regtester.Execute(casePaths)
|
ok, err := regtester.Execute(casePaths)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "mlr %s: %v\n", verbName, err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return 1
|
return 1
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ func (rt *RegTester) resetCounts() {
|
||||||
|
|
||||||
func (rt *RegTester) Execute(
|
func (rt *RegTester) Execute(
|
||||||
casePaths []string,
|
casePaths []string,
|
||||||
) bool {
|
) (bool, error) {
|
||||||
// Don't let the current user's settings affect expected results
|
// Don't let the current user's settings affect expected results
|
||||||
for _, name := range envVarsToUnset {
|
for _, name := range envVarsToUnset {
|
||||||
_ = os.Unsetenv(name)
|
_ = os.Unsetenv(name)
|
||||||
|
|
@ -175,7 +175,9 @@ func (rt *RegTester) Execute(
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, path := range casePaths {
|
for _, path := range casePaths {
|
||||||
rt.executeSinglePath(path)
|
if _, err := rt.executeSinglePath(path); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rt.failCaseNames) > 0 && rt.firstNFailsToShow > 0 {
|
if len(rt.failCaseNames) > 0 && rt.firstNFailsToShow > 0 {
|
||||||
|
|
@ -215,12 +217,12 @@ func (rt *RegTester) Execute(
|
||||||
if !rt.plainMode {
|
if !rt.plainMode {
|
||||||
fmt.Printf("%s overall\n", colorizer.MaybeColorizePass("PASS", true))
|
fmt.Printf("%s overall\n", colorizer.MaybeColorizePass("PASS", true))
|
||||||
}
|
}
|
||||||
return true
|
return true, nil
|
||||||
}
|
}
|
||||||
if !rt.plainMode {
|
if !rt.plainMode {
|
||||||
fmt.Printf("%s overall\n", colorizer.MaybeColorizeFail("FAIL", true))
|
fmt.Printf("%s overall\n", colorizer.MaybeColorizeFail("FAIL", true))
|
||||||
}
|
}
|
||||||
return false
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively invoked routine to process either a single .cmd file, or a
|
// Recursively invoked routine to process either a single .cmd file, or a
|
||||||
|
|
@ -228,15 +230,18 @@ func (rt *RegTester) Execute(
|
||||||
|
|
||||||
func (rt *RegTester) executeSinglePath(
|
func (rt *RegTester) executeSinglePath(
|
||||||
path string,
|
path string,
|
||||||
) bool {
|
) (bool, error) {
|
||||||
handle, err := os.Stat(path)
|
handle, err := os.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%s: %v\n", path, err)
|
fmt.Printf("%s: %v\n", path, err)
|
||||||
return false
|
return false, nil
|
||||||
}
|
}
|
||||||
mode := handle.Mode()
|
mode := handle.Mode()
|
||||||
if mode.IsDir() {
|
if mode.IsDir() {
|
||||||
passed, hasCaseSubdirectories := rt.executeSingleDirectory(path)
|
passed, hasCaseSubdirectories, err := rt.executeSingleDirectory(path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
if hasCaseSubdirectories {
|
if hasCaseSubdirectories {
|
||||||
if passed {
|
if passed {
|
||||||
rt.directoryPassCount++
|
rt.directoryPassCount++
|
||||||
|
|
@ -245,7 +250,7 @@ func (rt *RegTester) executeSinglePath(
|
||||||
rt.failDirNames = append(rt.failDirNames, path)
|
rt.failDirNames = append(rt.failDirNames, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return passed
|
return passed, nil
|
||||||
} else if mode.IsRegular() {
|
} else if mode.IsRegular() {
|
||||||
basename := filepath.Base(path)
|
basename := filepath.Base(path)
|
||||||
if basename == CmdName {
|
if basename == CmdName {
|
||||||
|
|
@ -256,21 +261,24 @@ func (rt *RegTester) executeSinglePath(
|
||||||
rt.caseFailCount++
|
rt.caseFailCount++
|
||||||
rt.failCaseNames = append(rt.failCaseNames, path)
|
rt.failCaseNames = append(rt.failCaseNames, path)
|
||||||
}
|
}
|
||||||
return passed
|
return passed, nil
|
||||||
}
|
}
|
||||||
return true // No .cmd files directly inside
|
return true, nil // No .cmd files directly inside
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s: neither directory nor regular file.\n", path)
|
fmt.Printf("%s: neither directory nor regular file.\n", path)
|
||||||
return false // fall-through
|
return false, nil // fall-through
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rt *RegTester) executeSingleDirectory(
|
func (rt *RegTester) executeSingleDirectory(
|
||||||
dirName string,
|
dirName string,
|
||||||
) (bool, bool) {
|
) (bool, bool, error) {
|
||||||
passed := true
|
passed := true
|
||||||
// TODO: comment
|
// TODO: comment
|
||||||
fileNames, hasCaseSubdirectories := rt.hasCaseSubdirectories(dirName)
|
fileNames, hasCaseSubdirectories, err := rt.hasCaseSubdirectories(dirName)
|
||||||
|
if err != nil {
|
||||||
|
return false, false, err
|
||||||
|
}
|
||||||
|
|
||||||
if !rt.plainMode {
|
if !rt.plainMode {
|
||||||
if hasCaseSubdirectories && rt.verbosityLevel >= 2 {
|
if hasCaseSubdirectories && rt.verbosityLevel >= 2 {
|
||||||
|
|
@ -281,7 +289,10 @@ func (rt *RegTester) executeSingleDirectory(
|
||||||
for _, name := range fileNames {
|
for _, name := range fileNames {
|
||||||
path := dirName + "/" + name
|
path := dirName + "/" + name
|
||||||
|
|
||||||
ok := rt.executeSinglePath(path)
|
ok, err := rt.executeSinglePath(path)
|
||||||
|
if err != nil {
|
||||||
|
return false, false, err
|
||||||
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
passed = false
|
passed = false
|
||||||
}
|
}
|
||||||
|
|
@ -309,7 +320,7 @@ func (rt *RegTester) executeSingleDirectory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return passed, hasCaseSubdirectories
|
return passed, hasCaseSubdirectories, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sees if a directory contains a single test case. If so, we don't want to
|
// Sees if a directory contains a single test case. If so, we don't want to
|
||||||
|
|
@ -322,28 +333,26 @@ func (rt *RegTester) executeSingleDirectory(
|
||||||
|
|
||||||
func (rt *RegTester) hasCaseSubdirectories(
|
func (rt *RegTester) hasCaseSubdirectories(
|
||||||
dirName string,
|
dirName string,
|
||||||
) ([]string, bool) {
|
) ([]string, bool, error) {
|
||||||
f, err := os.Open(dirName)
|
f, err := os.Open(dirName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%s: %v\n", dirName, err)
|
return nil, false, err
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
defer func() { _ = f.Close() }()
|
defer func() { _ = f.Close() }()
|
||||||
|
|
||||||
names, err := f.Readdirnames(-1)
|
names, err := f.Readdirnames(-1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%s: %v\n", dirName, err)
|
return nil, false, err
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
|
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
path := dirName + string(filepath.Separator) + name
|
path := dirName + string(filepath.Separator) + name
|
||||||
if rt.isCaseDirectory(path) {
|
if rt.isCaseDirectory(path) {
|
||||||
return names, true
|
return names, true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return names, false
|
return names, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rt *RegTester) isCaseDirectory(
|
func (rt *RegTester) isCaseDirectory(
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import (
|
||||||
"github.com/johnkerl/miller/v6/pkg/lib"
|
"github.com/johnkerl/miller/v6/pkg/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func replUsage(verbName string, o *os.File, exitCode int) {
|
func replUsage(verbName string, o *os.File) {
|
||||||
exeName := path.Base(os.Args[0])
|
exeName := path.Base(os.Args[0])
|
||||||
fmt.Fprintf(o, "Usage: %s %s [options] {zero or more data-file names}\n", exeName, verbName)
|
fmt.Fprintf(o, "Usage: %s %s [options] {zero or more data-file names}\n", exeName, verbName)
|
||||||
|
|
||||||
|
|
@ -72,8 +72,6 @@ Or any --icsv, --ojson, etc. reader/writer options as for the main Miller comman
|
||||||
Any data-file names are opened just as if you had waited and typed :open {filenames}
|
Any data-file names are opened just as if you had waited and typed :open {filenames}
|
||||||
at the Miller REPL prompt.
|
at the Miller REPL prompt.
|
||||||
`)
|
`)
|
||||||
|
|
||||||
os.Exit(exitCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here the args are the full Miller command line: if the latter was "mlr
|
// Here the args are the full Miller command line: if the latter was "mlr
|
||||||
|
|
@ -97,7 +95,8 @@ func ReplMain(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
if args[argi] == "-h" || args[argi] == "--help" {
|
if args[argi] == "-h" || args[argi] == "--help" {
|
||||||
replUsage(replName, os.Stdout, 0)
|
replUsage(replName, os.Stdout)
|
||||||
|
return 0
|
||||||
|
|
||||||
} else if args[argi] == "-q" {
|
} else if args[argi] == "-q" {
|
||||||
showStartupBanner = false
|
showStartupBanner = false
|
||||||
|
|
@ -124,14 +123,16 @@ func ReplMain(args []string) int {
|
||||||
|
|
||||||
} else if args[argi] == "--load" {
|
} else if args[argi] == "--load" {
|
||||||
if argc-argi < 2 {
|
if argc-argi < 2 {
|
||||||
replUsage(replName, os.Stderr, 1)
|
replUsage(replName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
options.DSLPreloadFileNames = append(options.DSLPreloadFileNames, args[argi+1])
|
options.DSLPreloadFileNames = append(options.DSLPreloadFileNames, args[argi+1])
|
||||||
argi += 2
|
argi += 2
|
||||||
|
|
||||||
} else if args[argi] == "--mload" {
|
} else if args[argi] == "--mload" {
|
||||||
if argc-argi < 2 {
|
if argc-argi < 2 {
|
||||||
replUsage(replName, os.Stderr, 1)
|
replUsage(replName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
argi += 1
|
argi += 1
|
||||||
for argi < argc && args[argi] != "--" {
|
for argi < argc && args[argi] != "--" {
|
||||||
|
|
@ -153,7 +154,8 @@ func ReplMain(args []string) int {
|
||||||
} else if handled {
|
} else if handled {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
replUsage(replName, os.Stderr, 1)
|
replUsage(replName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,7 +190,7 @@ func ReplMain(args []string) int {
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
filenames := args[argi:]
|
filenames := args[argi:]
|
||||||
|
|
@ -199,18 +201,18 @@ func ReplMain(args []string) int {
|
||||||
err = repl.handleSession(os.Stdin)
|
err = repl.handleSession(os.Stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr %s: %v\n", repl.replName, err)
|
fmt.Fprintf(os.Stderr, "mlr %s: %v\n", repl.replName, err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
err = repl.bufferedRecordOutputStream.Flush()
|
err = repl.bufferedRecordOutputStream.Flush()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr %s: %v\n", repl.replName, err)
|
fmt.Fprintf(os.Stderr, "mlr %s: %v\n", repl.replName, err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
err = repl.closeBufferedOutputStream()
|
err = repl.closeBufferedOutputStream()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr %s: %v\n", repl.replName, err)
|
fmt.Fprintf(os.Stderr, "mlr %s: %v\n", repl.replName, err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"github.com/johnkerl/miller/v6/pkg/lib"
|
"github.com/johnkerl/miller/v6/pkg/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func scriptUsage(verbName string, o *os.File, exitCode int) {
|
func scriptUsage(verbName string, o *os.File) {
|
||||||
exeName := path.Base(os.Args[0])
|
exeName := path.Base(os.Args[0])
|
||||||
fmt.Fprintf(o, "Usage: %s %s [options] -f {script file} | -e {expression} [zero or more data-file names]\n", exeName, verbName)
|
fmt.Fprintf(o, "Usage: %s %s [options] -f {script file} | -e {expression} [zero or more data-file names]\n", exeName, verbName)
|
||||||
fmt.Fprint(o,
|
fmt.Fprint(o,
|
||||||
|
|
@ -39,7 +39,6 @@ Or any --icsv, --ojson, etc. reader/writer options.
|
||||||
|
|
||||||
Data-file names (or stdin if none) are the record input stream.
|
Data-file names (or stdin if none) are the record input stream.
|
||||||
`)
|
`)
|
||||||
os.Exit(exitCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ScriptMain(args []string) int {
|
func ScriptMain(args []string) int {
|
||||||
|
|
@ -59,7 +58,8 @@ func ScriptMain(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
if args[argi] == "-h" || args[argi] == "--help" {
|
if args[argi] == "-h" || args[argi] == "--help" {
|
||||||
scriptUsage(scriptName, os.Stdout, 0)
|
scriptUsage(scriptName, os.Stdout)
|
||||||
|
return 0
|
||||||
} else if args[argi] == "-w" {
|
} else if args[argi] == "-w" {
|
||||||
doWarnings = true
|
doWarnings = true
|
||||||
argi++
|
argi++
|
||||||
|
|
@ -68,42 +68,46 @@ func ScriptMain(args []string) int {
|
||||||
argi++
|
argi++
|
||||||
} else if args[argi] == "-f" {
|
} else if args[argi] == "-f" {
|
||||||
if argc-argi < 2 {
|
if argc-argi < 2 {
|
||||||
scriptUsage(scriptName, os.Stderr, 1)
|
scriptUsage(scriptName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
argi++
|
argi++
|
||||||
filename, err := cli.VerbGetStringArg("script", "-f", args, &argi, argc)
|
filename, err := cli.VerbGetStringArg("script", "-f", args, &argi, argc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
theseStrings, err := lib.LoadStringsFromFileOrDir(filename, ".mlr")
|
theseStrings, err := lib.LoadStringsFromFileOrDir(filename, ".mlr")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr script: cannot load script from \"%s\": %v\n", filename, err)
|
fmt.Fprintf(os.Stderr, "mlr script: cannot load script from \"%s\": %v\n", filename, err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
dslStrings = append(dslStrings, theseStrings...)
|
dslStrings = append(dslStrings, theseStrings...)
|
||||||
haveDSLStrings = true
|
haveDSLStrings = true
|
||||||
} else if args[argi] == "-e" {
|
} else if args[argi] == "-e" {
|
||||||
if argc-argi < 2 {
|
if argc-argi < 2 {
|
||||||
scriptUsage(scriptName, os.Stderr, 1)
|
scriptUsage(scriptName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
argi++
|
argi++
|
||||||
expr, err := cli.VerbGetStringArg("script", "-e", args, &argi, argc)
|
expr, err := cli.VerbGetStringArg("script", "-e", args, &argi, argc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
dslStrings = append(dslStrings, expr)
|
dslStrings = append(dslStrings, expr)
|
||||||
haveDSLStrings = true
|
haveDSLStrings = true
|
||||||
} else if args[argi] == "--load" {
|
} else if args[argi] == "--load" {
|
||||||
if argc-argi < 2 {
|
if argc-argi < 2 {
|
||||||
scriptUsage(scriptName, os.Stderr, 1)
|
scriptUsage(scriptName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
options.DSLPreloadFileNames = append(options.DSLPreloadFileNames, args[argi+1])
|
options.DSLPreloadFileNames = append(options.DSLPreloadFileNames, args[argi+1])
|
||||||
argi += 2
|
argi += 2
|
||||||
} else if args[argi] == "--mload" {
|
} else if args[argi] == "--mload" {
|
||||||
if argc-argi < 2 {
|
if argc-argi < 2 {
|
||||||
scriptUsage(scriptName, os.Stderr, 1)
|
scriptUsage(scriptName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
argi++
|
argi++
|
||||||
for argi < argc && args[argi] != "--" {
|
for argi < argc && args[argi] != "--" {
|
||||||
|
|
@ -122,13 +126,15 @@ func ScriptMain(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
} else if handled {
|
} else if handled {
|
||||||
} else {
|
} else {
|
||||||
scriptUsage(scriptName, os.Stderr, 1)
|
scriptUsage(scriptName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !haveDSLStrings {
|
if !haveDSLStrings {
|
||||||
fmt.Fprintf(os.Stderr, "mlr script: -f or -e is required\n")
|
fmt.Fprintf(os.Stderr, "mlr script: -f or -e is required\n")
|
||||||
scriptUsage(scriptName, os.Stderr, 1)
|
scriptUsage(scriptName, os.Stderr)
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cli.FinalizeReaderOptions(&options.ReaderOptions); err != nil {
|
if err := cli.FinalizeReaderOptions(&options.ReaderOptions); err != nil {
|
||||||
|
|
@ -147,25 +153,25 @@ func ScriptMain(args []string) int {
|
||||||
scr, err := NewScript(options, doWarnings, strictMode, dslStrings)
|
scr, err := NewScript(options, doWarnings, strictMode, dslStrings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr script: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr script: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
scr.openFiles(filenames)
|
scr.openFiles(filenames)
|
||||||
err = scr.run()
|
err = scr.run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr script: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr script: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
err = scr.bufferedRecordOutputStream.Flush()
|
err = scr.bufferedRecordOutputStream.Flush()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr script: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr script: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
err = scr.closeBufferedOutputStream()
|
err = scr.closeBufferedOutputStream()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "mlr script: %v\n", err)
|
fmt.Fprintf(os.Stderr, "mlr script: %v\n", err)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,19 +57,24 @@ func Dispatchable(arg string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func Dispatch(args []string) {
|
// Dispatch runs the terminal named by args[0] and returns the process exit
|
||||||
|
// code for the caller to propagate (via lib.ExitRequest) up to the entrypoint
|
||||||
|
// layer.
|
||||||
|
func Dispatch(args []string) int {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return
|
// Can't happen: the climain caller passes a non-empty terminal sequence.
|
||||||
|
fmt.Fprintf(os.Stderr, "mlr: internal coding error: empty terminal sequence.\n")
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
terminal := args[0]
|
terminal := args[0]
|
||||||
|
|
||||||
for _, entry := range _TERMINAL_LOOKUP_TABLE {
|
for _, entry := range _TERMINAL_LOOKUP_TABLE {
|
||||||
if terminal == entry.name {
|
if terminal == entry.name {
|
||||||
os.Exit(entry.main(args))
|
return entry.main(args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "mlr: terminal \"%s\" not found.\n", terminal)
|
fmt.Fprintf(os.Stderr, "mlr: terminal \"%s\" not found.\n", terminal)
|
||||||
os.Exit(1)
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// terminalListMain is the handler for 'mlr terminal-list'.
|
// terminalListMain is the handler for 'mlr terminal-list'.
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,10 @@ func TestRegression(t *testing.T) {
|
||||||
firstNFailsToShow,
|
firstNFailsToShow,
|
||||||
)
|
)
|
||||||
|
|
||||||
ok := regtester.Execute(casePaths)
|
ok, err := regtester.Execute(casePaths)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue