github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/subprocess_windows.go (about) 1 // Copyright 2012 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package nin 16 17 import ( 18 "context" 19 "os/exec" 20 "strings" 21 "syscall" 22 ) 23 24 func createCmd(ctx context.Context, c string, useConsole, enableSkipShell bool) *exec.Cmd { 25 // The commands being run use shell redirection. The C++ version uses 26 // system() which always uses the default shell. 27 // 28 // Determine if we use the experimental shell skipping fast track mode, 29 // saving an unnecessary exec(). Only use this when we detect no quote, no 30 // shell redirection character. 31 // TODO(maruel): This is incorrect and temporary. 32 skipShell := true || (enableSkipShell && !strings.ContainsAny(c, "%><&|^")) 33 34 ex := "" 35 var args []string 36 if skipShell { 37 // Ignore the parsed arguments on Windows and feedback the original string. 38 // 39 // TODO(maruel): Handle quoted space. It's only necessary from the 40 // perspective of finding the primary executable to run. 41 i := strings.IndexByte(c, ' ') 42 if i == -1 { 43 // A single executable with no argument. 44 ex = c 45 } else { 46 ex = c[:i] 47 } 48 args = []string{c} 49 } else { 50 ex = "cmd.exe" 51 args = []string{"/c", c} 52 } 53 var cmd *exec.Cmd 54 if useConsole { 55 cmd = exec.Command(ex, args...) 56 } else { 57 cmd = exec.CommandContext(ctx, ex, args...) 58 } 59 60 // Ignore the parsed arguments on Windows and feed back the original string. 61 // See https://pkg.go.dev/os/exec#Command for an explanation. 62 if skipShell { 63 cmd.SysProcAttr = &syscall.SysProcAttr{ 64 CmdLine: c, 65 } 66 cmd.Args = nil 67 } 68 if useConsole { 69 cmd.SysProcAttr.CreationFlags = syscall.CREATE_NEW_PROCESS_GROUP 70 } 71 72 // TODO(maruel): CTRL_C_EVENT and CTRL_BREAK_EVENT handling with 73 // GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT) when canceling plus 74 // PostQueuedCompletionStatus(CreateIoCompletionPort()) via SetConsoleCtrlHandler(fn, FALSE). 75 return cmd 76 }