comparison vendor/golang.org/x/sys/windows/exec_windows.go @ 66:787b5ee0289d draft

Use vendored modules Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
author yakumo.izuru
date Sun, 23 Jul 2023 13:18:53 +0000
parents
children
comparison
equal deleted inserted replaced
65:6d985efa0f7a 66:787b5ee0289d
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
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 var commandLine string
86 for i := range args {
87 if i > 0 {
88 commandLine += " "
89 }
90 commandLine += EscapeArg(args[i])
91 }
92 return commandLine
93 }
94
95 // DecomposeCommandLine breaks apart its argument command line into unescaped parts using CommandLineToArgv,
96 // as gathered from GetCommandLine, QUERY_SERVICE_CONFIG's BinaryPathName argument, or elsewhere that
97 // command lines are passed around.
98 func DecomposeCommandLine(commandLine string) ([]string, error) {
99 if len(commandLine) == 0 {
100 return []string{}, nil
101 }
102 var argc int32
103 argv, err := CommandLineToArgv(StringToUTF16Ptr(commandLine), &argc)
104 if err != nil {
105 return nil, err
106 }
107 defer LocalFree(Handle(unsafe.Pointer(argv)))
108 var args []string
109 for _, v := range (*argv)[:argc] {
110 args = append(args, UTF16ToString((*v)[:]))
111 }
112 return args, nil
113 }
114
115 func CloseOnExec(fd Handle) {
116 SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
117 }
118
119 // FullPath retrieves the full path of the specified file.
120 func FullPath(name string) (path string, err error) {
121 p, err := UTF16PtrFromString(name)
122 if err != nil {
123 return "", err
124 }
125 n := uint32(100)
126 for {
127 buf := make([]uint16, n)
128 n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
129 if err != nil {
130 return "", err
131 }
132 if n <= uint32(len(buf)) {
133 return UTF16ToString(buf[:n]), nil
134 }
135 }
136 }
137
138 // NewProcThreadAttributeList allocates a new ProcThreadAttributeListContainer, with the requested maximum number of attributes.
139 func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeListContainer, error) {
140 var size uintptr
141 err := initializeProcThreadAttributeList(nil, maxAttrCount, 0, &size)
142 if err != ERROR_INSUFFICIENT_BUFFER {
143 if err == nil {
144 return nil, errorspkg.New("unable to query buffer size from InitializeProcThreadAttributeList")
145 }
146 return nil, err
147 }
148 alloc, err := LocalAlloc(LMEM_FIXED, uint32(size))
149 if err != nil {
150 return nil, err
151 }
152 // size is guaranteed to be ≥1 by InitializeProcThreadAttributeList.
153 al := &ProcThreadAttributeListContainer{data: (*ProcThreadAttributeList)(unsafe.Pointer(alloc))}
154 err = initializeProcThreadAttributeList(al.data, maxAttrCount, 0, &size)
155 if err != nil {
156 return nil, err
157 }
158 return al, err
159 }
160
161 // Update modifies the ProcThreadAttributeList using UpdateProcThreadAttribute.
162 func (al *ProcThreadAttributeListContainer) Update(attribute uintptr, value unsafe.Pointer, size uintptr) error {
163 al.pointers = append(al.pointers, value)
164 return updateProcThreadAttribute(al.data, 0, attribute, value, size, nil, nil)
165 }
166
167 // Delete frees ProcThreadAttributeList's resources.
168 func (al *ProcThreadAttributeListContainer) Delete() {
169 deleteProcThreadAttributeList(al.data)
170 LocalFree(Handle(unsafe.Pointer(al.data)))
171 al.data = nil
172 al.pointers = nil
173 }
174
175 // List returns the actual ProcThreadAttributeList to be passed to StartupInfoEx.
176 func (al *ProcThreadAttributeListContainer) List() *ProcThreadAttributeList {
177 return al.data
178 }