| 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__ |