aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/sys/windows/exec_windows.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/sys/windows/exec_windows.go')
-rw-r--r--vendor/golang.org/x/sys/windows/exec_windows.go248
1 files changed, 248 insertions, 0 deletions
diff --git a/vendor/golang.org/x/sys/windows/exec_windows.go b/vendor/golang.org/x/sys/windows/exec_windows.go
new file mode 100644
index 0000000..9cabbb6
--- /dev/null
+++ b/vendor/golang.org/x/sys/windows/exec_windows.go
@@ -0,0 +1,248 @@
1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Fork, exec, wait, etc.
6
7package windows
8
9import (
10 errorspkg "errors"
11 "unsafe"
12)
13
14// EscapeArg rewrites command line argument s as prescribed
15// in http://msdn.microsoft.com/en-us/library/ms880421.
16// This function returns "" (2 double quotes) if s is empty.
17// Alternatively, these transformations are done:
18// - every back slash (\) is doubled, but only if immediately
19// followed by double quote (");
20// - every double quote (") is escaped by back slash (\);
21// - finally, s is wrapped with double quotes (arg -> "arg"),
22// but only if there is space or tab inside s.
23func EscapeArg(s string) string {
24 if len(s) == 0 {
25 return `""`
26 }
27 n := len(s)
28 hasSpace := false
29 for i := 0; i < len(s); i++ {
30 switch s[i] {
31 case '"', '\\':
32 n++
33 case ' ', '\t':
34 hasSpace = true
35 }
36 }
37 if hasSpace {
38 n += 2 // Reserve space for quotes.
39 }
40 if n == len(s) {
41 return s
42 }
43
44 qs := make([]byte, n)
45 j := 0
46 if hasSpace {
47 qs[j] = '"'
48 j++
49 }
50 slashes := 0
51 for i := 0; i < len(s); i++ {
52 switch s[i] {
53 default:
54 slashes = 0
55 qs[j] = s[i]
56 case '\\':
57 slashes++
58 qs[j] = s[i]
59 case '"':
60 for ; slashes > 0; slashes-- {
61 qs[j] = '\\'
62 j++
63 }
64 qs[j] = '\\'
65 j++
66 qs[j] = s[i]
67 }
68 j++
69 }
70 if hasSpace {
71 for ; slashes > 0; slashes-- {
72 qs[j] = '\\'
73 j++
74 }
75 qs[j] = '"'
76 j++
77 }
78 return string(qs[:j])
79}
80
81// ComposeCommandLine escapes and joins the given arguments suitable for use as a Windows command line,
82// in CreateProcess's CommandLine argument, CreateService/ChangeServiceConfig's BinaryPathName argument,
83// or any program that uses CommandLineToArgv.
84func ComposeCommandLine(args []string) string {
85 if len(args) == 0 {
86 return ""
87 }
88
89 // Per https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw:
90 // “This function accepts command lines that contain a program name; the
91 // program name can be enclosed in quotation marks or not.”
92 //
93 // Unfortunately, it provides no means of escaping interior quotation marks
94 // within that program name, and we have no way to report them here.
95 prog := args[0]
96 mustQuote := len(prog) == 0
97 for i := 0; i < len(prog); i++ {
98 c := prog[i]
99 if c <= ' ' || (c == '"' && i == 0) {
100 // Force quotes for not only the ASCII space and tab as described in the
101 // MSDN article, but also ASCII control characters.
102 // The documentation for CommandLineToArgvW doesn't say what happens when
103 // the first argument is not a valid program name, but it empirically
104 // seems to drop unquoted control characters.
105 mustQuote = true
106 break
107 }
108 }
109 var commandLine []byte
110 if mustQuote {
111 commandLine = make([]byte, 0, len(prog)+2)
112 commandLine = append(commandLine, '"')
113 for i := 0; i < len(prog); i++ {
114 c := prog[i]
115 if c == '"' {
116 // This quote would interfere with our surrounding quotes.
117 // We have no way to report an error, so just strip out
118 // the offending character instead.
119 continue
120 }
121 commandLine = append(commandLine, c)
122 }
123 commandLine = append(commandLine, '"')
124 } else {
125 if len(args) == 1 {
126 // args[0] is a valid command line representing itself.
127 // No need to allocate a new slice or string for it.
128 return prog
129 }
130 commandLine = []byte(prog)
131 }
132
133 for _, arg := range args[1:] {
134 commandLine = append(commandLine, ' ')
135 // TODO(bcmills): since we're already appending to a slice, it would be nice
136 // to avoid the intermediate allocations of EscapeArg.
137 // Perhaps we can factor out an appendEscapedArg function.
138 commandLine = append(commandLine, EscapeArg(arg)...)
139 }
140 return string(commandLine)
141}
142
143// DecomposeCommandLine breaks apart its argument command line into unescaped parts using CommandLineToArgv,
144// as gathered from GetCommandLine, QUERY_SERVICE_CONFIG's BinaryPathName argument, or elsewhere that
145// command lines are passed around.
146// DecomposeCommandLine returns an error if commandLine contains NUL.
147func DecomposeCommandLine(commandLine string) ([]string, error) {
148 if len(commandLine) == 0 {
149 return []string{}, nil
150 }
151 utf16CommandLine, err := UTF16FromString(commandLine)
152 if err != nil {
153 return nil, errorspkg.New("string with NUL passed to DecomposeCommandLine")
154 }
155 var argc int32
156 argv, err := commandLineToArgv(&utf16CommandLine[0], &argc)
157 if err != nil {
158 return nil, err
159 }
160 defer LocalFree(Handle(unsafe.Pointer(argv)))
161
162 var args []string
163 for _, p := range unsafe.Slice(argv, argc) {
164 args = append(args, UTF16PtrToString(p))
165 }
166 return args, nil
167}
168
169// CommandLineToArgv parses a Unicode command line string and sets
170// argc to the number of parsed arguments.
171//
172// The returned memory should be freed using a single call to LocalFree.
173//
174// Note that although the return type of CommandLineToArgv indicates 8192
175// entries of up to 8192 characters each, the actual count of parsed arguments
176// may exceed 8192, and the documentation for CommandLineToArgvW does not mention
177// any bound on the lengths of the individual argument strings.
178// (See https://go.dev/issue/63236.)
179func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err error) {
180 argp, err := commandLineToArgv(cmd, argc)
181 argv = (*[8192]*[8192]uint16)(unsafe.Pointer(argp))
182 return argv, err
183}
184
185func CloseOnExec(fd Handle) {
186 SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
187}
188
189// FullPath retrieves the full path of the specified file.
190func FullPath(name string) (path string, err error) {
191 p, err := UTF16PtrFromString(name)
192 if err != nil {
193 return "", err
194 }
195 n := uint32(100)
196 for {
197 buf := make([]uint16, n)
198 n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
199 if err != nil {
200 return "", err
201 }
202 if n <= uint32(len(buf)) {
203 return UTF16ToString(buf[:n]), nil
204 }
205 }
206}
207
208// NewProcThreadAttributeList allocates a new ProcThreadAttributeListContainer, with the requested maximum number of attributes.
209func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeListContainer, error) {
210 var size uintptr
211 err := initializeProcThreadAttributeList(nil, maxAttrCount, 0, &size)
212 if err != ERROR_INSUFFICIENT_BUFFER {
213 if err == nil {
214 return nil, errorspkg.New("unable to query buffer size from InitializeProcThreadAttributeList")
215 }
216 return nil, err
217 }
218 alloc, err := LocalAlloc(LMEM_FIXED, uint32(size))
219 if err != nil {
220 return nil, err
221 }
222 // size is guaranteed to be ≥1 by InitializeProcThreadAttributeList.
223 al := &ProcThreadAttributeListContainer{data: (*ProcThreadAttributeList)(unsafe.Pointer(alloc))}
224 err = initializeProcThreadAttributeList(al.data, maxAttrCount, 0, &size)
225 if err != nil {
226 return nil, err
227 }
228 return al, err
229}
230
231// Update modifies the ProcThreadAttributeList using UpdateProcThreadAttribute.
232func (al *ProcThreadAttributeListContainer) Update(attribute uintptr, value unsafe.Pointer, size uintptr) error {
233 al.pointers = append(al.pointers, value)
234 return updateProcThreadAttribute(al.data, 0, attribute, value, size, nil, nil)
235}
236
237// Delete frees ProcThreadAttributeList's resources.
238func (al *ProcThreadAttributeListContainer) Delete() {
239 deleteProcThreadAttributeList(al.data)
240 LocalFree(Handle(unsafe.Pointer(al.data)))
241 al.data = nil
242 al.pointers = nil
243}
244
245// List returns the actual ProcThreadAttributeList to be passed to StartupInfoEx.
246func (al *ProcThreadAttributeListContainer) List() *ProcThreadAttributeList {
247 return al.data
248}