Export library code in pkg/ (#1391)

* Export library code in `pkg/`

* new doc page
This commit is contained in:
John Kerl 2023-09-10 17:15:13 -04:00 committed by GitHub
parent 93b7c8eac0
commit 268a96d002
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
358 changed files with 1076 additions and 693 deletions

1
pkg/platform/README.md Normal file
View file

@ -0,0 +1 @@
This is Miller's platform-dependent code -- as of April 2021, all Windows vs not-Windows.

View file

@ -0,0 +1,14 @@
// ================================================================
// Handling diff or fc for regression-test.
// ================================================================
//go:build !windows
// +build !windows
package platform
// GetDiffRunArray gets the command for diffing actual/expected output in a
// regression test.
func GetDiffRunArray(filename1, filename2 string) []string {
return []string{"diff", "-u", filename1, filename2}
}

View file

@ -0,0 +1,14 @@
// ================================================================
// Handling diff or fc for regression-test.
// ================================================================
//go:build windows
// +build windows
package platform
// GetDiffRunArray gets the command for diffing actual/expected output in a
// regression test.
func GetDiffRunArray(filename1, filename2 string) []string {
return []string{"fc", filename1, filename2}
}

3
pkg/platform/doc.go Normal file
View file

@ -0,0 +1,3 @@
// Package platform is Miller's platform-dependent code -- as of April 2021,
// this is divided into Windows and not-Windows.
package platform

View file

@ -0,0 +1,17 @@
// ================================================================
// Handling single quotes and double quotes is different on Windows unless
// particular care is taken, which is what this file does.
// ================================================================
//go:build !windows
// +build !windows
package platform
import (
"os"
)
func GetArgs() []string {
return os.Args
}

View file

@ -0,0 +1,154 @@
// ================================================================
// Handling single quotes and double quotes is different on Windows unless
// particular care is taken, which is what this file does.
// ================================================================
//go:build windows
// +build windows
package platform
import (
"fmt"
"os"
"path/filepath"
"strings"
shellquote "github.com/kballard/go-shellquote"
"golang.org/x/sys/windows"
)
// GetArgs returns a copy of os.Args, as-is except for any arg wrapped in single quotes.
// This is for compatibility Linux/Unix/MacOS.
//
// For example:
//
// mlr --icsv --ojson put '$filename = $basename . ".ext"' ..\data\foo.csv
//
// The Windows way to say that is
//
// mlr --icsv --ojson put "$filename = $basename . """.ext"""" ..\data\foo.csv
//
// This function makes it possible to say the former, or the latter.
func GetArgs() []string {
// If this code is running in MSYS2: MSYS2 does the right thing already and
// we won't try to improve on that. This is true regardless of whether we
// were compiled inside MSYS2 or outside; what matters is whether we're
// _running_ inside MSYS2 or outside. (I.e. this is necessarily a run-time
// check, not a compile-time check.)
msystem := os.Getenv("MSYSTEM")
if msystem != "" {
return os.Args
}
//printArgs(os.Args, "ORIGINAL")
regrouped, ok := regroupForSingleQuote(os.Args)
if !ok {
return os.Args
}
//printArgs(regrouped, "REGROUPED")
// Things on the command line include: args[0], which is fine as-is;
// various flags like -x or --xyz which are fine as-is; DSL expressions
// such as '$a = $b . "ccc"'. We don't want to give back the result of
// shellquote.Split since that will remove the backslashes from things like
// ..\data\foo\dat or C:\foo\bar.baz.
rawCommandLine := windows.UTF16PtrToString(windows.GetCommandLine())
splitArgs, err := shellquote.Split(rawCommandLine)
if err != nil {
fmt.Fprintf(
os.Stderr,
"mlr: internal error: could not parse Windows raw command line: %v\n",
err,
)
}
retargs := make([]string, 0)
// TODO err/stetret if lens uneq
for i, oldArg := range regrouped {
if strings.HasPrefix(oldArg, "'") && strings.HasSuffix(oldArg, "'") {
retargs = append(retargs, splitArgs[i])
} else {
retargs = append(retargs, regrouped[i])
}
}
//printArgs(retargs, "NEW")
globbed := make([]string, 0)
for i, _ := range retargs {
// Expand things like *.csv
matches, err := filepath.Glob(retargs[i])
if matches != nil && err == nil {
globbed = append(globbed, matches...)
} else {
globbed = append(globbed, retargs[i])
}
}
//printArgs(globbed, "NEW")
return globbed
}
// ----------------------------------------------------------------
func printArgs(args []string, description string) {
fmt.Printf("%s:\n", description)
for i, arg := range args {
fmt.Printf("%d %s\n", i, arg)
}
fmt.Println()
}
// ----------------------------------------------------------------
func regroupForSingleQuote(inargs []string) ([]string, bool) {
outargs := make([]string, 0, len(inargs))
inside := false
var concat string
// TODO: comment
// TODO: UT this all
for _, inarg := range inargs {
if !inside {
if !strings.HasPrefix(inarg, "'") {
// Current arg is not single-quoted, and not inside a single-quoted region
outargs = append(outargs, inarg)
} else {
// Start of single-quoted region
if strings.HasSuffix(inarg, "'") {
// Start and end of single-quoted region, like '$y=$x'
outargs = append(outargs, inarg)
} else {
// Start but not end of single-quoted region, like '$y=
inside = true
concat = inarg
}
}
} else {
if !strings.HasSuffix(inarg, "'") {
// Continuation of single-quoted region
concat = concat + " " + inarg
} else {
// End of single-quoted region
inside = false
concat = concat + " " + inarg
outargs = append(outargs, concat)
concat = ""
}
}
}
// TODO: error not bool?
if inside {
return nil, false
}
return outargs, true
}

View file

@ -0,0 +1,12 @@
// ================================================================
// Wraps 'sh -c foo bar' or 'cmd /c foo bar', nominally for regression-testing.
// ================================================================
//go:build !windows
// +build !windows
package platform
func GetShellRunArray(command string) []string {
return []string{"/bin/sh", "-c", command}
}

View file

@ -0,0 +1,51 @@
// ================================================================
// Wraps 'sh -c foo bar' or 'cmd /c foo bar', nominally for regression-testing.
// ================================================================
//go:build windows
// +build windows
package platform
import (
"os"
)
// PowerShell or CMD?
//
// * Either PowerShell or CMD is fine for everything except the '...' in inline
// put/filter statements.
//
// * PowerShell allows fractionally more, e.g. in mlr put '$flag = $x > 10' CMD
// sees the ">" as a file-redirect (before our main() is ever entered) and
// PowerShell lets it be operator it is intended to be.
//
// * Neither of them allows multi-line '...' inputs.
//
// * Both of the previous mean that a large number of regression tests need to
// use mlr -f .../cases/.../0047.mlr .../cases/.../0047.input and PowerShell
// doesn't help us eliminate this.
//
// * PowerShell has about a 2-second startup time per invocation so if we
// use it for regression testing, we'd have to move away from one shell
// invocation per Miller invocation and back to batching lots of Miller
// statements into a file, doing diff/findstr to check status. That was the
// case for Miller's original bash/file regtest framework and it was hard to
// debug.
//
// In conclusion, we stick with CMD because it's faster, and PowerShell while
// more powerful isn't *sufficiently* more powerful to justify the
// batching-complexity to overcome its startup-latency overhead.
func GetShellRunArray(command string) []string {
if os.Getenv("MSYSTEM") != "" {
// Running inside MSYS2; sufficiently Unix-like already.
return []string{"/bin/sh", "-c", command}
} else {
cmd := os.Getenv("COMSPEC")
if cmd == "" {
cmd = "C:\\Windows\\System32\\cmd.exe"
}
return []string{cmd, "/c", command}
}
}

View file

@ -0,0 +1,7 @@
//go:build !windows
// +build !windows
package platform
func EnableAnsiEscapeSequences() {
}

View file

@ -0,0 +1,15 @@
//go:build windows
// +build windows
package platform
import (
"syscall"
sequences "github.com/nine-lives-later/go-windows-terminal-sequences"
)
func EnableAnsiEscapeSequences() {
sequences.EnableVirtualTerminalProcessing(syscall.Stdout, true)
sequences.EnableVirtualTerminalProcessing(syscall.Stderr, true)
}