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  }