line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# $File: //depot/libOurNet/BBSAgent/BBSAgent.pm $ $Author: autrijus $ |
2
|
|
|
|
|
|
|
# $Revision: #3 $ $Change: 6077 $ $DateTime: 2003/05/25 10:48:47 $ |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package OurNet::BBSAgent; |
5
|
1
|
|
|
1
|
|
15346
|
use 5.005; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
56
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
$OurNet::BBSAgent::VERSION = '1.61'; |
8
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
34
|
|
10
|
1
|
|
|
1
|
|
5
|
use vars qw/$AUTOLOAD/; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
69
|
|
11
|
1
|
|
|
|
|
7
|
use fields qw/bbsname bbsaddr bbsport bbsfile lastmatch loadstack |
12
|
1
|
|
|
1
|
|
763
|
debug timeout state proc var netobj hook loop errmsg/; |
|
1
|
|
|
|
|
1401
|
|
13
|
1
|
|
|
1
|
|
110
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
78
|
|
14
|
1
|
|
|
1
|
|
1474
|
use Net::Telnet; |
|
1
|
|
|
|
|
47135
|
|
|
1
|
|
|
|
|
123
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
OurNet::BBSAgent - Scriptable telnet-based virtual users |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 SYNOPSIS |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
#!/usr/local/bin/perl |
23
|
|
|
|
|
|
|
# To run it, make sure you have a 'elixus.bbs' file in the same |
24
|
|
|
|
|
|
|
# directory. The actual content is listed just below this section. |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
use strict; |
27
|
|
|
|
|
|
|
use OurNet::BBSAgent; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my $remote = 'elixus.bbs'; # template name |
30
|
|
|
|
|
|
|
my $timeout = undef; # no timeout |
31
|
|
|
|
|
|
|
my $logfile = 'elixus.log'; # log file |
32
|
|
|
|
|
|
|
my $bbs = OurNet::BBSAgent->new($remote, $timeout, $logfile); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
my ($user, $pass) = @ARGV; |
35
|
|
|
|
|
|
|
$user = 'guest' unless defined($user); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
$bbs->{debug} = 1; # debugging flag |
38
|
|
|
|
|
|
|
$bbs->login($user, $pass); # username and password |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# callback($bbs->message) while 1; # procedural interface |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
$bbs->Hook('message', \&callback); # callback-based interface |
43
|
|
|
|
|
|
|
$bbs->Loop(undef, 10); # loop indefinitely, send Ctrl-L |
44
|
|
|
|
|
|
|
# every 10 seconds (anti-idle) |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub callback { |
47
|
|
|
|
|
|
|
my ($caller, $message) = @_; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
print "Received: $message\n"; |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
($bbs->logoff, exit) if ($message eq '!quit'); |
52
|
|
|
|
|
|
|
$bbs->message_reply("$caller: $message"); |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head1 DESCRIPTION |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
OurNet::BBSAgent provides an object-oriented interface to TCP/IP |
58
|
|
|
|
|
|
|
based interactive services, by simulating as a I |
59
|
|
|
|
|
|
|
with action defined by a script language. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
The developer could then use the same methods to access different |
62
|
|
|
|
|
|
|
services, to easily implement interactive robots, spiders, or other |
63
|
|
|
|
|
|
|
cross-service agents. |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
The scripting language of B features both |
66
|
|
|
|
|
|
|
flow-control and event-driven capabilities, makes it especially |
67
|
|
|
|
|
|
|
well-suited for dealing with automation tasks involved with |
68
|
|
|
|
|
|
|
Telnet-based BBS systems. |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
This module is the foundation of the B back-end described |
71
|
|
|
|
|
|
|
in L. Please consult its man page for more information. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head2 Site Description File |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
This module has its own scripting language, which looks like this in |
76
|
|
|
|
|
|
|
a site description file: |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
Elixus BBS |
79
|
|
|
|
|
|
|
elixus.org:23 |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=login |
82
|
|
|
|
|
|
|
wait \e[7m |
83
|
|
|
|
|
|
|
send $[username]\n |
84
|
|
|
|
|
|
|
doif $[password] |
85
|
|
|
|
|
|
|
wait \e[7m |
86
|
|
|
|
|
|
|
send $[password]\nn\n |
87
|
|
|
|
|
|
|
endo |
88
|
|
|
|
|
|
|
# login failure, unsaved article, kick multi-logins |
89
|
|
|
|
|
|
|
send \n\n\n |
90
|
|
|
|
|
|
|
# skips splash screens (if any) |
91
|
|
|
|
|
|
|
send \x20\x20\x20 |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=main |
94
|
|
|
|
|
|
|
send qqqqqqee |
95
|
|
|
|
|
|
|
wait \e[;H\e[2J\e[1;44;37m |
96
|
|
|
|
|
|
|
till ]\e[31m |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=logoff |
99
|
|
|
|
|
|
|
call main |
100
|
|
|
|
|
|
|
send g\ng\ny\ny\n\n\n |
101
|
|
|
|
|
|
|
exit |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=message |
104
|
|
|
|
|
|
|
wait \e[1;33;46m |
105
|
|
|
|
|
|
|
wait m/../ |
106
|
|
|
|
|
|
|
till \x20\e[37;45m\x20 |
107
|
|
|
|
|
|
|
till \x20\e[m |
108
|
|
|
|
|
|
|
exit |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=message_reply |
111
|
|
|
|
|
|
|
send \x12 |
112
|
|
|
|
|
|
|
wait \e[m |
113
|
|
|
|
|
|
|
wait \e[23;1H |
114
|
|
|
|
|
|
|
send $[message]\n |
115
|
|
|
|
|
|
|
wait [Y] |
116
|
|
|
|
|
|
|
send \n |
117
|
|
|
|
|
|
|
wait \e[37;45m |
118
|
|
|
|
|
|
|
wait \e[m |
119
|
|
|
|
|
|
|
exit |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
The first two lines describe the service's title, its IP address and |
122
|
|
|
|
|
|
|
port number. Any number of I then begins with a C<=> sign |
123
|
|
|
|
|
|
|
(e.g. =B), which could be called as |
124
|
|
|
|
|
|
|
C<$object>-EC([I]) in the program. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head2 Directives |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
All procedures are consisted of following directives: |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=over 4 |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=item load I |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
This directive must be used before any procedures. It loads another |
135
|
|
|
|
|
|
|
BBS definition file under the same directory (or current directory). |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
If the I has an extension other than C<.bbs> (eg. C<.board>, |
138
|
|
|
|
|
|
|
C<.session>), BBSAgent will try to locate additional modules by |
139
|
|
|
|
|
|
|
expanding C<.> into C>, and look for the required module with an |
140
|
|
|
|
|
|
|
C<.inc> extension. For example, B C will look for |
141
|
|
|
|
|
|
|
C in the same directory. |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=item wait I |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=item till I |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=item or I |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
Tells the agent to wait until STRING is sent by remote host. May time |
150
|
|
|
|
|
|
|
out after C<$self>-EC<{timeout}> seconds. Each trailing B directives |
151
|
|
|
|
|
|
|
specifies an alternative string to match. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
If STRING matches the regex C, it will be treated as a regular |
154
|
|
|
|
|
|
|
expression. Capturing parentheses are silently ignored. |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
The B directive is functionally equivalent to B, except that |
157
|
|
|
|
|
|
|
it will puts anything between the last B or B and STRING |
158
|
|
|
|
|
|
|
into the return list. |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=item send I |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
Sends STRING to remote host. |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=item doif I |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=item elif I |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=item else |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=item endo |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
The usual flow control directives. Nested B...Bs are supported. |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=item goto I |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=item call I |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
Executes another procedure in the site description file. A B never |
179
|
|
|
|
|
|
|
returns, while a B always does. Also, a B will not occur if |
180
|
|
|
|
|
|
|
the destination was the last executed procedure, which does not end with |
181
|
|
|
|
|
|
|
B. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=item exit |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Marks the termination of a procedure; also denotes that this procedure is |
186
|
|
|
|
|
|
|
not a I - that is, multiple Bs to it will all be executed. |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=item setv I I |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
Sets a global, non-overridable variable (see below). |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=item idle I |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
Sleep that much seconds. |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=back |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=head2 Variable Handling |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
Whenever a variable in the form of $[name] is encountered as part |
201
|
|
|
|
|
|
|
of a directive, it will be looked up in the global B hash |
202
|
|
|
|
|
|
|
B<$self-E{var}> first, then at the procedure-scoped variable hash, |
203
|
|
|
|
|
|
|
then finally Bed from the argument list if none are found. |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
For example: |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
setv foo World! |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=login |
210
|
|
|
|
|
|
|
send $[bar] # sends the first argument |
211
|
|
|
|
|
|
|
send $[foo] # sends 'World!' |
212
|
|
|
|
|
|
|
send $[baz] # sends the second argument |
213
|
|
|
|
|
|
|
send $[bar] # sends the first argument again |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
A notable exception are digits-only subscripts (e.g. B<$[1]>), which |
216
|
|
|
|
|
|
|
contains the matched string in the previous B or B directive. |
217
|
|
|
|
|
|
|
If there are multiple strings via B directives, the subscript correspond |
218
|
|
|
|
|
|
|
to the matched alternative. |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
For example: |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=match |
223
|
|
|
|
|
|
|
wait foo |
224
|
|
|
|
|
|
|
or m/baz+/ |
225
|
|
|
|
|
|
|
doif $[1] # 'foo' matched |
226
|
|
|
|
|
|
|
send $[1] # sends 'foo' |
227
|
|
|
|
|
|
|
else |
228
|
|
|
|
|
|
|
send $[2] # sends 'bazzzzz...' |
229
|
|
|
|
|
|
|
endo |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=head2 Event Hooks |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
In addition to call the procedures one-by-one, you can B those |
234
|
|
|
|
|
|
|
that begins with B (optionally preceded by B) so whenever |
235
|
|
|
|
|
|
|
the strings they expected are received, the responsible procedure is |
236
|
|
|
|
|
|
|
immediately called. You may also supply a call-back function to handle |
237
|
|
|
|
|
|
|
its results. |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
For example, the code in L above I a callback function |
240
|
|
|
|
|
|
|
to procedure B, then enters a event loop by calling B, |
241
|
|
|
|
|
|
|
which goes on forever until the agent receives C via the C |
242
|
|
|
|
|
|
|
procedure. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
The internal hook table could be accessed by C<$obj>-EC<{hook}>. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head1 METHODS |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Following methods are offered by B: |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=head2 new($class, $bbsfile, [$timeout], [$logfile]) |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
Constructor class method. Takes the BBS description file's name and |
253
|
|
|
|
|
|
|
two optional arguments, and returns a B object. |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
If no files are found at C<$bbsfile>, the method will try to locate |
256
|
|
|
|
|
|
|
it on the B sub-directory of each @INC entries. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=cut |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub new { |
261
|
27
|
|
|
27
|
1
|
172930204
|
my $class = shift; |
262
|
|
|
|
|
|
|
my OurNet::BBSAgent $self = ($] > 5.00562) |
263
|
|
|
|
|
|
|
? fields::new($class) |
264
|
1
|
50
|
|
1
|
|
8
|
: do { no strict 'refs'; bless [\%{"$class\::FIELDS"}], $class }; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3874
|
|
|
27
|
|
|
|
|
8624
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
265
|
|
|
|
|
|
|
|
266
|
27
|
50
|
|
|
|
8287
|
$self->{bbsfile} = shift |
267
|
|
|
|
|
|
|
or croak('You need to specify the bbs definition file'); |
268
|
|
|
|
|
|
|
|
269
|
27
|
|
|
|
|
77
|
$self->{timeout} = shift; |
270
|
|
|
|
|
|
|
|
271
|
27
|
50
|
|
|
|
179
|
croak("Cannot find bbs definition file: $self->{bbsfile}") |
272
|
|
|
|
|
|
|
unless -f ($self->{bbsfile} = _locate($self->{bbsfile})); |
273
|
|
|
|
|
|
|
|
274
|
27
|
|
|
|
|
1985
|
open(local *_FILE, $self->{bbsfile}); |
275
|
|
|
|
|
|
|
|
276
|
27
|
|
|
|
|
137
|
$self->{bbsname} = _readline(\*_FILE); |
277
|
27
|
|
|
|
|
71
|
$self->{bbsaddr} = _readline(\*_FILE); |
278
|
|
|
|
|
|
|
|
279
|
27
|
50
|
|
|
|
337
|
if ($self->{bbsaddr} =~ /^(.*?)(:\d+)?\r?$/) { |
280
|
27
|
|
|
|
|
137
|
$self->{bbsaddr} = $1; |
281
|
27
|
100
|
|
|
|
136
|
$self->{bbsport} = $2 ? substr($2, 1) : 23; |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
else { |
284
|
0
|
|
|
|
|
0
|
croak("Malformed location line: $self->{bbsaddr}"); |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
27
|
|
|
|
|
453
|
close *_FILE; |
288
|
|
|
|
|
|
|
|
289
|
27
|
|
|
|
|
122
|
local $^W; # work around 'numeric' Net::Telnet 3.12 bug |
290
|
|
|
|
|
|
|
|
291
|
27
|
|
|
|
|
138
|
$self->loadfile($self->{bbsfile}); |
292
|
|
|
|
|
|
|
|
293
|
27
|
|
|
|
|
360
|
$self->{netobj} = Net::Telnet->new( |
294
|
|
|
|
|
|
|
Timeout => $self->{timeout}, |
295
|
|
|
|
|
|
|
); |
296
|
|
|
|
|
|
|
|
297
|
27
|
|
|
|
|
9475
|
$self->{netobj}->open( |
298
|
|
|
|
|
|
|
Host => $self->{bbsaddr}, |
299
|
|
|
|
|
|
|
Port => $self->{bbsport}, |
300
|
|
|
|
|
|
|
); |
301
|
|
|
|
|
|
|
|
302
|
6
|
|
|
|
|
1606895
|
$self->{netobj}->output_record_separator(''); |
303
|
6
|
50
|
|
|
|
114
|
$self->{netobj}->input_log($_[0]) if $_[0]; |
304
|
6
|
|
|
|
|
24
|
$self->{state} = ''; |
305
|
|
|
|
|
|
|
|
306
|
6
|
|
|
|
|
77
|
return $self; |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
sub _locate { |
310
|
27
|
|
|
27
|
|
61
|
my $file = shift; |
311
|
27
|
|
|
|
|
67
|
my $pkg = __PACKAGE__; $pkg =~ s|::|/|g; |
|
27
|
|
|
|
|
170
|
|
312
|
|
|
|
|
|
|
|
313
|
27
|
50
|
|
|
|
3986
|
return $file if -f $file; |
314
|
|
|
|
|
|
|
|
315
|
0
|
|
|
|
|
0
|
foreach my $path (map { $_, "$_/$pkg" } ('.', @INC)) { |
|
0
|
|
|
|
|
0
|
|
316
|
0
|
0
|
|
|
|
0
|
return "$path/$file" if -f "$path/$file"; |
317
|
0
|
0
|
|
|
|
0
|
return "$path/$file.bbs" if -f "$path/$file.bbs"; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
sub _plain { |
322
|
0
|
|
|
0
|
|
0
|
my $str = $_[0]; |
323
|
|
|
|
|
|
|
|
324
|
0
|
|
|
|
|
0
|
$str =~ s/([\x00-\x20])/sprintf('\x%02x', ord($1))/eg; |
|
0
|
|
|
|
|
0
|
|
325
|
|
|
|
|
|
|
|
326
|
0
|
|
|
|
|
0
|
return $str; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=head2 loadfile($self, $bbsfile, [$path]) |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Reads in a BBS description file, parse its contents, and return |
332
|
|
|
|
|
|
|
the object itself. The optional C<$path> argument may be used |
333
|
|
|
|
|
|
|
to specify a root directory where files included by the B |
334
|
|
|
|
|
|
|
directive should be found. |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=cut |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
sub _readline { |
339
|
3826
|
|
|
3826
|
|
5753
|
my $fh = shift; my $line; |
|
3826
|
|
|
|
|
5149
|
|
340
|
|
|
|
|
|
|
|
341
|
3826
|
|
|
|
|
4133
|
while ($line = readline(*{$fh})) { |
|
4990
|
|
|
|
|
46134
|
|
342
|
4902
|
100
|
|
|
|
28339
|
last unless $line =~ /^#|^\s*$/; |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
|
345
|
3826
|
100
|
|
|
|
19692
|
$line =~ s/\r?\n?$// if defined($line); |
346
|
|
|
|
|
|
|
|
347
|
3826
|
|
|
|
|
22162
|
return $line; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub loadfile { |
351
|
100
|
|
|
100
|
1
|
229
|
my ($self, $bbsfile, $path) = @_; |
352
|
|
|
|
|
|
|
|
353
|
100
|
100
|
|
|
|
537
|
return if $self->{loadstack}{$bbsfile}++; # prevents recursion |
354
|
|
|
|
|
|
|
|
355
|
88
|
|
|
|
|
183
|
$bbsfile =~ tr|\\|/|; |
356
|
88
|
|
66
|
|
|
390
|
$path ||= substr($bbsfile, 0, rindex($bbsfile, '/') + 1); |
357
|
|
|
|
|
|
|
|
358
|
88
|
50
|
|
|
|
5744
|
open(local *_FILE, $bbsfile) or croak "cannot find file: $bbsfile"; |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
# skips headers |
361
|
88
|
|
|
|
|
282
|
_readline(\*_FILE); |
362
|
88
|
100
|
|
|
|
2134
|
_readline(\*_FILE) if $bbsfile =~ /\.bbs$/i; |
363
|
|
|
|
|
|
|
|
364
|
88
|
|
|
|
|
286
|
while (my $line = _readline(\*_FILE)) { |
365
|
3569
|
|
|
|
|
15110
|
$line =~ s/\s+(?:\#\s+.+)?$//; |
366
|
|
|
|
|
|
|
|
367
|
3569
|
100
|
|
|
|
16127
|
if ($line =~ /^=(\w+)$/) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
368
|
462
|
|
|
|
|
1045
|
$self->{state} = $1; |
369
|
462
|
|
|
|
|
3925
|
$self->{proc}{$1} = []; |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
elsif ( |
372
|
|
|
|
|
|
|
$line =~ /^\s*( |
373
|
|
|
|
|
|
|
idle|load|doif|endo|goto|call|wait|send|else|till|setv|exit |
374
|
|
|
|
|
|
|
)\s*(.*)$/x |
375
|
|
|
|
|
|
|
) { |
376
|
3011
|
100
|
|
|
|
8094
|
if (!$self->{state}) { |
377
|
|
|
|
|
|
|
# directives must belong to procedures... |
378
|
|
|
|
|
|
|
|
379
|
241
|
100
|
|
|
|
837
|
if ($1 eq 'setv') { # ...but 'setv' is an exception. |
|
|
50
|
|
|
|
|
|
380
|
168
|
|
|
|
|
728
|
my ($var, $val) = split(/\s/, $2, 2); |
381
|
|
|
|
|
|
|
|
382
|
168
|
|
|
|
|
286
|
$val =~ s/\x5c\x5c/_!!!_/g; |
383
|
168
|
|
|
|
|
214
|
$val =~ s/\\n/\015\012/g; |
384
|
168
|
|
|
|
|
290
|
$val =~ s/\\e/\e/g; |
385
|
|
|
|
|
|
|
#$val =~ s/\\c./qq("$&")/eeg; |
386
|
168
|
|
|
|
|
294
|
$val =~ s/\\c(.)/"$1" & "\x1F"/eg; |
|
22
|
|
|
|
|
87
|
|
387
|
168
|
|
|
|
|
344
|
$val =~ s/\\x([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg; |
|
122
|
|
|
|
|
430
|
|
388
|
168
|
|
|
|
|
214
|
$val =~ s/_!!!_/\x5c/g; |
389
|
|
|
|
|
|
|
|
390
|
168
|
|
|
|
|
228
|
$val =~ s{\$\[([^\]]+)\]}{ |
391
|
0
|
0
|
|
|
|
0
|
(exists $self->{var}{$1}) |
392
|
|
|
|
|
|
|
? $self->{var}{$1} |
393
|
|
|
|
|
|
|
: croak("variable $1 not defined") |
394
|
|
|
|
|
|
|
}e; |
395
|
|
|
|
|
|
|
|
396
|
168
|
|
|
|
|
619
|
$self->{var}{$var} = $val; |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
elsif ($1 eq 'load') { # ...and 'load' is another exception. |
399
|
73
|
|
|
|
|
192
|
my $file = $2; |
400
|
|
|
|
|
|
|
|
401
|
73
|
50
|
|
|
|
205
|
if ($file !~ /\.bbs$/) { |
402
|
73
|
|
|
|
|
116
|
$file =~ tr|.|/|; |
403
|
73
|
50
|
|
|
|
1371
|
$file = "$path$file.inc" unless -e $file; |
404
|
73
|
|
|
|
|
406
|
$file =~ s|^(\w+)/\1/|$1/|; |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
73
|
50
|
|
|
|
3479
|
croak("cannot read file: $file") unless -e $file; |
408
|
|
|
|
|
|
|
|
409
|
73
|
|
|
|
|
282
|
$self->loadfile($file, $path); |
410
|
|
|
|
|
|
|
|
411
|
73
|
|
|
|
|
177
|
$self->{state} = ''; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
else { |
414
|
0
|
|
|
|
|
0
|
croak("Not in a procedure: $line"); |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
} |
417
|
3011
|
|
100
|
|
|
3285
|
push @{$self->{proc}{$self->{state} || ''}}, $1, $2; |
|
3011
|
|
|
|
|
29829
|
|
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
elsif ($line =~ /^\s*or\s*(.+)$/) { |
420
|
96
|
50
|
|
|
|
238
|
croak('Not in a procedure') unless $self->{state}; |
421
|
96
|
50
|
66
|
|
|
779
|
croak('"or" directive not after a "wait" or "till"') |
422
|
|
|
|
|
|
|
unless $self->{proc}{$self->{state}}->[-2] eq 'wait' |
423
|
|
|
|
|
|
|
or $self->{proc}{$self->{state}}->[-2] eq 'till'; |
424
|
|
|
|
|
|
|
|
425
|
96
|
|
|
|
|
112
|
${$self->{proc}{$self->{state}}}[-1] .= "\n$1"; |
|
96
|
|
|
|
|
532
|
|
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
else { |
428
|
0
|
|
|
|
|
0
|
carp("Error parsing '$line'"); |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
88
|
|
|
|
|
1341
|
return $self; |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=head2 Hook($self, $procedure, [\&callback], [@args]) |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
Adds a procedure to the trigger table, with an optional callback |
439
|
|
|
|
|
|
|
function and parameters on invoking that procedure. |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
If specified, the callback function will be invoked after the |
442
|
|
|
|
|
|
|
hooked procedure's execution, using its return value as arguments. |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=cut |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
sub Hook { |
447
|
0
|
|
|
0
|
1
|
|
my ($self, $sub, $callback) = splice(@_, 0, 3); |
448
|
|
|
|
|
|
|
|
449
|
0
|
0
|
|
|
|
|
if (exists $self->{proc}{$sub}) { |
450
|
0
|
|
|
|
|
|
my ($state, $wait, %var) = ''; |
451
|
0
|
|
|
|
|
|
my @proc = @{$self->{proc}{$sub}}; |
|
0
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
|
453
|
0
|
|
|
|
|
|
($state, $wait) = $self->_chophook(\@proc, \%var, [@_]); |
454
|
|
|
|
|
|
|
|
455
|
0
|
0
|
|
|
|
|
print "Hook $sub: State=$state, Wait=$wait\n" if $self->{debug}; |
456
|
|
|
|
|
|
|
|
457
|
0
|
|
|
|
|
|
$self->{hook}{$state}{$sub} = [$sub, $wait, $callback, @_]; |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
else { |
460
|
0
|
|
|
|
|
|
croak "Hook: Undefined procedure '$sub'"; |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=head2 Unhook($self, $procedure) |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
Unhooks the procedure from event table. Raises an error if the |
467
|
|
|
|
|
|
|
specified procedure did not exist. |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
=cut |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
sub Unhook { |
472
|
0
|
|
|
0
|
1
|
|
my ($self, $sub) = @_; |
473
|
|
|
|
|
|
|
|
474
|
0
|
0
|
|
|
|
|
if (exists $self->{proc}{$sub}) { |
475
|
0
|
|
|
|
|
|
my ($state, %var); |
476
|
0
|
|
|
|
|
|
my @proc = @{$self->{proc}{$sub}}; |
|
0
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
|
478
|
0
|
|
|
|
|
|
$state = $self->_chophook(\@proc, \%var, \@_); |
479
|
|
|
|
|
|
|
|
480
|
0
|
0
|
|
|
|
|
print "Unhook $sub\n" if $self->{debug}; |
481
|
0
|
|
|
|
|
|
delete $self->{hook}{$state}{$sub}; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
else { |
484
|
0
|
|
|
|
|
|
croak "Unhook: undefined procedure '$sub'"; |
485
|
|
|
|
|
|
|
} |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=head2 Loop($self, [$timeout], [$refresh]) |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
Causes a B loop to be executed for C<$timeout> seconds, or |
491
|
|
|
|
|
|
|
indefinitely if not specified. If the C<$refresh> argument is |
492
|
|
|
|
|
|
|
specified, B will send out a Ctrl-L (C<\cL>) upon entering |
493
|
|
|
|
|
|
|
the loop, and then every C<$refresh> seconds during the Loop. |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
=cut |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
sub Loop { |
498
|
0
|
|
|
0
|
1
|
|
my ($self, $timeout, $refresh) = @_; |
499
|
0
|
|
|
|
|
|
my $time = time; |
500
|
|
|
|
|
|
|
|
501
|
0
|
0
|
|
|
|
|
$self->{netobj}->send("\cL") if $refresh; |
502
|
|
|
|
|
|
|
|
503
|
0
|
|
0
|
|
|
|
do { |
504
|
0
|
0
|
|
|
|
|
$self->Expect( |
|
|
0
|
|
|
|
|
|
505
|
|
|
|
|
|
|
undef, defined $refresh ? $refresh : |
506
|
|
|
|
|
|
|
defined $timeout ? $timeout : -1 |
507
|
|
|
|
|
|
|
); |
508
|
0
|
0
|
|
|
|
|
$self->{netobj}->send("\cL") if $refresh; |
509
|
|
|
|
|
|
|
} until (defined $timeout and time - $time < $timeout); |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=head2 Expect($self, [$string], [$timeout]) |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
Implements the B and B directives; all hooked procedures |
515
|
|
|
|
|
|
|
are also checked in parallel. |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
Note that multiple strings could be specified in one C<$string> by |
518
|
|
|
|
|
|
|
using \n as the delimiter. |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=cut |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
sub Expect { |
523
|
0
|
|
|
0
|
1
|
|
my ($self, $param, $timeout) = @_; |
524
|
|
|
|
|
|
|
|
525
|
0
|
|
0
|
|
|
|
$timeout ||= $self->{timeout}; |
526
|
|
|
|
|
|
|
|
527
|
0
|
0
|
|
|
|
|
if ($self->{netobj}->timeout ne $timeout) { |
528
|
0
|
|
|
|
|
|
$self->{netobj}->timeout($timeout); |
529
|
0
|
0
|
|
|
|
|
print "Timeout change to $timeout\n" if $self->{debug}; |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
|
532
|
0
|
|
|
|
|
|
my (@keys, $retval, $retkey, $key, $val, %wait); |
533
|
|
|
|
|
|
|
|
534
|
0
|
|
|
|
|
|
while (($key, $val) = each %{$self->{hook}{$self->{state}}}) { |
|
0
|
|
|
|
|
|
|
535
|
0
|
0
|
|
|
|
|
push @keys, $val->[1] unless exists $wait{$val->[1]}; |
536
|
0
|
|
|
|
|
|
$wait{$val->[1]} = $val; |
537
|
|
|
|
|
|
|
} |
538
|
|
|
|
|
|
|
|
539
|
0
|
0
|
|
|
|
|
if (defined $self->{state}) { |
540
|
0
|
|
|
|
|
|
while (($key, $val) = each %{$self->{hook}{''}}) { |
|
0
|
|
|
|
|
|
|
541
|
0
|
0
|
|
|
|
|
push @keys, $val->[1] unless exists $wait{$val->[1]}; |
542
|
0
|
|
|
|
|
|
$wait{$val->[1]} = $val; |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
} |
545
|
|
|
|
|
|
|
|
546
|
0
|
0
|
|
|
|
|
if (defined $param) { |
547
|
0
|
|
|
|
|
|
foreach my $key (split('\n', $param)) { |
548
|
0
|
0
|
|
|
|
|
push @keys, $key unless exists $wait{$key}; |
549
|
0
|
|
|
|
|
|
$wait{$key} = undef; |
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
# Let's see the counts... |
554
|
0
|
0
|
|
|
|
|
return unless @keys; |
555
|
|
|
|
|
|
|
|
556
|
0
|
0
|
|
|
|
|
print "Waiting: [", _plain(join(",", @keys)), "]\n" if $self->{debug}; |
557
|
|
|
|
|
|
|
|
558
|
0
|
|
|
|
|
|
undef $self->{errmsg}; |
559
|
0
|
0
|
|
|
|
|
eval {($retval, $retkey) = ($self->{netobj}->waitfor(map { |
|
0
|
|
|
|
|
|
|
560
|
0
|
|
|
|
|
|
m|^m/.*/[imsx]*$| ? ('Match' => $_) : ('String' => $_) |
561
|
|
|
|
|
|
|
} @keys)) }; |
562
|
|
|
|
|
|
|
|
563
|
0
|
0
|
|
|
|
|
$self->{errmsg} = $@ if $@; |
564
|
|
|
|
|
|
|
|
565
|
0
|
0
|
|
|
|
|
if ($retkey) { |
566
|
|
|
|
|
|
|
# which one matched? |
567
|
0
|
|
|
|
|
|
$self->{lastmatch} = []; |
568
|
|
|
|
|
|
|
|
569
|
0
|
|
|
|
|
|
foreach my $idx (0 .. $#keys) { |
570
|
0
|
|
|
|
|
|
$self->{lastmatch}[$idx+1] = |
571
|
|
|
|
|
|
|
($keys[$idx] =~ m|^m/.*/[imsx]*$| |
572
|
0
|
0
|
|
|
|
|
? (eval{"\$retkey =~ $keys[$idx]"}) |
|
|
0
|
|
|
|
|
|
573
|
|
|
|
|
|
|
: $retkey eq $keys[$idx]) ? $retkey : undef; |
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
|
577
|
0
|
0
|
|
|
|
|
return if $self->{errmsg}; |
578
|
|
|
|
|
|
|
|
579
|
0
|
0
|
|
|
|
|
if ($wait{$retkey}) { |
580
|
|
|
|
|
|
|
# Hook call. |
581
|
0
|
|
|
|
|
|
my $sub = $AUTOLOAD = $wait{$retkey}->[0]; |
582
|
0
|
|
|
|
|
|
my $code = $wait{$retkey}->[2]; |
583
|
|
|
|
|
|
|
|
584
|
0
|
0
|
|
|
|
|
if (UNIVERSAL::isa($code, 'CODE')) { |
585
|
0
|
|
|
|
|
|
$self->Unhook($sub); |
586
|
0
|
|
|
|
|
|
$code->($self->AUTOLOAD(\'1', @{$wait{$retkey}}[3 .. $#{$wait{$retkey}}])); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
587
|
0
|
|
|
|
|
|
$self->Hook($sub, $code); |
588
|
|
|
|
|
|
|
} |
589
|
|
|
|
|
|
|
else { |
590
|
0
|
|
|
|
|
|
$self->AUTOLOAD(\'1', @{$wait{$retkey}}[3 .. $#{$wait{$retkey}}]) |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
} |
592
|
|
|
|
|
|
|
} |
593
|
|
|
|
|
|
|
else { |
594
|
|
|
|
|
|
|
# Direct call. |
595
|
0
|
0
|
|
|
|
|
return (defined $retval ? $retval : '') if defined wantarray; |
|
|
0
|
|
|
|
|
|
596
|
|
|
|
|
|
|
} |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
# Chops the first one or two lines from a procedure to determine |
600
|
|
|
|
|
|
|
# if it could be used as a hook, and performs assorted magic. |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
sub _chophook { |
603
|
0
|
|
|
0
|
|
|
my ($self, $procref, $varref, $paramref) = @_; |
604
|
0
|
|
|
|
|
|
my ($state, $wait); |
605
|
0
|
|
|
|
|
|
my $op = shift(@{$procref}); |
|
0
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
|
607
|
0
|
0
|
|
|
|
|
if ($op eq 'call') { |
608
|
0
|
|
|
|
|
|
$state = shift(@{$procref}); |
|
0
|
|
|
|
|
|
|
609
|
0
|
|
|
|
|
|
$state =~ s/\$\[(.+?)\]/$varref->{$1} || |
|
0
|
|
|
|
|
|
|
610
|
0
|
0
|
|
|
|
|
($varref->{$1} = shift(@{$paramref}))/eg; |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
# Chophook won't cut the wait op under scalar context. |
613
|
0
|
0
|
0
|
|
|
|
return $state if (defined wantarray xor wantarray); |
614
|
|
|
|
|
|
|
|
615
|
0
|
|
|
|
|
|
$op = shift(@{$procref}); |
|
0
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
|
618
|
0
|
0
|
|
|
|
|
if ($op eq 'wait') { |
619
|
0
|
|
|
|
|
|
$wait = shift(@{$procref}); |
|
0
|
|
|
|
|
|
|
620
|
0
|
|
|
|
|
|
$wait =~ s/\$\[(.+?)\]/$varref->{$1} || |
|
0
|
|
|
|
|
|
|
621
|
0
|
0
|
|
|
|
|
($varref->{$1} = shift(@{$paramref}))/eg; |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
# Don't bother any more under void context. |
624
|
0
|
0
|
|
|
|
|
return unless wantarray; |
625
|
|
|
|
|
|
|
|
626
|
0
|
|
|
|
|
|
$wait =~ s/\x5c\x5c/_!!!_/g; |
627
|
0
|
|
|
|
|
|
$wait =~ s/\\n/\015\012/g; |
628
|
0
|
|
|
|
|
|
$wait =~ s/\\e/\e/g; |
629
|
0
|
|
|
|
|
|
$wait =~ s/\\c(.)/"$1" & "\x1F"/eg; |
|
0
|
|
|
|
|
|
|
630
|
0
|
|
|
|
|
|
$wait =~ s/\\x([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg; |
|
0
|
|
|
|
|
|
|
631
|
0
|
|
|
|
|
|
$wait =~ s/_!!!_/\x5c/g; |
632
|
|
|
|
|
|
|
} |
633
|
|
|
|
|
|
|
else { |
634
|
0
|
|
|
|
|
|
croak "Chophook: Procedure does not start with 'wait'"; |
635
|
|
|
|
|
|
|
} |
636
|
|
|
|
|
|
|
|
637
|
0
|
|
|
|
|
|
return ($state, $wait); |
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
=head2 AUTOLOAD($self, [@args]) |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
The actual implementation of named procedures. All method calls made to a |
644
|
|
|
|
|
|
|
B object would resolve to the corresponding procedure |
645
|
|
|
|
|
|
|
defined it its site description file, which pushes values to the return |
646
|
|
|
|
|
|
|
stack through the B directive. |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
An error is raised if the procedure called is not found. |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
=cut |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
sub AUTOLOAD { |
653
|
0
|
|
|
0
|
|
|
my $self = shift; |
654
|
0
|
0
|
|
|
|
|
my $flag = ${shift()} if ref($_[0]); |
|
0
|
|
|
|
|
|
|
655
|
0
|
0
|
|
|
|
|
my $params = join(',', @_) if @_; |
656
|
0
|
|
|
|
|
|
my $sub = $AUTOLOAD; $sub =~ s/^.*:://; |
|
0
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
|
658
|
0
|
0
|
|
|
|
|
croak "Undefined procedure '$sub' called" |
659
|
|
|
|
|
|
|
unless (exists $self->{proc}{$sub}); |
660
|
|
|
|
|
|
|
|
661
|
0
|
|
|
|
|
|
local $^W = 0; # no warnings here |
662
|
|
|
|
|
|
|
|
663
|
0
|
|
|
|
|
|
my @proc = @{$self->{proc}{$sub}}; |
|
0
|
|
|
|
|
|
|
664
|
0
|
|
|
|
|
|
my @cond = 1; # the condition stack |
665
|
0
|
|
|
|
|
|
my (@result, %var); |
666
|
|
|
|
|
|
|
|
667
|
0
|
0
|
|
|
|
|
print "Entering $sub ($params)\n" if $self->{debug}; |
668
|
|
|
|
|
|
|
|
669
|
0
|
0
|
|
|
|
|
$self->_chophook(\@proc, \%var, \@_) if $flag; |
670
|
|
|
|
|
|
|
|
671
|
0
|
|
|
|
|
|
while (my $op = shift(@proc)) { |
672
|
0
|
|
|
|
|
|
my $param = shift(@proc); |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
# condition tests |
675
|
0
|
0
|
|
|
|
|
pop(@cond), next if $op eq 'endo'; |
676
|
0
|
0
|
|
|
|
|
$cond[-1] = !$cond[-1], next if $op eq 'else'; |
677
|
0
|
0
|
|
|
|
|
next unless ($cond[-1]); |
678
|
|
|
|
|
|
|
|
679
|
0
|
|
|
|
|
|
$param =~ s/\x5c\x5c/_!!!_/g; |
680
|
0
|
|
|
|
|
|
$param =~ s/\\n/\015\012/g; |
681
|
0
|
|
|
|
|
|
$param =~ s/\\e/\e/g; |
682
|
0
|
|
|
|
|
|
$param =~ s/\\c(.)/"$1" & "\x1F"/eg; |
|
0
|
|
|
|
|
|
|
683
|
0
|
|
|
|
|
|
$param =~ s/\\x([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg; |
|
0
|
|
|
|
|
|
|
684
|
0
|
|
|
|
|
|
$param =~ s/_!!!_/\x5c/g; |
685
|
|
|
|
|
|
|
|
686
|
0
|
0
|
|
|
|
|
$param =~ s{\$\[([\-\d]+)\]}{ |
687
|
0
|
|
|
|
|
|
$self->{lastmatch}[$1] |
688
|
|
|
|
|
|
|
}eg unless $op eq 'call'; |
689
|
|
|
|
|
|
|
|
690
|
0
|
0
|
|
|
|
|
$param =~ s{\$\[([^\]]+)\]}{ |
691
|
0
|
0
|
|
|
|
|
$var{$1} || ($var{$1} = (exists $self->{var}{$1} |
|
|
0
|
|
|
|
|
|
692
|
|
|
|
|
|
|
? $self->{var}{$1} : shift)) |
693
|
|
|
|
|
|
|
}eg unless $op eq 'call'; |
694
|
|
|
|
|
|
|
|
695
|
0
|
0
|
|
|
|
|
print "*** $op ", _plain($param), "\n" if $self->{debug}; |
696
|
|
|
|
|
|
|
|
697
|
0
|
0
|
|
|
|
|
if ($op eq 'doif') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
698
|
0
|
|
|
|
|
|
push(@cond, $param); |
699
|
|
|
|
|
|
|
} |
700
|
|
|
|
|
|
|
elsif ($op eq 'call') { |
701
|
|
|
|
|
|
|
# for kkcity |
702
|
0
|
|
|
|
|
|
$param =~ s{\$\[([^\]]+)\]}{ |
703
|
0
|
0
|
|
|
|
|
$var{$1} || ($var{$1} = (exists $self->{var}{$1} |
|
|
0
|
|
|
|
|
|
704
|
|
|
|
|
|
|
? $self->{var}{$1} : shift)) |
705
|
|
|
|
|
|
|
}eg; |
706
|
|
|
|
|
|
|
|
707
|
0
|
|
|
|
|
|
my @params = split(',', $param); |
708
|
0
|
|
|
|
|
|
($param, $params[0]) = split(/\s/, $params[0], 2); |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
s{\$\[(.+?)\]}{ |
711
|
0
|
0
|
|
|
|
|
$var{$1} || ($var{$1} = (exists $self->{var}{$1} |
|
|
0
|
|
|
|
|
|
712
|
|
|
|
|
|
|
? $self->{var}{$1} : shift)) |
713
|
0
|
|
|
|
|
|
}eg foreach @params; |
714
|
|
|
|
|
|
|
|
715
|
0
|
0
|
|
|
|
|
$self->$param(@params) |
716
|
|
|
|
|
|
|
unless $self->{state} eq "$param ".join(',',@params); |
717
|
|
|
|
|
|
|
|
718
|
0
|
0
|
|
|
|
|
print "Return from $param (",join(',',@params),")\n" |
719
|
|
|
|
|
|
|
if $self->{debug}; |
720
|
|
|
|
|
|
|
} |
721
|
|
|
|
|
|
|
elsif ($op eq 'goto') { |
722
|
0
|
0
|
|
|
|
|
$self->$param() unless $self->{state} eq $param; |
723
|
0
|
0
|
|
|
|
|
return wantarray ? @result : $result[0]; |
724
|
|
|
|
|
|
|
} |
725
|
|
|
|
|
|
|
elsif ($op eq 'wait') { |
726
|
0
|
0
|
|
|
|
|
defined $self->Expect($param) or return; |
727
|
|
|
|
|
|
|
} |
728
|
|
|
|
|
|
|
elsif ($op eq 'till') { |
729
|
0
|
|
|
|
|
|
my $lastidx = $#result; |
730
|
0
|
|
|
|
|
|
push @result, $self->Expect($param); |
731
|
0
|
0
|
|
|
|
|
return if $lastidx == $#result; |
732
|
|
|
|
|
|
|
} |
733
|
|
|
|
|
|
|
elsif ($op eq 'send') { |
734
|
0
|
|
|
|
|
|
undef $self->{errmsg}; |
735
|
0
|
|
|
|
|
|
$self->{netobj}->send($param); |
736
|
0
|
0
|
|
|
|
|
return if $self->{errmsg}; |
737
|
|
|
|
|
|
|
} |
738
|
|
|
|
|
|
|
elsif ($op eq 'exit') { |
739
|
0
|
0
|
|
|
|
|
$result[0] = '' unless defined $result[0]; |
740
|
0
|
0
|
|
|
|
|
return wantarray ? @result : $result[0]; |
741
|
|
|
|
|
|
|
} |
742
|
|
|
|
|
|
|
elsif ($op eq 'setv') { |
743
|
0
|
|
|
|
|
|
my ($var, $val) = split(/\s/, $param, 2); |
744
|
0
|
|
|
|
|
|
$self->{var}{$var} = $val; |
745
|
|
|
|
|
|
|
} |
746
|
|
|
|
|
|
|
elsif ($op eq 'idle') { |
747
|
0
|
|
|
|
|
|
sleep $param; |
748
|
|
|
|
|
|
|
} |
749
|
|
|
|
|
|
|
else { |
750
|
0
|
|
|
|
|
|
die "No such operator: $op"; |
751
|
|
|
|
|
|
|
} |
752
|
|
|
|
|
|
|
} |
753
|
|
|
|
|
|
|
|
754
|
0
|
|
|
|
|
|
$self->{state} = "$sub $params"; |
755
|
|
|
|
|
|
|
|
756
|
0
|
0
|
|
|
|
|
print "Set State: $self->{state}\n" if $self->{debug}; |
757
|
0
|
0
|
|
|
|
|
return wantarray ? @result : $result[0]; |
758
|
|
|
|
|
|
|
} |
759
|
|
|
|
|
|
|
|
760
|
0
|
|
|
0
|
|
|
sub DESTROY {} |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
1; |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
__END__ |