File Coverage

blib/lib/OurNet/BBSAgent.pm
Criterion Covered Total %
statement 92 266 34.5
branch 32 192 16.6
condition 6 18 33.3
subroutine 11 19 57.8
pod 6 6 100.0
total 147 501 29.3


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__