Index: trunk/cstr/etc/collect.cfg
===================================================================
--- trunk/cstr/etc/collect.cfg	(revision 891)
+++ trunk/cstr/etc/collect.cfg	(revision 891)
@@ -0,0 +1,37 @@
+creator       sjboddie@cs.waikato.ac.nz
+maintainer    sjboddie@cs.waikato.ac.nz
+public        true
+beta          true
+key           84138963
+indexes        document:text
+defaultindex  document:text
+#plugins       GMLPlug PrePlug ArcPlug RecPlug
+plugin		GMLPlug
+plugin		PrePlug
+plugin		ArcPlug
+plugin		RecPlug
+macrofiles    style.dm base.dm query.dm help.dm \
+              pref.dm about.dm home.dm document.dm \
+	      status.dm cstr.dm authen.dm english.dm \
+	      chinese.dm maori.dm
+classify      CSTRList
+
+
+format        QueryResults "<td valign=top>[link][icon][/link]</td><td valign=top>{If}{[parent(Top):Summary],[parent(Top):Summary],[Summary]}<br><b>Abstract:</b>{Or}{[parent(Top):Description],[Description], None}</td>"
+
+format        result "<td valign=top>[icon]</td><td valign=top>{If}{[parent(Top):Summary],[parent(Top):Summary],[Summary]}<br><b>Abstract:</b>{Or}{[parent(Top):Description],[Description], None}</td>"
+format        ListBrowse "<td valign=top>[link][icon][/link]</td><td>{If}{[classifytype],[Title],<a href=_httpcurrentdocument_&gg=postscript&gga=[cgisafe:Source]>[Source]</a>&#044; <i>[Date]</i><br>[Summary]}</td>"
+format        DocumentTopPages false
+format        DocumentButtons  "Detach|Highlight|Expand Text"
+
+
+collectionmeta collectionname  "Computer Science Technical Reports"
+collectionmeta collectionextra "Your source for up-to-date academic computer science publications!  This collection provides an index to\ncomputer science technical reports that we have downloaded from selected sites on the Internet.  The reports are mostly in English, but\nthere are some in a variety of different languages.  The collection contains many large documents such as PhD theses.  Every single word on\nevery page of every report is indexed -- not just the first few words, as in many other search engines -- so you can search for things that\noccur near the end of the document (like references) too.\n<p>This collection contains <b>_about:numdocs_</b> technical reports located in <b>296</b> sites around the world. The <b>_about:nummegbytes_ Mb</b> of text is extracted from <b>34 Gb</b> of PostScript by a <a\nhref=http://www.nzdl.org/cgi-bin/gw?c=cstr&a=page&p=Tec&z=x-D4xPMx>system developed here</a>.  All <b>_about:numwords_</b> words from <b>1.3 Million</b>\npages are indexed. The collection also contains <b>28846</b> figures extracted from the reports."
+
+collectionmeta iconcollection "http://www.nzdl.org/gsdl/collect/cstr/images/cstr.gif"
+collectionmeta iconcollectionsmall "http://www.nzdl.org/gsdl/collect/cstr/images/cstrsm.gif"
+
+collectionmeta .section:text  "document pages"
+collectionmeta .document:text "entire documents"
+receptionist   http://www.nzdl.org/cgi-bin/cstrlibrary?a=p&p=about&c=cstr
+
Index: trunk/cstr/macros/cstr.dm
===================================================================
--- trunk/cstr/macros/cstr.dm	(revision 891)
+++ trunk/cstr/macros/cstr.dm	(revision 891)
@@ -0,0 +1,94 @@
+package document
+
+_pagetitle_ [c=cstr] {_collectionname_}
+
+_javapostscript_ [c=cstr] {
+	ps\_on = new Image(_widthepsx_, _heightepsx_);
+	ps\_on.src = "_httpiconepson_";
+	ps\_off = new Image(_widthepsx_, _heightepsx_);
+	ps\_off.src = "_httpiconepsof_";
+}
+
+_javaviewtext_ [c=cstr] {
+        viewtext\_on = new Image(_widthetextx_, _heightetextx_); 
+        viewtext\_on.src = "_httpiconetexton_";
+        viewtext\_off = new Image(_widthetextx_, _heightetextx_); 
+        viewtext\_off.src = "_httpiconetextof_";
+}
+
+_javaabstract_ [c=cstr] {
+        abstract\_on = new Image(_widtheabstrx_, _heighteabstrx_); 
+        abstract\_on.src = "_httpiconeabstron_";
+        abstract\_off = new Image(_widtheabstrx_, _heighteabstrx_); 
+        abstract\_off.src = "_httpiconeabstrof_";
+}
+
+_javafigures_ [c=cstr] {
+        figures\_on = new Image(_widthefigx_, _heightefigx_); 
+        figures\_on.src = "_httpiconefigon_";
+        figures\_off = new Image(_widthefigx_, _heightefigx_); 
+        figures\_off.src = "_httpiconefigof_";
+}
+
+
+_iconpostscript_ [c=cstr] {<img name="ps" src="_httpiconepsof_" width=_widthepsx_ height=_heightepsx_ alt="_texticonpostscript_" border=0>}
+_iconpostscript_ [c=cstr,v=1] {_texticonpostscript_}
+
+_iconviewtext_ [c=cstr] {<img name="viewtext" src="_httpiconetextof_" width="_widthetextx_" height="_heightetextx_" alt="_texticonviewtext_" border="0">}
+_iconviewtext_ [c=cstr,v=1] {_texticonviewtext_}
+
+_iconabstract_ [c=cstr] {<img name="abstract" src="_httpiconeabstrof_" width="_widtheabstrx_" height="_heighteabstrx_" alt="_texticonabstract_" border="0">}
+_iconabstract_[c=cstr,v=1] {_texticonabstract_}
+
+_iconfigures_ [c=cstr] {<img name="figures" src="_httpiconefigof_" width="_widthefigx_" height="_heightefigx_" alt="_texticonfigures_" border="0">}
+_iconfigures_ [c=cstr,v=1] {_texticonfigures_}
+
+
+_imagepostscript_ [c=cstr] {<a href="_httpcurrentdocument_&gg=postscript&gga=_1_" onMouseover = "img\_on('ps')"
+onMouseout = "img\_off('ps')">_iconpostscript_</a>}
+_imagepostscript_ [c=cstr,v=1] {<a href="_httpcurrentdocument_&gg=postscript&gga=_1_">_texticonpostscript_</a><br>}
+
+_imageviewtext_ [c=cstr] {<a href="_httpcurrentdocument_&gg=text" onMouseover = "img\_on('viewtext')"
+onMouseout = "img\_off('viewtext')">_iconviewtext_</a>}
+_imageviewtext_ [c=cstr,v=1] {<a href="_httpcurrentdocument_&gg=text">_texticonviewtext_</a><br>}
+
+_imageabstract_ [c=cstr] {<a href="_httpcurrentdocument_&gg=abstract" onMouseover = "img\_on('abstract')"
+onMouseout = "img\_off('abstract')">_iconabstract_</a>}
+_imageabstract_ [c=cstr,v=1] {<a href="_httpcurrentdocument_&gg=abstract">_texticonabstract_</a><br>}
+
+_imagefigures_ [c=cstr] {<a href="_httpcurrentdocument_&gg=figures" onMouseover = "img\_on('figures')"
+onMouseout = "img\_off('figures')">_iconfigures_</a>}
+_imagefigures_ [c=cstr,v=1] {<a href="_httpcurrentdocument_&gg=figures">_texticonfigures_</a><br>}
+
+
+_httpiconepsof_ [c=cstr] {_httpimg_/epsof.gif}
+_httpiconepson_ [c=cstr] {_httpimg_/epson.gif}
+_widthepsx_ [c=cstr] {64}
+_heightepsx_ [c=cstr] {30}
+
+_httpiconetextof_ [c=cstr] {_httpimg_/etextof.gif}
+_httpiconetextof_ [c=cstr,l=mi] {_httpimg_/metextof.gif}
+_httpiconetexton_ [c=cstr] {_httpimg_/etexton.gif}
+_httpiconetexton_ [c=cstr,l=mi] {_httpimg_/metexton.gif}
+_widthetextx_ [c=cstr] {30}
+_widthetextx_ [c=cstr,l=mi] {31}
+_widthetextx_ [c=cstr] {30}
+_heightetextx_ [c=cstr] {30}
+
+_httpiconeabstrof_ [c=cstr] {_httpimg_/eabstrof.gif}
+_httpiconeabstron_ [c=cstr] {_httpimg_/eabstron.gif}
+_widtheabstrx_ [c=cstr] {59}
+_heighteabstrx_ [c=cstr] {30}
+
+_httpiconefigof_ [c=cstr] {_httpimg_/efigof.gif}
+_httpiconefigon_ [c=cstr] {_httpimg_/efigon.gif}
+_widthefigx_ [c=cstr] {47}
+_heightefigx_ [c=cstr] {30}
+
+
+_texticonpostscript_ [c=cstr] {Download postscript of this document}
+_texticonviewtext_ [c=cstr] {View the text of this page}
+_texticonabstract_ [c=cstr] {View the abstract of this document}
+_texticonfigures_ [c=cstr] {View figures extracted from document postscript}
+
+_texticonviewtext_ [c=cstr,l=mi] {Titiro ki ng&auml; kupu kua tuhia k&euml; ki te rorohiko}
Index: trunk/cstr/perllib/classify/CSTRList.pm
===================================================================
--- trunk/cstr/perllib/classify/CSTRList.pm	(revision 891)
+++ trunk/cstr/perllib/classify/CSTRList.pm	(revision 891)
@@ -0,0 +1,94 @@
+###########################################################################
+#
+# CSTRList.pm --
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 1999 New Zealand Digital Library Project
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+
+# hacked about version of List classifier for use with cstr collection. 
+# Creates list of Source's (ftp sites), each of which contains a list of
+# those documents originating from Source.
+
+# note that 'Source' metadata contains entire path to document so
+# we strip it down to get the ftp site.
+
+package CSTRList;
+
+use sorttools;
+
+sub new {
+    my ($class, @options) = @_;
+
+    my $listname = &util::filename_cat($ENV{'GSDLHOME'},"perllib/classify/List.pm"); 
+    if (-e $listname) { require $listname; }
+    else { die "CSTRList ERROR - couldn't find classifier \"$listname\"\n"; }
+
+    return bless {
+	'list'=>{},
+    }, $class;
+}
+
+sub init {
+    my $self = shift (@_);
+    $self->{'list'} = {};
+}
+
+sub classify {
+    my $self = shift (@_);
+    my ($doc_obj) = @_;
+
+    my $doc_OID = $doc_obj->get_OID();
+
+    my $source = $doc_obj->get_metadata_element ($doc_obj->get_top_section(), 'Source');
+
+    if (defined $source) {
+	my ($ftp_site) = $source =~ /^((ftp:\/\/)?([^\/\\]*))/;
+	print STDERR "CSTRList: Warning: ftp_site undefined\n" unless defined $ftp_site;
+	if (!defined ($self->{'list'}->{$ftp_site})) {
+	    my ($listclassobj);
+	    eval ("\$listclassobj = new List(\"sort=Date\", \"title=\$ftp_site\")");
+	    die "$@" if $@;
+	    $listclassobj->init();
+	    $self->{'list'}->{$ftp_site} = $listclassobj;
+	    $self->{'list'}->{$ftp_site}->classify ($doc_obj);
+	} else {
+	    $self->{'list'}->{$ftp_site}->classify ($doc_obj);
+	}	    
+    }
+}
+
+sub get_classify_info {
+    my $self = shift (@_);
+
+    # organise into classification structure
+    my %classifyinfo = ('classifytype'=>'List',
+			'Title'=>'Browse',
+			'contains'=>[]);
+    foreach $ftp_site (sort keys %{$self->{'list'}}) {
+	push (@{$classifyinfo{'contains'}}, 
+	      $self->{'list'}->{$ftp_site}->get_classify_info());
+    }
+    
+    return \%classifyinfo;
+}
+
+
+1;
Index: trunk/cstr/perllib/plugins/PrePlug.pm
===================================================================
--- trunk/cstr/perllib/plugins/PrePlug.pm	(revision 891)
+++ trunk/cstr/perllib/plugins/PrePlug.pm	(revision 891)
@@ -0,0 +1,281 @@
+###########################################################################
+#
+# PrePlug.pm --
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 1999 New Zealand Digital Library Project
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+
+# plugin that processes simple html files like those output 
+# by prescript when converting postscript to html. 
+# prescript output has <p> tags separating paragraphs
+# and <!--End Of Page--> tags separating pages.
+# it may also have <!--Page No--> tags at top or bottom of 
+# some or all pages.
+
+# if a .html1 version of a .html file exists then that version 
+# will be used so that the author name can be extracted
+# (.html1 files were generated by software which automatically 
+# extracts author names from .html files).
+
+# also looks for a .info file containing metadata (as used by 
+# the cstr collection)
+
+package PrePlug;
+
+use BasPlug;
+use sorttools;
+
+sub BEGIN {
+    @ISA = ('BasPlug');
+}
+
+sub new {
+    my ($class) = @_;
+    $self = new BasPlug ();
+
+    return bless $self, $class;
+}
+
+sub is_recursive {
+    my $self = shift (@_);
+
+    return 0; # this is not a recursive plugin
+}
+
+
+# return number of files processed, undef if can't process
+# Note that $base_dir might be "" and that $file might 
+# include directories
+sub read {
+    my $self = shift (@_);
+    my ($pluginfo, $base_dir, $file, $metadata, $processor) = @_;
+
+    my $filename = &util::filename_cat($base_dir, $file);
+
+    return 0 if ($filename =~ /\.(html1|html1\.gz|info|gif)/);
+
+    return undef unless ($filename =~ /\.(html(\.gz)?)$/i && (-e $filename));
+
+    my $gz = 0;
+    $gz = 1 if (defined $2 && $2 eq ".gz");
+
+    my $filename1 = $filename;
+    if ($gz) {
+	$filename1 =~ s/\.html\.gz$/\.html1\.gz/;
+    } else {
+	$filename1 .= "1";
+    }
+    
+    $filename = $filename1 if (-e $filename1);
+
+    print STDERR "PrePlug: processing $filename\n" if $processor->{'verbosity'};
+
+    # create a new document
+    my $doc_obj = new doc ($file, "indexed_doc");
+
+    if ($gz) {
+	open (FILE, "zcat $filename |") || die "PrePlug::read - zcat can't open $filename\n";
+    } else {
+	open (FILE, $filename) || die "PrePlug::read - can't open $filename\n";
+    }
+    my $cursection = $doc_obj->get_top_section();
+
+    my $text = "";
+    my $line = "";
+    my $pagenumber = 1;
+    my @creators = ();
+    my $numlines = 0;
+    while (defined ($line = <FILE>)) {
+	if ($filename =~ /html1$/) {
+	    while ($text =~ s/<_author_search_\([^\)]*\)>([^<]*)<\/a>/$1/i) {
+		push (@creators, $1);
+	    }
+	}
+
+	# numlines sorts out documents that just have a single <!-- End of Page--> tag at the end
+	if ($line =~ /<!--End\s+Of\s+Page-->/i && $numlines < 200) {
+
+	    $numlines = -1000;
+	    $cursection = 
+		$doc_obj->insert_section($doc_obj->get_end_child($doc_obj->get_top_section()));
+
+	    $text =~ s/<!--Page No-->\s*<p>\s*<center>\s*<b>\s*(\d+)<\/b>\s*<\/center>\s*//i;
+#	    if (defined $1 && $1 != $pagenumber) {
+#		print STDERR "PrePlug Warning: Pagenumbers don't line up in $filename\n";
+#	    }
+	    $doc_obj->add_text ($cursection, $text);
+	    $doc_obj->add_metadata ($cursection, "Title", $pagenumber);
+	    $pagenumber ++;
+	    $text = "";
+	} else {
+	    $text .= $line;
+	    $numlines ++; 
+	}
+    }
+    close FILE;
+
+    if ($cursection eq $doc_obj->get_top_section()) {
+	# there weren't any <!--End Of Page--> tags
+	
+	# see if there were any <!--Page No--> tags to split on
+	if ($text =~ /<!--Page No-->/i) {
+	    while ($text =~ s/^(.*?)<!--Page No-->\s*<p>\s*<center>\s*<b>\s*\d+<\/b>\s*<\/center>\s*//i) {
+		$cursection = 
+		    $doc_obj->insert_section($doc_obj->get_end_child($doc_obj->get_top_section()));
+		$doc_obj->add_text ($cursection, $text);
+		$doc_obj->add_metadata ($cursection, "Title", $pagenumber);
+		$pagenumber ++;
+	    }
+	}
+	# otherwise we'll just have to split pages on set number of lines
+	else {
+	    my $pagetext = "";
+	    my $line = "";
+	    my $count = 0;
+	    while (length ($text) && $text =~ s/^(.*?)(\n|$)//) {
+		$line = $1;
+		$line = "" unless defined $line;
+		$pagetext .= $line . "\n";
+		if (($count >= 50 && $line !~ /\w/) || $count == 80) {
+		    $cursection = 
+			$doc_obj->insert_section($doc_obj->get_end_child($doc_obj->get_top_section()));
+		    $doc_obj->add_text ($cursection, $pagetext);
+		    $doc_obj->add_metadata ($cursection, "Title", $pagenumber);
+		    $pagetext = "";
+		    $pagenumber ++;
+		    $count = 0;
+		} else {
+		    $count ++;
+		}
+	    }
+	}
+	    
+    }
+	    
+    # final section
+    if ($text =~ /\w/) {
+	$cursection = 
+	    $doc_obj->insert_section($doc_obj->get_end_child($doc_obj->get_top_section()));
+	
+	$text =~ s/<!--Page No-->\s*<p>\s*<center>\s*<b>\s*(\d+)<\/b>\s*<\/center>\s*//;
+#	if (defined $1 && $1 != $pagenumber) {
+#	    print STDERR "PrePlug Warning: Pagenumbers don't line up in $filename\n";
+#	}
+	$doc_obj->add_text ($cursection, $text);
+	$doc_obj->add_metadata ($cursection, "Title", $pagenumber);
+    }
+
+    # add meta data for top level of document
+    map {$doc_obj->add_metadata ($doc_obj->get_top_section(), "Creator", $_)} @creators;
+
+    my ($filesuff) = $filename =~ /^(.*?)\.html1?/;
+    if (!-e $filesuff . ".info") {
+	print STDERR "Preplug Warning: $filename has no corresponding .info file\n";
+    } else {
+	my ($dir) = $filesuff =~ /^(.*?)~?[^~]*$/;
+	open (INFO, $filesuff . ".info") || die "PrePlug::read - can't open $filesuff.info\n";
+	my $line = "";
+	while (defined ($line = <INFO>)) {
+	    chop $line;
+	    my ($key, $value) = $line =~ /^<([^>]*)>(.*)$/;
+	    next if $key =~ /^(pages|compressedsize|\/?info|size)$/i;
+	    next if $value !~ /\w/;
+	    if ($key =~ /^abstract$/i) {
+		$key = "Description";
+	    } elsif ($key =~ /^url$/i) {
+		$key = "Source";
+	    } elsif ($key =~ /^filedate$/i) {
+		$key = "Date";
+		my ($day, $month, $year) = split /\//, $value;
+		$value = &sorttools::format_date ($day, $month, $year);
+	    } elsif ($key =~ /^transferdate$/i) {
+		my ($day, $month, $year) = split /\//, $value;
+		$value = &sorttools::format_date ($day, $month, $year);
+	    } elsif ($key =~ /^facsimiles$/i) {
+		$value =~ s/^\s+//;
+		$value =~ s/\s+$//;
+		my @facsimiles = split /\s+/, $value;
+		$value = join ",", @facsimiles;
+		foreach $facsimile (@facsimiles) {
+		    # assume images are in the same directory
+		    my $imagefile = $dir . "~" . $facsimile;
+		    if (-e $imagefile) {
+			my ($imagetype) = $facsimile =~ /\.([^\.]*)$/;
+			$doc_obj->associate_file($imagefile, $facsimile, "image/" . $imagetype);
+		    } else {
+			print STDERR "PrePlug: Warning - facsimile file $imagefile doesn't exist\n";
+			next;
+		    }
+		}
+	    } elsif ($key =~ /^figures$/i) {
+		$value =~ s/^\s+//;
+		$value =~ s/\s+$//;
+		my @figures = split /\s+/, $value;
+		foreach $figure (@figures) {
+		    # assume images are in the same directory
+		    my $imagefile = $dir . "~" . $figure;
+		    if (-e $imagefile) {
+			my ($imagetype) = $figure =~ /\.([^\.]*)$/;
+			$doc_obj->associate_file($imagefile, $figure, "image/" . $imagetype);
+		    } else {
+			print STDERR "PrePlug: Warning - figure file $imagefile doesn't exist\n";
+			next;
+		    }
+		    $value = join ",", @figures;
+		}
+	    }
+	    $doc_obj->add_metadata ($doc_obj->get_top_section(), $key, $value);
+	}
+	close INFO;
+    }
+
+    foreach $field (keys(%$metadata)) {
+	# $metadata->{$field} may be an array reference
+	if (ref ($metadata->{$field}) eq "ARRAY") {
+	    map { 
+		$doc_obj->add_metadata ($doc_obj->get_top_section(), $field, $_); 
+	    } @{$metadata->{$field}};
+	} else {
+	    $doc_obj->add_metadata ($doc_obj->get_top_section(), $field, $metadata->{$field}); 
+	}
+    }
+
+    # add OID
+    $doc_obj->set_OID ();
+
+    # process the document
+    $processor->process($doc_obj);
+
+    return 1; # processed the file
+}
+
+1;
+
+
+
+
+
+
+
+
+
+
+
Index: trunk/cstr/src/recpt/Makefile.in
===================================================================
--- trunk/cstr/src/recpt/Makefile.in	(revision 891)
+++ trunk/cstr/src/recpt/Makefile.in	(revision 891)
@@ -0,0 +1,145 @@
+###########################################################################
+#
+# Makefile -- Makefile for cstrlibrary
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 1999 New Zealand Digital Library Project
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+
+GD = ../../../..
+MAINRECPTDIR = $(GD)/src/recpt
+MAINCOLSERVRDIR = $(GD)/src/colservr
+
+AR = ar
+CC = @CC@
+CCFLAGS = @CFLAGS@ -Wall -Wunused -pedantic -W -Woverloaded-virtual
+DEFS = -DNZDL -DQUIET -DSHORT_SUFFIX -DPARADOCNUM -DGSDLSERVER \
+       -D_LITTLE_ENDIAN -DGSDLSERVER @DEFS@
+
+USE_FASTCGI = @USE_FASTCGI@
+ifneq ($(USE_FASTCGI), 1)
+INCLUDES = -I$(MAINRECPTDIR) -I$(MAINCOLSERVRDIR) -I$(GD)/lib \
+	   -I$(GD)/packages/mg -I$(GD)/packages/mg/lib -I$(GD)/packages/mg/src/text 
+else
+INCLUDES = -I$(MAINRECPTDIR) -I$(MAINCOLSERVRDIR) -I$(GD)/lib \
+	   -I$(GD)/packages/mg -I$(GD)/packages/mg/lib -I$(GD)/packages/mg/src/text \
+           -I$(GD)/packages/fcgi/include
+endif
+
+RANLIB = @RANLIB@
+LDFLAGS = @LDFLAGS@
+LIBS = -lgdbm -lstdc++ -lm -lcrypt
+
+GSDLOS = @gsdlos@
+
+
+COMPILE = $(CC) -c $(CCFLAGS) $(DEFS) $(INCLUDES)
+LINK = $(CC) $(LDFLAGS) -o $@
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+.c.o:
+	$(COMPILE) $<
+
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(COMPILE) $< 
+
+
+HEADERS = cstrbrowsetools.h cstrdocaction.h cstrqueryaction.h \
+	  cstrquerytools.h
+
+SOURCES = cstrlibrarymain.cpp cstrbrowsetools.cpp cstrdocaction.cpp \
+	  cstrqueryaction.cpp cstrquerytools.cpp
+
+
+OBJECTS = cstrlibrarymain.o cstrbrowsetools.o cstrdocaction.o \
+	  cstrqueryaction.o cstrquerytools.o
+
+
+EXEC    = cstrlibrary
+
+all : $(EXEC)
+
+install: $(EXEC)
+	cp $(EXEC) /home/nzdl/nzdl-1.2/cgi-bin;
+#	strip /home/httpd/cgi-bin/$(EXEC)
+
+clean:
+	rm -f $(OBJECTS) $(EXEC)
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CCFLAGS) -- $(SOURCES)
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+CSTRLIBRARY_OBJS = $(OBJECTS) \
+    $(MAINRECPTDIR)/receptionist.o \
+    $(MAINRECPTDIR)/recptconfig.o \
+    $(MAINRECPTDIR)/cgiwrapper.o \
+    $(MAINRECPTDIR)/cgiargs.o \
+    $(MAINRECPTDIR)/action.o \
+    $(MAINRECPTDIR)/converter.o \
+    $(MAINRECPTDIR)/cgiutils.o \
+    $(MAINRECPTDIR)/OIDtools.o \
+    $(MAINRECPTDIR)/htmlgen.o \
+    $(MAINRECPTDIR)/statusaction.o \
+    $(MAINRECPTDIR)/pageaction.o \
+    $(MAINRECPTDIR)/comtypes.o \
+    $(MAINRECPTDIR)/recptproto.o \
+    $(MAINRECPTDIR)/pingaction.o \
+    $(MAINRECPTDIR)/queryaction.o \
+    $(MAINRECPTDIR)/documentaction.o \
+    $(MAINRECPTDIR)/htmlutils.o \
+    $(MAINRECPTDIR)/nullproto.o \
+    $(MAINRECPTDIR)/infodbclass.o \
+    $(MAINRECPTDIR)/formattools.o \
+    $(MAINRECPTDIR)/tipaction.o \
+    $(MAINRECPTDIR)/authenaction.o \
+    $(MAINRECPTDIR)/usersaction.o \
+    $(MAINRECPTDIR)/userdb.o \
+    $(MAINCOLSERVRDIR)/mgq.o \
+    $(MAINCOLSERVRDIR)/querycache.o \
+    $(MAINCOLSERVRDIR)/queryinfo.o \
+    $(MAINCOLSERVRDIR)/collectserver.o \
+    $(MAINCOLSERVRDIR)/colservrconfig.o \
+    $(MAINCOLSERVRDIR)/maptools.o \
+    $(MAINCOLSERVRDIR)/filter.o \
+    $(MAINCOLSERVRDIR)/source.o \
+    $(MAINCOLSERVRDIR)/browsefilter.o \
+    $(MAINCOLSERVRDIR)/queryfilter.o \
+    $(MAINCOLSERVRDIR)/phrasequeryfilter.o \
+    $(MAINCOLSERVRDIR)/phrasesearch.o \
+    $(MAINCOLSERVRDIR)/mggdbmsource.o \
+    $(MAINCOLSERVRDIR)/mgsearch.o \
+    $(GD)/lib/gsdllib.a  \
+    $(GD)/packages/mg/src/text/libtextin.a \
+    $(GD)/packages/mg/lib/libmg.a
+
+ifneq ($(USE_FASTCGI), 1)
+CSTRLIB_OBJS = $(CSTRLIBRARY_OBJS)
+else
+CSTRLIB_OBJS = $(CSTRLIBRARY_OBJS) $(GD)/packages/fcgi/libfcgi/libfcgi.a
+endif
+
+cstrlibrary: $(CSTRLIB_OBJS)
+	$(LINK) $(CSTRLIB_OBJS) $(LIBS) 
Index: trunk/cstr/src/recpt/cstrbrowsetools.cpp
===================================================================
--- trunk/cstr/src/recpt/cstrbrowsetools.cpp	(revision 891)
+++ trunk/cstr/src/recpt/cstrbrowsetools.cpp	(revision 891)
@@ -0,0 +1,847 @@
+/**********************************************************************
+ *
+ * cstrbrowsetools.cpp -- 
+ * Copyright (C) 1999  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ *
+ *********************************************************************/
+
+/*
+   $Log$
+   Revision 1.1  2000/02/01 22:32:37  sjboddie
+   Initial revision
+
+   Revision 1.1  1999/09/07 21:49:41  sjboddie
+   new cstr receptionist
+
+ */
+
+
+#include "cstrbrowsetools.h"
+#include "OIDtools.h"
+#include "cgiutils.h"
+
+// simply checks to see if formatstring begins with a <td> tag
+static bool is_table_content (const text_t &formatstring) {
+  text_t::const_iterator here = formatstring.begin();
+  text_t::const_iterator end = formatstring.end();
+  
+  while (here != end) {
+    if (*here != ' ') {
+      if (*here == '<') {
+	if ((*(here+1) == 't' || *(here+1) == 'T') &&
+	    (*(here+2) == 'd' || *(here+2) == 'D') &&
+	    (*(here+3) == '>' || *(here+3) == ' '))
+	  return true;
+      } else return false;
+    }
+    here ++;
+  }
+  return false;
+}
+
+static bool is_table_content (const format_t *formatlistptr) {
+
+  if (formatlistptr == NULL) return false;
+  
+  if (formatlistptr->command == comText)
+    return is_table_content (formatlistptr->text);
+    
+  return false;
+}
+
+
+// output_controls displays the detach, expand/contract contents,
+// expand/contract text and highlighting/no highlighting buttons
+static void output_controls (cgiargsclass &args, const text_tarray &ibuttons,
+			     recptproto */*collectproto*/, displayclass &disp, 
+			     outconvertclass &outconvert, ostream &textout, 
+			     ostream &/*logout*/) {
+
+  if (args["u"] != "1") {
+  
+    FilterResponse_t response;
+    text_tset metadata;
+    text_tarray buttons;
+
+    text_t &arg_gg = args["gg"];
+
+    text_tarray::const_iterator here = ibuttons.begin();
+    text_tarray::const_iterator end = ibuttons.end();
+
+    while (here != end) {
+    
+      if (*here == "Detach")
+	buttons.push_back ("_document:imagedetach_");
+      else if (*here == "Highlight" && arg_gg == "text") {
+	if (args["hl"] == "1") 
+	  buttons.push_back ("_document:imagenohighlight_");
+	else 
+	  buttons.push_back ("_document:imagehighlight_");
+	//      } else if (*here == "Expand Contents") {
+	//	if (args["gc"] == "1")
+	//	  buttons.push_back ("_document:imagecontracttoc_");
+	//	else 
+	//	  buttons.push_back ("_document:imageexpandtoc_");
+      } else if (*here == "Expand Text" && arg_gg == "text") {
+	if (args.getintarg("gt"))
+	  buttons.push_back ("_document:imagecontracttext_");
+	else 
+	  buttons.push_back ("_document:imageexpandtext_");
+      }
+      here ++;
+    }
+
+    here = buttons.begin();
+    end = buttons.end();
+    int count = 0;
+    while (here != end) {
+      if ((count != 0) && ((count % 3) == 0)) textout << "<br>\n";
+      textout << outconvert << disp << *here;
+      count ++;
+      here ++;
+    }
+  }
+}
+
+static void output_cstr_controls (cgiargsclass &args, const text_t &source, 
+				  const text_t &description, const text_t &figures, 
+				  displayclass &disp, outconvertclass &outconvert, 
+				  ostream &textout) {
+  text_tarray buttons;
+  text_t &arg_gg = args["gg"];
+  
+  if (!source.empty())
+    buttons.push_back ("_document:imagepostscript_(" + cgi_safe(source) + ")");
+
+  if ((!description.empty()) && (arg_gg != "abstract"))
+    buttons.push_back ("_document:imageabstract_");
+
+  if ((!figures.empty()) && (arg_gg != "figures"))
+    buttons.push_back ("_document:imagefigures_");
+
+  if (arg_gg != "text")
+    buttons.push_back ("_document:imageviewtext_");
+
+  textout << "<br>\n";
+
+  text_tarray::const_iterator here = buttons.begin();
+  text_tarray::const_iterator end = buttons.end();
+  int count = 0;
+  while (here != end) {
+    if ((count != 0) && ((count % 3) == 0)) textout << "<br>\n";
+    textout << outconvert << disp << *here;
+    count ++;
+    here ++;
+  }
+}
+
+/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+Functions for generating a document type tables of contents. These aren't really tables of
+contents at all, just a title, some navigation buttons and arrows and maybe a page ? of ?
+type thing. These should only be called for document level tocs (i.e. when the "d" argument 
+is set) as I don't think it makes sense to display top level classifications in this way.
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
+
+void output_document_toc (cgiargsclass &args, const formatinfo_t &formatinfo,
+			  recptproto *collectproto, displayclass &disp, 
+			  outconvertclass &outconvert, ostream &textout, 
+			  ostream &logout) {
+
+  text_t &arg_d = args["d"];
+  if (arg_d.empty()) return;
+
+  text_tset metadata;
+  FilterResponse_t response;
+  ResultDocInfo_t docinfo;
+  text_t booktop, facsimiles, figures, description, source, summary;
+  text_t &collection = args["c"];
+
+  metadata.insert ("Title");
+  if (get_info (arg_d, collection, metadata, false, collectproto, response, logout)) {
+
+    metadata.erase (metadata.begin(), metadata.end());
+    docinfo = response.docInfo[0];
+
+    textout 
+      << "\n<!-- Table of Contents produced by cstrbrowsetools::output_document_toc -->\n\n";
+
+    // get title and author of book
+    get_top (arg_d, booktop);
+    metadata.insert ("facsimiles");
+    metadata.insert ("figures");
+    metadata.insert ("Description");
+    metadata.insert ("Source");
+    metadata.insert ("Summary");
+    get_info (booktop, collection, metadata, false, collectproto, response, logout);
+    facsimiles = response.docInfo[0].metadata[0].values[0];
+    figures = response.docInfo[0].metadata[1].values[0];
+    description = response.docInfo[0].metadata[2].values[0];
+    source = response.docInfo[0].metadata[3].values[0];
+    summary = response.docInfo[0].metadata[4].values[0];
+
+    metadata.erase (metadata.begin(), metadata.end());
+  
+    textout << outconvert << disp 
+	    << "<p><center>\n"
+	    << "<table cellpadding=0 cellspacing=0 width=_pagewidth_>\n"
+	    << "<tr valign=top><td>\n";
+
+    // don't want arrows and page number if text expanded
+    if (formatinfo.DocumentArrowsBottom && !args.getintarg ("gt")) {
+    
+      // previous arrow
+      textout << outconvert << disp << "<table><tr valign=top>\n"
+	      << "<td align=left>_document:prevarrow_</td>\n";
+    
+      // page ? of ? text
+      textout << "<td align=center>\n";
+      
+      if (is_number (docinfo.metadata[0].values[0])) {
+	text_t doctop;
+	metadata.insert ("Title");
+	get_top (arg_d, doctop);
+	// get title of last sibling
+	if (get_info (doctop + ".lc", collection, metadata, false, collectproto, response, logout)) {
+	  if (is_number (response.docInfo[0].metadata[0].values[0])) {
+	    textout << outconvert << disp << "_document:page_" << docinfo.metadata[0].values[0]
+		    << "_document:of_" << response.docInfo[0].metadata[0].values[0];
+	  } else {
+	    textout << outconvert << docinfo.metadata[0].values[0];
+	  }
+	}
+      } else {
+	textout << outconvert << docinfo.metadata[0].values[0];
+      }
+  
+      // next arrow
+      textout << outconvert << disp << "</td>\n<td align=right>_document:nextarrow_</td>\n</table>\n";
+    }
+
+    // goto line ****** ????????????
+    //if (formatinfo.DocumentGoTo)
+    //  textout << outconvert << disp << "_document:gotoform_";
+
+    // control buttons
+    output_controls (args, formatinfo.DocumentButtons, collectproto, 
+		     disp, outconvert, textout, logout);
+    textout << "</td><td valign=top>\n";
+
+    if (!summary.empty())
+      textout << outconvert << summary << "\n";
+
+    output_cstr_controls (args, source, description, figures, 
+			  disp, outconvert, textout);
+
+    textout << "</td>\n";
+    
+    // facsimile image
+    // only want the first facsimile (some documents have two)
+    if (!facsimiles.empty()) {
+      text_t::const_iterator fbegin = facsimiles.begin();
+      facsimiles = substr (fbegin, findchar (fbegin, facsimiles.end(), ','));
+      textout << outconvert << disp << "<td valign=top><a href=\"_httpcurrentdocument_&gg=facsimiles\">"
+	      << "<img src=\"_httpcollection_/archives/_thisOID_/" 
+	      << facsimiles << "\" width=150 height=250></a>" << "</td>\n";
+    }
+
+
+    textout << "</td></tr></table></center>\n";
+    textout << "\n<!-- end of Table of Contents -->\n";
+
+  }
+}
+
+
+/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+Functions for generating a "Hierarchy" type table of contents. These can be used either
+at top classification level or at document level (the difference being that a cover
+image and control buttons may be displayed at document level).
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
+
+// prototypes
+
+static void output_contracted_hierarchy_toc (const text_t &classifytype, text_t &formatstring, 
+					     cgiargsclass &args, recptproto *collectproto, 
+					     displayclass &disp, outconvertclass &outconvert, 
+					     ostream &textout, ostream &logout);
+
+static void output_expanded_hierarchy_toc (cgiargsclass &args, recptproto *collectproto,
+					   displayclass &disp, outconvertclass &outconvert,
+					   ostream &textout, ostream &logout);
+
+static void output_parents_toc (cgiargsclass &args, const FilterResponse_t &parents,
+				int &tabcount, displayclass &disp,
+				format_t *formatlistptr, outconvertclass &outconvert, 
+				ostream &textout, ostream &logout);
+
+static void output_siblings_toc (const text_t &classifytype, cgiargsclass &args, 
+				 const FilterResponse_t &siblings, int &tabcount, 
+				 displayclass &disp, format_t *formatlistptr, 
+				 outconvertclass &outconvert, ostream &textout, 
+				 ostream &logout);
+
+static void output_stdlist (cgiargsclass &args, ResultDocInfo_tarray::iterator &here,
+			    ResultDocInfo_tarray::iterator &end, bool intable,
+			    displayclass &disp, format_t *formatlistptr, 
+			     outconvertclass &outconvert, ostream &textout);
+
+static void output_datelist (cgiargsclass &args, ResultDocInfo_tarray::iterator &here,
+			     ResultDocInfo_tarray::iterator &end, bool intable,
+			     displayclass &disp, format_t *formatlistptr, 
+			     outconvertclass &outconvert, ostream &textout);
+
+
+void output_standard_toc (const text_t &classifytype, const formatinfo_t &formatinfo,
+			  text_t &formatstring, cgiargsclass &args, 
+			  recptproto *collectproto, displayclass &disp, 
+			  outconvertclass &outconvert, ostream &textout, 
+			  ostream &logout) {
+
+  bool havecontrols = false;
+
+  textout << "\n<!-- Table of Contents produced by cstrbrowsetools::output_standard_toc -->\n\n";
+
+  // get the cover image (if there is one) and the control buttons
+  // if we're inside a book
+  if (!args["d"].empty()) {
+    textout << "<p><table cellpadding=0 cellspacing=0><tr>\n";
+    textout << "<td valign=top width=200>\n";
+    //    if (cover) output_cover_image (args, collectproto, disp, outconvert, textout, logout);
+    output_controls (args, formatinfo.DocumentButtons, collectproto, disp, outconvert, textout, logout);
+    textout << "</td><td valign=top>\n";
+    havecontrols = true;
+  }
+  
+  // get table of contents
+  textout << "<table>\n";
+
+  if (args.getintarg("gc"))
+    output_expanded_hierarchy_toc(args, collectproto, disp, outconvert, textout, logout);
+  else
+    output_contracted_hierarchy_toc(classifytype, formatstring, args, collectproto, 
+				    disp, outconvert, textout, logout);
+
+  textout << "</table>\n";
+
+  if (havecontrols) textout << "</td></tr></table>\n";
+
+  textout << "\n<!-- end of Table of Contents -->\n";
+}
+
+
+void output_contracted_hierarchy_toc (const text_t &classifytype, text_t &formatstring, 
+				      cgiargsclass &args, recptproto *collectproto, 
+				      displayclass &disp, outconvertclass &outconvert, 
+				      ostream &textout, ostream &logout) {
+
+  int tabcount = 0;
+  text_tarray parents;
+  text_tset metadata;
+  FilterResponse_t fsiblings, fparents;
+  bool getParents = false;
+
+  text_t &arg_d = args["d"];
+  text_t OID = arg_d;
+  if (OID.empty()) OID = args["cl"];
+  text_t &collection = args["c"];
+
+  // if format string is empty use the default
+  if (formatstring.empty()) 
+    formatstring = "<td valign=top nowrap>[link][icon][/link]</td><td>{Or}{[Title],Untitled}</td>";
+
+  format_t *formatlistptr = new format_t();
+  parse_formatstring (formatstring, formatlistptr, metadata, getParents);
+
+  metadata.insert ("Date");
+  metadata.insert ("hastxt");
+  metadata.insert ("haschildren");
+  metadata.insert ("doctype");
+  metadata.insert ("classifytype");
+
+  if (has_children (OID, collection, collectproto, logout)) {
+      get_parents_array (OID + ".fc", parents);
+
+      if (!get_children (OID, collection, metadata, getParents,
+			 collectproto, fsiblings, logout))
+	return;
+  } else {
+    get_parents_array (OID, parents);
+    if (!get_children (OID + ".pr", collection, metadata, 
+		       getParents, collectproto, fsiblings, logout))
+	return;
+  }
+  if (!get_info (parents, collection, metadata, false, collectproto, fparents, logout)) return;
+  
+  if (!fparents.docInfo.empty()) 
+    output_parents_toc(args, fparents, tabcount, disp, 
+		       formatlistptr, outconvert, textout, logout);
+  
+  if (!fsiblings.docInfo.empty()) 
+    output_siblings_toc (classifytype, args, fsiblings, tabcount, disp, 
+			 formatlistptr, outconvert, textout, logout);
+}
+void output_parents_toc (cgiargsclass &args, const FilterResponse_t &parents,
+			 int &tabcount, displayclass &disp,
+			 format_t *formatlistptr, outconvertclass &outconvert,
+			 ostream &textout, ostream &/*logout*/) {
+
+  text_t tab, icon;
+  text_t &arg_cl = args["cl"];
+  text_t &arg_d = args["d"];
+  int numcols = parents.docInfo.size() + 1;
+  
+  bool intable = is_table_content (formatlistptr);
+  
+  ResultDocInfo_tarray::iterator thisparent = parents.docInfo.begin();
+  ResultDocInfo_tarray::iterator end = parents.docInfo.end();
+
+  int len = (*thisparent).metadata.size();
+  const text_t &classifytype = (*thisparent).metadata[len-1].values[0];
+
+  // don't want top level of any classifications to be displayed
+  if (arg_d.empty() && thisparent != end) {thisparent ++; numcols --;}
+
+  // don't want second level of classifications using classification links
+  if ((classifytype == "AZList" || classifytype == "DateList") && (thisparent != end))
+    {thisparent ++; numcols --;}
+
+  // the tab line
+  if (thisparent != end) {
+    text_t coltabs;
+    for (int i = 0; i < numcols; i ++) coltabs += "_document:tab_";
+    textout << outconvert << disp << "<tr>" << coltabs << "<td></td></tr>\n";
+  }
+
+  while (thisparent != end) {
+
+    const text_t &doctype = (*thisparent).metadata[len-2].values.back();
+
+    // set up icon and pointer
+    icon = "_document:icon";
+    if ((doctype != "classify") && ((*thisparent).OID == arg_d))
+      icon += "arrow";
+    if (doctype == "classify") icon += "openbookshelf_";
+    else if (is_top((*thisparent).OID)) icon += "openbook_";
+    else icon += "openfolder_";
+    
+    if (tabcount == 1) textout << "<td></td>\n";
+    else if (tabcount > 1) textout << "<td colspan=" << tabcount << "></td>";
+    
+    // set up the link
+    text_t link = "<a href=\"_httpdocument_";
+    if (is_top((*thisparent).OID) && args.getintarg("x")) link = "<a name=top>";
+    else 
+      if (doctype == "classify") link += "&cl=" + (*thisparent).OID + ".pr\">";
+      else 
+	if (is_top ((*thisparent).OID)) 
+	  if (arg_cl.empty())
+	    link = "<a name=top>";
+	  else if (arg_cl == "search")
+	    link = "<a href=\"_httpquery_\">";
+	  else
+	    link += "&cl=" + arg_cl + "\">";
+	else link += "&cl=" + arg_cl + "&d=" + (*thisparent).OID + ".pr\">";
+    
+    if ((numcols-tabcount) == 1) textout << "<td>";
+    else if ((numcols-tabcount) > 1) textout << "<td colspan=" 
+					     << (numcols-tabcount) << ">";
+
+    if (intable) textout << "<table><tr>";
+    
+    textout << outconvert << disp 
+	    << get_formatted_string (*thisparent, formatlistptr, link, icon);
+    
+    if (intable) textout << "</tr></table>";
+    textout << "</td></tr>\n";
+
+    tabcount ++;
+    thisparent ++;
+  }
+}
+
+
+void output_siblings_toc (const text_t &classifytype, cgiargsclass &args, 
+			  const FilterResponse_t &siblings, int &tabcount, 
+			  displayclass &disp, format_t *formatlistptr, 
+			  outconvertclass &outconvert, ostream &textout, 
+			  ostream &/*logout*/) {
+
+  bool intable = is_table_content (formatlistptr); 
+
+  ResultDocInfo_tarray::const_iterator thissibling = siblings.docInfo.begin();
+  ResultDocInfo_tarray::const_iterator sibend = siblings.docInfo.end();
+
+  if (thissibling == sibend) return;
+
+  // tabbing
+  if (tabcount) {
+    if (tabcount == 1) textout << "<tr><td></td>\n";
+    else if (tabcount > 1) textout << "<tr><td colspan=" << tabcount << "></td>";
+    textout << "<td><table>\n";
+  }
+
+  if (!intable) textout << "<tr><td>\n";
+  
+  if (classifytype == "DateList")
+    output_datelist (args, thissibling, sibend, intable, disp, 
+		     formatlistptr, outconvert, textout);
+  else 
+    output_stdlist (args, thissibling, sibend, intable, disp, 
+		    formatlistptr, outconvert, textout);
+
+  if (!intable) textout << "</td></tr></table>\n";
+  if (tabcount) textout << "</table></td></tr>\n";
+}
+
+void output_stdlist (cgiargsclass &args, ResultDocInfo_tarray::iterator &here,
+		     ResultDocInfo_tarray::iterator &end, bool intable,
+		     displayclass &disp, format_t *formatlistptr, 
+		     outconvertclass &outconvert, ostream &textout) {
+
+  text_t icon;
+  int count = 1;
+  int gt = args.getintarg("gt");
+  text_t &arg_cl = args["cl"];
+  text_t &arg_d = args["d"];
+
+  while (here != end) {
+
+    int len = (*here).metadata.size();
+    const text_t &doctype = (*here).metadata[len-2].values.back();
+    const text_t &hastxt = (*here).metadata[len-4].values.back(); 
+    const text_t &haschildren = (*here).metadata[len-3].values.back();
+
+    // set up icon and pointer
+    icon = "_document:icon";
+    if (doctype == "classify") {
+      if (((*here).OID == arg_cl) && (hastxt == "1"))
+    	icon += "arrow";
+    } else if ((*here).OID == arg_d) icon += "arrow";
+
+    if (haschildren == "0") icon += "smalltext_";
+    else if (doctype == "classify") icon += "closedbookshelf_";
+    else if (is_top((*here).OID)) icon += "closedbook_";
+    else icon += "closedfolder_";
+    
+    // set up link
+    text_t link = "<a href=\"_httpdocument_";
+    if (doctype == "classify") link += "&cl=" + (*here).OID + "\">";
+    else link += "&cl=" + arg_cl + "&d=" + (*here).OID + "\">";
+    if (gt) {
+      link = "<a href=\"#" + text_t(count) + "\">";
+      count ++;
+    }
+
+    if (intable) textout << "<tr>";
+    
+    textout << outconvert << disp 
+	    << get_formatted_string (*here, formatlistptr, link, icon) << "\n";
+    
+    if (intable) textout << "</tr>";
+
+    here ++;
+  }
+}
+
+
+void output_datelist (cgiargsclass &args, ResultDocInfo_tarray::iterator &here,
+		      ResultDocInfo_tarray::iterator &end, bool intable,
+		      displayclass &disp, format_t *formatlistptr, 
+		      outconvertclass &outconvert, ostream &textout) {
+
+  text_t lastyear = "0000";
+  text_t lastmonth = "00";
+
+  while (here != end) {
+
+    int len = (*here).metadata.size();
+    const text_t &doctype = (*here).metadata[len-2].values.back();
+    const text_t &date = (*here).metadata[len-5].values.back(); 
+
+    // bail on this document if it has no date
+    if (date.empty()) continue;
+
+    text_t::const_iterator datebegin = date.begin();
+
+    text_t link = "<a href=\"_httpdocument_&cl=";
+    text_t icon = "_document:iconclosedbook_";
+      
+    if (doctype == "classify") {
+      icon = "_document:iconclosedbookshelf_";
+      link += (*here).OID + "\">";
+    } else link += args["cl"] + "&d=" + (*here).OID + "\">";
+
+    text_t thisyear = substr (datebegin, datebegin+4);
+    text_t thismonth = substr (datebegin+4, datebegin+6); 
+
+    textout << "<tr>\n";
+
+    if (thisyear != lastyear) {
+      textout << outconvert << "<td><b>" << thisyear << "</b></td>";
+      lastyear = thisyear;
+    } else
+      textout << "<td></td>";
+
+    if (thismonth != lastmonth) {
+      textout << outconvert << disp << ("<td><b>_textmonth" + thismonth + "_</b></td>");
+      lastmonth = thismonth;
+    } else
+      textout << "<td></td>";
+
+    if (!intable) textout << "<td>\n";
+    
+    textout << outconvert << disp 
+	      << get_formatted_string (*here, formatlistptr, link, icon) << "\n";
+
+    if (!intable) textout << "</td>";
+      
+    textout << "</tr>\n";
+
+    here ++;
+  }
+}
+
+void output_expanded_hierarchy_toc (cgiargsclass &args, recptproto *collectproto,
+				    displayclass &disp, outconvertclass &outconvert,
+				    ostream &textout, ostream &logout) {
+
+  text_t OID, topOID, icon;
+  bool is_classify;
+  FilterResponse_t response;
+  int tabcols=0, totalcols=0, lasttabcols=0;
+
+  int gt = args.getintarg("gt");
+  text_t doclink = "<a href=\"_httpdocument_&cl=";
+
+  text_t &arg_d = args["d"];
+  text_t &arg_cl = args["cl"];
+
+  if (arg_d.empty()) {
+    if (arg_cl.empty()) return;
+    OID = arg_cl;
+    topOID = OID; // don't always want to expand from top if expanding classifications
+    is_classify=true;
+  } else {
+    OID = arg_d;
+    get_top (arg_d, topOID);
+    is_classify=false;
+  }
+
+  // Get OIDs and metadata of all topOIDs contents (and topOID itself)
+  text_tset metadata;
+  metadata.insert ("Title");
+  metadata.insert ("haschildren");
+  metadata.insert ("doctype");
+  //  metadata.insert ("hastxt");
+
+  get_contents (topOID, is_classify, metadata, totalcols, 
+		args["c"], collectproto, response, logout);
+  int excess = countchar (topOID.begin(), topOID.end(), '.');
+  totalcols -= excess;
+
+  // allow for pointer
+  if (!is_classify && !gt) totalcols += 2;
+  else totalcols += 1;
+
+  ResultDocInfo_tarray::iterator thissection = response.docInfo.begin();
+  ResultDocInfo_tarray::iterator lastsection = response.docInfo.end();
+  
+  int count = 1;
+  while (thissection != lastsection) {
+    
+    const text_t &title = (*thissection).metadata[0].values[0];
+    const int haschildren = (*thissection).metadata[1].values[0].getint();
+    const text_t &doctype = (*thissection).metadata[2].values[0];
+
+    text_t icontabs, tab, pointer;
+
+    // set up icon
+    icon = "_document:iconsmalltext_";
+    if (is_top((*thissection).OID))
+      if (classifytype == "Document") icon = "_document:iconopenbook_";
+      else 
+	if (doctype == "classify") icon = "_document:iconopenbookshelf_";
+	else icon = "_document:iconclosedbook_";
+    else if (haschildren) 
+      if (classifytype == "Document") icon = "_document:iconopenfolder_";
+      else icon = "_document:iconopenbookshelf_";
+    
+    // set up tabbing
+    if ((classifytype == "classify") && (doctype != "classify"))
+      tabcols = lasttabcols + 1;
+    else {
+      tabcols = countchar ((*thissection).OID.begin(), (*thissection).OID.end(), '.');
+      lasttabcols = tabcols;
+    }
+    tabcols -= excess;
+
+    for (int i = 0; i < tabcols; i++) 
+      icontabs += "_document:icontab_";
+    
+    // set up pointer
+    if (classifytype == "Document" && !gt) {
+      if ((*thissection).OID == OID) pointer = "_document:iconpointer_";
+      else pointer = "_document:icontab_";
+      tabcols ++;
+    }
+    
+    int colsremaining = totalcols - tabcols;
+
+    if (tabcols > 0) {
+      tab = "<td";
+      if (tabcols > 1) tab += " colspan=" + text_t(tabcols);
+      tab += ">" + icontabs + pointer + "</td>";
+    }
+
+    textout << outconvert << disp << "<tr>" << tab << "<td>";
+    
+    if ((classifytype == "Document") && (is_top((*thissection).OID)) && 
+	(args.getintarg("x")))
+      textout << outconvert << disp << icon << "</td><td";
+
+    else {
+      if (!gt) {
+	const text_t &thisOID = (*thissection).OID;
+	text_t link;
+	if (is_top (thisOID))
+	  if (classifytype == "classify")
+	    link = doclink + arg_cl + "&d=" + thisOID + "\">";
+	  else
+	    if (arg_cl.empty())
+	      link.clear();
+	    else if (arg_cl == "search")
+	      link = "<a href=\"_httpquery_\">";
+	    else
+	      link = doclink + arg_cl + "\">";
+	else
+	  if (haschildren)
+	    if (classifytype == "classify")
+	      link = doclink + thisOID + ".pr\">";
+	    else
+	      link = doclink + arg_cl + "&d=" + thisOID + ".pr\">";
+	  else
+	    if (classifytype == "classify")
+	      link = doclink + thisOID + "\">";
+	    else
+	      link = doclink + arg_cl + "&d=" + thisOID + "\">";
+	
+	textout << outconvert << disp << link;
+      } else {
+	textout << "<a href=\"#" << count << "\">";
+	count ++;
+      }
+    
+      textout << outconvert << disp << icon << "</a></td><td";
+    }
+    if (colsremaining > 1) textout << " colspan=" << colsremaining;
+    textout << outconvert << disp << ">" << title << "</td></tr>\n";
+
+    thissection ++;
+  }
+}
+
+/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
+
+// set_arrow_macros sets the _httpprevarrow_ and _httpnextarrow_ macros
+// it shouldn't be called if OID is the top level of a book 
+// as the "hasprevious" and "hasnext" metadata won't be set
+// when the filter can't get OIDs parent info - one day I'll
+// fix this ;-)
+void set_arrow_macros (const text_t &OID, const text_t &classifytype,
+		       bool showtoppage, displayclass &disp, 
+		       recptproto *collectproto, const text_t &collection, 
+		       ostream &logout) {
+
+  if (OID.empty()) return;
+
+  text_tset metadata;
+  FilterResponse_t response;
+
+  metadata.insert ("haschildren");
+  metadata.insert ("hasnext");
+  metadata.insert ("hasprevious");
+  // get "haschildren", "hasnext" and "hasprevious" metadata for OID
+  if (get_info (OID, collection, metadata, false, collectproto, response, logout)) {
+
+    text_t haschildren = response.docInfo[0].metadata[0].values[0];
+    text_t hasnext = response.docInfo[0].metadata[1].values[0];
+    text_t hasprevious = response.docInfo[0].metadata[2].values[0];
+
+    if ((classifytype == "Hierarchy") || (classifytype == "Book")) {
+      if (haschildren == "1") 
+	disp.setmacro ("httpnextarrow", "document", 
+		       "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.fc,_httpdocument_&cl=_cgiargcl_.fc)");
+      else if (hasnext == "1")
+	disp.setmacro ("httpnextarrow", "document", 
+		       "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.ns,_httpdocument_&cl=_cgiargcl_.ns)");
+      else {
+	// see if parent has younger siblings
+	if (get_info (OID + ".pr", collection, metadata, false, collectproto, response, logout)) {
+	  if (!response.docInfo[0].metadata.empty() &&
+	      response.docInfo[0].metadata[1].values[0] == "1")
+	    disp.setmacro ("httpnextarrow", "document", 
+			   "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.pr.ns,_httpdocument_&cl=_cgiargcl_.pr.ns)");
+	}
+      }
+
+      if (hasprevious == "1") {
+	// see if OIDs older sibling has children
+	if (get_info (OID + ".ps", collection, metadata, false, collectproto, response, logout)) {
+	  if (response.docInfo[0].metadata[0].values[0] == "1")
+	    disp.setmacro ("httpprevarrow", "document", 
+			   "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.ps.lc,_httpdocument_&cl=_cgiargcl_.ps.lc)");
+	  else 
+	    disp.setmacro ("httpprevarrow", "document", 
+			   "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.ps,_httpdocument_&cl=_cgiargcl_.ps)");
+	} 
+      } else if (showtoppage && !is_top (OID))
+	disp.setmacro ("httpprevarrow", "document", 
+		       "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.pr,_httpdocument_&cl=_cgiargcl_.pr)");
+    }
+
+    else {
+      if (hasnext == "1")
+	disp.setmacro ("httpnextarrow", "document", 
+		       "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.ns,_httpdocument_&cl=_cgiargcl_.ns)");
+      if (hasprevious == "1")
+	disp.setmacro ("httpprevarrow", "document", 
+		       "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.ps,_httpdocument_&cl=_cgiargcl_.ps)");
+    }
+  }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: trunk/cstr/src/recpt/cstrbrowsetools.h
===================================================================
--- trunk/cstr/src/recpt/cstrbrowsetools.h	(revision 891)
+++ trunk/cstr/src/recpt/cstrbrowsetools.h	(revision 891)
@@ -0,0 +1,59 @@
+/**********************************************************************
+ *
+ * cstrbrowsetools.h -- 
+ * Copyright (C) 1999  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ *
+ *********************************************************************/
+
+
+#ifndef CSTRBROWSETOOLS_H
+#define CSTRBROWSETOOLS_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "cgiargs.h"
+#include "display.h"
+#include "recptproto.h"
+#include "formattools.h"
+
+
+void output_document_toc (cgiargsclass &args, const formatinfo_t &formatinfo,
+			  recptproto *collectproto, displayclass &disp, 
+			  outconvertclass &outconvert, ostream &textout, 
+			  ostream &logout);
+
+void output_standard_toc (const text_t &classifytype, const formatinfo_t &formatinfo,
+			  text_t &formatstring, cgiargsclass &args, 
+			  recptproto *collectproto, displayclass &disp, 
+			  outconvertclass &outconvert, ostream &textout, 
+			  ostream &logout);
+
+void set_arrow_macros (const text_t &OID, const text_t &classifytype,
+		       bool showtoppage, displayclass &disp, 
+		       recptproto *collectproto, const text_t &collection, 
+		       ostream &logout);
+
+#endif
+
+
+
Index: trunk/cstr/src/recpt/cstrdocaction.cpp
===================================================================
--- trunk/cstr/src/recpt/cstrdocaction.cpp	(revision 891)
+++ trunk/cstr/src/recpt/cstrdocaction.cpp	(revision 891)
@@ -0,0 +1,439 @@
+/**********************************************************************
+ *
+ * cstrdocaction.cpp -- 
+ * Copyright (C) 1999  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ *
+ *********************************************************************/
+
+/*
+   $Log$
+   Revision 1.1  2000/02/01 22:32:37  sjboddie
+   Initial revision
+
+   Revision 1.1  1999/09/07 21:49:42  sjboddie
+   new cstr receptionist
+
+ */
+
+
+#include <string.h>
+#include "cstrdocaction.h"
+#include "cstrbrowsetools.h"
+#include "OIDtools.h"
+#include "querytools.h"
+#include "unitool.h"
+
+cstrdocaction::cstrdocaction () {
+  // this action uses cgi variable "gg" and "gga" as well as
+  // those used by the standard documentaction
+
+  cgiarginfo arg_ainfo;
+
+  // in this action "gg" is the format of the
+  // document to view (i.e. "text", "postscript", "abstract", "figures", or "facsimiles")
+  arg_ainfo.shortname = "gg";
+  arg_ainfo.longname = "document format";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "text";
+  arg_ainfo.savedarginfo = cgiarginfo::can;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // in this action "gga" is the path to the postscript
+  // when "gg" == "postscript
+  arg_ainfo.shortname = "gga";
+  arg_ainfo.longname = "postscript path";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::none;
+  arg_ainfo.argdefault = "";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+}
+
+cstrdocaction::~cstrdocaction () {
+}
+
+
+void cstrdocaction::get_cgihead_info (cgiargsclass &args, recptprotolistclass * /*protos*/,
+				      response_t &response,text_t &response_data, 
+				      ostream &/*logout*/) {
+
+  if (args["gg"] == "postscript") {
+    response = location;
+    response_data = args["gga"];
+  } else {
+    response = content;
+    response_data = "text/html";
+  }
+}
+
+
+void cstrdocaction::set_java_macros (cgiargsclass &args, displayclass &disp) {
+
+  text_t javaimagescontent = "_javaextras__javapostscript_";
+
+  text_t &arg_gg = args["gg"];
+  if (arg_gg == "text")
+    javaimagescontent += "_javaabstract__javafigures_";
+  else if (arg_gg == "abstract")
+    javaimagescontent += "_javafigures__javaviewtext_";
+  else if (arg_gg == "figures")
+    javaimagescontent += "_javaabstract__javaviewtext_";
+  else if (arg_gg == "facsimiles")
+    javaimagescontent += "_javaabstract__javaviewtext__javafigures_";
+	
+  int arg_gt = args.getintarg("gt");
+  //  int arg_gc = args.getintarg("gc");
+  int arg_hl = args.getintarg("hl");
+
+  text_tarray::const_iterator button_here = formatinfo.DocumentButtons.begin();
+  text_tarray::const_iterator button_end = formatinfo.DocumentButtons.end();
+
+  while (button_here != button_end) {
+    if (*button_here == "Detach") 
+      javaimagescontent += "_javadetach_";
+    else if (*button_here == "Expand Text" && arg_gg == "text") {
+      if (arg_gt == 1)
+	javaimagescontent += "_javacontracttext__javacontinue_";
+      else if (arg_gt == 2)
+	javaimagescontent += "_javacontracttext_";
+      else
+	javaimagescontent += "_javaexpandtext_";
+      //    } else if (*button_here == "Expand Contents") {
+      //      if (arg_gc == 1)
+      //	javaimagescontent += "_javacontractcontents_";
+      //      else
+      //	javaimagescontent += "_javaexpandcontents_";
+    } else if (*button_here == "Highlight" && arg_gg == "text") {
+      if (arg_hl == 1)
+	javaimagescontent += "_javanohighlighting_";
+      else 
+	javaimagescontent += "_javahighlighting_";
+    }
+    button_here ++;
+  }
+  disp.setmacro ("javaimagescontent", "document", javaimagescontent);
+}
+
+
+
+/*
+// define all the macros which are related to pages generated
+// by this action. we also load up the formatinfo structure 
+// here (it's used in do_action as well as here)
+void cstrdocaction::define_internal_macros (const ColInfoResponse_t &collectinfo, displayclass &disp, 
+					    cgiargsclass &args, recptproto *collectproto, 
+					    ostream &logout) {
+  
+  // define_internal_macros sets the following macros:
+
+  // _pagetitle_            the title to be displayed at the top of the browser window
+  
+  // _imagethispage_        the title image to be displayed at top right of page
+  
+  // _classificationlinks_  the links between classifications to be displayed
+  //                        at top of list or datelist classifications
+
+  // _httpprevarrow_        these are set if next or previous arrows
+  // _httpnextarrow_        are to be used - (i.e. if it's an AZList or a DateList 
+  //                        classifytype at a top level or if it's a Book or a
+  //                        Hierarchy classifytype at document level 
+
+  // _navarrows_            this may be overridden to "" when format option 
+  //                        DocumentArrowsBottom is false
+
+  // _header_               the header macro is overridden if we're not at a top level 
+  //                        classification to remove the title block
+
+  // _javaimagescontent_    this is the javascript code to shove in to make the 
+  //                        flashy buttons work
+
+  // _thisOID_              the OID (directory) of the current document - this corresponds
+  //                        to the archivedir metadata element
+
+  // can't do anything if collectproto is null (i.e. no collection was specified)
+  if (collectproto == NULL) return;
+  
+  text_tarray metadata;
+  FilterResponse_t response;
+  text_t &arg_d = args["d"];
+  text_t &arg_cl = args["cl"];
+  text_t &collection = args["c"];
+  int gt = args.getintarg("gt");
+  load_formatinfo (collectinfo.format, gt);
+
+  // don't want arrows or goto if we're in figures or facsimiles
+  if (args["gg"] != "text") {
+    formatinfo.DocumentArrowsBottom = false;
+    formatinfo.DocumentArrowsTop = false;
+    formatinfo.DocumentGoTo = false;
+  }
+
+  if (!formatinfo.DocumentArrowsBottom)
+    disp.setmacro("navarrows", "document", "");
+
+  metadata.push_back ("Title");
+  metadata.push_back ("classifytype");
+  metadata.push_back ("archivedir");
+  
+  if (!arg_d.empty()) {
+    // we're at document level
+    
+    // get metadata for this document and it's parents
+    if (get_info (arg_d, collection, metadata, true, collectproto, response, logout)) {
+      disp.setmacro ("header", "document", "_textheader_");
+
+      disp.setmacro ("thisOID", "Global", response.docInfo[0].metadata[2].values[0]);
+
+      text_t &classifytype = response.docInfo[0].metadata[1].values[0];
+      if (classifytype.empty()) classifytype = "Book"; // defaults to Book
+
+      // set arrow macros if required
+      if (((classifytype == "Book") && (formatinfo.DocumentArrowsTop || formatinfo.DocumentArrowsBottom)) ||
+	  ((classifytype == "Hierarchy") && formatinfo.DocumentArrowsBottom))
+	set_arrow_macros (arg_d, classifytype, formatinfo.DocumentTopPages, 
+			  disp, collectproto, collection, logout);
+      
+      if (args["u"] != "1")
+	set_java_macros (args, disp);
+
+    }
+  } else {
+    if (!arg_cl.empty()) {
+	
+      // get metadata for top level classification
+      text_t classtop;
+      get_top (arg_cl, classtop);
+      if (get_info (classtop, collection, metadata, false, collectproto, response, logout)) {
+	  
+	text_t &title = response.docInfo[0].metadata[0].values[0];
+	text_t &classifytype = response.docInfo[0].metadata[1].values[0];
+
+	disp.setmacro ("pagetitle", "document", "_text" + title + "page_");
+	disp.setmacro ("imagethispage", "document", "_icon" + title + "page_");
+
+	// now get the metadata for each child of top level
+	// so we can generate the _classificationlinks_ and arrow macros
+	// (if they're required by the current classification type)
+	
+	if ((classifytype == "AZList") || (classifytype == "DateList"))
+	  get_classificationlinks (arg_cl, collection, collectproto, disp, logout);
+      
+      }
+    }
+  }
+}
+
+*/
+
+void cstrdocaction::output_document (const text_t &OID, cgiargsclass &args, 
+				     recptproto *collectproto, displayclass &disp, 
+				     outconvertclass &outconvert, ostream &textout, 
+				     ostream &logout) {
+  FilterResponse_t inforesponse;
+  text_tset metadata;
+  bool getParents = false;
+  text_t &collection = args["c"];
+  text_t &arg_gg = args["gg"];
+
+  if (arg_gg == "abstract") {
+    metadata.insert ("Description");
+    text_t top;
+    get_top (OID, top);
+    if (get_info (top, collection, metadata, getParents, collectproto, inforesponse, logout)) {
+      text_t &description = inforesponse.docInfo[0].metadata["Description"].values[0];
+
+      if (!description.empty())
+	textout << outconvert << disp << "<p>\n" << description << "\n";
+      else
+	textout << outconvert << disp << "_textnoabstract_";
+    }
+  } else if (arg_gg == "figures") {
+    metadata.insert ("figures");
+    text_t top;
+    get_top (OID, top);
+    if (get_info (top, collection, metadata, false, collectproto, inforesponse, logout)) {
+      text_t &figures = inforesponse.docInfo[0].metadata["figures"].values[0];
+
+      if (!figures.empty()) {
+	text_tarray figure_array;
+	splitchar (figures.begin(), figures.end(), ',', figure_array);
+	text_tarray::const_iterator this_figure = figure_array.begin();
+	text_tarray::const_iterator end_figure = figure_array.end();
+	while (this_figure != end_figure) {
+	  textout << outconvert << disp 
+		  << "<p><center><img border=1 src=\"_httpcollection_/archives/_thisOID_/" 
+		  << *this_figure << "\"></center>\n";
+	  this_figure ++;
+	}
+      } else {
+	textout << outconvert << disp << "_textnofigures_";
+      }
+    }
+  } else if (arg_gg == "facsimiles") {
+    metadata.insert ("facsimiles");
+    text_t top;
+    get_top (OID, top);
+    if (get_info (top, collection, metadata, false, collectproto, inforesponse, logout)) {
+      text_t &facsimiles = inforesponse.docInfo[0].metadata["facsimiles"].values[0];
+      
+      if (!facsimiles.empty()) {
+	text_tarray facs_array;
+	splitchar (facsimiles.begin(), facsimiles.end(), ',', facs_array);
+	text_tarray::const_iterator this_facs = facs_array.begin();
+	text_tarray::const_iterator end_facs = facs_array.end();
+	while (this_facs != end_facs) {
+	  textout << outconvert << disp 
+		    << "<p><center><img border=1 src=\"_httpcollection_/archives/_thisOID_/" 
+		  << *this_facs << "\"></center>\n";
+	  this_facs ++;
+	}
+      } else {
+	textout << outconvert << disp << "_textnofacsimiles_";
+      }
+    }      
+    
+  } else {
+
+    FilterResponse_t queryresponse;
+    bool highlight = false;
+    int wanttext = 0;
+    int arg_gt = args.getintarg("gt");
+
+    // if we have a query string and highlighting is turned on we need
+    // to redo the query to get the terms for highlighting
+    if (!args["q"].empty() && args.getintarg("hl")) {
+      FilterRequest_t request;
+      request.filterResultOptions = FRmatchTerms;
+      comerror_t err;
+      text_t formattedstring = args["q"];
+      format_querystring(formattedstring, args.getintarg("b"));
+      set_queryfilter_options (request, formattedstring, args);
+      collectproto->filter (args["c"], request, queryresponse, err, logout);
+      if (err != noError) {
+	outconvertclass text_t2ascii;
+	logout << text_t2ascii
+	       << "documentaction::output_document: call to QueryFilter failed "
+	       << "for " << args["c"] << " collection (" << get_comerror_string (err) << ")\n";
+	highlight = false;
+      } else {
+	highlight = true;
+      }   
+
+    }
+    
+    format_t *formatlistptr = new format_t();
+    parse_formatstring (formatinfo.DocumentText, formatlistptr, metadata, getParents);
+    
+    metadata.insert ("hastxt");
+    metadata.insert ("haschildren");
+    
+    if (formatinfo.DocumentText == "[Text]")
+      wanttext = 1;
+    else {
+      char *docformat = formatinfo.DocumentText.getcstr();
+      if (strstr (docformat, "[Text]") != NULL)
+	wanttext = 2;
+      delete docformat;
+    }
+    
+    int metasize = metadata.size();
+    
+    if (get_info (OID, collection, metadata, getParents, collectproto, inforesponse, logout)) {
+      int hastxt = inforesponse.docInfo[0].metadata["hastxt"].values[0].getint();
+      int haschildren = inforesponse.docInfo[0].metadata["haschildren"].values[0].getint();
+      
+      if (arg_gt == 0) {
+	output_text (inforesponse.docInfo[0], formatlistptr, queryresponse.termInfo, 
+		     OID, highlight, hastxt, wanttext, collection, collectproto, 
+		     disp, outconvert, textout, logout);
+	
+	
+      } else {
+	
+	ResultDocInfo_t thisdocinfo = inforesponse.docInfo[0];
+	
+	// text is to be expanded
+	text_t exOID = OID;
+	if (haschildren != 1) exOID = get_parent (OID);
+	if (exOID.empty()) exOID = OID;
+	
+	// if we're not in a document (i.e. we're in a top level classification)
+	// we need to pass the "classify" string to get_contents so that it 
+	// doesn't recurse all the way through each document in the classification
+	bool is_classify = false;
+	if (args["d"].empty()) is_classify = true;
+
+	get_contents (exOID, is_classify, metadata, collection, 
+		      collectproto, inforesponse, logout);
+	
+	ResultDocInfo_tarray::iterator sechere = inforesponse.docInfo.begin();
+	ResultDocInfo_tarray::iterator secend = inforesponse.docInfo.end();
+	
+	if (arg_gt == 1) {
+	  // check if there are more than 10 sections containing text to be expanded - 
+	  // if there are output warning message - this isn't a great way to do this
+	  // since the sections may be very large or very small - one day I'll fix it 
+	  // -- Stefan.
+	  int seccount = 0;
+	  while (sechere != secend) {
+	    int shastxt = (*sechere).metadata["hastxt"].values[0].getint();
+	    if (shastxt == 1) seccount ++;
+	    if (seccount > 10) break;
+	    sechere ++;
+	  }
+	  if (seccount > 10) {
+	    // more than 10 sections so output warning message and text 
+	    // for current section only
+	    textout << outconvert << disp << "_document:textltwarning_";
+
+	    output_text (thisdocinfo, formatlistptr, queryresponse.termInfo, 
+			 OID, highlight, hastxt, wanttext, collection, 
+			 collectproto, disp, outconvert, textout, logout);
+	  
+	  }
+	  else arg_gt = 2;
+	}
+	
+	if (arg_gt == 2) {
+	  // get the text for each section
+	  sechere = inforesponse.docInfo.begin();
+	  int count = 0;
+	  while (sechere != secend) {
+	    textout << outconvert << disp << "\n<p><a name=" << count << "></a>\n";
+
+	    int shastxt = (*sechere).metadata["hastxt"].values[0].getint();
+
+	    output_text (*sechere, formatlistptr, queryresponse.termInfo, 
+			 (*sechere).OID, highlight, shastxt, wanttext, collection, 
+			 collectproto, disp, outconvert, textout, logout);
+	    count ++;
+	    sechere ++;
+	  }
+	}
+      }
+    }
+    delete formatlistptr;
+  }
+}
Index: trunk/cstr/src/recpt/cstrdocaction.h
===================================================================
--- trunk/cstr/src/recpt/cstrdocaction.h	(revision 891)
+++ trunk/cstr/src/recpt/cstrdocaction.h	(revision 891)
@@ -0,0 +1,64 @@
+/**********************************************************************
+ *
+ * cstrdocaction.h -- 
+ * Copyright (C) 1999  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ *
+ *********************************************************************/
+
+
+#ifndef CSTRDOCACTION_H
+#define CSTRDOCACTION_H
+
+#include "documentaction.h"
+
+class cstrdocaction : public documentaction {
+
+protected:
+
+  void set_java_macros (cgiargsclass &args, displayclass &disp);
+
+  void output_document (const text_t &OID, cgiargsclass &args, 
+			recptproto *collectproto, displayclass &disp, 
+			outconvertclass &outconvert, ostream &textout, 
+			ostream &logout);
+
+public:
+  cstrdocaction ();
+  virtual ~cstrdocaction ();
+
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response,text_t &response_data, 
+			 ostream &logout);
+
+    //  void define_internal_macros (const ColInfoResponse_t &collectinfo, displayclass &disp, 
+    //			       cgiargsclass &args, recptproto *collectproto, 
+    //			       ostream &logout);
+
+};
+
+
+
+#endif
+
+
Index: trunk/cstr/src/recpt/cstrlibrarymain.cpp
===================================================================
--- trunk/cstr/src/recpt/cstrlibrarymain.cpp	(revision 891)
+++ trunk/cstr/src/recpt/cstrlibrarymain.cpp	(revision 891)
@@ -0,0 +1,183 @@
+/**********************************************************************
+ *
+ * cstrlibrarymain.cpp -- 
+ * Copyright (C) 1999  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ *
+ *********************************************************************/
+
+/*
+   $Log$
+   Revision 1.1  2000/02/01 22:32:37  sjboddie
+   Initial revision
+
+   Revision 1.1  1999/09/07 21:49:43  sjboddie
+   new cstr receptionist
+
+ */
+
+#include "receptionist.h"
+#include "cgiwrapper.h"
+#include "nullproto.h"
+#include "collectserver.h"
+#include "filter.h"
+#include "browsefilter.h"
+#include "queryfilter.h"
+#include "infodbclass.h"
+#include "mgsearch.h"
+#include "mggdbmsource.h"
+#include "fileutil.h"
+#include <assert.h>
+
+#include "action.h"
+#include "statusaction.h"
+#include "pageaction.h"
+#include "pingaction.h"
+#include "queryaction.h"
+#include "cstrdocaction.h"
+#include "authenaction.h"
+#include "usersaction.h"
+#include "authenaction.h"
+
+#include "browserclass.h"
+#include "vlistbrowserclass.h"
+#include "hlistbrowserclass.h"
+#include "datelistbrowserclass.h"
+#include "invbrowserclass.h"
+#include "pagedbrowserclass.h"
+#include "htmlbrowserclass.h"
+
+#include "gsdlhome.h"
+
+
+int main () {
+  receptionist recpt;
+  nullproto nproto;
+  
+  // add a collection server
+  text_tarray collections;
+  collections.push_back ("cstr");
+
+  text_tarray::const_iterator thiscol = collections.begin();
+  text_tarray::const_iterator endcol = collections.end();
+
+  while (thiscol != endcol) {
+
+    // this memory is created but never destroyed
+    // we're also not doing any error checking to make sure we didn't
+    // run out of memory
+    collectserver *cserver = new collectserver();
+    gdbmclass *gdbmhandler = new gdbmclass();
+    mgsearchclass *mgsearch = new mgsearchclass();
+    
+    // add a null filter
+    filterclass *filter = new filterclass ();
+    cserver->add_filter (filter);
+    
+    // add a browse filter
+    browsefilterclass *browsefilter = new browsefilterclass();
+    browsefilter->set_gdbmptr (gdbmhandler);
+    cserver->add_filter (browsefilter);
+    
+    // add a query filter
+    queryfilterclass *queryfilter = new queryfilterclass();
+    queryfilter->set_gdbmptr (gdbmhandler);
+    queryfilter->set_mgsearchptr (mgsearch);
+    cserver->add_filter (queryfilter);
+
+    // add a mg and gdbm source
+    mggdbmsourceclass *mggdbmsource = new mggdbmsourceclass ();
+    mggdbmsource->set_gdbmptr (gdbmhandler);
+    mggdbmsource->set_mgsearchptr (mgsearch);
+    cserver->add_source (mggdbmsource);
+    
+    // inform collection server and everything it contains about its
+    // collection name
+    cserver->configure ("collection", *thiscol);
+    
+    nproto.add_collectserver (cserver);
+    thiscol ++;
+  }
+  
+  // add the protocol to the receptionist
+  recpt.add_protocol (&nproto);
+
+  // add other converters
+  utf8inconvertclass utf8inconvert;
+  utf8outconvertclass utf8outconvert;
+  recpt.add_converter ("u", &utf8inconvert, &utf8outconvert);
+
+  mapinconvertclass gbinconvert;
+  gbinconvert.setmapfile (GSDL_GSDLHOME, "gbku", 0x25a1);
+  mapoutconvertclass gboutconvert;
+  gboutconvert.setmapfile (GSDL_GSDLHOME, "ugbk", 0xa1f5);
+  recpt.add_converter ("g", &gbinconvert, &gboutconvert);
+
+  // the list of actions. Note: these actions will become invalid
+  // at the end of this function.
+  statusaction astatusaction;
+  astatusaction.set_receptionist (&recpt);
+  recpt.add_action (&astatusaction);
+
+  pageaction apageaction;
+  recpt.add_action (&apageaction);
+
+  pingaction apingaction;
+  recpt.add_action (&apingaction);
+
+  queryaction aqueryaction;
+  recpt.add_action (&aqueryaction);
+
+  cstrdocaction adocumentaction;
+  recpt.add_action (&adocumentaction);
+
+  usersaction ausersaction;
+  recpt.add_action (&ausersaction);
+
+  authenaction aauthenaction;
+  aauthenaction.set_receptionist(&recpt);
+  recpt.add_action (&aauthenaction);
+
+  // list of browsers
+  vlistbrowserclass avlistbrowserclass;
+  recpt.add_browser (&avlistbrowserclass);
+  recpt.setdefaultbrowser ("VList");
+
+  hlistbrowserclass ahlistbrowserclass;
+  recpt.add_browser (&ahlistbrowserclass);
+
+  datelistbrowserclass adatelistbrowserclass;
+  recpt.add_browser (&adatelistbrowserclass);
+
+  invbrowserclass ainvbrowserclass;
+  recpt.add_browser (&ainvbrowserclass);
+
+  pagedbrowserclass apagedbrowserclass;
+  recpt.add_browser (&apagedbrowserclass);
+
+  htmlbrowserclass ahtmlbrowserclass;
+  recpt.add_browser (&ahtmlbrowserclass);
+
+  cgiwrapper (recpt, "cstr");
+  return 0;
+}
+
Index: trunk/cstr/src/recpt/cstrqueryaction.cpp
===================================================================
--- trunk/cstr/src/recpt/cstrqueryaction.cpp	(revision 891)
+++ trunk/cstr/src/recpt/cstrqueryaction.cpp	(revision 891)
@@ -0,0 +1,192 @@
+/**********************************************************************
+ *
+ * cstrqueryaction.cpp -- 
+ * Copyright (C) 1999  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ *
+ *********************************************************************/
+
+/*
+   $Log$
+   Revision 1.1  2000/02/01 22:32:37  sjboddie
+   Initial revision
+
+   Revision 1.1  1999/09/07 21:49:43  sjboddie
+   new cstr receptionist
+
+
+ */
+
+
+#include "cstrqueryaction.h"
+#include "cstrquerytools.h"
+#include "formattools.h"
+#include "OIDtools.h"
+#include "text_t.h"
+
+
+struct ltint 
+{
+  bool operator()(const int &t1, const int &t2) const
+  { return t1 < t2; }
+};
+
+
+bool cstrqueryaction::do_action (cgiargsclass &args, const ColInfoResponse_t &collectinfo, 
+				 recptproto *collectproto, displayclass &disp, 
+				 outconvertclass &outconvert, ostream &textout, 
+				 ostream &logout) {
+
+  if (formatstring.empty()) {
+    text_tmap::const_iterator result = collectinfo.format.find("result");
+    if (result != collectinfo.format.end())
+      formatstring = (*result).second;
+  }
+
+  // if we still don't have a format string use the default
+  if (formatstring.empty()) 
+    formatstring = "<td valign=top nowrap>[link]_icontext_[/link]</td><td>[Title]</td>";
+
+  if (collectproto == NULL) {
+    logout << "cstrqueryaction::do_action called with NULL collectproto\n";
+    textout << outconvert << disp << "_query:header_\n" 
+	    << "Error: Attempt to do query without setting collection\n"
+	    << "_query:footer_\n";
+  } else {  
+
+    FilterRequest_t request;
+    FilterResponse_t response;
+    format_t *formatlistptr = new format_t();
+
+    parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
+
+    request.fields.push_back ("Title"); 
+    int metasize = request.fields.size();
+
+    // do the query
+    request.filterResultOptions = FROID | FRmetadata | FRtermFreq;
+    if (!do_query (request, args, collectproto, response, logout))
+      return false;
+    
+    // set macros 
+    define_query_macros (args, disp, response);
+    
+    // output the header
+    textout << outconvert << disp << "_query:header_\n"
+	    << "_query:content_";
+
+    // output the results
+    textout << "<table cellspacing=4>\n";
+    ResultDocInfo_tarray::const_iterator this_doc = response.docInfo.begin();
+    ResultDocInfo_tarray::const_iterator end_doc = response.docInfo.end();
+
+    text_t top;
+    map<text_t, int, lttext_t> topOIDs;
+    map<int, ResultDocInfo_tarray, ltint> OIDs;
+    int count = 0;
+
+    // sort the result set to get document pages together
+    while (this_doc != end_doc) {
+      // don't include docs that didn't match phrases (if there were any)
+      // those that did match will have been sorted to the top
+      if ((*this_doc).num_phrase_match < num_phrases) break;
+
+      get_top ((*this_doc).OID, top);
+      map<text_t, int, lttext_t>::const_iterator it = topOIDs.find (top);
+      if (it == topOIDs.end()) {
+	// haven't seen this document before
+	topOIDs[top] = count;
+	OIDs[count].push_back (*this_doc);
+	count ++;
+      } else {
+	OIDs[(*it).second].push_back (*this_doc);
+      }
+      this_doc ++;
+    }
+
+    text_t link, icon;
+    map<int, ResultDocInfo_tarray, ltint>::iterator here = OIDs.begin();
+    map<int, ResultDocInfo_tarray, ltint>::iterator end = OIDs.end();
+    count = 1;
+    bool done = false;
+    int arg_r = args.getintarg("r");
+    int arg_o = args.getintarg("o");
+    int arg_m = args.getintarg("m");
+    while (here != end) {
+      ResultDocInfo_tarray::const_iterator thisOID = (*here).second.begin();
+      ResultDocInfo_tarray::const_iterator lastOID = (*here).second.end();
+      
+      icon.clear();
+      link.clear(); // link will always be empty as we're including the link in the icon
+      bool first = true;
+
+      while (thisOID != lastOID) {
+
+	if (count < arg_r) {thisOID ++; count ++; continue;}
+	if ((count >= (arg_o + arg_r)) || count > arg_m) {
+	  done = true; 
+	  if (!icon.empty()) {
+	    textout << outconvert << disp << "<tr>\n"
+		    << get_formatted_string (*thisOID, formatlistptr, link, icon) << "\n"
+		    << "</tr>\n";
+	  }
+	  break;
+
+	} else {
+
+	  const text_t &title = (*thisOID).metadata[metasize-1].values.back();
+	  
+	  if (title.empty() && ((*here).second.size() == 1)) 
+	    icon = "<a href=\"_httpdocument_&cl=search&d=" + (*thisOID).OID + "\">_iconclosedbook_</a>";
+	  else {
+	    if (first) icon += "_icontext_<br>";
+	    icon += "<a href=\"_httpdocument_&cl=search&d=" + (*thisOID).OID + "\">page " +
+	      title + "</a><br>\n";
+	  }
+	  
+	  if ((thisOID + 1) == lastOID) {
+	    textout << outconvert << disp << "<tr>\n"
+		    << get_formatted_string (*thisOID, formatlistptr, link, icon) << "\n"
+		    << "</tr>\n";
+	  }
+	  count ++;
+	}
+	if (done) break;
+	first = false;
+	thisOID ++;
+      }
+      here ++;
+    }
+
+    textout << "</table>\n";
+
+    delete (formatlistptr);
+
+    // output the footer
+    textout << outconvert << disp << "_query:footer_";
+  }
+  
+  return true;
+}
+
+
+
Index: trunk/cstr/src/recpt/cstrqueryaction.h
===================================================================
--- trunk/cstr/src/recpt/cstrqueryaction.h	(revision 891)
+++ trunk/cstr/src/recpt/cstrqueryaction.h	(revision 891)
@@ -0,0 +1,49 @@
+/**********************************************************************
+ *
+ * cstrqueryaction.h -- 
+ * Copyright (C) 1999  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ *
+ *********************************************************************/
+
+
+#ifndef CSTRQUERYACTION_H
+#define CSTRQUERYACTION_H
+
+#include "gsdlconf.h"
+#include "queryaction.h"
+
+
+class cstrqueryaction : public queryaction {
+
+public:
+  cstrqueryaction () {}
+  virtual ~cstrqueryaction () {}
+  
+  bool do_action (cgiargsclass &args, const ColInfoResponse_t &collectinfo, 
+		  recptproto *collectproto, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+};
+
+
+#endif
Index: trunk/cstr/src/recpt/cstrquerytools.cpp
===================================================================
--- trunk/cstr/src/recpt/cstrquerytools.cpp	(revision 891)
+++ trunk/cstr/src/recpt/cstrquerytools.cpp	(revision 891)
@@ -0,0 +1,230 @@
+/**********************************************************************
+ *
+ * cstrquerytools.cpp -- 
+ * Copyright (C) 1999  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ *
+ *********************************************************************/
+
+/*
+   $Log$
+   Revision 1.1  2000/02/01 22:32:37  sjboddie
+   Initial revision
+
+   Revision 1.1  1999/09/07 21:49:44  sjboddie
+   new cstr receptionist
+
+
+ */
+
+
+// this is overridden for cstr as we're always going to get
+// maxdocs matches. They can then be sorted in the 
+// cstrqueryaction
+
+#include "cstrquerytools.h"
+
+
+// do_query sets the filter options and makes the protocol call to
+// do a query. The results are returned in response. 
+// request.filterResultOptions and request.fields (if required)
+// should be set by the calling function.
+
+bool do_query (FilterRequest_t &request, cgiargsclass &args, 
+	       recptproto *collectproto, FilterResponse_t &response, 
+	       ostream &logout) {
+
+  request.filterName = "QueryFilter";
+
+  comerror_t err;
+  OptionValue_t option;
+  text_t formattedstring = args["q"];
+  format_querystring (formattedstring, args.getintarg("b"));  
+  
+  option.name = "Term";
+  option.value = formattedstring;
+  request.filterOptions.push_back (option);
+
+  option.name = "QueryType";
+  option.value = (args.getintarg("t")) ? "ranked" : "boolean";
+  request.filterOptions.push_back (option);
+
+  option.name = "Casefold";
+  option.value = (args.getintarg("k")) ? "true" : "false";
+  request.filterOptions.push_back (option);
+
+  option.name = "Stem";
+  option.value = (args.getintarg("s")) ? "true" : "false";
+  request.filterOptions.push_back (option);
+
+  if (!args["h"].empty()) {
+    option.name = "Index";
+    option.value = args["h"];
+    request.filterOptions.push_back (option);
+  }
+
+  if (!args["j"].empty()) {
+    option.name = "Subcollection";
+    option.value = args["j"];
+    request.filterOptions.push_back (option);
+  }
+
+  if (!args["n"].empty()) {
+    option.name = "Language";
+    option.value = args["n"];
+    request.filterOptions.push_back (option);
+  }
+
+  // fill in the second query if needed
+  if (!args["cq2"].empty()) {
+    option.name = "CombineQuery";
+    option.value = args["cq2"];
+    request.filterOptions.push_back (option);
+    
+    text_t formattedstring2 = args["q2"];
+    format_querystring (formattedstring2, args.getintarg("b"));
+
+    option.name = "Term";
+    option.value = formattedstring2;
+    request.filterOptions.push_back (option);
+
+    option.name = "QueryType";
+    option.value = (args.getintarg("t")) ? "ranked" : "boolean";
+    request.filterOptions.push_back (option);
+
+    option.name = "Casefold";
+    option.value = (args.getintarg("k")) ? "true" : "false";
+    request.filterOptions.push_back (option);
+
+    option.name = "Stem";
+    option.value = (args.getintarg("s")) ? "true" : "false";
+    request.filterOptions.push_back (option);
+
+    if (!args["h2"].empty()) {
+      option.name = "Index";
+      option.value = args["h2"];
+      request.filterOptions.push_back (option);
+    }
+
+    if (!args["j2"].empty()) {
+      option.name = "Subcollection";
+      option.value = args["j2"];
+      request.filterOptions.push_back (option);
+    }
+
+    if (!args["n2"].empty()) {
+      option.name = "Language";
+      option.value = args["n2"];
+      request.filterOptions.push_back (option);
+    }
+  }
+
+  // we always want to retrieve all documents (i.e. 1 to maxdocs)
+  // for this collection
+  option.name = "Maxdocs";
+  option.value = args["m"];
+  request.filterOptions.push_back (option);
+  
+  option.name = "StartResults";
+  option.value = 1;
+  request.filterOptions.push_back (option);
+
+  option.name = "EndResults";
+  option.value = args["m"];
+  request.filterOptions.push_back (option);
+
+  collectproto->filter (args["c"], request, response, err, logout);
+
+  if (err != noError) {
+    outconvertclass text_t2ascii;
+    logout << text_t2ascii
+	   << "Error: call to QueryFilter failed in queryaction ("
+	   << get_comerror_string (err) << ")\n";
+    return false;
+  }
+  return true;
+}
+
+void format_querystring (text_t &querystring, int querymode) {
+  text_t formattedstring;
+
+  text_t::const_iterator here = querystring.begin();
+  text_t::const_iterator end = querystring.end();
+
+  // space is used to insert spaces between Chinese
+  // characters. No space is needed before the first
+  // Chinese character.
+  bool space = false;
+
+  // want to remove ()|!& from querystring so boolean queries are just
+  // "all the words" queries (unless querymode is advanced)
+  while (here != end) {
+    if ((querymode == 0) && (*here == '(' || *here == ')' || *here == '|' ||
+			     *here == '!' || *here == '&')) {
+      formattedstring.push_back(' ');
+    } else {
+      if ((*here >= 0x4e00 && *here <= 0x9fa5) ||
+	  (*here >= 0xf900 && *here <= 0xfa2d)) {
+	// Chinese character
+	if (space) formattedstring.push_back (0x200b);
+	formattedstring.push_back (*here);
+	formattedstring.push_back (0x200b);
+	space = true;
+      } else {
+	// non-Chinese character
+	formattedstring.push_back (*here);
+	space = false;
+      }
+    }
+    here ++;
+  }
+  querystring = formattedstring;
+}
+
+void get_phrases (const text_t &querystring, text_tarray &phrases) {
+
+  phrases.erase (phrases.begin(), phrases.end());
+  if (!querystring.empty()) {
+    
+    text_t::const_iterator end = querystring.end();
+    text_t::const_iterator here = findchar (querystring.begin(), end, '"');
+    if (here != end) {
+      text_t tmptext;
+      bool foundquote = false;
+      while (here != end) {
+	if (*here == '"') {
+	  if (foundquote) {
+	    if (!tmptext.empty()) {
+	      phrases.push_back(tmptext);
+	      tmptext.clear();
+	    }
+	    foundquote = false;
+	  } else foundquote = true;
+	} else {
+	  if (foundquote) tmptext.push_back (*here);
+	}
+	here ++;
+      }
+    }
+  }
+}
+
Index: trunk/cstr/src/recpt/cstrquerytools.h
===================================================================
--- trunk/cstr/src/recpt/cstrquerytools.h	(revision 891)
+++ trunk/cstr/src/recpt/cstrquerytools.h	(revision 891)
@@ -0,0 +1,41 @@
+/**********************************************************************
+ *
+ * cstrquerytools.h -- 
+ * Copyright (C) 1999  The New Zealand Digital Library Project
+ *
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ *
+ *********************************************************************/
+
+
+#ifndef CSTRQUERYTOOLS_H
+#define CSTRQUERYTOOLS_H
+
+#include "cgiargs.h"
+#include "recptproto.h"
+
+bool do_query (FilterRequest_t &request, cgiargsclass &args, 
+	       recptproto *collectproto, FilterResponse_t &response, 
+	       ostream &logout);
+void format_querystring (text_t &querystring, int querymode);
+void get_phrases (const text_t &querystring, text_tarray &phrases);
+
+#endif
