Source file src/cmd/vendor/golang.org/x/sys/windows/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 windows
     8  
     9  import (
    10  	errorspkg "errors"
    11  	"unsafe"
    12  
    13  	"golang.org/x/sys/internal/unsafeheader"
    14  )
    15  
    16  // EscapeArg rewrites command line argument s as prescribed
    17  // in http://msdn.microsoft.com/en-us/library/ms880421.
    18  // This function returns "" (2 double quotes) if s is empty.
    19  // Alternatively, these transformations are done:
    20  // - every back slash (\) is doubled, but only if immediately
    21  //   followed by double quote (");
    22  // - every double quote (") is escaped by back slash (\);
    23  // - finally, s is wrapped with double quotes (arg -> "arg"),
    24  //   but only if there is space or tab inside s.
    25  func EscapeArg(s string) string {
    26  	if len(s) == 0 {
    27  		return "\"\""
    28  	}
    29  	n := len(s)
    30  	hasSpace := false
    31  	for i := 0; i < len(s); i++ {
    32  		switch s[i] {
    33  		case '"', '\\':
    34  			n++
    35  		case ' ', '\t':
    36  			hasSpace = true
    37  		}
    38  	}
    39  	if hasSpace {
    40  		n += 2
    41  	}
    42  	if n == len(s) {
    43  		return s
    44  	}
    45  
    46  	qs := make([]byte, n)
    47  	j := 0
    48  	if hasSpace {
    49  		qs[j] = '"'
    50  		j++
    51  	}
    52  	slashes := 0
    53  	for i := 0; i < len(s); i++ {
    54  		switch s[i] {
    55  		default:
    56  			slashes = 0
    57  			qs[j] = s[i]
    58  		case '\\':
    59  			slashes++
    60  			qs[j] = s[i]
    61  		case '"':
    62  			for ; slashes > 0; slashes-- {
    63  				qs[j] = '\\'
    64  				j++
    65  			}
    66  			qs[j] = '\\'
    67  			j++
    68  			qs[j] = s[i]
    69  		}
    70  		j++
    71  	}
    72  	if hasSpace {
    73  		for ; slashes > 0; slashes-- {
    74  			qs[j] = '\\'
    75  			j++
    76  		}
    77  		qs[j] = '"'
    78  		j++
    79  	}
    80  	return string(qs[:j])
    81  }
    82  
    83  // ComposeCommandLine escapes and joins the given arguments suitable for use as a Windows command line,
    84  // in CreateProcess's CommandLine argument, CreateService/ChangeServiceConfig's BinaryPathName argument,
    85  // or any program that uses CommandLineToArgv.
    86  func ComposeCommandLine(args []string) string {
    87  	var commandLine string
    88  	for i := range args {
    89  		if i > 0 {
    90  			commandLine += " "
    91  		}
    92  		commandLine += EscapeArg(args[i])
    93  	}
    94  	return commandLine
    95  }
    96  
    97  // DecomposeCommandLine breaks apart its argument command line into unescaped parts using CommandLineToArgv,
    98  // as gathered from GetCommandLine, QUERY_SERVICE_CONFIG's BinaryPathName argument, or elsewhere that
    99  // command lines are passed around.
   100  func DecomposeCommandLine(commandLine string) ([]string, error) {
   101  	if len(commandLine) == 0 {
   102  		return []string{}, nil
   103  	}
   104  	var argc int32
   105  	argv, err := CommandLineToArgv(StringToUTF16Ptr(commandLine), &argc)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	defer LocalFree(Handle(unsafe.Pointer(argv)))
   110  	var args []string
   111  	for _, v := range (*argv)[:argc] {
   112  		args = append(args, UTF16ToString((*v)[:]))
   113  	}
   114  	return args, nil
   115  }
   116  
   117  func CloseOnExec(fd Handle) {
   118  	SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
   119  }
   120  
   121  // FullPath retrieves the full path of the specified file.
   122  func FullPath(name string) (path string, err error) {
   123  	p, err := UTF16PtrFromString(name)
   124  	if err != nil {
   125  		return "", err
   126  	}
   127  	n := uint32(100)
   128  	for {
   129  		buf := make([]uint16, n)
   130  		n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
   131  		if err != nil {
   132  			return "", err
   133  		}
   134  		if n <= uint32(len(buf)) {
   135  			return UTF16ToString(buf[:n]), nil
   136  		}
   137  	}
   138  }
   139  
   140  // NewProcThreadAttributeList allocates a new ProcThreadAttributeListContainer, with the requested maximum number of attributes.
   141  func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeListContainer, error) {
   142  	var size uintptr
   143  	err := initializeProcThreadAttributeList(nil, maxAttrCount, 0, &size)
   144  	if err != ERROR_INSUFFICIENT_BUFFER {
   145  		if err == nil {
   146  			return nil, errorspkg.New("unable to query buffer size from InitializeProcThreadAttributeList")
   147  		}
   148  		return nil, err
   149  	}
   150  	// size is guaranteed to be ≥1 by InitializeProcThreadAttributeList.
   151  	al := &ProcThreadAttributeListContainer{data: (*ProcThreadAttributeList)(unsafe.Pointer(&make([]byte, size)[0]))}
   152  	err = initializeProcThreadAttributeList(al.data, maxAttrCount, 0, &size)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	return al, err
   157  }
   158  
   159  // Update modifies the ProcThreadAttributeList using UpdateProcThreadAttribute.
   160  // Note that the value passed to this function will be copied into memory
   161  // allocated by LocalAlloc, the contents of which should not contain any
   162  // Go-managed pointers, even if the passed value itself is a Go-managed
   163  // pointer.
   164  func (al *ProcThreadAttributeListContainer) Update(attribute uintptr, value unsafe.Pointer, size uintptr) error {
   165  	alloc, err := LocalAlloc(LMEM_FIXED, uint32(size))
   166  	if err != nil {
   167  		return err
   168  	}
   169  	var src, dst []byte
   170  	hdr := (*unsafeheader.Slice)(unsafe.Pointer(&src))
   171  	hdr.Data = value
   172  	hdr.Cap = int(size)
   173  	hdr.Len = int(size)
   174  	hdr = (*unsafeheader.Slice)(unsafe.Pointer(&dst))
   175  	hdr.Data = unsafe.Pointer(alloc)
   176  	hdr.Cap = int(size)
   177  	hdr.Len = int(size)
   178  	copy(dst, src)
   179  	al.heapAllocations = append(al.heapAllocations, alloc)
   180  	return updateProcThreadAttribute(al.data, 0, attribute, unsafe.Pointer(alloc), size, nil, nil)
   181  }
   182  
   183  // Delete frees ProcThreadAttributeList's resources.
   184  func (al *ProcThreadAttributeListContainer) Delete() {
   185  	deleteProcThreadAttributeList(al.data)
   186  	for i := range al.heapAllocations {
   187  		LocalFree(Handle(al.heapAllocations[i]))
   188  	}
   189  	al.heapAllocations = nil
   190  }
   191  
   192  // List returns the actual ProcThreadAttributeList to be passed to StartupInfoEx.
   193  func (al *ProcThreadAttributeListContainer) List() *ProcThreadAttributeList {
   194  	return al.data
   195  }
   196  

View as plain text