diff options
author | Rutger Broekhoff | 2023-12-29 21:31:53 +0100 |
---|---|---|
committer | Rutger Broekhoff | 2023-12-29 21:31:53 +0100 |
commit | 404aeae4545d2426c089a5f8d5e82dae56f5212b (patch) | |
tree | 2d84e00af272b39fc04f3795ae06bc48970e57b5 /vendor/golang.org/x/sys/windows/exec_windows.go | |
parent | 209d8b0187ed025dec9ac149ebcced3462877bff (diff) | |
download | gitolfs3-404aeae4545d2426c089a5f8d5e82dae56f5212b.tar.gz gitolfs3-404aeae4545d2426c089a5f8d5e82dae56f5212b.zip |
Make Nix builds work
Diffstat (limited to 'vendor/golang.org/x/sys/windows/exec_windows.go')
-rw-r--r-- | vendor/golang.org/x/sys/windows/exec_windows.go | 248 |
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 | |||
7 | package windows | ||
8 | |||
9 | import ( | ||
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. | ||
23 | func 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. | ||
84 | func 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. | ||
147 | func 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.) | ||
179 | func 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 | |||
185 | func CloseOnExec(fd Handle) { | ||
186 | SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0) | ||
187 | } | ||
188 | |||
189 | // FullPath retrieves the full path of the specified file. | ||
190 | func 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. | ||
209 | func 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. | ||
232 | func (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. | ||
238 | func (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. | ||
246 | func (al *ProcThreadAttributeListContainer) List() *ProcThreadAttributeList { | ||
247 | return al.data | ||
248 | } | ||