The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use Config qw(%Config);
use File::Basename qw(basename dirname);
chdir(dirname($0));
($file = basename($0)) =~ s/\.PL$//;
$file =~ s/\.pl$//
	if ($Config{'osname'} eq 'VMS' or
	    $Config{'osname'} eq 'OS2');  # "case-forgiving"
open OUT,">$file" or die "Can't create $file: $!";
chmod(0755, $file);
print "Extracting $file (with variable substitutions)\n";

print OUT <<"!GROK!THIS!";
$Config{'startperl'} -w

!GROK!THIS!

print OUT <<'!NO!SUBS!';

use strict;
use Config qw(%Config);

my $progname = $0;
$progname =~ s,.*/,,; # loose path

my $file;
if (!@ARGV) {
    $file = "<stdin>";
    *F = *STDIN;
} elsif (@ARGV == 1) {
    $file = shift;
    open(F, $file) || die "Can't open '$file': $!\n";
} else {
    usage();
}

print "$Config{'startperl'} -w\n";

print "# !!! DO NOT EDIT !!!\n";
print "# This program was automatically generated from '$file' by $progname\n";

print <<'EOT';

use strict;

use URI ();
use HTTP::Request ();
use LWP::UserAgent ();
#use LWP::Debug qw(+);

use HTML::Form ();
use WWW::Chat qw(fail OK ERROR);

use vars qw($ua $uri $base $req $res $status $ct @forms $form @links $TRACE);

$base ||= "http://localhost";
unless ($ua) {
    $ua  = LWP::UserAgent->new;
    $ua->agent("webchat/0.01 " . $ua->agent);
    $ua->env_proxy;
}

$TRACE = $ENV{WEBCHAT_TRACE};

EOT

print "#line 1 \"$file\"\n";

use Data::Dump qw(dump);

my $seen_end;
my $level = 0;

while (<F>) {
    if ($seen_end) {
	print;
	next;
    }

    if (/^(\s*)GET\s+(\S+)\s*$/) {
	my $indent = $1;
	my $uri = $2;
	$uri = dump($uri) unless $uri =~ /^\$/;
	print "$indent#GET $uri\n";
	print "${indent}eval {\n";
	$level++;
        print "$indent    local \$uri = URI->new_abs($uri, \$base);\n";
	print "$indent    local \$req = HTTP::Request->new(GET => \$uri);\n";
	request("$indent    ");
	line();

    } elsif (/^(\s*)FOLLOW\s(.*)/) {
	my $indent = $1;
	my $what = $2;
	$what =~ s/\s+$//;
	print "${indent}# FOLLOW $what\n";
	print "${indent}eval {\n";
	$level++;
	if ($what =~ m,^/,) {
	    print "$indent    local \$uri;\n";
	    print "$indent    for (\@links) { \$uri = \$_->[0], last if \$_->[1] =~ $what }\n";
	    my $text = dump("FOLLOW $what");
	    print "$indent    fail($text) unless defined \$uri;\n";
	    print "$indent    \$uri = URI->new_abs(\$uri, \$base);\n";
	} else {
	    $what = dump($what);
	    print "$indent    local \$uri = WWW::Chat::locate_link($what, \\\@links, \$base);\n";
	}
	print "$indent    local \$req = HTTP::Request->new(GET => \$uri);\n";
	request("$indent    ");
	line();

    } elsif (/^(\s*)FORM:?(\d+)?(?:\s+(\S+))?\s*$/) {
	my $indent = $1;
	my $form_no = $2 || 1;
        my $uri = $3;
	$uri = dump($uri) if !defined($uri) || $uri !~ /^\$/;
	print $indent, "\$form = WWW::Chat::findform(\\\@forms, $form_no, $uri);\n";

    } elsif (/^(\s*)EXPECT\s+(.*)$/) {
	my $indent = $1;
	my $what = $2;
	$what =~ s/;$//;
	#print "$indent#EXPECT $what\n";
	my $text = dump($what);
	print $indent, "fail($text) unless $what;\n";

    } elsif (/^(\s*)BACK(?:\s+(ALL|\d+))?\s*$/) {
	my $indent = $1;
	my $done = $2 || "1";
	done($indent, $done);
	
    } elsif (/^(\s*)F\s+([\w.:\-*\#]+)\s*=\s*(.*)/) {
	my $indent = $1;
	my $name   = $2;
	my $val    = dump("$3");

	my $no = 1;
	$no = $1 if $name =~ s/:(\d+)$//;
	$name = dump($name);

	if ($no == 1) {
	    print "$indent\$form->value($name => $val);\n";
	} else {
	    print "$indent\$form->find_input($name, $no)->value($val);\n";
	}

    } elsif (/^(\s*)(?:CLICK|SUMBIT)(?:\s+(\w+))?(?:(\d+)\s+(\d+))?/) {
	my $indent = $1;
	my $name = $2;
	$name = dump($name);
	my $x = $3;
	my $y = $4;
	for ($x, $y) { $_ = 1 unless defined; }
	print "$indent#CLICK $name $x $y\n";
	print $indent, "eval {\n";
	$level++;
	print $indent, "    local \$uri = \$form->uri;\n";
	print $indent, "    local \$req = \$form->click($name, $x, $y);\n";
	request("$indent    ");
	line();

    } elsif (/^__END__$/) {
	done("", "ALL") if $level;
	print;
	$seen_end++;

    } else {
	print;
    }
}
done("", "ALL") if $level;
exit;


sub usage
{
    die "Usage: $progname [<file>]\n";
}

sub done
{
    my($indent, $done) = @_;
    $done = $level if $done eq "ALL" || $done > $level;
    $level -= $done;
    for (1 .. $done) {
	print $indent, "}; WWW::Chat::check_eval(\$@);\n";
    }
    line() if $done > 1;
}

sub request
{
    my $indent = shift;
    print $indent, "local \$res = WWW::Chat::request(\$req);\n";
    print $indent, "#print STDERR \$res->as_string;\n";
    print $indent, "local \$status = \$res->code;\n";
    print $indent, "local \$base = \$res->base;\n";
    print $indent, "local \$ct = \$res->content_type || \"\";\n";
    print $indent, "local \$_ = \$res->content;\n";
    print $indent, "local(\@forms, \$form, \@links);\n";
    print $indent, "if (\$ct eq 'text/html') {\n";
    print $indent, "    \@forms = HTML::Form->parse(\$_, \$res->base);\n";
    print $indent, "    \$form = \$forms[0] if \@forms;\n";
    print $indent, "    \@links = WWW::Chat::extract_links(\$_);\n";
    print $indent, "}\n";
}

sub line
{
    my $line = $. + 1;
    print qq(#line $line "$file"\n);
}

__END__

=head1 NAME

webchatpp - Preprocessor for Web Chat scripts

=head1 SYNOPSIS

 webchatpp chat-script | perl

=head1 DESCRIPTION

The C<webchatpp> program is a preprocessor that turns chat scripts
into plain perl scripts.  When this script is feed to perl it will
perform the chatting.  The I<webchat language> consist of perl code
with some lines interpreted and expanded by I<webchatpp>.  The
following statements are recognized:

=over 4

=item GET <uri>

This will fetch the indicated URI.  The URI can be relative to the
previous base.  Each GET gets it's own eval block within the perl
program to catch any exceptions.  Inside this block, the following
dynamically scoped variables are available:

  $uri       The current URI
  $req       The current request object        [HTTP::Request]
  $res       The response received             [HTTP::Response]
  $status    The status code of the response
  $ct        The content type of the response
  $base      The base URI for current response
  $_         The content of the response
  @forms     Forms found in current content    [HTML::Form]
  $form      Current form                      [HTML::Form]
  @links     Links found in current content

=item EXPECT <expr>

Evaluate the perl expression <expr> and report failure if it does not
return TRUE.  The fact that content of the response is available in $_
makes it easy to match it with regular expressions.  The routines OK()
and ERROR() is available to test the response status code.

When EXPECT fails all tests nested within this one is aborted too.

=item FORM[:<no>] [<name>]

Define which <FORM> should be the current one.  Sets the C<$form>
variable.  If no number is specified, then number 1 is assumed.  If a
form with the given number (or name, if specified) can not be found,
then an exception is raised, and this block is aborted.

=item F <name>=<value>

Set input values within the current form.  If the current form has no
field named <name>, then an exception is raised.  If the form has
multiple fields named <name>, then only the first one is affected.

=item CLICK [<name>] [<x> <y>]

Submit a <form>.  If there are multiple submit buttons, then you can
specify which one by passing the <name>.  You can also indicate which
coordinates within the buttion, was clicked.  Only makes a difference
for C<input/image> submits.

CLICK enters a new block similar to GET, and make the same dynamically
scoped variables available.

You can use the keyword "SUBMIT" instead of "CLICK" if you prefer.  It
is a plain alias.

=item FOLLOW /text/

Tries to find a hyperlink with the given description, and will then
follow it.  Enters a new block like GET.

=item FOLLOW <num>

Will follow the <num>'th hyperlink on the current page.  Enters a new
block like GET.

=item BACK [<levels> | "ALL"]

Ends the previous GET or CLICK blocks.  The argument can be used to
get out of multiple blocks.

=item _END__

Stop looking for I<webchat language> directives.  The rest of the
script is dumped directly (and is also ignored by perl).  Implies
"C<BACK ALL>".

=back

The following variables are global within the whole web chat script
produced:

  $ua        User agent object    [LWP::UserAgent]
  $uri       Current URI
  $TRACE     Print trace showing HTTP requests/responses.

=head1 ENVIRONMENT

The initial value of the $TRACE variable is initialized from the
WEBCHAT_TRACE environment variable.

Proxy settings are picked up from the environment too. See
L<LWP::UserAgent/env_proxy>.

=head1 EXAMPLES

This is a sample script:

 GET http://www.perl.com
    EXPECT OK && /perl/
    GET not_there.html
       EXPECT ERROR
    BACK
 BACK

And here is another one

 GET http://www.altavista.com
 EXPECT OK
 F q=Gisle
 CLICK
 EXPECT OK && /Gisle Aas/

=head1 SEE ALSO

L<LWP>, L<HTML::Form>

=head1 COPYRIGHT

Copyright 1998 Gisle Aas.

This script is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

=cut
!NO!SUBS!