zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

exec_windows.go (5346B)


      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 }