mirror of
https://github.com/johnkerl/miller.git
synced 2026-01-23 02:14:13 +00:00
Export library code in pkg/ (#1391)
* Export library code in `pkg/` * new doc page
This commit is contained in:
parent
93b7c8eac0
commit
268a96d002
358 changed files with 1076 additions and 693 deletions
1
pkg/platform/README.md
Normal file
1
pkg/platform/README.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
This is Miller's platform-dependent code -- as of April 2021, all Windows vs not-Windows.
|
||||
14
pkg/platform/diff_notwindows.go
Normal file
14
pkg/platform/diff_notwindows.go
Normal 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}
|
||||
}
|
||||
14
pkg/platform/diff_windows.go
Normal file
14
pkg/platform/diff_windows.go
Normal 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
3
pkg/platform/doc.go
Normal 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
|
||||
17
pkg/platform/getargs_notwindows.go
Normal file
17
pkg/platform/getargs_notwindows.go
Normal 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
|
||||
}
|
||||
154
pkg/platform/getargs_windows.go
Normal file
154
pkg/platform/getargs_windows.go
Normal 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
|
||||
}
|
||||
12
pkg/platform/shellrun_notwindows.go
Normal file
12
pkg/platform/shellrun_notwindows.go
Normal 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}
|
||||
}
|
||||
51
pkg/platform/shellrun_windows.go
Normal file
51
pkg/platform/shellrun_windows.go
Normal 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}
|
||||
}
|
||||
}
|
||||
7
pkg/platform/terminal_notwindows.go
Normal file
7
pkg/platform/terminal_notwindows.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package platform
|
||||
|
||||
func EnableAnsiEscapeSequences() {
|
||||
}
|
||||
15
pkg/platform/terminal_windows.go
Normal file
15
pkg/platform/terminal_windows.go
Normal 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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue