OSDN Git Service

libgo: Update to Go 1.0.3.
[pf3gnuchains/gcc-fork.git] / libgo / go / syscall / exec_windows.go
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 syscall
8
9 import (
10         "sync"
11         "unicode/utf16"
12         "unsafe"
13 )
14
15 var ForkLock sync.RWMutex
16
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 "\"\""
29         }
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
38                 }
39         }
40         if hasSpace {
41                 n += 2
42         }
43         if n == len(s) {
44                 return s
45         }
46
47         qs := make([]byte, n)
48         j := 0
49         if hasSpace {
50                 qs[j] = '"'
51                 j++
52         }
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++
66                         }
67                         qs[j] = '\\'
68                         j++
69                         qs[j] = s[i]
70                 }
71                 j++
72         }
73         if hasSpace {
74                 for ; slashes > 0; slashes-- {
75                         qs[j] = '\\'
76                         j++
77                 }
78                 qs[j] = '"'
79                 j++
80         }
81         return string(qs[:j])
82 }
83
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 += " "
91                 }
92                 s += EscapeArg(v)
93         }
94         return s
95 }
96
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]
104         }
105         length := 0
106         for _, s := range envv {
107                 length += len(s) + 1
108         }
109         length += 1
110
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
118         }
119         copy(b[i:i+1], []byte{0})
120
121         return &utf16.Encode([]rune(string(b)))[0]
122 }
123
124 func CloseOnExec(fd Handle) {
125         SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
126 }
127
128 func SetNonblock(fd Handle, nonblocking bool) (err error) {
129         return nil
130 }
131
132 // getFullPath retrieves the full path of the specified file.
133 // Just a wrapper for Windows GetFullPathName api.
134 func getFullPath(name string) (path string, err error) {
135         p, err := utf16PtrFromString(name)
136         if err != nil {
137                 return "", err
138         }
139         buf := make([]uint16, 100)
140         n, err := GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
141         if err != nil {
142                 return "", err
143         }
144         if n > uint32(len(buf)) {
145                 // Windows is asking for bigger buffer.
146                 buf = make([]uint16, n)
147                 n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
148                 if err != nil {
149                         return "", err
150                 }
151                 if n > uint32(len(buf)) {
152                         return "", EINVAL
153                 }
154         }
155         return UTF16ToString(buf[:n]), nil
156 }
157
158 func isSlash(c uint8) bool {
159         return c == '\\' || c == '/'
160 }
161
162 func normalizeDir(dir string) (name string, err error) {
163         ndir, err := getFullPath(dir)
164         if err != nil {
165                 return "", err
166         }
167         if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
168                 // dir cannot have \\server\share\path form
169                 return "", EINVAL
170         }
171         return ndir, nil
172 }
173
174 func volToUpper(ch int) int {
175         if 'a' <= ch && ch <= 'z' {
176                 ch += 'A' - 'a'
177         }
178         return ch
179 }
180
181 func joinExeDirAndFName(dir, p string) (name string, err error) {
182         if len(p) == 0 {
183                 return "", EINVAL
184         }
185         if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
186                 // \\server\share\path form
187                 return p, nil
188         }
189         if len(p) > 1 && p[1] == ':' {
190                 // has drive letter
191                 if len(p) == 2 {
192                         return "", EINVAL
193                 }
194                 if isSlash(p[2]) {
195                         return p, nil
196                 } else {
197                         d, err := normalizeDir(dir)
198                         if err != nil {
199                                 return "", err
200                         }
201                         if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
202                                 return getFullPath(d + "\\" + p[2:])
203                         } else {
204                                 return getFullPath(p)
205                         }
206                 }
207         } else {
208                 // no drive letter
209                 d, err := normalizeDir(dir)
210                 if err != nil {
211                         return "", err
212                 }
213                 if isSlash(p[0]) {
214                         return getFullPath(d[:2] + p)
215                 } else {
216                         return getFullPath(d + "\\" + p)
217                 }
218         }
219         // we shouldn't be here
220         return "", EINVAL
221 }
222
223 type ProcAttr struct {
224         Dir   string
225         Env   []string
226         Files []uintptr
227         Sys   *SysProcAttr
228 }
229
230 type SysProcAttr struct {
231         HideWindow bool
232         CmdLine    string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
233 }
234
235 var zeroProcAttr ProcAttr
236 var zeroSysProcAttr SysProcAttr
237
238 func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
239         if len(argv0) == 0 {
240                 return 0, 0, EWINDOWS
241         }
242         if attr == nil {
243                 attr = &zeroProcAttr
244         }
245         sys := attr.Sys
246         if sys == nil {
247                 sys = &zeroSysProcAttr
248         }
249
250         if len(attr.Files) > 3 {
251                 return 0, 0, EWINDOWS
252         }
253
254         if len(attr.Dir) != 0 {
255                 // StartProcess assumes that argv0 is relative to attr.Dir,
256                 // because it implies Chdir(attr.Dir) before executing argv0.
257                 // Windows CreateProcess assumes the opposite: it looks for
258                 // argv0 relative to the current directory, and, only once the new
259                 // process is started, it does Chdir(attr.Dir). We are adjusting
260                 // for that difference here by making argv0 absolute.
261                 var err error
262                 argv0, err = joinExeDirAndFName(attr.Dir, argv0)
263                 if err != nil {
264                         return 0, 0, err
265                 }
266         }
267         argv0p, err := utf16PtrFromString(argv0)
268         if err != nil {
269                 return 0, 0, err
270         }
271
272         var cmdline string
273         // Windows CreateProcess takes the command line as a single string:
274         // use attr.CmdLine if set, else build the command line by escaping
275         // and joining each argument with spaces
276         if sys.CmdLine != "" {
277                 cmdline = sys.CmdLine
278         } else {
279                 cmdline = makeCmdLine(argv)
280         }
281
282         var argvp *uint16
283         if len(cmdline) != 0 {
284                 argvp, err = utf16PtrFromString(cmdline)
285                 if err != nil {
286                         return 0, 0, err
287                 }
288         }
289
290         var dirp *uint16
291         if len(attr.Dir) != 0 {
292                 dirp, err = utf16PtrFromString(attr.Dir)
293                 if err != nil {
294                         return 0, 0, err
295                 }
296         }
297
298         // Acquire the fork lock so that no other threads
299         // create new fds that are not yet close-on-exec
300         // before we fork.
301         ForkLock.Lock()
302         defer ForkLock.Unlock()
303
304         p, _ := GetCurrentProcess()
305         fd := make([]Handle, len(attr.Files))
306         for i := range attr.Files {
307                 if attr.Files[i] > 0 {
308                         err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
309                         if err != nil {
310                                 return 0, 0, err
311                         }
312                         defer CloseHandle(Handle(fd[i]))
313                 }
314         }
315         si := new(StartupInfo)
316         si.Cb = uint32(unsafe.Sizeof(*si))
317         si.Flags = STARTF_USESTDHANDLES
318         if sys.HideWindow {
319                 si.Flags |= STARTF_USESHOWWINDOW
320                 si.ShowWindow = SW_HIDE
321         }
322         si.StdInput = fd[0]
323         si.StdOutput = fd[1]
324         si.StdErr = fd[2]
325
326         pi := new(ProcessInformation)
327
328         err = CreateProcess(argv0p, argvp, nil, nil, true, CREATE_UNICODE_ENVIRONMENT, createEnvBlock(attr.Env), dirp, si, pi)
329         if err != nil {
330                 return 0, 0, err
331         }
332         defer CloseHandle(Handle(pi.Thread))
333
334         return int(pi.ProcessId), uintptr(pi.Process), nil
335 }
336
337 func Exec(argv0 string, argv []string, envv []string) (err error) {
338         return EWINDOWS
339 }