-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlexpect.lua
More file actions
233 lines (206 loc) · 4.94 KB
/
lexpect.lua
File metadata and controls
233 lines (206 loc) · 4.94 KB
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
-- a Lua expect-like library
require 'path'
if not path.loadlib('lexpect','spawnx') then
print 'cannot find spawnx in same directory as lexpect.lua'
end
local append = table.insert
local find = string.find
local remove = table.remove
local concat = table.concat
local type = type
local ipairs = ipairs
local unpack = unpack
local io = io
local print = print
local spawn = spawn
local path = path
module 'lexpect'
read = spawn.reads
write = spawn.writes
open = spawn.open
lines = {} -- shd be local --4
function split(s,re,keep_delim)
local i1 = 1
local ls = {}
if not re then re = '%s+' end
while true do
local i2,i3 = s:find(re,i1)
if not i2 then
local last = s:sub(i1)
if last ~= '' then append(ls,last) end
if #ls == 1 and ls[1] == '' then
return {}
else
return ls
end
end
local ie
if keep_delim then ie = i3 else ie = i2-1 end
append(ls,s:sub(i1,ie))
i1 = i3+1
end
end
function readln()
if #lines == 0 then
local chunk = read()
if not chunk then return nil end
--~ print('*',chunk)
lines = split(chunk,'[\r\n]+',true)
end
return remove(lines,1)
end
local function finder(pat)
if type(pat) == 'table' then
return function(s,pat,start,plain)
for _,val in ipairs(pat) do
if find(s,val,start,plain) then return true end
end
end
else
return find
end
end
function expect(pat,lpat)
local plain = not lpat
local find = finder(pat)
repeat
local line = readln()
until find(line,pat,1,plain)
end
function ends_with_linefeed (line)
return line:find('\n$')
end
plain_match = true
function read_upto(pat)
local find = finder(pat)
local line,success
local res = {}
repeat
line = readln()
if not line then return nil end
-- this somewhat complicated condition is to ensure that we get the last instance of
-- the pattern, and only matching on the start of new lines. Sometimes you will
-- get small chunks coming in, and the idea is only to test chunks that follow
-- a chunk that ended with a linefeed.
success = find(line,pat,1,plain_match) and #lines == 0
if success and #res > 0 and not ends_with_linefeed(res[#res]) then
success = false
end
if success then
if include_prompt then append(res,line) end
return res
else
append(res,line)
end
until success
end
function read_response()
return read_upto(prompt)
end
function read_response_string()
return join(read_response())
end
function join(t,delim)
delim = delim or ''
return concat(t,delim)
end
function writeln(s)
write (s..'\n')
end
function command (cmd)
writeln(cmd)
return read_upto (prompt)
end
function command_string(cmd)
return join(command(cmd))
end
function strip_eol(s)
local i = s:find('[\r\n]+')
if i then return s:sub(1,i-1) else return s end
end
function join_args(a,istart)
local argstr = ''
istart = istart or 1
for i,v in ipairs(a) do
if v:find('%s') then
v = '"'..v..'"'
end
if i >= istart then
argstr = argstr..' '..v
end
end
return argstr
end
local queue = {}
local events = {}
local command_handler
function check_line (line)
local cpy = {unpack(events)}
local ignore
for i,callback in ipairs(cpy) do
local discard,please_ignore = callback(line)
if discard then
remove(events,i)
end
ignore = ignore or please_ignore
end
return ignore
end
function set_line_handler(action)
append(events,action)
end
function queue_command(cmd,action)
append(queue,cmd)
if action then set_line_handler(action) end
end
function set_command_handler (handler)
command_handler = handler
end
function split2 (line)
local i1,i2 = line:find('%s+')
if i1 then
return line:sub(1,i1-1), line:sub(i2+1)
else
return line
end
end
function filter (inf,outf)
local cmd,res
-- seems to be necessary on Windows, no harm otherwise.
io.stdout:setvbuf("no")
while true do
local handled
if not include_prompt then outf:write(prompt) end
if #queue > 0 then
cmd = remove(queue,1)
else
cmd = inf:read()
end
if command_handler then
handled = command_handler(split2(cmd))
end
if not handled then
res = command(cmd)
if not res then return end
for _,line in ipairs(res) do
if not check_line(line) then
outf:write(line)
end
end
else
handled = false
end
end
end
function printq(s)
if type(s) == 'table' then s = join(s)
elseif not s then s = '<nada>' end
io.write(s)
end
function echo_to_end ()
local res = read()
while res do
io.write(res)
res = read()
end
end