-
Notifications
You must be signed in to change notification settings - Fork 222
/
Copy pathgetargs_windows.go
154 lines (130 loc) · 3.98 KB
/
getargs_windows.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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
}