-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathwinpty.go
executable file
·158 lines (125 loc) · 3.74 KB
/
winpty.go
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package winpty
import (
"fmt"
"os"
"syscall"
"unsafe"
)
type Options struct {
// DLLPrefix is the path to winpty.dll and winpty-agent.exe
DLLPrefix string
// AppName sets the title of the console
AppName string
// Command is the full command to launch
Command string
// Dir sets the current working directory for the command
Dir string
// Env sets the environment variables. Use the format VAR=VAL.
Env []string
// Flags to pass to agent config creation
Flags uint32
// Initial size for Columns and Rows
InitialCols uint32
InitialRows uint32
}
type WinPTY struct {
StdIn *os.File
StdOut *os.File
wp uintptr
childHandle uintptr
closed bool
}
// accepts path to command to execute, then arguments.
// returns WinPTY object pointer, error.
// remember to call Close on WinPTY object when done.
func Open(dllPrefix, cmd string) (*WinPTY, error) {
return OpenWithOptions(Options{
DLLPrefix: dllPrefix,
Command: cmd,
})
}
// the same as open, but uses defaults for Env & Dir
func OpenDefault(dllPrefix, cmd string) (*WinPTY, error) {
wd, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("Failed to get dir on setup: %s", err)
}
return OpenWithOptions(Options{
DLLPrefix: dllPrefix,
Command: cmd,
Dir: wd,
Env: os.Environ(),
})
}
func OpenWithOptions(options Options) (*WinPTY, error) {
setupDefines(options.DLLPrefix)
// create config with specified Flags
agentCfg, err := createAgentCfg(options.Flags)
if err != nil {
return nil, err
}
// Set the initial size to 40x40 if options is 0
if options.InitialCols <= 0 {
options.InitialCols = 40
}
if options.InitialRows <= 0 {
options.InitialRows = 40
}
winpty_config_set_initial_size.Call(agentCfg, uintptr(options.InitialCols), uintptr(options.InitialRows))
var openErr uintptr
defer winpty_error_free.Call(openErr)
wp, _, _ := winpty_open.Call(agentCfg, uintptr(unsafe.Pointer(openErr)))
if wp == uintptr(0) {
return nil, fmt.Errorf("Error Launching WinPTY agent, %s", GetErrorMessage(openErr))
}
winpty_config_free.Call(agentCfg)
stdin_name, _, _ := winpty_conin_name.Call(wp)
stdout_name, _, _ := winpty_conout_name.Call(wp)
obj := &WinPTY{}
stdin_handle, err := syscall.CreateFile((*uint16)(unsafe.Pointer(stdin_name)), syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, 0, 0)
if err != nil {
return nil, fmt.Errorf("Error getting stdin handle. %s", err)
}
obj.StdIn = os.NewFile(uintptr(stdin_handle), "stdin")
stdout_handle, err := syscall.CreateFile((*uint16)(unsafe.Pointer(stdout_name)), syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING, 0, 0)
if err != nil {
return nil, fmt.Errorf("Error getting stdout handle. %s", err)
}
obj.StdOut = os.NewFile(uintptr(stdout_handle), "stdout")
spawnCfg, err := createSpawnCfg(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, options.AppName, options.Command, options.Dir, options.Env)
if err != nil {
return nil, err
}
var (
spawnErr uintptr
lastError *uint32
)
spawnRet, _, _ := winpty_spawn.Call(wp, spawnCfg, uintptr(unsafe.Pointer(&obj.childHandle)), uintptr(0), uintptr(unsafe.Pointer(lastError)), uintptr(unsafe.Pointer(spawnErr)))
winpty_spawn_config_free.Call(spawnCfg)
defer winpty_error_free.Call(spawnErr)
if spawnRet == 0 {
return nil, fmt.Errorf("Error spawning process...")
} else {
obj.wp = wp
return obj, nil
}
}
func (obj *WinPTY) SetSize(ws_col, ws_row uint32) {
if ws_col == 0 || ws_row == 0 {
return
}
winpty_set_size.Call(obj.wp, uintptr(ws_col), uintptr(ws_row), uintptr(0))
}
func (obj *WinPTY) Close() {
if obj.closed {
return
}
winpty_free.Call(obj.wp)
obj.StdIn.Close()
obj.StdOut.Close()
syscall.CloseHandle(syscall.Handle(obj.childHandle))
obj.closed = true
}
func (obj *WinPTY) GetProcHandle() uintptr {
return obj.childHandle
}