turbo-editors/turbo-corepublic Fork 0
v1.0.2
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

🛟 Updated. 28d5985 · on v1.0.2 · k33g · 13h ago
windows.go · 133 lines · 4.4 KBGo Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package terminal

// The pure half of the Windows support: the shapes Windows wants things in,
// built here with no Windows API so that they can be tested on any platform.
// pty_windows.go hands them to the API.

import (
	"strings"
	"unicode/utf16"
)

// windowsFallbackShell is what runs when COMSPEC is unset, which it never is
// on a working Windows: cmd.exe is found through PATH.
const windowsFallbackShell = "cmd.exe"

// windowsShell returns the shell Windows says is the user's — the value of
// COMSPEC, which is cmd.exe on every Windows since NT — or the fallback when
// the variable is empty.
func windowsShell(comspec string) string {
	if comspec != "" {
		return comspec
	}
	return windowsFallbackShell
}

// environmentBlock encodes an environment the way CreateProcess wants it: each
// entry as UTF-16, ended by a NUL, the whole block ended by a second NUL.
//
// An empty environment is two NULs, which CreateProcess reads as "no
// variables" rather than as "inherit mine" — the latter is a nil block, which
// this never returns, because the caller has always set TERM.
func environmentBlock(env []string) []uint16 {
	if len(env) == 0 {
		return []uint16{0, 0}
	}
	block := make([]uint16, 0, 64*len(env)+2)
	for _, entry := range env {
		block = append(block, utf16.Encode([]rune(entry))...)
		block = append(block, 0)
	}
	return append(block, 0)
}

// windowsCommandLine composes the single command-line string Windows starts a
// process from, since Windows has no argv: the program receives one string and
// splits it by its own rules.
//
// cmd.exe's rules are its own and are not the C runtime's. Given `/S /C`, it
// strips the first and last quote of what follows and hands the rest to its
// parser verbatim, so the command is wrapped in exactly one pair of quotes and
// nothing inside it is escaped — a `"` inside the command has to reach cmd.exe
// as a `"`, and the C-runtime spelling `\"` would reach it as a backslash and
// a quote. Every other program gets the C-runtime quoting that Go's own exec
// uses, which is what a Go, Node or Python program on the other end expects.
func windowsCommandLine(program string, args []string) string {
	if isCmdExe(program) && len(args) >= 1 && strings.EqualFold(args[0], "/S") {
		return cmdExeCommandLine(program, args)
	}

	parts := make([]string, 0, 1+len(args))
	parts = append(parts, escapeArgument(program))
	for _, arg := range args {
		parts = append(parts, escapeArgument(arg))
	}
	return strings.Join(parts, " ")
}

// isCmdExe reports whether a program path names cmd.exe, whatever its case
// and wherever it is.
func isCmdExe(program string) bool {
	name := program
	if at := strings.LastIndexAny(name, `\/`); at >= 0 {
		name = name[at+1:]
	}
	return strings.EqualFold(name, "cmd.exe") || strings.EqualFold(name, "cmd")
}

// cmdExeCommandLine writes `"cmd.exe" /S /C "command"`: the switches verbatim,
// and the command — the last argument — inside one pair of quotes that /S
// tells cmd.exe to strip.
func cmdExeCommandLine(program string, args []string) string {
	switches := args[:len(args)-1]
	command := args[len(args)-1]

	parts := []string{`"` + program + `"`}
	parts = append(parts, switches...)
	parts = append(parts, `"`+command+`"`)
	return strings.Join(parts, " ")
}

// escapeArgument quotes one argument the way the C runtime's argv parser
// undoes it: quotes when it holds a space, a tab or a quote, `\"` for a quote,
// and doubled backslashes only where they precede a quote or end the argument.
// It is the rule syscall.EscapeArg follows, written here because that function
// exists only on Windows and this one has to be testable everywhere.
func escapeArgument(arg string) string {
	if arg == "" {
		return `""`
	}
	if !strings.ContainsAny(arg, " \t\"") {
		return arg
	}

	var b strings.Builder
	b.WriteByte('"')
	backslashes := 0
	for i := 0; i < len(arg); i++ {
		switch arg[i] {
		case '\\':
			backslashes++
			continue
		case '"':
			b.WriteString(strings.Repeat(`\`, 2*backslashes+1))
			b.WriteByte('"')
		default:
			b.WriteString(strings.Repeat(`\`, backslashes))
			b.WriteByte(arg[i])
		}
		backslashes = 0
	}
	b.WriteString(strings.Repeat(`\`, 2*backslashes))
	b.WriteByte('"')
	return b.String()
}

// programName returns the last element of a Windows or Unix path, which is
// what a window title shows for the shell.
func programName(program string) string {
	if at := strings.LastIndexAny(program, `\/`); at >= 0 {
		return program[at+1:]
	}
	return program
}