go-tg-screenshot-bot

Форк
0
248 строк · 7.3 Кб
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
}
249

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.