read-rtl.c: split out read_rtx_operand from read_rtx_code
[official-gcc.git] / libgo / go / syscall / exec_windows.go
blob5a01843d2bef08e6cb35dc71918a2c21e8255c3c
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.
5 // Fork, exec, wait, etc.
7 package syscall
9 import (
10 "sync"
11 "unicode/utf16"
12 "unsafe"
15 var ForkLock sync.RWMutex
17 // EscapeArg rewrites command line argument s as prescribed
18 // in http://msdn.microsoft.com/en-us/library/ms880421.
19 // This function returns "" (2 double quotes) if s is empty.
20 // Alternatively, these transformations are done:
21 // - every back slash (\) is doubled, but only if immediately
22 // followed by double quote (");
23 // - every double quote (") is escaped by back slash (\);
24 // - finally, s is wrapped with double quotes (arg -> "arg"),
25 // but only if there is space or tab inside s.
26 func EscapeArg(s string) string {
27 if len(s) == 0 {
28 return "\"\""
30 n := len(s)
31 hasSpace := false
32 for i := 0; i < len(s); i++ {
33 switch s[i] {
34 case '"', '\\':
35 n++
36 case ' ', '\t':
37 hasSpace = true
40 if hasSpace {
41 n += 2
43 if n == len(s) {
44 return s
47 qs := make([]byte, n)
48 j := 0
49 if hasSpace {
50 qs[j] = '"'
51 j++
53 slashes := 0
54 for i := 0; i < len(s); i++ {
55 switch s[i] {
56 default:
57 slashes = 0
58 qs[j] = s[i]
59 case '\\':
60 slashes++
61 qs[j] = s[i]
62 case '"':
63 for ; slashes > 0; slashes-- {
64 qs[j] = '\\'
65 j++
67 qs[j] = '\\'
68 j++
69 qs[j] = s[i]
71 j++
73 if hasSpace {
74 for ; slashes > 0; slashes-- {
75 qs[j] = '\\'
76 j++
78 qs[j] = '"'
79 j++
81 return string(qs[:j])
84 // makeCmdLine builds a command line out of args by escaping "special"
85 // characters and joining the arguments with spaces.
86 func makeCmdLine(args []string) string {
87 var s string
88 for _, v := range args {
89 if s != "" {
90 s += " "
92 s += EscapeArg(v)
94 return s
97 // createEnvBlock converts an array of environment strings into
98 // the representation required by CreateProcess: a sequence of NUL
99 // terminated strings followed by a nil.
100 // Last bytes are two UCS-2 NULs, or four NUL bytes.
101 func createEnvBlock(envv []string) *uint16 {
102 if len(envv) == 0 {
103 return &utf16.Encode([]rune("\x00\x00"))[0]
105 length := 0
106 for _, s := range envv {
107 length += len(s) + 1
109 length += 1
111 b := make([]byte, length)
112 i := 0
113 for _, s := range envv {
114 l := len(s)
115 copy(b[i:i+l], []byte(s))
116 copy(b[i+l:i+l+1], []byte{0})
117 i = i + l + 1
119 copy(b[i:i+1], []byte{0})
121 return &utf16.Encode([]rune(string(b)))[0]
124 func CloseOnExec(fd Handle) {
125 SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
128 func SetNonblock(fd Handle, nonblocking bool) (err error) {
129 return nil
132 // FullPath retrieves the full path of the specified file.
133 func FullPath(name string) (path string, err error) {
134 p, err := UTF16PtrFromString(name)
135 if err != nil {
136 return "", err
138 n := uint32(100)
139 for {
140 buf := make([]uint16, n)
141 n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
142 if err != nil {
143 return "", err
145 if n <= uint32(len(buf)) {
146 return UTF16ToString(buf[:n]), nil
151 func isSlash(c uint8) bool {
152 return c == '\\' || c == '/'
155 func normalizeDir(dir string) (name string, err error) {
156 ndir, err := FullPath(dir)
157 if err != nil {
158 return "", err
160 if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
161 // dir cannot have \\server\share\path form
162 return "", EINVAL
164 return ndir, nil
167 func volToUpper(ch int) int {
168 if 'a' <= ch && ch <= 'z' {
169 ch += 'A' - 'a'
171 return ch
174 func joinExeDirAndFName(dir, p string) (name string, err error) {
175 if len(p) == 0 {
176 return "", EINVAL
178 if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
179 // \\server\share\path form
180 return p, nil
182 if len(p) > 1 && p[1] == ':' {
183 // has drive letter
184 if len(p) == 2 {
185 return "", EINVAL
187 if isSlash(p[2]) {
188 return p, nil
189 } else {
190 d, err := normalizeDir(dir)
191 if err != nil {
192 return "", err
194 if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
195 return FullPath(d + "\\" + p[2:])
196 } else {
197 return FullPath(p)
200 } else {
201 // no drive letter
202 d, err := normalizeDir(dir)
203 if err != nil {
204 return "", err
206 if isSlash(p[0]) {
207 return FullPath(d[:2] + p)
208 } else {
209 return FullPath(d + "\\" + p)
212 // we shouldn't be here
213 return "", EINVAL
216 type ProcAttr struct {
217 Dir string
218 Env []string
219 Files []uintptr
220 Sys *SysProcAttr
223 type SysProcAttr struct {
224 HideWindow bool
225 CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
226 CreationFlags uint32
229 var zeroProcAttr ProcAttr
230 var zeroSysProcAttr SysProcAttr
232 func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
233 if len(argv0) == 0 {
234 return 0, 0, EWINDOWS
236 if attr == nil {
237 attr = &zeroProcAttr
239 sys := attr.Sys
240 if sys == nil {
241 sys = &zeroSysProcAttr
244 if len(attr.Files) > 3 {
245 return 0, 0, EWINDOWS
247 if len(attr.Files) < 3 {
248 return 0, 0, EINVAL
251 if len(attr.Dir) != 0 {
252 // StartProcess assumes that argv0 is relative to attr.Dir,
253 // because it implies Chdir(attr.Dir) before executing argv0.
254 // Windows CreateProcess assumes the opposite: it looks for
255 // argv0 relative to the current directory, and, only once the new
256 // process is started, it does Chdir(attr.Dir). We are adjusting
257 // for that difference here by making argv0 absolute.
258 var err error
259 argv0, err = joinExeDirAndFName(attr.Dir, argv0)
260 if err != nil {
261 return 0, 0, err
264 argv0p, err := UTF16PtrFromString(argv0)
265 if err != nil {
266 return 0, 0, err
269 var cmdline string
270 // Windows CreateProcess takes the command line as a single string:
271 // use attr.CmdLine if set, else build the command line by escaping
272 // and joining each argument with spaces
273 if sys.CmdLine != "" {
274 cmdline = sys.CmdLine
275 } else {
276 cmdline = makeCmdLine(argv)
279 var argvp *uint16
280 if len(cmdline) != 0 {
281 argvp, err = UTF16PtrFromString(cmdline)
282 if err != nil {
283 return 0, 0, err
287 var dirp *uint16
288 if len(attr.Dir) != 0 {
289 dirp, err = UTF16PtrFromString(attr.Dir)
290 if err != nil {
291 return 0, 0, err
295 // Acquire the fork lock so that no other threads
296 // create new fds that are not yet close-on-exec
297 // before we fork.
298 ForkLock.Lock()
299 defer ForkLock.Unlock()
301 p, _ := GetCurrentProcess()
302 fd := make([]Handle, len(attr.Files))
303 for i := range attr.Files {
304 if attr.Files[i] > 0 {
305 err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
306 if err != nil {
307 return 0, 0, err
309 defer CloseHandle(Handle(fd[i]))
312 si := new(StartupInfo)
313 si.Cb = uint32(unsafe.Sizeof(*si))
314 si.Flags = STARTF_USESTDHANDLES
315 if sys.HideWindow {
316 si.Flags |= STARTF_USESHOWWINDOW
317 si.ShowWindow = SW_HIDE
319 si.StdInput = fd[0]
320 si.StdOutput = fd[1]
321 si.StdErr = fd[2]
323 pi := new(ProcessInformation)
325 flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT
326 err = CreateProcess(argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi)
327 if err != nil {
328 return 0, 0, err
330 defer CloseHandle(Handle(pi.Thread))
332 return int(pi.ProcessId), uintptr(pi.Process), nil
335 func Exec(argv0 string, argv []string, envv []string) (err error) {
336 return EWINDOWS