gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

exec_windows.go (4944B)


      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 }