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