golang.org/x/sys@v0.9.0/windows/exec_windows.go (about)

     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  // DecomposeCommandLine returns error if commandLine contains NUL.
    99  func DecomposeCommandLine(commandLine string) ([]string, error) {
   100  	if len(commandLine) == 0 {
   101  		return []string{}, nil
   102  	}
   103  	utf16CommandLine, err := UTF16FromString(commandLine)
   104  	if err != nil {
   105  		return nil, errorspkg.New("string with NUL passed to DecomposeCommandLine")
   106  	}
   107  	var argc int32
   108  	argv, err := CommandLineToArgv(&utf16CommandLine[0], &argc)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	defer LocalFree(Handle(unsafe.Pointer(argv)))
   113  	var args []string
   114  	for _, v := range (*argv)[:argc] {
   115  		args = append(args, UTF16ToString((*v)[:]))
   116  	}
   117  	return args, nil
   118  }
   119  
   120  func CloseOnExec(fd Handle) {
   121  	SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
   122  }
   123  
   124  // FullPath retrieves the full path of the specified file.
   125  func FullPath(name string) (path string, err error) {
   126  	p, err := UTF16PtrFromString(name)
   127  	if err != nil {
   128  		return "", err
   129  	}
   130  	n := uint32(100)
   131  	for {
   132  		buf := make([]uint16, n)
   133  		n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
   134  		if err != nil {
   135  			return "", err
   136  		}
   137  		if n <= uint32(len(buf)) {
   138  			return UTF16ToString(buf[:n]), nil
   139  		}
   140  	}
   141  }
   142  
   143  // NewProcThreadAttributeList allocates a new ProcThreadAttributeListContainer, with the requested maximum number of attributes.
   144  func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeListContainer, error) {
   145  	var size uintptr
   146  	err := initializeProcThreadAttributeList(nil, maxAttrCount, 0, &size)
   147  	if err != ERROR_INSUFFICIENT_BUFFER {
   148  		if err == nil {
   149  			return nil, errorspkg.New("unable to query buffer size from InitializeProcThreadAttributeList")
   150  		}
   151  		return nil, err
   152  	}
   153  	alloc, err := LocalAlloc(LMEM_FIXED, uint32(size))
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	// size is guaranteed to be ≥1 by InitializeProcThreadAttributeList.
   158  	al := &ProcThreadAttributeListContainer{data: (*ProcThreadAttributeList)(unsafe.Pointer(alloc))}
   159  	err = initializeProcThreadAttributeList(al.data, maxAttrCount, 0, &size)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	return al, err
   164  }
   165  
   166  // Update modifies the ProcThreadAttributeList using UpdateProcThreadAttribute.
   167  func (al *ProcThreadAttributeListContainer) Update(attribute uintptr, value unsafe.Pointer, size uintptr) error {
   168  	al.pointers = append(al.pointers, value)
   169  	return updateProcThreadAttribute(al.data, 0, attribute, value, size, nil, nil)
   170  }
   171  
   172  // Delete frees ProcThreadAttributeList's resources.
   173  func (al *ProcThreadAttributeListContainer) Delete() {
   174  	deleteProcThreadAttributeList(al.data)
   175  	LocalFree(Handle(unsafe.Pointer(al.data)))
   176  	al.data = nil
   177  	al.pointers = nil
   178  }
   179  
   180  // List returns the actual ProcThreadAttributeList to be passed to StartupInfoEx.
   181  func (al *ProcThreadAttributeListContainer) List() *ProcThreadAttributeList {
   182  	return al.data
   183  }