#!/usr/bin/perl

$REVISION = '$Id: algae,v 1.171 2007-11-13 03:06:23 eric Exp $ ';

use strict;


package W3C::Rdf::AlgaeTableRenderer;
@W3C::Rdf::AlgaeTableRenderer::ISA = qw(W3C::Util::TableRenderer);

sub new {
    my ($proto, $atomDictionary, $flags) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new(%$flags);
    $self->{-atomDictionary} = $atomDictionary;
    $self->{-flags} = $flags;
    return $self;
}

sub setBoolean {
    my ($self, $val) = @_;
    $self->addData([$val ? 'true' : 'false']);
}

sub addData {
    my ($self, @data) = @_;
    $self->SUPER::addData(map {$self->{-atomDictionary}->renderAtom($_, %{$self->{-flags}})} @data);
}


package W3C::Rdf::AlgaeShutdownException;
@W3C::Rdf::AlgaeShutdownException::ISA = qw(W3C::Util::Exception);
sub new {
    my ($proto, @parms) = @_;
    my $class = ref($proto) || $proto;
    my $self = $class->SUPER::new(@parms);
    $self->{-reason} = '' if (!$self->{-reason});
    $self->fillInStackTrace;
    return $self;
}
sub getSpecificMessage {$_[0]->getReason();}
sub getReason {return $_[0]->{-reason}}

#BEGIN {unshift@INC,('../../..');}
package algaeScript;
use vars qw(%SerializerClassAbbreviations

	    $NS_algae $NS_algaeTests $NS_attrib $NS_dawgManifest $NS_dawgTests
	    $NS_dawgQuery $NS_dc $NS_dcTerms $NS_doap $NS_earl $NS_foaf 
	    $NS_federate $NS_rdfs $NS_rs $NS_rdf $NS_xsd

	    $IFACE $IFACE_unknown $IFACE_Gnu $IFACE_Perl
	    $TYPE_posSyntax $TYPE_negSyntax);

use Pod::Usage;
use W3C::Util::Scriptopt qw(:config guess_shell permute no_ignore_case bundling);
use W3C::Util::Exception qw(&throw &catch &DieHandler);
use W3C::Rdf::RdfApp;
use W3C::Rdf::Atoms qw($Value_NULL);

%SerializerClassAbbreviations = ('statements' => 'W3C::Rdf::StatementsSerializer', 
				 'rdfxml' => 'W3C::Rdf::XmlSerializer', 
				 'arrows' => 'W3C::Rdf::ArrowSerializer', 
				 'n3' => 'W3C::Rdf::N3Serializer', 
				 'turtle' => 'W3C::Rdf::N3Serializer', 
				 'ntriples' => 'W3C::Rdf::NTriplesSerializer', 
				 'dot' => 'W3C::Rdf::DotSerializer'
				 );
$NS_algae = 'http://www.w3.org/1999/02/26-modules/Algae-HOWTO#';
$NS_algaeTests = 'http://www.w3.org/1999/02/26-modules/tests/';
$NS_attrib = 'http://www.w3.org/2001/12/attributions/ns#';
$NS_dawgManifest = 'http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#';
$NS_dawgTests = 'http://www.w3.org/2001/sw/DataAccess/tests/#';
$NS_dawgQuery = 'http://www.w3.org/2001/sw/DataAccess/tests/test-query#';
$NS_dc = 'http://purl.org/dc/elements/1.1/';
$NS_dcTerms = 'http://purl.org/dc/terms/';
$NS_doap = 'http://usefulinc.com/ns/doap#';
$NS_earl = 'http://www.w3.org/ns/earl#';
$NS_foaf = 'http://xmlns.com/foaf/0.1/';
$NS_federate = 'http://www.w3.org/1999/02/26-modules/';
$NS_rdfs = 'http://www.w3.org/2000/01/rdf-schema#';
$NS_rs = 'http://www.w3.org/2001/sw/DataAccess/tests/result-set#';
$NS_rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
$NS_xsd = 'http://www.w3.org/2001/XMLSchema#';

$TYPE_posSyntax = "${NS_dawgManifest}PositiveSyntaxTest";
$TYPE_negSyntax = "${NS_dawgManifest}NegativeSyntaxTest";

($IFACE_unknown, $IFACE_Gnu, $IFACE_Perl) = (0..2);
$IFACE = $IFACE_unknown;

eval {
    local($SIG{"__DIE__"}) = \&DieHandler;
    new algaeScript()->execute();
}; if ($@) {if (my $ex = &catch('W3C::Util::Exception')) {
	die $ex->toString();
    } else {
	die $@;
    }
}

sub new {
    my ($proto, $context) = @_;
    my $class = ref($proto) || $proto;
    my @gmtime = gmtime();
    my $timeStr = sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", $gmtime[5]+1900, 
			  $gmtime[4]+1, reverse(@gmtime[0..3]));
    my $self = {EARL => undef, STARTTIME => $timeStr, TESTERname => undef, TESTERmbox => undef};
    bless ($self, $class);

    my $nsh = new W3C::Util::NamespaceHandler(
	-passUnknownNamespaces => 1, 
	-useSAX2 => 1, 
	-collide => $W3C::Util::NamespaceHandler::NS_IGNORE);
    $self->{-defaultMediaType} = undef;
    $self->{-uriMediaTypes} = {};
    my $context = new W3C::Rdf::RdfApp(-forceHost => undef, 
				       -forceDir => undef, 
				       -defaultMediaType => \$self->{-defaultMediaType}, 
				       -uriMediaTypes => $self->{-uriMediaTypes}, 
				       NAMESPACE_HANDLER => $nsh);

    $self->{-queryHandler} = $context->getQueryHandler($self, {-uniqueResults => 1});
    $self->{-context} = $context;
    $self->{LOCATION_STACK} = [];
    $self->{-atomDictionary} = $context->{-atomDictionary}; # for convenience
    $self->{-ignoreRequires} = 0;
    $self->{-stfu} = 0;
    $main::bnodeIsExt = 0;
    $main::warnUnknownVariables = 0;
    $main::dieUnknownVariables = 0;
    return $self;
}
use vars qw($testResults);
sub execute {
    my ($self) = @_;

    my $queryLangNames = {'Algae' => $QL_ALGAE, 
			  'n3' => $QL_N3, 
			  'RDQL' => $QL_RDQL, 
			  'SPARQL' => $QL_SPARQL, 
			  'SeRQL' => $QL_SeRQL};
    my $queryLang = $QL_SPARQL;

    my $inputLangNames = {'rdfxml' => undef, 
			  'n3' => undef, 
			  'srx' => undef, 
			  'turtle' => undef, 
			  'guess' => undef};
    my $inputLang = 'rdfxml';
    my $sparqlServerUrl = undef;
    my $debugOutput = undef;

    *DBG = *STDOUT;
    my ($help, $man);
    my ($baseUri, $reportStream, $reportParms, $outputFile, $outputHandle, $listenPort, $limit, $testList, $dumpGraph);
    $reportParms = [];
    $testList = [];
    $dumpGraph = [];
    $outputHandle = \*STDOUT;
    binmode STDOUT, ":utf8";
    my $argsProcessed = 0;
    &W3C::Util::Scriptopt::setLocationHandler($self);
    my $res = &GetOptionsScript(
	'algae' => sub {}, # do nothing -- read script as an @parm

	'attach|a=s' => sub {
	    require W3C::Rdf::SqlDB;
	    $self->{-context}{-rdfDB} = new W3C::Rdf::SqlDB(
		-properties => $_[1], -atomDictionary => $self->{-atomDictionary}, 
		-lazyReification => 1, -assertionAttribution => $self->{-queryHandler}{-sourceAttribution});
	    $self->{-queryHandler} = $self->{-context}->getQueryHandler($self, {-uniqueResults => 1});
	}, 

	# -b http://example.org/foo# : URI against which relative URIs are evaluated.
	'base|b=s' => sub {
	    $baseUri = $_[1];
	    my $base = $self->{-context}->getInputAttribution->getSource->getUri;
	    $base = new URI::URL($baseUri, $base)->abs;
	    $self->{-context}->makeInputAttribution($base);
	}, 
	'bnode-is-ext' => \$main::bnodeIsExt, 
	'dump=s' => sub {
	    $baseUri = $_[1];
	    my $base = $self->{-context}->getInputAttribution->getSource->getUri;
	    $base = new URI::URL($baseUri, $base)->abs;
	    eval {
		push (@$dumpGraph, $self->{-queryHandler}->getSourceByName($base));
	    }; if ($@ && (my $ex = &catch('W3C::Util::Exception'))) {
		warn "WARNING: ", $ex->getMessage, " -- skipping\n";
	    }
	}, 
	'dump-default' => sub {push (@$dumpGraph, $self->{-context}{-rdfDB})}, 
	'warn-unknown-variables' => \$main::warnUnknownVariables, 
	'die-unknown-variables' => \$main::dieUnknownVariables, 
	'lang|l=s' => sub {
	    local($SIG{"__DIE__"}) = \&DieHandler;
	    my (undef, $val) = @_;
	    if (exists $queryLangNames->{$val}) {
		$queryLang = $queryLangNames->{$val};
	    } else {
		my $langs = join(', ', keys %$queryLangNames);
		&throw(new W3C::Util::Exception(
		-message => 
		"\"$val\" not supported. Use one of $langs."));
	    }
	}, 
	'input|i=s' => sub {$inputLang = &checkConstant($_[1], $inputLangNames)}, 
	'sClass=s' => \$reportStream, # deprecated
	'sParm=s@' => $reportParms, # deprecated
	'forceHost=s' => \$self->{-context}{-forceHost}, 
	'forcePath=s' => \$self->{-context}{-forcePath}, 
	'limit=i' => \$limit, 
	'reportClass=s' => \$reportStream, 
	'reportParm=s@' => $reportParms, 
	'queryParm=s' => sub {
	    my ($parm, $value) = split('=', $_[1], 2);
	    my $parms = $self->{-queryHandler}->getDefaultParms();
	    my $atom = $value =~ m/^<(.*?)>$/ ? $self->{-atomDictionary}->getUri($1, undef) : 
		$value =~ m/^'(.*?)'$/ ? $self->{-atomDictionary}->getString($1, undef, 'PLAIN', undef) : 
		$value =~ m/^"(.*?)"$/ ? $self->{-atomDictionary}->getString($1, undef, 'PLAIN', undef) : 
		$self->{-atomDictionary}->getString($value, undef, 'PLAIN', undef);
	    $parms->{$parm} = $atom;
	}, 

	'default-media-type=s' => \$self->{-defaultMediaType}, 

	# --url-media-type text/plain:http://example.org/foo# : override 
	'uri-media-type=s' => sub {
	    my ($type, $url) = split(':', $_[1], 2);
	    $self->{-uriMediaTypes}{$url} = $type;
	}, 

	# --ns foo:http://example.org/foo# : assign prefix foo to this namespace
	'ns=s' => sub {
	    my ($prefix, $ns) = split(':', $_[1], 2);
	    $self->{-context}->getNamespaceHandler()->addNamespace($prefix, $ns);
	}, 

	# -o foo.out : send output to foo.out
	'o=s' => sub {
	    $outputFile = $_[1];
	    local($SIG{"__DIE__"}) = \&DieHandler;
	    close ($outputHandle);
	    if (!open (STDOUT, '>:utf8', $outputFile)) {
		&throw(new W3C::Util::FileOperationException(
						     -filename => $outputFile,
						     -operation => 'write'));
	    }
	    $outputHandle = \ * STDOUT;
	}, 

	# -p 7999 : run simple line-server at this port.
	'port|p=i' => \$listenPort, 

	# -d foo.ttl : read data from foo.ttl
	'read|d=s' => sub {
	    my $filename = $_[1];
	    local($SIG{"__DIE__"}) = \&DieHandler;
	    my $lang = $inputLang eq 'guess' ? &_guessType($filename) : $inputLang;
	    $self->_parseData($filename, $lang, $baseUri, 0);
	}, 

	# -g foo.ttl : read named graph data from foo.ttl
	'readGraph|g=s' => sub {
	    my $filename = $_[1];
	    local($SIG{"__DIE__"}) = \&DieHandler;
	    my $lang = $inputLang eq 'guess' ? &_guessType($filename) : $inputLang;
	    $self->_parseData($filename, $lang, $baseUri, 1);
	}, 

	# -u http://localhost:8000/sparql : run sparql server at this address.
	'server-url|u=s' => sub {
	    $sparqlServerUrl = new URI::URL($_[1], 'http://localhost/')->abs;
	}, 

	# Vestigial interactive query builder.
	'q' => sub {
	    local($SIG{"__DIE__"}) = \&DieHandler;
	    $self->interactiveQueryBuilder();
	    ++$argsProcessed;
	}, 

	# .
	'stfu' => \$self->{-stfu}, 

	# -f foo.rq : read query from foo.rq
	'query|f=s' => sub {
	    local($SIG{"__DIE__"}) = \&DieHandler;
	    my $queryFile = $_[1];
	    open (QF, '<:utf8', $queryFile) || 
		&throw(new W3C::Util::FileOperationException(-filename => $queryFile,
							     -operation => 'read'));
	    local $/ = undef;
	    my $q = <QF>;
	    close QF;
	    &utf8::decode($q);
	    eval {
		$self->_executeQuery($queryLang, $q, $baseUri, $reportStream, $reportParms, $outputHandle);
	    }; if ($@) {
		if ($outputFile) {
		    unlink $outputFile;
		}
		if (my $ex = &catch('W3C::Util::Exception')) {
		    &throw($ex);
		} else {
		    &throw(new W3C::Util::PerlException());
		}
	    }
	    ++$argsProcessed;
	}, 

	# Other command line args interpreated as queries.
	'<>' => sub {
	    local($SIG{"__DIE__"}) = \&DieHandler;
	    eval {
		$self->_executeQuery($queryLang, $_[0], $baseUri, $reportStream, $reportParms, $outputHandle);
	    }; if ($@) {
		if ($outputFile) {
		    unlink $outputFile;
		}
		if (my $ex = &catch('W3C::Util::Exception')) {
		    &throw($ex);
		} else {
		    &throw(new W3C::Util::PerlException());
		}
	    }
	    ++$argsProcessed;
	}, 

	##############
	# Help stuff #
	'help|?' => \$help, 
	'man' => \$man, 

	##############
	# Test stuff #

	# --manifest tests.ttl : execute tests in tests.ttl
	'manifest|m=s' => sub {
	    # Read and execute a test manifest
	    local($SIG{"__DIE__"}) = \&DieHandler;

	    # Build a ResultSet for the test results.
	    $testResults ||= new W3C::Rdf::ResultSet(-atomDictionary => $self->{-atomDictionary});
	    my $iName = $testResults->bindSymbol('name');
	    $testResults->addSelect($iName);
	    my $iRes = $testResults->bindSymbol('result');
	    $testResults->addSelect($iRes);
	    my $iError = $testResults->bindSymbol('error');
	    $testResults->addSelect($iError);

	    $self->_executeManifest($_[1], $testResults->elements->nextElement, 
				    $testResults, $iName, $iRes, $iError, 
				    $testList, $outputHandle);
	    splice(@$testList, 0, scalar @$testList); # clear out for --test args before next manifest.

	    # Fake a report in order to use the regular reporting scheme.
	    $self->{-queryHandler}{LABELS} = ['name', 'result', 'error'];
	    $self->{-queryHandler}{RESULT_SET} = $testResults;
	    (undef, $self->{SELECTS}, undef, undef) = $self->{-queryHandler}->getReport();
	    $self->{TempRS} = $self->{-queryHandler}->getResultSet();
	    ++$argsProcessed;
	}, 

	# --test foo : run test foo from the manifest being tested.
	'test=s@' => $testList, 

	# --create-results : create resultSet file in manifest.
	'create-results' => sub {$self->{CreateResults} = $_[1];}, 

	'ignore-requires' => sub {$self->{-ignoreRequires} = $_[1];}, 

	# --sumary : dump test resultSet when comparing to reference resultSet.
	'summary' => sub {$self->{Summary} = $_[1];},

	# show summary in EARL.
	'earl' => sub {$self->{EARL} = $_[1];}, 
	'earl-name=s' => sub {$self->{TESTERname} = $_[1];}, 
	'earl-mbox=s' => sub {$self->{TESTERmbox} = $_[1];}, 

	###################
	# Debugging stuff #

	# Set up a debug output
	't=s' => sub {
	    local($SIG{"__DIE__"}) = \&DieHandler;
	    if ($debugOutput) {
		close (DBG);
	    }
	    *DBG = undef;
	    $debugOutput = $_[1];
	    open (DBG, '>:utf8', $debugOutput) || 
		&throw(new W3C::Util::FileOperationException(-filename => $debugOutput,
							     -operation => 'read'));
	    $main::DBG = \*DBG;
	}, 

    );
    &pod2usage(-exitstatus => 0, -verbose => 1) if $help;
    &pod2usage(-exitstatus => 0, -verbose => 2) if $man;
    if ($sparqlServerUrl) {
	$argsProcessed++;
	my $scheme = $sparqlServerUrl->scheme;
	if ($scheme ne 'http') {
	    &throw(new W3C::Util::Exception("\"scheme\" not supported. try one of http, or http, or maybe http"));
	}
	$self->pushLocation($self->{-context}->getInputAttribution->getSource->getUri);
	$self->httpServer($sparqlServerUrl->port, $queryLang, $reportStream, $reportParms, $sparqlServerUrl);
	$self->popLocation($self->{-context}->getInputAttribution->getSource->getUri);
    } elsif ($listenPort)  {
	$argsProcessed++;
	$self->simpleServer($listenPort, $queryLang, $reportStream, $reportParms);
    }
    if (@$dumpGraph) {
	foreach my $graph (@$dumpGraph) {
	    $self->{-queryHandler}{-templateDB} = $graph;
	    my $postPrint = '';
	    if (@$dumpGraph > 1) {
#		eval {
		    printf("GRAPH <%s> {\n", $self->{-queryHandler}->getNameBySource($graph));
		    $postPrint = "}\n";
#		}; # might be the default graph, in which case ...
	    }
	    if (!$reportStream) {
		# Pick a report language for the user.
		$reportStream = 'turtle';
	    }
	    print $outputHandle $self->_report($reportStream, $reportParms, $limit);
	    print $postPrint;
	}
    } elsif ($argsProcessed == 0) {
	&pod2usage(-exitstatus => 1, -verbose => 1, 
		   -message => "No queries processed.") ;
    } else {
	# Set up reporter (output) stream.
	print $outputHandle $self->_report($reportStream, $reportParms, $limit);
    }
}

sub checkConstant {
    my ($val, $inputOpts) = @_;
    local($SIG{"__DIE__"}) = \&DieHandler;
    if (exists $inputOpts->{$val}) {
	return $val;
    } else {
	my $langs = join(', ', keys %$inputOpts);
	&throw(new W3C::Util::Exception(
		   -message => 
		   "\"$val\" not supported. Use one of $langs."));
    }
}

sub httpServer {
    my ($self, $listenPort, $queryLang, $reportStream, $reportParms, $url) = @_;
    require HTTP::Daemon;
    require HTTP::Status;
    require HTTP::Headers;

    my $d = HTTP::Daemon->new(LocalAddr => $url->host, 
			      LocalPort => $url->port, 
			      ReuseAddr => 1, 
			      ReusePort => 0) || &throw(new W3C::Util::Exception(-message => "Error launcing HTTP::Daemon: $!"));;
    my $u = $d->url;
    printf("Serving <%s%s> with base <%s>.\n", substr($u, 0, length($u) - 1), $url->path, $self->{-context}->getInputAttribution->getSource->getUri) unless $self->{-stfu};
    my $done = undef;
    for (my $c; !defined $done && ($c = $d->accept()); ) {
	while (!defined $done && (my $r = $c->get_request)) {
	    if ($r->url->path eq $url->path) {
		if (my %f = $r->url->query_form) {

		    # (eventual) Support for named graph
		    my @namedGraphs;
		    my $defaultGraph;
		    {
			my @formAsArray = $r->url->query_form;
			while (my $attr = shift @formAsArray) {
			    if (my $val = shift @formAsArray) {
				if ($attr eq 'named-graph-uri' && $val) {
				    push (@namedGraphs, $val);
				} elsif ($attr eq 'default-graph-uri' && $val) {
				    $defaultGraph = $val;
				}
			    }
			}
		    }

		    my $headers = HTTP::Headers->new;
		    my $res;
		    eval {
			my $comment = "(unescaped) query parameters:\n";
			if ($defaultGraph) {
			    $self->_parseData($defaultGraph, &_guessType($defaultGraph), undef, 0);
			    $comment .= "default-graph-uri: $defaultGraph\n";
			}
			foreach my $namedGraph (@namedGraphs) {
			    $self->_parseData($namedGraph, &_guessType($namedGraph), undef, 1);
			    $comment .= "named-graph-uri: $defaultGraph\n";
			}

			my $query = $f{'query'};
			$comment .= "query: $query";
			&utf8::decode($query);
			printf("%d byte query from %s:%d", length($query), $c->peerhost, $c->peerport) unless $self->{-stfu};
			$self->_executeQuery($queryLang, $query, undef, $reportStream, $reportParms, undef);
			my $resultSet = $self->{-queryHandler}->getResultSet();

			my $body;
			my $mType = 'application/xml'; # args to set up 'text/plain; charset=utf8' ?
			my $localReportStream = $reportStream;
			my $localReportParms = [@$reportParms];

			# Put query in an XML comment
			$comment =~ s/--/\x{2013}\x{2013}/g; # endashes look like '-'s
			$comment = "<!-- $comment -->\n";

			# Finagle priorities for graph and array languages.
			if ($resultSet->getPresentationMimetype() eq 'application/rdf+xml') {
			    $localReportStream ||= 'turtle';
			    if ($localReportStream eq 'turtle') {
				$mType = 'application/turtle';
				$comment =  join('', map {"# $_\n"} split(/\r\n|\n/, $query));
			    } else {
				$mType = 'application/rdf+xml';
			    }
			} else {
			    $localReportStream = undef;
			    push (@$localReportParms, -x => 1);
			}

			$body = $comment.$self->_report($localReportStream, $localReportParms, undef);
			&utf8::encode($body);
			$headers->header('Content-Type' => $mType);
			$res = HTTP::Response->new(200, 'OK', $headers, $body);
		    }; if ($@) {
			$headers->header('Content-Type' => 'text/plain; charset=utf8');
			if (my $ex = &catch('W3C::Util::Exception')) {
			    if ($ex->{-chainedException} && $ex->{-chainedException}->isa(qw(W3C::Rdf::AlgaeShutdownException))) {
				my $msg = $ex->{-chainedException}->getMessage();
				$res = HTTP::Response->new(200, 'OK', $headers, $msg);
				$done = $msg;
			    } else {
				my $msg = $ex->toString();
				&utf8::encode($msg);
				my $resp = UNIVERSAL::isa($ex, 'W3C::Util::YappDriver::YappContextException') ? 400 : 500;
				$res = HTTP::Response->new($resp, 'OK', $headers, $msg);
				print $ex->toString, "\n" unless $self->{-stfu};
			    }
			} else {
			    $res = HTTP::Response->new(500, 'OK', $headers, $@);
			}
		    }
		    $c->send_response($res);
		    $c->force_last_request();
		    $self->{-queryHandler}->clear();
		    if ($f{'clearDefaultDB'}) {
			$self->{-context}{-rdfDB}->clear();
		    }
		    $self->{-queryHandler}->clearTemplateDB();
		    printf(" => %d: %d bytes of %s .\n", $res->code, length($res->content), $res->header('Content-Type')) unless $self->{-stfu};
		}
		else {
		    $c->send_file_response('SPARQL-form.html');
		}
	    } else {
		$c->send_redirect($url);
	    }
	}
	$c->close;
	undef($c);
    }
    printf("Server exiting: %s\n", $done) unless $self->{-stfu};
}

sub simpleServer {
    my ($self, $listenPort, $queryLang, $reportStream, $reportParms) = @_;
    require IO::Socket::INET;
    my $sock = IO::Socket::INET->new(Listen    => 5, 
#					 LocalAddr => 'localhost', 
				     LocalPort => $listenPort, 
				     Proto     => 'tcp', 
				     Reuse     => 1);
    print "listening on $listenPort\n" unless $self->{-stfu};

    my $quit = 0;
    while ((my $socket = $sock->accept()) && !$quit) {
	my $input;
	while ($input .= <$socket>) {
	    if ($input =~ m/^(.*?)\nEND\n(.*)$/sm || $input =~ m/^(.*?)\r\nEND\r\n(.*)$/sm) {
		my ($query, $next) = ($1, $2);
		print "my (\$nodes, \$selects, \$messages, \$proofs) = \$queryHandler->interpret($query, undef, \$self->{QUERY_LANG}, 0x00);\n" unless $self->{-stfu};
		eval {
		    $self->_executeQuery($queryLang, $query, undef, $reportStream, $reportParms, $socket);
		    $self->{-queryHandler}->clear();
		}; if ($@) {if (my $ex = &catch('W3C::Util::Exception')) {
		    warn $ex->toString();
			    } else {
				warn $@;
			    }}
		$input = $next;
		last;
	    }
	}
	warn "lost connection";
	close($socket);
    }
}

# callback from Scriptopt
sub pushLocation {
    my ($self, $location) = @_;
    push (@{$self->{LOCATION_STACK}}, $location);
    $self->_setAttribution($location);
}

sub popLocation {
    my ($self, $location) = @_;
    if ($self->{LOCATION_STACK}[-1] ne $location) {
	&throw(new W3C::Util::Exception(-message => "popping location \"$location\" when expecting  \"$self->{LOCATION_STACK}[-1]\""));
    }
    pop (@{$self->{LOCATION_STACK}});
    my $location = @{$self->{LOCATION_STACK}} ? $self->{LOCATION_STACK}[-1] : undef;
    $self->_setAttribution($location);
}

sub _setAttribution {
    my ($self, $uri) = @_;
    my $attrib = $uri ? $self->{-context}->makeInputAttribution($uri) : undef;
    $self->{-queryHandler}->setSourceAttribution($attrib);
}

sub _executeQuery {
    my ($self, $queryLang, $query, $baseUri, $reportStream, $reportParms, $outputHandle) = @_;

    # Provide a data: attribution if we are reading things off the
    # command line.
    if (!@{$self->{LOCATION_STACK}}) {
	# Lightening up on requires so copping this from CGI::Util
	my $queryStr = $query;
	$queryStr=~s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
	# Was:
	# use CGI::Util qw(unescape escape);
	# my $queryStr = &escape($query);
	$self->_setAttribution("data:,$queryStr");
    }

    # $query =~ s/^\#[^\n]*//; # Why on earth did I do this?
    my $queryHandler = $self->{-queryHandler};

    my $debug = 0; # 0x1f;
    my $messages;
    (undef, $self->{SELECTS}, $messages, undef) = $queryHandler->interpret($query, $baseUri || $self->{LOCATION_STACK}[-1], $queryLang, $debug);
    $self->{TempRS} = $queryHandler->getResultSet();
    if ($main::bnodeIsExt && 0) {
	$self->{TempRS}->eliminateRedundantBNodes();
    }
}

sub _executeManifest {
    my ($self, $manifestFile, $row, $testResults, $iName, $iRes, $iError, $testList, $outputHandle) = @_;
    my $baseUri = $manifestFile;

    # Parse manifest file.
    $self->newQuery();
    my $manifestDB = $self->{-context}{-rdfDB};
    $self->_parseData($manifestFile, &_guessType($manifestFile), undef, 0);
    # Swap back, execute queries on the test database.
    my ($entries, undef, undef, undef) = $self->{-queryHandler}->interpret("
PREFIX mf: <$NS_dawgManifest>
PREFIX rdfs: <$NS_rdfs>
SELECT ?entry ?name ?comment ?result ?type
 WHERE { ?m mf:entries members(?entry) .
         ?entry mf:name    ?name .
         OPTIONAL { ?entry a ?type } .
         OPTIONAL { ?entry rdfs:comment ?comment } .
         OPTIONAL { ?entry mf:result  ?result } }", $baseUri, $QL_SPARQL, 0x00);

    # Register this namespace
    if ($manifestFile =~ m/\/([^\/]+)\/manifest\.ttl$/) {
	my $prefix = $1;
	my $namespace = "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/$prefix/manifest#";
	$self->{-context}{NAMESPACE_HANDLER}->addNamespace($prefix, $namespace);
    }

#     my $t = "sort-6"; $entries = [sort {$a->[0]->getString() eq $t ? -1 : $b->[0]->getString() eq $t ? 1 : 0} @$entries];

    my $testNames = @$testList ? $testList : [];
    my $entriesByName = {};
    foreach my $entrySet (@$entries) {
	my ($entry, $name, $comment, $result, $type) = @$entrySet;
	my $nameStr = $name->getString();
	if (!@$testList) {
	    push (@$testNames, $nameStr);
	}
	my $commentStr = $comment == $Value_NULL ? '@@ no rdfs:comment supplied @@' : $comment->getString();
	my $resultURI = $result == $Value_NULL ? undef : $result->getUri();
	my $syntaxTest = $type == $Value_NULL ? 0 : 
	    $type->getUri() eq $TYPE_posSyntax ? \$TYPE_posSyntax : 
	    $type->getUri() eq $TYPE_negSyntax ? \$TYPE_negSyntax :
	    undef;
	$entriesByName->{$nameStr} = [$entry, $name, $commentStr, $resultURI, $syntaxTest];
    }

    foreach my $nameStr (@$testNames) {
	if (!$entriesByName->{$nameStr}) {
	    print STDERR "!test \"$nameStr\" not found\n";
	    next;
	}
	my ($entry, $name, $commentStr, $resultURI, $syntaxTest) = @{$entriesByName->{$nameStr}};
	my $disposition;

	my $newRow = $row->duplicate;
	eval {
	    $self->runTest($nameStr, $commentStr, $resultURI, $syntaxTest, 
			   \$disposition, $baseUri, $manifestDB, 
			   " in test $nameStr in manifest $manifestFile", 
			   $newRow, $iError, $outputHandle);
	}; if ($@) {
	    my $errStr;
	    if (my $ex = &catch('W3C::Rdf::ExtensionsNotSupportedException')) {
		$errStr = join("\n ", @{$ex->getUris()});
		$disposition = $self->{-atomDictionary}->getString('unsupported extensions', undef, 'PLAIN');
	    } elsif (my $ex = &catch('W3C::Rdf::UnknownFormatException')) {
		$errStr = $ex->getFormat();
		$disposition = $self->{-atomDictionary}->getString('unsupported format', undef, 'PLAIN');
	    } elsif (my $ex = &catch('W3C::Rdf::UnmentionedVariableException')) {
		$errStr = $ex->getVar();
		$disposition = $self->{-atomDictionary}->getString('unknown variable', undef, 'PLAIN');
	    } elsif (my $ex = &catch('W3C::Rdf::UnknownFunctionException')) {
		$errStr = $ex->getFunction();
		$disposition = $self->{-atomDictionary}->getString('unknown function', undef, 'PLAIN');
	    } elsif (my $ex = &catch('W3C::Util::Exception')) {
		$errStr = $ex->toString();
	    } else {
		$errStr = $@;
	    }
	    $newRow->set($iError, $self->{-atomDictionary}->getString($errStr, undef, 'PLAIN'));
	}

	# Store disposition.
	$newRow->set($iName, $name);
	$newRow->set($iRes, $disposition);
	$self->addResultTriples($entry, $newRow, $name, $commentStr, $disposition, $newRow->get($iError));
    }
    $row->eliminate;
}

sub runTest {
    my ($self, $nameStr, $commentStr, $resultURI, $syntaxTest, $pDisposition, $baseUri, $manifestDB, $contextStr, $newRow, $iError, $outputHandle) = @_;

    # Test result constants:
    my $notTested = $self->{-atomDictionary}->getString('not tested', undef, 'PLAIN');
    my $noQuerySpecified = $self->{-atomDictionary}->getString('no query specified', undef, 'PLAIN');
    my $queryNotFound = $self->{-atomDictionary}->getString('query not found', undef, 'PLAIN');
    my $proposed = $self->{-atomDictionary}->getString('results proposed', undef, 'PLAIN');
    my $passed = $self->{-atomDictionary}->getString('passed', undef, 'PLAIN');
    my $failed = $self->{-atomDictionary}->getString('failed', undef, 'PLAIN');
    my $manifestError = $self->{-atomDictionary}->getString('manifest error', undef, 'PLAIN');
    my $dataError = $self->{-atomDictionary}->getString('data error', undef, 'PLAIN');
    my $queryError = $self->{-atomDictionary}->getString('query error', undef, 'PLAIN');
    my $outUri = $self->{-atomDictionary}->getUri("pipe://stdout/", undef);
    my $outAttribution = $self->{-atomDictionary}->getGroundFactAttribution($outUri, undef, undef, undef);

    $$pDisposition = $manifestError;

    my $query;
    print $outputHandle "executing $nameStr \"$commentStr\"\n" unless ($self->{-stfu});

    # Read requires.
    $self->{-queryHandler}->clear;
    $self->{-queryHandler}{-rdfDB} = $self->{-context}{-rdfDB} = $manifestDB;
    my ($reqs, undef, undef, undef) = $self->{-queryHandler}->interpret("
PREFIX mf: <$NS_dawgManifest>
SELECT ?requires
 WHERE { ?entry  mf:name   \"$nameStr\" .
         ?entry  mf:requires ?requires }", $baseUri, $QL_SPARQL, 0x00);
    $self->newQuery();
    my $requires = {};
    foreach my $req (@$reqs) {
	print $outputHandle "  require ", $req->[0]->getUri, "\n";
	$requires->{$req->[0]->getUri} = 1;
    }

    # Read actions.
    $self->{-queryHandler}->clear;
    $self->{-queryHandler}{-rdfDB} = $self->{-context}{-rdfDB} = $manifestDB;
    my ($actions, $actionSelects, undef, undef) = $self->{-queryHandler}->interpret(
	$syntaxTest ? 
"PREFIX mf: <$NS_dawgManifest>
SELECT ?action
 WHERE { ?entry  mf:name   \"$nameStr\" .
         ?entry  mf:action   ?action }" : 
"PREFIX mf: <$NS_dawgManifest>
SELECT ?verb ?target
 WHERE { ?entry  mf:name   \"$nameStr\" .
         ?entry  mf:action ?action .
         ?action ?verb     ?target }", $baseUri, $QL_SPARQL, 0x00);
    $self->newQuery();

    # Add extensions before parsing data or queries.
    if (!$self->{-ignoreRequires}) {
	$self->{-queryHandler}->addTypes($W3C::Rdf::Algae2::TypeExtensions, keys %$requires);
    }

    foreach my $action (@$actions) {
	my ($verb, $target) = $syntaxTest ? 
	    ($self->{-atomDictionary}->getUri("${NS_dawgQuery}query", undef), $action->[0]) : 
	    @$action;
	my $verbStr = $verb->getUri();
	my $targetStr = $target->toString();

	if ($verbStr eq "${NS_dawgQuery}data" || 
	    $verbStr eq "${NS_dawgQuery}graphData") {
	    print $outputHandle "  reading $targetStr\n" unless ($self->{-stfu});
	    $$pDisposition = $dataError;
	    $self->_parseData($targetStr, &_guessType($targetStr), undef, $verbStr eq "${NS_dawgQuery}graphData");
	    $$pDisposition = $manifestError;
	} elsif ($verbStr eq "${NS_dawgQuery}query") {
	    $$pDisposition = $queryError;
	    if ($query) {
		&throw(new W3C::Util::Exception(-message => "second query \"$targetStr\"$contextStr"));
	    }
	    $query = $targetStr;
	    $$pDisposition = $manifestError;
	} else {
	    &throw(new W3C::Util::Exception(-message => "unknown action \"$verbStr\"$contextStr"));
	}
    }
    if (!$query) {
	$$pDisposition = $noQuerySpecified;
	# return;
	&throw(new W3C::Util::Exception(-message => "no query defined$contextStr"));
    }

    $$pDisposition = $notTested;

    # Read the query file.
    print $outputHandle "  executing query $query\n" unless ($self->{-stfu});
    my $q;
    eval {
	$q = &W3C::Rdf::RdfApp::getInputSource($query)->getByteStream();
	&utf8::decode($q);
    }; if ($@) {if (my $ex = &catch('W3C::Util::FileNotFoundException')) {
	$$pDisposition = $queryNotFound;
	return;
    } else {
	&throw();
    }}
    $self->{-queryHandler}->setParserMode($syntaxTest);
    $self->pushLocation(new URI($query));
    eval {
	$self->{-queryHandler}->interpret($q, $baseUri, $QL_SPARQL); 
    };
    my $err = $@;
    $self->popLocation(new URI($query));
    $self->{-queryHandler}->setParserMode(0);
    foreach my $source ($self->{-queryHandler}->getSourceNames()) {	    
	$self->{-queryHandler}->dropSource($source);
    }
    $@ = $err;
    if ($@) {if ($syntaxTest == \$TYPE_negSyntax && (my $ex = &catch('W3C::Util::Exception'))) {
	$$pDisposition = $passed;
	return;
    } else {
	if ($syntaxTest == \$TYPE_posSyntax) {
	    $$pDisposition = $failed;
	    # Fall through to &throw to give the tester the parser feedback.
	}
	$self->{-queryHandler}->clearTemplateDB(); # ...just in case it was a construct.
	&throw();
    }}
    $self->{-queryHandler}->delTypes(keys %$requires);
    my $testRS = $self->{-queryHandler}->getResultSet();
    my $constructedDB = undef;
    if ($testRS->getPresentationMimetype() eq 'application/rdf+xml') {
	$constructedDB = $self->{-queryHandler}->getTemplateDB();
	$self->{-queryHandler}->clearTemplateDB();
    }
    if ($syntaxTest) {
	$$pDisposition = $syntaxTest == \$TYPE_posSyntax ? $passed : $failed;
	return;
    }
    my $selects = $self->{-queryHandler}->getLabels();

    if ($resultURI) {
	print $outputHandle "  expect <$resultURI>\n" unless ($self->{-stfu});
	$$pDisposition = $failed;

	$self->newQuery();
	eval {
	    if ($constructedDB) {
		my $refDB = new W3C::Rdf::RdfDB(-atomDictionary => $self->{-atomDictionary});
		my $oldDB = $self->{-queryHandler}{-rdfDB};
		$self->{-queryHandler}{-rdfDB} = $refDB;
		$$pDisposition = $dataError;
		$self->_parseData($resultURI, &_guessType($resultURI), undef, 0);
		$$pDisposition = $failed;
		$self->{-queryHandler}{-rdfDB} = $oldDB;
		if ((my @errors = $refDB->compare($constructedDB, ALGAE2 => $self->{-queryHandler})) != 0) {
		    $self->summarizeErrors(\@errors, $newRow, $iError);
		} else {
		    $$pDisposition = $passed;
		}
		if ($self->{Summary}) {
		    &throw(new W3C::Util::ProgramFlowException());
		}
	    } else {
		my ($nodes, $proofs) = $testRS->gatherCollect();
		my $boolean = @$nodes == 1 && ($nodes->[0] == 0 || $nodes->[0] == 1) ? $nodes->[0] : undef;
		$self->_parseRSResults($resultURI, $baseUri, $boolean, $pDisposition, $failed, $passed, $testRS, $newRow, $iError);
		if ($self->{Summary}) {
		    print $outputHandle $self->arrayRenderer($testRS, $selects, undef, {-showTypes => 1});
		}
	    }
	}; if ($@) {if ($self->{CreateResults} && (my $ex = &catch('W3C::Util::FileNotFoundException'))) {
	    my $filename = $ex->getFilename;
	    print $outputHandle "  creating $filename:\n";
	    open (RES, '>:utf8', $filename) || &throw(new W3C::Util::FileCreationException(-filename => $filename));

	    if ($constructedDB) {
		print RES $constructedDB->dumpN3;
	    } else {
		my $reportType = {'turtle'=>'-rs', 'srx'=>'-x', 'rdf'=>'-rdfxml'}->{&_guessType($filename)};
		$self->_proposeResults($selects, $testRS, $reportType, $outAttribution, $outputHandle, \ *RES);
	    }

	    $$pDisposition = $proposed;
	    close RES;
	} else {
	    &throw();
	}}
    } else {
	print $outputHandle "  propose results:\n";
	if ($constructedDB) {
	    print $outputHandle $constructedDB->dumpN3;
	} else {
	    $self->_proposeResults($selects, $testRS, '-rs', $outAttribution, $outputHandle, $outputHandle);
	}
	$$pDisposition = $proposed;
    }
}

sub _proposeResults {
    my ($self, $selects, $testRS, $reportType, $outAttribution, $arrayHandle, $reportHandle) = @_;
    print $arrayHandle @$selects ? $self->arrayRenderer($testRS, $selects, undef, {-showTypes => 1}) : 
	$testRS->testTrue ? "TRUE\n" : "FALSE\n";
    print $reportHandle $self->showQueryResults(undef, $testRS, $outAttribution, $selects, undef, {$reportType => $testRS->isSorted ? 'sorted' : 'unsorted'});
}

sub addResultTriples {
    my ($self, $entry, $newRow, $name, $commentStr, $disposition, $error) = @_;
    my $atoms = $self->{-atomDictionary};
    my $passed = $disposition == $atoms->getString('passed', undef, 'PLAIN');

    my $hostname = `localhost`;
    eval {$hostname = `hostname -f`; chomp $hostname; };
    my $username = (getpwuid($>))[0];
    my $testName = "$username\@$hostname,$self->{STARTTIME}";
    my $attrib = $atoms->getGroundFactAttribution(
	$atoms->getAbsoluteUri("${NS_algaeTests}$testName"));
    my $assertion = $self->{-atomDictionary}->createBNode($attrib);
    my $result = $self->{-atomDictionary}->createBNode($attrib);
    my $outcome = $self->{-atomDictionary}->getAbsoluteUri(
	$passed ?
	"${NS_earl}pass" : 
	"${NS_earl}fail");
    $newRow->addProofs([
	$atoms->getStatement($atoms->getAbsoluteUri("${NS_earl}test"), $assertion, $entry, undef, $attrib), 
	$atoms->getStatement($atoms->getAbsoluteUri("${NS_earl}result"), $assertion, $result, undef, $attrib), 
	$atoms->getStatement($atoms->getAbsoluteUri("${NS_rdf}type"), $result, $atoms->getAbsoluteUri("${NS_earl}TestResult"), undef, $attrib), 
	$atoms->getStatement($atoms->getAbsoluteUri("${NS_earl}outcome"), $result, $outcome, undef, $attrib), 
		       ]);
    if ($self->{EARL}) {
	# Common
	my $federate = $atoms->getAbsoluteUri($NS_federate);
	if (!$self->{-algaeNode}) {
	    $self->{-algaeNode} = $self->{-atomDictionary}->createBNode($attrib);
	    my $algaeDate = $atoms->getString('$Date: 2007-11-13 03:06:23 $', undef, 'PLAIN'); # umm, now, i guess
	    my $algaeTitle = $atoms->getString('Algae2', undef, 'PLAIN');
	    my $algaeVersion = $atoms->getString('$Revision: 1.171 $', undef, 'PLAIN'); # umm, now, i guess
	    my $release = $self->{-atomDictionary}->createBNode($attrib);
	    $newRow->addProofs([
		$atoms->getStatement($atoms->getAbsoluteUri("${NS_rdf}type"), $self->{-algaeNode}, $atoms->getAbsoluteUri("${NS_doap}Project"), undef, $attrib), 
		$atoms->getStatement($atoms->getAbsoluteUri("${NS_doap}name"), $self->{-algaeNode}, $algaeTitle, undef, $attrib), 
		$atoms->getStatement($atoms->getAbsoluteUri("${NS_doap}release"), $self->{-algaeNode}, $release, undef, $attrib), 
		$atoms->getStatement($atoms->getAbsoluteUri("${NS_rdf}type"), $release, $atoms->getAbsoluteUri("${NS_doap}Version"), undef, $attrib), 
		$atoms->getStatement($atoms->getAbsoluteUri("${NS_doap}name"), $release, $algaeVersion, undef, $attrib), 
			       ]);
	    $self->{-assertorNode} = $self->{-atomDictionary}->createBNode($attrib);
	    my $testerName = $self->{TESTERname} || (split(',', (getpwuid($>))[6]))[0];
	    my $assertorName = $atoms->getString($testerName, undef, 'PLAIN');
	    my $testerMbox = $self->{TESTERmbox} || 'mailto:$username@$hostname';
	    my $assertorMbox = $atoms->getAbsoluteUri($testerMbox);
	    my $ericPpage = $atoms->getAbsoluteUri($NS_algae);
	    $newRow->addProofs([
		$atoms->getStatement($atoms->getAbsoluteUri("${NS_rdf}type"), $self->{-assertorNode}, $atoms->getAbsoluteUri("${NS_foaf}Person"), undef, $attrib), 
		$atoms->getStatement($atoms->getAbsoluteUri("${NS_foaf}name"), $self->{-assertorNode}, $assertorName, undef, $attrib), 
		$atoms->getStatement($atoms->getAbsoluteUri("${NS_earl}mbox"), $self->{-assertorNode}, $assertorMbox, undef, $attrib), 
			       ]);
	}

	# Assertion
	my $nameStr = $name->getString;
	my $comment = $atoms->getString($commentStr, undef, 'PLAIN');
	$newRow->addProofs([
	    $atoms->getStatement($atoms->getAbsoluteUri("${NS_dc}description"), $entry, $comment, undef, $attrib), 
	    $atoms->getStatement($atoms->getAbsoluteUri("${NS_earl}subject"), $assertion, $self->{-algaeNode}, undef, $attrib), 
	    $atoms->getStatement($atoms->getAbsoluteUri("${NS_rdf}type"), $assertion, $atoms->getAbsoluteUri("${NS_earl}Assertion"), undef, $attrib), 
	    $atoms->getStatement($atoms->getAbsoluteUri("${NS_earl}assertedBy"), $assertion, $self->{-assertorNode}, undef, $attrib), 
			   ]);
	if ($error) {
	    $newRow->addProofs([
		$atoms->getStatement($atoms->getAbsoluteUri("${NS_dc}description"), $result, $error, undef, $attrib), 
			       ]);
	}
    }
    if (0) {
	my $nsh = new W3C::Util::NamespaceHandler();
	my $u = $attrib->getSource->getUri; chop $u;
	$nsh->addNamespace('test', $u);
	foreach my $p qw(NS_algae NS_algaeTests NS_dawgTests NS_dc NS_dcTerms NS_earl NS_foaf NS_rdf) {
	    $nsh->addNamespace("\L$p", eval "\$$p"); }
	print $newRow->toString(-proofs => 1, -namespaceHandler => $nsh), "\n";
    }
}

sub _guessType { # static
    my ($name) = @_;
    return $name =~ m/\.(n3|ttl|nt)/ ? 'turtle' : 
	$name =~ m/\.(srx)/ ? 'srx' : 
	$name =~ m/\.(srj)/ ? 'jason' : 'rdf'
}

sub newQuery {
    my ($self, $keepDB) = @_;
    # Shamelessly muck with private variables.
    if (!$keepDB) {
	$self->{-queryHandler}{-rdfDB} = $self->{-context}{-rdfDB} = 
	    new W3C::Rdf::RdfDB(-atomDictionary => $self->{-atomDictionary});
    }
    $self->{-queryHandler}{RESULT_SET} = new W3C::Rdf::ResultSet(-atomDictionary => $self->{-atomDictionary});
    $self->{-queryHandler}{LABELS} = [];
}

sub _parseRSResults {
    my ($self, $resultURI, $baseUri, $boolean, $pDisposition, $failed, $passed, $testRS, $newRow, $iError) = @_;
    $self->_parseData($resultURI, &_guessType($resultURI), undef, 0);
    my ($nodes, $selects, undef, $proofs) = $self->{-queryHandler}->interpret("
PREFIX rs: <$NS_rs>
SELECT ?var
 WHERE { ?rs rs:resultVariable ?var }", $baseUri, $QL_SPARQL, 0x00);
    $self->newQuery(1);

    my @vars = undef;
    my $resultsQuery;
    if (defined $boolean) {
	$resultsQuery = "
PREFIX rs: <$NS_rs>
SELECT ?b_
 WHERE { ?r_ rs:boolean ?b_ }";
    } else {
	@vars = map {$_->[0]->getString()} @$nodes;
	my $vars = join(' ', map {"?$_"} @vars);
	my $bindings = join("\n", map {"
         OPTIONAL { ?s_ rs:binding ?b_$_ .
                    ?b_$_ rs:variable \"$_\" .
                    ?b_$_ rs:value ?$_ } ."} @vars);
	$resultsQuery = "
PREFIX rs: <$NS_rs>
SELECT ?index_ $vars
 WHERE { ?r_ rs:solution ?s_ .
         OPTIONAL { ?s_ rs:index ?index_ } .$bindings }
 ORDER BY ?index_";
    }
    my ($nodes, $selects, undef, $proofs) = $self->{-queryHandler}->interpret($resultsQuery, $baseUri, $QL_SPARQL, 0x00);
    my $refRS = $self->{-queryHandler}->getResultSet();
    my $errorString = undef;
    if (defined $boolean) {
	$$pDisposition = $failed;
	eval {
	    if (@{$nodes->[0]} == 1) {
		if ($self->{-queryHandler}->getEBV($nodes->[0][0]) == $boolean) {
		    $$pDisposition = $passed;
		} else {
		    $newRow->set($iError, $self->{-atomDictionary}->getString('yielded:'.($boolean ? 'TRUE' : 'FALSE'), undef, 'PLAIN'));
		}
	    }
	};
    } elsif ((my @errors = $refRS->compare($testRS, [@vars], $self->{-queryHandler}, -verbose => 0)) != 0) {
	$$pDisposition = $failed;
	#print "got\n", $testRS->toString, "\nexpected\n", $refRS->toString, "\n";
	$self->summarizeErrors(\@errors, $newRow, $iError);
    } else {
	$$pDisposition = $passed;
    }
}

sub summarizeErrors { 
    my ($self, $errors, $newRow, $iError) = @_;
    my @errorLines;
    my $lastErrIndex = undef;
    for (my $errNo = 0; $errNo < @$errors; $errNo++) {
	my $prefix = @errorLines == 0 ? '' : ' ';

	if ($errors->[$errNo] =~ m/^(\d+):(<|>) (.*)$/) {
	    my ($errIndex, $direc, $text) = ($1, $2, $3);
	    if (defined $lastErrIndex && 
		(($direc eq '>' && $lastErrIndex == $errIndex) || 
		 ($direc eq '<' && $lastErrIndex == $errIndex-1) )){
	    } else {
		push (@errorLines, "$prefix$errIndex: ");
	    }
	    $lastErrIndex = $errIndex;
	    my $marker = $direc eq '>' ? '+' : '-';
	    push (@errorLines, "  $marker $text");

	} else {
	    push (@errorLines, "$prefix$errors->[$errNo]");
	}
    }
    $newRow->set($iError, $self->{-atomDictionary}->getString(join("\n", @errorLines), undef, 'PLAIN'));
}

sub _parseData {
    my ($self, $resource, $language, $baseUri, $named) = @_;
    $self->pushLocation($resource);
    eval {
	my $input = &W3C::Rdf::RdfApp::getInputSource($resource);
	my $text = $input->getByteStream();
	&utf8::decode($text);
	my $oldDB = $self->{-queryHandler}{-rdfDB};
	if ($named) {
	    ## $self->{-queryHandler}->getSourceByName($resource);
	    require W3C::Rdf::RdfDB;
#	my $fakeParser = new W3C::Rdf::AlgaeCompileTree::FakeParser(undef, -sourceAttribution => $self->{-context}->getInputAttribution, -atomDictionary => $self->{-atomDictionary});
	    my $db = new W3C::Rdf::RdfDB(-atomDictionary => $self->{-atomDictionary});
	    $self->{-queryHandler}->addSource($resource, $db);
	    $self->{-queryHandler}{-rdfDB} = $db;
	}
	$self->{-context}->parse($text, 
				 $baseUri || $input->getPublicId(), 
				 $language, $self->{-queryHandler});
	$self->{-queryHandler}{-rdfDB} = $oldDB;
    };
    my $ex = $@;
    $self->popLocation($resource);
    if ($ex) {
	&throw($ex);
    }
}

sub _report {
    my ($self, $reportStream, $reportParms, $limit) = @_;
    my $queryHandler = $self->{-queryHandler};
    my $rs = $queryHandler->getResultSet();

    my $queryHandler = $self->{-queryHandler};

    my $outUri = $self->{-atomDictionary}->getUri("pipe://stdout/", undef);
    my $outAttribution = $self->{-atomDictionary}->getGroundFactAttribution($outUri, undef, undef, undef);
    my $serializationParms = {};
    my $reporter = $self->makeReportStream($reportStream, $reportParms, $serializationParms);
    return $self->showQueryResults($reporter, $self->{TempRS}, $outAttribution, $self->{SELECTS}, $limit, $serializationParms);
}

# ./algae -d"\"W3C::Rdf::SqlDB\" (\"name:local:/db\" \"\"properties:rdf.prop)" -i

sub interactiveQueryBuilder {
    my ($self) = @_;
    eval {require Term::ReadLine;}; if ($@) {&throw()}
    my $term = new Term::ReadLine 'Query Builder';
    my $attribs = $term->Attribs;

    # Setup terminal and readline.
    my $origAttribs = {};
    my $SET1 = '"\'`('; # '"\'`<>!(';
    my $overrideAttribs = {'basic_word_break_characters' => $SET1, 
			   'completer_word_break_characters' => $SET1};
    foreach my $attrib (keys %$overrideAttribs) {
	$origAttribs->{$attrib} = $attribs->{$attrib};
	$attribs->{$attrib} = $overrideAttribs->{$attrib};
    }
    $term->ornaments('md,me,,');	# bold face prompt

    my $prompt = "command: ";

    # Some diagnostics to improve life for the user of the default perl inst.
    eval {require Term::ReadLine::Gnu}; if (!$@) {$IFACE = $IFACE_Gnu;}
    else {
	eval {require Term::ReadLine::Perl}; if (!$@) {$IFACE = $IFACE_Perl}
    }
    print DBG $IFACE == $IFACE_Gnu ? "Using GNU readline interface.\n" :
	$IFACE == $IFACE_Perl ? "Using perl readline interface.\n" :
	"Using unknown readline interface.\nYou may need Term::ReadLine::Gnu or Term::ReadLine::Perl.\n";

    my $preput = '';
    my $p = new W3C::Rdf::RLAlgaeParser('asdfasdf', $self->{-queryHandler}, 'unix://localhost/stdin');
    while (defined ($_ = $term->readline($prompt, $preput)) ) {
	my $res = eval {
	    my $query = $_;
	    if ($query =~ m/^ *(quit|exit|bye) *$/) {goto quitWithCommand}
	    my $actions = $p->parse(0x00);
	}, "\n";
	if ($@) {
	    if (my $ex = &catch('W3C::Util::Exception')) {
		warn $ex->toString;
	    } else {
		warn $@;
	    }
	};
	$term->addhistory($_) if /\S/;
    }
    print DBG "<EOF>\n";
  quitWithCommand:
    foreach my $attrib (keys %$origAttribs) {
	$attribs->{$attrib} = $origAttribs->{$attrib};
    }
}

sub _hairyCompletionFunction {
    my ($self, $term, $pCounter, $text, $line, $start, $end) = @_;
    print "_hairyCompletionFunction($term, $pCounter, $text, $line, $start, $end)\n";
    my $attribs = $term->Attribs;

    if ($line =~ /^\s*\(/ && 1) {
	my ($tree, $delim, $newSymbs, $variableSet) = (undef, undef, undef, {});
	my $algae = substr($line, 0, $start);
	eval {
	    ($tree, $delim, $newSymbs) = 
		new W3C::Util::SExpr()->parse(\$algae, undef, 
					      {-noTokensAtRoot => 1, 
					       -noNonTermException => 1, 
					       -laterSymbols => $variableSet});
	};
    }

    if (substr($line, 0, $start) =~ /^\s*$/) {
	$attribs->{completion_word} = ['quit', 'exit', 'bye', '('];
	undef $attribs->{completion_display_matches_hook};
	return $term->completion_matches($text,
					 $attribs->{'list_completion_function'});
    } elsif ($line =~ /^(\s*\(()\s*)(\w*)$/) {
	my $soFar = $1;
	my $len = length $soFar;
	my $cmds = ['ask', 'collect', 'namespace', 'attach'];
	my @list = grep {substr($_, 0, $len) eq $soFar} @$cmds;
	$attribs->{completion_word} = [@$cmds]; # @list];
	undef $attribs->{completion_display_matches_hook};
	return $term->completion_matches($text,
					 $attribs->{'list_completion_function'});
    } elsif ($line =~ /^(\s*\(()\s*)(\w*) +$/) {
	$attribs->{completion_word} = ['\'('];
	undef $attribs->{completion_display_matches_hook};
	return $term->completion_matches($text,
					 $attribs->{'list_completion_function'});
    } elsif ($line =~ /^(\s*\(()\s*)(\w*) *\'\( *([\w\:\/0-9\_\-]*)$/) {
#	$attribs->{completion_display_matches_hook} = sub {$self->db_display_match_list($term, @_)};
	return $term->completion_matches($text,
					 sub {$self->db_name_completion_function($term, $pCounter, ['('], @_)});
    } elsif ($line =~ /^(\s*\(()\s*)(\w*) *\'\( *([\w\:\/0-9\_\-]+) +$/) {
	$attribs->{completion_word} = ['('];
	undef $attribs->{completion_display_matches_hook};
	return $term->completion_matches($text,
					 $attribs->{'list_completion_function'});
    } else {			# put mput lcd
	undef $attribs->{completion_display_matches_hook};
	return ();		# local file name completion
    }
}

sub db_display_match_list {
    my ($self, $term, $matches, $num_matches, $max_length) = @_;
    #print "\n\nMATCH:{", &_ray($matches), ", $num_matches, $max_length\n\n";
#    map { $_ =~ s|.*/([^/])|\1|; }(@{$matches});
    $term->display_match_list($matches);
    $term->forced_update_display;
    #print " ==> $_}";
}

sub _ray {
    my ($array, @rest) = @_;
    if (ref $array eq 'ARRAY') {
	my $ret = join (' ', @$array);
	return "[$ret]";
    } else {
	my $ret = join (' ', $array, @rest);
	return "($ret)";
    }
}

sub db_name_completion_function ( $$ ) {
    my($self, $term, $pCounter, $suffix, $text, $state) = @_;
    my $attribs = $term->Attribs;
    my $entry;

    my @list = ($self->{-queryHandler}->getSourceNames(), @$suffix);
    #my @list = (qw(one two three), @$suffix);
    #print &_ray(@list), "\n";
    #print "\ndb_name_completion($text, $state)";
    #$self->dumpVars($term);
    if (!$state) {
	$$pCounter = 0;
	# $attribs->{completion_append_character} = ' ';
    } else {
	$$pCounter++;
    }
    my $ret = undef;
    for (; $$pCounter < @list; $$pCounter++) {
	if ($list[$$pCounter] =~ m/^$text/) {
	    $ret = $list[$$pCounter];
	    last;
	}
    }
    #print " ==> $ret\n";
    return $ret;
}

sub dumpVars {
    my ($self, $term) = @_;
    my $attribs = $term->Attribs;
    my @list = ('completion_query_items', 'basic_word_break_characters', 'basic_quote_characters', 'completer_word_break_characters', 'completer_quote_characters', 'filename_quote_characters', 'special_prefixes', 'completion_append_character', 'completion_suppress_append', 'completion_mark_symlink_dirs', 'ignore_completion_duplicates', 'filename_completion_desired', 'filename_quoting_desired', 'attempted_completion_over', 'completion_type', 'inhibit_completion');
    foreach my $attrib (@list) {
	print "$attrib: \"$attribs->{$attrib}\"\n";
    }
}

sub makeReportStream {
    my ($self, $reportStream, $reportParms, $serializationParms) = @_;
    if (exists $SerializerClassAbbreviations{$reportStream}) {
	$reportStream = $SerializerClassAbbreviations{$reportStream};
    }

    foreach my $reportParm (@$reportParms) {
	if ($reportParm =~ m/([^= ]+) = (.*)/x) {
	    $serializationParms->{$1} = $2;
	} else {
	    $serializationParms->{$reportParm} = 1;
	}
    }

    my $ret = undef;
    if ($reportStream) {

	# @@@ duplicated
	my $sourceStr = "pipe://stdout/";
	my $outUri = $self->{-atomDictionary}->getUri($sourceStr, undef);
	my $outAttribution = $self->{-atomDictionary}->getGroundFactAttribution($outUri, undef, undef, undef);
	my $source = $outAttribution->getSource()->getUri();
	my $nsMap = {'r' => $NS_rdf, 
		     'attrib' => $NS_attrib};

	# Put the flags together.
	my $flags = {-indent => 3, 
		     -publicId => $sourceStr, 
		     -systemId => $sourceStr, 
		     -noRdfTag => 0, 
		     -RdfDB => $self->{-context}->getRdfDB(), 
		     -allowAnonymousRefs => 1, 
		     -ignoreReifications => 1, 
		     -ignoreAnonymousReifications => 1, 
		     -neededNamespaces999 =>[keys %$nsMap],
		     -atomDictionary => $self->{-atomDictionary}, 
		     -resource => $outAttribution, 
		     -createNamespaces => 1, 
		     -importMap => $self->{-context}->getNamespaceHandler()};

	# Incorporate serialization parms.
	$flags = {%$flags, %$serializationParms};
	# n3 could use -nestNonAnonNode => 0, 

	# Build the serializer class.
	$ret;
	eval "require $reportStream; \$ret = new $reportStream(\$flags);";
	if ($@) {&throw();}
    }
    return $ret;
}

sub showQueryResults {
    my ($self, $reporter, $resultSet, $outAttribution, $selects, $limit, $serializationParms) = @_;
    my ($nodes, $proofs) = $resultSet ? $resultSet->gatherCollect() : ();
    my $source = $outAttribution->getSource()->getUri();
    my @ret;
    my $nsMap = {'r' => $NS_rdf, 
		 'attrib' => $NS_attrib};
    if ($reporter) {
	# @$proofs is an array of proofs.
	# Each proof is an array of statements.
	my $db = new W3C::Rdf::RdfDB(-atomDictionary => $self->{-atomDictionary});
	if ($serializationParms->{-resultSet}) {
	    my $at = $self->{-atomDictionary};
	    my $RS = 'http://jena.hpl.hp.com/2003/03/result-set#';
	    $nsMap->{'rs'} = $RS;
	    my $aU = $at->getUri('stdio:stdout');
	    my $a = $at->getGroundFactAttribution($aU, undef, undef, undef);
	    my $root = $at->createBNode($a);
	    my $tP = $at->getUri("${NS_rdf}type");
	    my $rsT = $at->getUri("${RS}ResultSet");
	    my $rbT = $at->getUri("${RS}ResultBinding");
	    my $rvP = $at->getUri("${RS}resultVariable");
	    my $sizeP = $at->getUri("${RS}size");
	    my $solnP = $at->getUri("${RS}soln");
	    my $rslnT = $at->getUri("${RS}ResultSolution");
	    my $bindingP = $at->getUri("${RS}binding");
	    my $varP = $at->getUri("${RS}variable");
	    my $valP = $at->getUri("${RS}value");
	    $db->addTriple($at->getStatement($tP, $root, $rsT), $a);
	    my $variables = [];
	    foreach my $select (@$selects) {
		my $str = $at->getString($select, undef, 'PLAIN');
		push (@$variables, $str);
		$db->addTriple($at->getStatement($rvP, $root, $str), $a);
	    }

	    # Guarantee unique solutions.
	    for (my ($iRow, $uniques) = (0, {}); $iRow < @$nodes; $iRow++) {
		# The atomDictionary guarantees that nodes are normalized and
		# have no '|'s in them.
		my $key = join('|', @{$nodes->[$iRow]});
		if (exists $uniques->{$key}) {
		    # remove the row
		    splice(@$nodes, $iRow, 1);
		    $iRow--;
		} else {
		    $uniques->{$key} = $iRow;
		}
	    }

	    {
		my $str = $at->getString((scalar @$nodes), undef, 'PLAIN');
		$db->addTriple($at->getStatement($sizeP, $root, $str), $a);
	    }
	    foreach my $row (@$nodes) {
		my $soln = $at->createBNode($a);
		$db->addTriple($at->getStatement($solnP, $root, $soln), $a);
		$db->addTriple($at->getStatement($tP, $root, $rslnT), $a);

		for (my $iCol = 0; $iCol < @$row; $iCol++) {
		    my $binding = $at->createBNode($a);
		    $db->addTriple($at->getStatement($solnP, $soln, $binding), $a);
		    $db->addTriple($at->getStatement($tP, $binding, $rbT), $a);
		    $db->addTriple($at->getStatement($varP, $binding, $variables->[$iCol]), $a);
		    $db->addTriple($at->getStatement($valP, $binding, $row->[$iCol]), $a);
		}
	    }
	} elsif ($self->{-queryHandler}{-templateDB}) {
	    $db = $self->{-queryHandler}{-templateDB};
	} else {
	    foreach my $proof (@$proofs) {
		$db->copyTriples($proof);
		if (defined $limit) {
		    if (--$limit <= 0) {
			last;
		    }
		}
	    }
	}
	push (@ret, $self->showRdf($db, $reporter, $source, $nsMap));
    } else {
	if ($serializationParms->{-barebones}) {
	    foreach my $row (@$nodes) {
		my @line;
		foreach my $col (@$row) {
		    $_ = $col;
		    &renderNode;
		    push (@line, $_);
		}
		push (@ret, join (',', @line));
		if (defined $limit) {
		    if (--$limit <= 0) {
			last;
		    }
		}
	    }
	    push (@ret, undef); # newline at end
	} elsif ($serializationParms->{-resultSet}) {
	    push (@ret, $self->_simpleRSRenderer($nodes, $selects, $proofs));
	} else {
	    push (@ret, $self->arrayRenderer($resultSet, $selects, $limit, 
				  $serializationParms));
	}
    }
    return join("\n", @ret);
}

sub renderNode {
    $_ = !defined $_ ? '!unbound!' : 
	$_ == $Value_NULL ? 'NULL' : 
	ref $_ eq 'ARRAY' ? $_ : 
	$_->isa('W3C::Rdf::Uri') ? '<'.$_->getUri.'>' : 
	$_->isa('W3C::Rdf::String') ? $_->getDatatype eq $main::XSD_INT ? $_->getString : '"'.$_->getString.'"' : 
	$_->isa('W3C::Rdf::BNode') ? '_:g'.$_->getId.'' : 
	$_->isa('W3C::Rdf::Attribution') ? '['.$_->getUri->getUri.']' : 
	&throw(new W3C::Util::Exception(-message => "don't know how to serialize \"$_\""));
}

sub arrayRenderer {
    my ($self, $resultSet, $selects, $limit, $serializationParms) = @_;
    my ($rows, $proofs) = $resultSet->gatherCollect();

    my %flags = (%$serializationParms);
    my $nsHandler = delete $flags{-ns} ? 
	new W3C::Util::NamespaceReducer(-relay => 
	    $self->{-context}->getNamespaceHandler()) : undef;
    if ($nsHandler) {
	$flags{-namespaceHandler} = $nsHandler;
    }

    my $tr;
    if ($serializationParms->{-xhtml}) {
	require W3C::XML::HTMLTableRenderer;
	$tr = new W3C::XML::HTMLTableRenderer();
    } elsif (exists $serializationParms->{-rs}) {
	require W3C::Rdf::ResultSetRenderer;
	$tr = new W3C::Rdf::ResultSetRenderer(-namespaceHandler => $self->{-context}->getNamespaceHandler(), 
					      -sorted => $serializationParms->{-rs} eq 'sorted');
    } elsif ($serializationParms->{-x}) {
	require W3C::Rdf::SPARQLXmlRenderer;
	$tr = new W3C::Rdf::SPARQLXmlRenderer(-atomDictionary => $self->{-atomDictionary}, 
					      -createNamespaces => 1, 
					      -importMap => $self->{-context}->getNamespaceHandler());
    } else {
	require W3C::Util::TableRenderer;
	$tr = new W3C::Rdf::AlgaeTableRenderer($self->{-atomDictionary}, \%flags);
    }

    if (defined $resultSet->getSingletonValue) {
	$tr->addHeaders(['count']);
	$tr->addData($self->{-atomDictionary}->getString($resultSet->getSingletonValue, undef, 'PLAIN', undef));
    } elsif (defined $resultSet->getTruthValue) {
	#$tr->addHeaders(['truth']);
	$tr->setBoolean($resultSet->getTruthValue);
    } else {

	# Allocate an extra column for spillover in the proof strings.
	$tr->addHeaders($serializationParms->{-proofs} ? [@$selects, ''] : $selects);

	# Walk through each row and optionally add the proofString after it.
	for (my $row = 0; $row < @$rows; $row++) {
	    $tr->addData(@{$rows->[$row]});
	    if ($serializationParms->{-proofs}) {
		my $proofStr = join ("\n", map {$_->toString(%flags)} $proofs->[$row]->getTriples());
		$tr->addRow($proofStr);
		if ($row < @$rows-1) {
		    $tr->underline();
		}
	    }
	    if (defined $limit) {
		if (--$limit <= 0) {
		    last;
		}
	    }
	}
    }

    my $nsStr = $nsHandler ? $nsHandler->toString(%flags)."\n" : '';
    return $nsStr.$tr->toString."\n";
}

sub _simpleRSRenderer {
    my ($self, $rows, $selects, $nodes) = @_;
    my $nsh = new W3C::Util::NamespaceInventor(-relay => $self->{-context}->getNamespaceHandler());
    my @ret = ();
    push (@ret, "\@prefix rdf:    <$NS_rdf> .");
    push (@ret, "\@prefix rs:     <http://jena.hpl.hp.com/2003/03/result-set#> .");
    push (@ret, '');
    push (@ret, "[] rdf:type rs:ResultSet ;");
    for (my $iCol = 0; $iCol < @$selects; $iCol++) {
	push (@ret, "    rs:resultVariable \"$selects->[$iCol]\" ;");
    }
    my $rowCount = @$rows;
    my $punct = @$rows ? ';' : '.';
    push (@ret, "    rs:size \"$rowCount\" $punct");
    my $genIds = {};
    for (my $iRow = 0; $iRow < @$rows; $iRow++) {
	push (@ret, "    rs:solution");
	push (@ret, "        [ rdf:type rs:ResultSolution ;");
	for (my $iCol = 0; $iCol < @$selects; $iCol++) {
	    my $value = $rows->[$iRow][$iCol];
	    my $predicate = 'value';
	    if (UNIVERSAL::isa($value, 'W3C::Rdf::Uri')) {
		$value = '<'.$value->getUri.'>';
	    } elsif (UNIVERSAL::isa($value, 'W3C::Rdf::String')) {
		$value = '"'.$value->getString.'"';
	    } elsif (UNIVERSAL::isa($value, 'W3C::Rdf::BNode')) {
		my $t = $genIds->{$value};
		if (!$t) {
		    $genIds->{$value} = $t = keys (%{$genIds}) + 1;
		}
		$value = '_:'.$t;
	    } elsif ($value == $Value_NULL) {
		$predicate = 'nonValue';
		$value = "\"NULL\"";
	    } else {
		&throw(new W3C::Util::ProgramFlowException());
	    }
	    $punct = $iCol < @$selects-1 ? ';' : '';
	    push (@ret, "          rs:binding [ rdf:type rs:ResultBinding ;");
	    push (@ret, "                       rs:variable \"$selects->[$iCol]\" ; rs:$predicate $value ] $punct");
	}
	$punct = $iRow < @$rows-1 ? ';' : '.';
	push (@ret, "        ] $punct");
    }
    return @ret, ''; # make sure there's a blank line at the end
}

sub showRdf {
    my ($self, $db, $serializer, $resource, $nsMap) = @_;
    my $result;

    {
	my $aggregate = $self->{-context}->getNamespaceHandler();

	foreach my $qname (keys %{$self->{-queryHandler}{NAMESPACES}}) {
	    my $uri = $self->{-queryHandler}{NAMESPACES}{$qname};
	    $aggregate->addNamespace($qname, $uri, $resource);
	}

	foreach my $ns (keys %$nsMap) {
	    $aggregate->addNamespace($ns, $nsMap->{$ns}, $resource);
	}
    }

    my $iterator = $db->makeSerializerIterator(undef, $self->{-queryHandler}, 
					       -scheme => 'guess', 
					       -ignoreReifications => 1, 
					       -ignoreAnonymousReifications => 1);
    eval {
	$iterator->iterate($serializer);
	$result = $serializer->getText();
    }; if ($@) {
	if (my $ex = &catch('W3C::Util::Exception')) {
	    $result = $ex->toString;
	} else {
	    $result = "died with $@";
	}
    }
    return $result;
}

__END__

=head1 NAME

algae - command line script access to the Algae query engine.

=head1 SYNOPSIS

algae [options] [file ...]

=head1 OPTIONS

=over 8

=item B<-l|lang>

Language name of next query (eg. SPARQL, RDQL, Algae, SeRQL)

=item B<-i|input>

Language name of next data file (eg. rdfxml, n3)

=item B<-reportClass> class

Generate results in the form of B<class>.

=item B<-reportParms> ...

See examples.

=item B<uri-media-type> media/type:uri

Ex. application/rdf+xml:http://example.org/foaf.rdf
Associate the media type with the given URI. This is often needed to override the types that misconfigured servers report.

=item B<default-media-type> media/type

If the reported media type for some resource is not known, assume it has this media type.

=item B<-forceHost> host

Force relative URIs to use host B<host> rather than the current machine's hostname.

=item B<-forcePath> path

Force relative URIs to use the path B<host> rather than the current working directory.

=item B<--ignore-requires>

Do not tell Algae engine about requires directives.

=item B<--warn-unknown-variables>

Sending warnings about references to unknown variables to standard error.

=item B<--die-unknown-variables>

Abort on first reference to an unknown variable.

=item B<-o> file

Place output in file B<file>.

=item B<-d|read> file

Read data from file B<file>.

=item B<-g|readGraph> file

Read named graph data from file B<file>.

=item B<-f|query> file

Read query from file B<file>.

=item B<-m|manifest> file

Read and execute a test manifest.

=item B<-help>

Print a brief help message and exit.

=item B<-man>

Send the manual page to the $PAGER and exit.

=item B<file>

Parse B<file> and put resuls into the B<current database>.

=back

=head1 DESCRIPTION

B<algae> reads a series of queries in any of the B<supported languages>.

This module is part of the W3C::Rdf CPAN module.

=head1 AUTHOR

Eric Prud'hommeaux <eric@w3.org>

=head1 SEE ALSO

L<W3C::Rdf::Algae2>
L<W3C::Rdf::RdfDB>

=head1 COPYRIGHT

Copyright Massachusetts Institute of technology, 1998.

THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS
OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR
FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE
ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. 

COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. 

The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining 
to the software without specific, written prior permission. Title to copyright in this software and 
any associated documentation will at all times remain with copyright holders. 

=cut

